Статьи

Шесть функций Java, чтобы держаться подальше от

Я потратил бесчисленные часы на поиск и устранение неисправностей в различных приложениях. По своему опыту я могу сделать вывод о нескольких функциях / API Java SE, от которых большинству разработчиков следует держаться подальше. Когда я имею в виду большинство разработчиков, я имею в виду постоянных разработчиков Java EE, а не разработчиков библиотек / инженеров инфраструктуры.

Полное раскрытие: я искренне считаю, что в долгосрочной перспективе большинству команд лучше держаться подальше от следующих функций. Но, как всегда, есть исключения. Если у вас сильная команда и вы полностью знаете, что делаете, продолжайте. Однако в большинстве случаев, если вы начнете включать в свой арсенал следующие инструменты, вы пожалеете об этом в долгосрочной перспективе:

  • отражение
  • Манипулирование байт-кодом
  • ThreadLocals
  • Загрузчики классов
  • Слабые / Мягкие ссылки
  • Розетки

Но достаточно введения, позвольте мне пройти список предупреждающих знаков, подкрепленных объяснением основных проблем:

Отражение В популярных библиотеках, таких как Spring и Hibernate, отражение имеет свое место. Но анализ бизнес-кода — это нечто плохое по многим причинам, поэтому я почти всегда рекомендую избегать его:

Сначала идет читабельность кода / поддержка инструментов. Откройте вашу любимую среду IDE и найдите взаимозависимости в своем коде Java. Легко, не правда ли? Теперь замените вызовы метода на отражение и попробуйте повторить процесс. Ситуация выходит из-под контроля еще больше, когда вы начинаете изменять состояние, которое вы обычно должны были скрыть. Если вам нужен пример, взгляните на следующий код:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
public class Secret {
    private String secrecy;
    public Secret(String secrecy) {
        this.secrecy = secrecy;
    }
    public String getSecrecy() {
        return null;
    }
}
 
public class TestSecrecy {
    public static void main(String[] args) throws Exception {
        Secret s = new Secret("TOP SECRET");
        Field f = Secret.class.getDeclaredField("secrecy");
        f.setAccessible(true);
        System.out.println(f.get(s));
    }
}

Тогда вы собираетесь пропустить безопасность во время компиляции. Видя тот же пример выше, вы уже можете видеть, что опечатка в параметре getDeclaredField () обнаруживается только во время выполнения. Как вы помните, обнаружение ошибок во время выполнения намного сложнее, чем отклонение скриптом сборки.

И, наконец, будут накладные расходы. Отражение вызовов по-разному оптимизируется JIT. Некоторые оптимизации требуют больше времени для применения, а некоторые даже не могут быть применены. Таким образом, потери производительности при отражении могут иногда составлять порядки. Но в типичном бизнес-приложении вы не заметите накладных расходов, так что это определенно меньшее из зол.

Подводя итог, могу сказать, что единственное разумное (косвенное) использование отражения в вашем бизнес-коде — через АОП . Кроме этого, вам лучше держаться подальше от отражения.

Манипулирование байт-кодом . Если я вижу, что вы используете CGLIB или ASM непосредственно для своего кода приложения Java EE, я чувствую, что хочу немедленно убежать. Возьмите причины, которые я описал в блоке отражения, умножьте воздействие на пять, и вы можете начать чувствовать боль.

Что еще хуже, так это то, что во время компиляции у вас нигде не видно исполняемого кода. По сути, вы не знаете, какой код на самом деле работает в вашем производстве. Поэтому, сталкиваясь с проблемами, вы сталкиваетесь с устранением неполадок и отладкой во время выполнения — что, если вы согласитесь со мной, «немного» отнимает больше времени.

ThreadLocals . Есть две несвязанные причины, по которым появление ThreadLocals в бизнес-коде заставляет меня дрожать. Во-первых, с помощью ThreadLocals вы можете начать испытывать искушение использовать переменные, не пропуская их явно через цепочку вызовов методов. Что может быть полезно в определенных случаях. Но когда вы не будете осторожны, я могу гарантировать, что вы в конечном итоге создадите много неожиданных зависимостей в вашем коде.

Вторая причина связана с моей повседневной работой. Хранение данных в ThreadLocals создает путь к утечкам памяти. По крайней мере, одна из десяти утечек permgen, с которыми я сталкиваюсь, вызвана интенсивным использованием ThreadLocal. В сочетании с загрузчиками классов и пулами потоков старый добрый «java.lang.OutOfMemoryError: Permgen space» уже не за горами .

Загрузчики классов . Начнем с того, что загрузчики классов — это сложные звери. Сначала вы должны понять их, иерархию, механизм делегирования, кэширование классов и т. Д. И даже если вы думаете, что получили, первые (десять?) Попыток все равно не будут работать должным образом. Как минимум, вы в конечном итоге создадите утечку загрузчика классов. Поэтому я могу только рекомендовать оставить эту задачу на серверах приложений.

Слабые и мягкие ссылки . Только что узнали, что они и как они работают? Хорошо. Теперь вы немного лучше разбираетесь в Java. У вас есть блестящая идея переписать все ваши кеши с мягкими ссылками? Нехорошо. Я знаю, что если вы вооружены молотом, вы оглядываетесь на гвоздь.

Почему я думаю, что кеширование не очень хороший гвоздь для такого молотка, вы можете спросить. В конце концов, построение кэша на мягких ссылках может быть хорошим примером того, как делегировать некоторые сложности GC вместо того, чтобы реализовывать это самостоятельно.

Давайте возьмем произвольный кеш в качестве примера. Вы создаете его, используя мягкие ссылки для значений, чтобы при исчерпании памяти GC мог вмешаться и начать очистку. Но теперь вы не можете контролировать, какие объекты были удалены из кэша, и, скорее всего, будете воссоздавать их при следующем промахе кэша. Если памяти все еще недостаточно, вы запускаете GC, чтобы очистить их снова. Я предполагаю, что вы можете видеть, как образуется порочный круг, и ваше приложение становится связанным с CPU, когда Full GC постоянно работает.

Розетки . Простой старый java.net.Socket слишком сложен, чтобы разобраться. Я думаю, что это в корне ошибочно из-за его блокирующего характера. При написании типичного приложения Java EE с веб-интерфейсом требуется высокая степень параллелизма для поддержки многочисленных пользователей. То, чего вы сейчас не хотите, — это чтобы ваш пул потоков, который не так масштабируем для всех, сидел там в ожидании заблокированных сокетов.

Тем не менее, есть прекрасные сторонние библиотеки, доступные для этой задачи, поэтому вместо того, чтобы пытаться все исправить самостоятельно, идите и возьмите Netty .

Ссылка: Шесть функций Java, чтобы держаться подальше от нашего партнера JCG Никиты Сальникова Тарновски в блоге Plumbr Blog .