Потенциальное загрязнение кучи через параметр varargs

389

Я понимаю, что это происходит с Java 7 при использовании varargs с общим типом;

Но мой вопрос...

Что именно означает Eclipse, когда говорится: "Его использование может потенциально загрязнить кучу?"

И

Как предотвращает это аннотация @SafeVarargs?

Показать ещё 1 комментарий
Теги:
generics
variadic-functions

5 ответов

226
Лучший ответ

Загрязнение кучи - технический термин. Это относится к ссылкам, которые имеют тип, который не является супертипом объекта, на который они указывают.

List<A> listOfAs = new ArrayList<>();
List<B> listOfBs = (List<B>)(Object)listOfAs; // points to a list of As

Это может привести к необъяснимым ClassCastException.

// if the heap never gets polluted, this should never throw a CCE
B b = listOfBs.get(0); 

@SafeVarargs совсем не мешает этому. Однако есть методы, которые доказуемо не будут загрязнять кучу, компилятор просто не может этого доказать. Раньше вызывающие такие API получали раздражающие предупреждения, которые были совершенно бессмысленными, но их нужно было подавлять на каждом сайте вызовов. Теперь автор API может отключить его один раз на сайте декларации.

Однако, если метод на самом деле небезопасен, пользователи больше не будут предупреждены.

  • 2
    Итак, мы говорим, что куча загрязнена, потому что она содержит ссылки, типы которых не соответствуют нашим ожиданиям? (Список <A> против списка <B> в вашем примере)
  • 34
Показать ещё 2 комментария
201

Когда вы объявляете

public static <T> void foo(List<T>... bar) компилятор преобразует его в

public static <T> void foo(List<T>[] bar), затем до

public static <T> void foo(List[] bar)

Тогда возникает опасность, что вы ошибочно назначите неверные значения в списке, и компилятор не вызовет никаких ошибок. Например, если T является String, тогда следующий код будет компилироваться без ошибок, но не будет выполняться во время выполнения:

// First, strip away the array type (arrays allow this kind of upcasting)
Object[] objectArray = bar;

// Next, insert an element with an incorrect type into the array
objectArray[0] = Arrays.asList(new Integer(42));

// Finally, try accessing the original array. A runtime error will occur
// (ClassCastException due to a casting from Integer to String)
T firstElement = bar[0].get(0);

Если вы рассмотрели метод, чтобы убедиться, что он не содержит таких уязвимостей, вы можете аннотировать его с помощью @SafeVarargs, чтобы пресечь это предупреждение. Для интерфейсов используйте @SuppressWarnings("unchecked").

Если вы получите это сообщение об ошибке:

Метод Varargs может привести к загрязнению кучи от невосстанавливаемого параметра varargs

и вы уверены, что ваше использование будет безопасным, вместо этого вы должны использовать @SuppressWarnings("varargs"). См. Является ли @SafeVarargs подходящей аннотацией для этого метода? и https://stackoverflow.com/questions/14231037/java-safevarargs-annotation-does-a-standard-or-best-practice-exist для приятного объяснения этой второй вид ошибки.

Литература:

  • 2
    Я думаю, что я понимаю лучше. Опасность наступает, когда вы накладываете varargs на Object[] . Пока вы не преобразуете в Object[] , это звучит так, как будто вы должны быть в порядке.
  • 2
    В качестве примера глупости, которую вы можете сделать: static <T> void bar(T...args) { ((Object[])args)[0] = "a"; } А затем вызовите bar(Arrays.asList(1,2)); ,
Показать ещё 5 комментариев
7

@SafeVarargs не мешает ему произойти, однако он требует, чтобы компилятор был более строгим при компиляции кода, который его использует.

http://docs.oracle.com/javase/7/docs/api/java/lang/SafeVarargs.html объясняет это более подробно.

Загрязнение кучи - это когда вы получаете ClassCastException при выполнении операции на общем интерфейсе, и он содержит другой тип, чем объявлено.

  • 0
    Дополнительные ограничения компилятора на его использование не кажутся особенно актуальными.
5

Когда вы используете varargs, это может привести к созданию Object[] для хранения аргументов.

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

AFAIK @SafeVarargs подавляет предупреждение компилятором и не изменяет поведение JIT.

  • 6
    Интересно, что на самом деле он не отвечает на его вопрос о @SafeVarargs .
  • 1
    Нету. Это не загрязнение кучи. «Загрязнение кучи происходит, когда переменная параметризованного типа относится к объекту, который не относится к этому параметризованному типу». Ссылка: docs.oracle.com/javase/tutorial/java/generics/…
0

Причина в том, что 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 отключит это предупреждение.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню