Possible heap pollution via varargs parameter

JavaEclipseGenericsVariadic Functions

Java Problem Overview


I understand this occurs with Java 7 when using varargs with a generic type;

But my question is..

What exactly does Eclipse mean when it says "its use could potentially pollute the heap?"

And

How does the new @SafeVarargs annotation prevent this?

Java Solutions


Solution 1 - Java

Heap pollution is a technical term. It refers to references which have a type that is not a supertype of the object they point to.

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

This can lead to "unexplainable" ClassCastExceptions.

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

@SafeVarargs does not prevent this at all. However, there are methods which provably will not pollute the heap, the compiler just can't prove it. Previously, callers of such APIs would get annoying warnings that were completely pointless but had to be suppressed at every call site. Now the API author can suppress it once at the declaration site.

However, if the method actually is not safe, users will no longer be warned.

Solution 2 - Java

When you declare

public static <T> void foo(List<T>... bar) the compiler converts it to

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

public static void foo(List[] bar)

The danger then arises that you'll mistakenly assign incorrect values into the list and the compiler will not trigger any error. For example, if T is a String then the following code will compile without error but will fail at runtime:

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

If you reviewed the method to ensure that it doesn't contain such vulnerabilities then you can annotate it with @SafeVarargs to suppress the warning. For interfaces, use @SuppressWarnings("unchecked").

If you get this error message:

> Varargs method could cause heap pollution from non-reifiable varargs parameter

and you are sure that your usage is safe then you should use @SuppressWarnings("varargs") instead. See https://stackoverflow.com/q/30130262/14731 and https://stackoverflow.com/a/14252221/14731 for a nice explanation of this second kind of error.

References:

Solution 3 - Java

@SafeVarargs does not prevent it from happening, however it mandates that the compiler is stricter when compiling code that uses it.

http://docs.oracle.com/javase/7/docs/api/java/lang/SafeVarargs.html explains this in futher detail.

Heap pollution is when you get a ClassCastException when doing an operation on a generic interface and it contains another type than declared.

Solution 4 - Java

When you use varargs, it can result in the creation of an Object[] to hold the arguments.

Due to escape analysis, the JIT can optimise away this array creation. (One of the few times I have found it does so) Its not guaranteed to be optimised away, but I wouldn't worry about it unless you see its an issue in your memory profiler.

AFAIK @SafeVarargs suppresses a warning by the compiler and doesn't change how the JIT behaves.

Solution 5 - Java

The reason is because varargs give the option of being called with a non-parametrized object array. So if your type was List < A > ... , it can also be called with List[] non-varargs type.

Here is an example:

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

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

As you can see List[] b can contain any type of consumer, and yet this code compiles. If you use varargs, then you are fine, but if you use the method definition after type-erasure - void test(List[]) - then the compiler will not check the template parameter types. @SafeVarargs will suppress this warning.

Solution 6 - Java

It's rather safe to add @SafeVarargs annotation to the method when you can control the way it's called (e.g. a private method of a class). You must make sure that only the instances of the declared generic type are passed to the method.

If the method exposed externally as a library, it becomes hard to catch such mistakes. In this case it's best to avoid this annotation and rewrite the solution with a collection type (e.g. Collection<Type1<Type2>>) input instead of varargs (Type1<Type2>...).

As for the naming, the term heap pollution phenomenon is quite misleading in my opinion. In the documentation the actual JVM heap is not event mentioned. There is a question at Software Engineering that contains some interesting thoughts on the naming of this phenomenon.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionhertzsprungView Question on Stackoverflow
Solution 1 - JavaBen SchulzView Answer on Stackoverflow
Solution 2 - JavaGiliView Answer on Stackoverflow
Solution 3 - JavajontroView Answer on Stackoverflow
Solution 4 - JavaPeter LawreyView Answer on Stackoverflow
Solution 5 - Javauser1122069View Answer on Stackoverflow
Solution 6 - JavaPavelView Answer on Stackoverflow