Возможная загрязнение кучи через параметр varargs
Я понимаю, что эта проблема возникает в Java 7 при использовании varargs с обобщенными типами.
Но у меня вопрос…
Что конкретно подразумевает Eclipse, когда говорит, что "его использование может потенциально загрязнить кучу"?
И как новая аннотация @SafeVarargs
предотвращает это?
4 ответ(ов)
Heap pollution — это технический термин, который относится к ссылкам, имеющим тип, не являющийся суперклассом объекта, на который они указывают.
Пример кода на Java:
List<A> listOfAs = new ArrayList<>();
List<B> listOfBs = (List<B>)(Object)listOfAs; // указывает на список объектов A
Это может привести к "необъяснимым" исключениям ClassCastException
.
// если куча никогда не загрязняется, это не должно вызвать CCE
B b = listOfBs.get(0);
Аннотация @SafeVarargs
не предотвращает это загрязнение. Однако существуют методы, которые на самом деле не загрязняют кучу, но компилятор не может это доказать. Ранее вызывающие такие API сталкивались с надоедливыми предупреждениями, которые были абсолютно бессмысленными, но их необходимо было подавлять на каждом месте вызова. Теперь автор API может подавить эти предупреждения один раз на месте объявления.
Тем не менее, если метод фактически не безопасен, пользователи больше не будут получать предупреждения.
@SafeVarargs
не предотвращает проблему с загрязнением кучи, однако он требует, чтобы компилятор был более строгим при компиляции кода, который его использует.
Более подробно об этом можно прочитать в официальной документации: http://docs.oracle.com/javase/7/docs/api/java/lang/SafeVarargs.html.
Загрязнение кучи происходит, когда вы получаете ClassCastException
при выполнении операции с обобщённым интерфейсом, если он содержит другой тип, отличный от заявленного.
Когда вы используете varargs, это может привести к созданию массива Object[]
, который будет хранить переданные аргументы.
Благодаря анализу выхода, JIT может оптимизировать создание этого массива. (Это один из немногих случаев, когда я заметил такую оптимизацию.) Однако это не гарантировано, так что не стоит беспокоиться об этом, если вы не увидите проблемы в профайлере памяти.
Насколько я знаю, аннотация @SafeVarargs
подавляет предупреждение компилятора и не изменяет поведение JIT.
Причина в том, что varargs дают возможность вызывать метод с непараметрированным массивом объектов. Таким образом, если ваш тип — List<A>
, то метод также может быть вызван с List[]
, что является типом, не использующим varargs.
Вот пример:
public static void testCode(){
List[] b = new List[1];
test(b);
}
@SafeVarargs
public static void test(List<A>... a){
}
Как вы можете видеть, List[] b
может содержать элементы любого типа, и тем не менее, этот код компилируется. Если вы используете varargs, то у вас всё будет в порядке, но если вы используете определение метода после стирания типов — void test(List[])
, — то компилятор не будет проверять типы параметров шаблона. Аннотация @SafeVarargs
подавляет это предупреждение.
Ошибка «Необходимо переопределить метод суперкласса» после импорта проекта в Eclipse
Как создать обобщённый массив в Java?
Получить обобщённый тип класса во время выполнения
IntelliJ: Показать подсказки JavaDocs при наведении мыши
Eclipse: Как установить максимальную длину строки для автоматического форматирования?