Poluição de heap potencial via parâmetro varargs

Eu entendo isso ocorre com o Java 7 ao usar varargs com um tipo genérico;

Mas a minha pergunta é ..

O que exatamente significa o Eclipse quando diz “seu uso poderia poluir o heap?”

E

Como a nova anotação do @SafeVarargs evita isso?

A poluição de heap é um termo técnico. Refere-se a referências que têm um tipo que não é um supertipo do object para o qual elas apontam.

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

Isso pode levar a ClassCastException s “inexplicáveis”.

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

@SafeVarargs não impede isso de @SafeVarargs nenhum. No entanto, existem methods que provavelmente não irão poluir o heap, o compilador simplesmente não pode provar isso. Anteriormente, os chamadores dessas APIs recebiam avisos incômodos que eram completamente inúteis, mas precisavam ser suprimidos em todos os sites de chamadas. Agora, o autor da API pode suprimi-lo uma vez no site de declaração.

No entanto, se o método não for seguro, os usuários não serão mais avisados.

Quando você declara

public static void foo(List... bar) o compilador converte para

public static void foo(List[] bar) depois para

public static void foo(List[] bar)

Surge então o perigo de, por engano, atribuir valores incorretos à lista e o compilador não acionar nenhum erro. Por exemplo, se T for uma String , o código a seguir será compilado sem erro, mas falhará no tempo de execução:

 // 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); 

Se você revisou o método para garantir que ele não contenha essas vulnerabilidades, você pode anotá-lo com @SafeVarargs para suprimir o aviso. Para interfaces, use @SuppressWarnings("unchecked") .

Se você receber esta mensagem de erro:

O método Varargs pode causar poluição de heap a partir do parâmetro varargs não reutilizável

e você tem certeza de que seu uso é seguro, então você deve usar @SuppressWarnings("varargs") . Veja @SafeVarargs é uma anotação apropriada para este método? e https://stackoverflow.com/a/14252221/14731 para uma boa explicação deste segundo tipo de erro.

Referências:

@SafeVarargs não impede que isso aconteça, no entanto, exige que o compilador seja mais rigoroso ao compilar o código que o utiliza.

http://docs.oracle.com/javase/7/docs/api/java/lang/SafeVarargs.html explica isso em mais detalhes.

Poluição de heap é quando você recebe um ClassCastException ao fazer uma operação em uma interface genérica e contém outro tipo de declarado.

Quando você usa varargs, isso pode resultar na criação de um Object[] para manter os argumentos.

Devido à análise de escape, o JIT pode otimizar a criação desta matriz. (Uma das poucas vezes que eu encontrei isso) não é garantido para ser otimizado, mas eu não me preocuparia com isso a menos que você veja o seu problema em seu perfilador de memory.

AFAIK @SafeVarargs suprime um aviso do compilador e não altera o comportamento do JIT.

A razão é porque os varargs fornecem a opção de serem chamados com um array de objects não parametrizados. Portanto, se o seu tipo era List …, ele também pode ser chamado com o tipo List [] não varargs.

Aqui está um exemplo:

 public static void testCode(){ List[] b = new List[1]; test(b); } @SafeVarargs public static void test(List... a){ } 

Como você pode ver, a List [] b pode conter qualquer tipo de consumidor, e ainda assim este código é compilado. Se você usar varargs, então você está bem, mas se você usar a definição do método após o tipo de eliminação – void test (List []) – então o compilador não irá verificar os tipos de parâmetro do modelo. @SafeVarargs irá suprimir este aviso.