Home » Java » Potential heap pollution via varargs parameter

Potential heap pollution via varargs parameter

Posted by: admin November 2, 2017 Leave a comment

Questions:

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?

Answers:

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.

Questions:
Answers:

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 <T> 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 Is @SafeVarargs an appropriate annotation for this method? and https://stackoverflow.com/a/14252221/14731 for a nice explanation of this second kind of error.

References:

Questions:
Answers:

@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.

Questions:
Answers:

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.