Mögliche Haufenverschmutzung über Varargs-Parameter

Ich verstehe, dass dies bei Java 7 auftritt, wenn Varargs mit einem generischen Typ verwendet werden.

Aber meine Frage ist ..

Was genau bedeutet Eclipse, wenn es sagt, “seine Verwendung könnte den Haufen potenziell verschmutzen?”

Und

Wie verhindert die neue Annotation @SafeVarargs dies?

   

Heap Pollution ist ein technischer Begriff. Es bezieht sich auf Referenzen, die einen Typ haben, der kein Supertyp des Objekts ist, auf das sie zeigen.

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

Dies kann zu “unerklärlichen” ClassCastException s führen.

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

@SafeVarargs verhindert das überhaupt nicht. Es gibt jedoch Methoden, die nachweislich den Heap nicht verschmutzen, der Compiler kann es einfach nicht beweisen. Frühere Aufrufer solcher APIs würden ärgerliche Warnungen bekommen, die völlig sinnlos waren, aber an jeder Aufrufseite unterdrückt werden mussten. Jetzt kann der API-Autor sie einmal an der Deklarationssite unterdrücken.

Wenn die Methode jedoch nicht sicher ist, werden Benutzer nicht mehr gewarnt.

Wenn Sie deklarieren

public static void foo(List... bar) der Compiler konvertiert es in

public static void foo(List[] bar) dann zu

public static void foo(List[] bar)

Es besteht dann die Gefahr, dass Sie fälschlicherweise falsche Werte in die Liste aufnehmen und der Compiler keinen Fehler austriggers. Wenn T beispielsweise ein String ist, wird der folgende Code ohne Fehler kompiliert, wird aber zur Laufzeit fehlschlagen:

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

Wenn Sie die Methode überprüft haben, um sicherzustellen, dass diese Sicherheitsanfälligkeiten nicht enthalten sind, können Sie sie mit @SafeVarargs annotieren, um die Warnung zu unterdrücken. Verwenden @SuppressWarnings("unchecked") für Schnittstellen @SuppressWarnings("unchecked") .

Wenn Sie diese Fehlermeldung erhalten:

Die Varargs-Methode könnte eine Heap-Verschmutzung durch nicht-veränderbare Varargs-Parameter verursachen

@SuppressWarnings("varargs") Sie sicher sind, dass Ihre Verwendung sicher ist, sollten Sie stattdessen @SuppressWarnings("varargs") verwenden. Siehe Is @ SafeVarargs eine entsprechende Annotation für diese Methode? und https://Stackoverflow.com/a/14252221/14731 für eine nette Erklärung dieser zweiten Fehlerart.

Verweise:

@SafeVarargs verhindert das nicht, erfordert jedoch, dass der Compiler beim Kompilieren von Code, der es verwendet, strenger ist.

http://docs.oracle.com/javase/7/docs/api/java/lang/SafeVarars.html erklärt dies im Detail.

Heap Pollution tritt auf, wenn Sie eine ClassCastException wenn Sie eine Operation an einer generischen Schnittstelle ClassCastException und sie einen anderen Typ als deklariert enthält.

Wenn Sie varargs verwenden, kann dies zur Erstellung eines Object[] , das die Argumente enthält.

Aufgrund der Escape-Analyse kann das JIT diese Array-Erstellung optimieren. (Eine der wenigen Male, die ich es gefunden habe) Es ist nicht garantiert, dass es weg optimiert wird, aber ich würde mir keine Sorgen machen, es sei denn, Sie sehen ein Problem in Ihrem Speicher-Profiler.

@SafeVarargs unterdrückt eine Warnung durch den Compiler und ändert nicht, wie sich das JIT verhält.

Der Grund dafür ist, dass Varargs die Möglichkeit bieten, mit einem nicht parametrisierten Objektarray aufgerufen zu werden. Wenn Ihr Typ also List … ist, kann er auch mit List [] non-varargs type aufgerufen werden.

Hier ist ein Beispiel:

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

Wie Sie sehen können, kann List [] b jede Art von Consumer enthalten, und dennoch kompiliert dieser Code. Wenn Sie varargs verwenden, geht es Ihnen gut, aber wenn Sie die Methodendefinition nach type-erasure – void test (List []) verwenden – dann prüft der Compiler die Vorlagenparameter nicht. @SafeVarargs unterdrückt diese Warnung.