541 Stimmen

Mögliche Heap-Verschmutzung über varargs-Parameter

Soweit ich weiß, tritt dies in Java 7 bei der Verwendung von varargs mit einem generischen Typ auf;

Aber meine Frage ist

Was genau meint Eclipse mit der Aussage, dass seine Verwendung den Heap potenziell verschmutzen könnte?"

Und

Wie funktioniert die neue @SafeVarargs Vermerk dies verhindern?

315voto

Ben Schulz Punkte 5891

Haldenverschmutzung ist ein Fachbegriff. Er bezieht sich auf Referenzen, die einen Typ haben, der kein Supertyp des Objekts ist, auf das sie zeigen.

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

Dies kann zu "Unerklärlichem" führen. ClassCastException s.

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

@SafeVarargs verhindert dies in keiner Weise. Es gibt jedoch Methoden, die den Heap wahrscheinlich nicht verschmutzen, der Compiler kann es nur nicht beweisen. Früher bekamen die Aufrufer solcher APIs lästige Warnungen, die völlig sinnlos waren, aber an jeder Aufrufstelle unterdrückt werden mussten. Jetzt kann der API-Autor sie einmalig an der Deklarationsseite unterdrücken.

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

301voto

Gili Punkte 80842

Wenn Sie erklären

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

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

public static void foo(List[] bar)

Es besteht dann die Gefahr, dass man versehentlich falsche Werte in die Liste einträgt und der Compiler keinen Fehler auslöst. Zum Beispiel, wenn T ist eine String dann wird der folgende Code ohne Fehler kompiliert, schlägt aber zur Laufzeit fehl:

// 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 sie keine derartigen Schwachstellen enthält, können Sie sie mit dem Vermerk @SafeVarargs um die Warnung zu unterdrücken. Für Schnittstellen verwenden Sie @SuppressWarnings("unchecked") .

Wenn Sie diese Fehlermeldung erhalten:

Varargs-Methode könnte Heap-Verschmutzung durch nicht reifbare varargs-Parameter verursachen

und Sie sicher sind, dass Ihre Nutzung sicher ist, sollten Sie die @SuppressWarnings("varargs") stattdessen. Siehe Ist @SafeVarargs eine geeignete Anmerkung für diese Methode? und https://stackoverflow.com/a/14252221/14731 für eine gute Erklärung dieser zweiten Art von Fehler.

Referenzen:

11voto

jontro Punkte 9646

@SafeVarargs verhindert dies nicht, verlangt aber, dass der Compiler beim Kompilieren von Code, der dies verwendet, strenger vorgeht.

http://docs.oracle.com/javase/7/docs/api/java/lang/SafeVarargs.html wird dies näher erläutert.

Von Haufenverschmutzung spricht man, wenn man eine ClassCastException wenn eine Operation an einer generischen Schnittstelle durchgeführt wird und diese einen anderen als den deklarierten Typ enthält.

7voto

Peter Lawrey Punkte 511323

Wenn Sie varargs verwenden, kann dies zur Erstellung eines Object[] um die Argumente festzuhalten.

Dank der Escape-Analyse kann das JIT diese Array-Erstellung wegoptimieren. (Eines der wenigen Male, die ich gefunden habe, tut es so) Seine nicht garantiert, weg optimiert werden, aber ich würde nicht darüber Sorgen, es sei denn, Sie sehen, seine ein Problem in Ihrem Speicher-Profiler.

AFAIK @SafeVarargs unterdrückt eine Warnung des Compilers und ändert nichts am Verhalten des JIT.

2voto

user1122069 Punkte 1647

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

Hier ist ein Beispiel:

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

@SafeVarargs
public static void test(List<A>... a){
}

Wie Sie sehen können, kann List[] b jede Art von Verbraucher enthalten, und dennoch lässt sich dieser Code kompilieren. Wenn Sie Varargs verwenden, ist alles in Ordnung, aber wenn Sie die Methodendefinition nach der Typüberprüfung verwenden - void test(List[]) - dann wird der Compiler die Typen der Vorlagenparameter nicht überprüfen. Mit @SafeVarargs wird diese Warnung unterdrückt.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X