538

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?

Yassin Hajaj
  • 21,337
  • 9
  • 51
  • 89
hertzsprung
  • 9,445
  • 4
  • 42
  • 77
  • 11
    Details here: http://docs.oracle.com/javase/specs/jls/se7/html/jls-9.html#jls-9.6.3.7 – assylias Sep 17 '12 at 15:15
  • possible duplicate of [Java 1.7 varargs function reported as unchecked warning](http://stackoverflow.com/questions/11185774/java-1-7-varargs-function-reported-as-unchecked-warning) – 200_success Jul 30 '14 at 06:09
  • I am seeing this in my editor: `Possible heap pollution from parameterized vararg type` – Alexander Mills Jan 29 '19 at 10:47
  • 2
    If you are confused (like me) when you can safely use the annotation, [here](https://stackoverflow.com/q/14231037/2448440) is a useful explanation to check whether your method is safe to annotate with `@SafeVarargs` – Qw3ry Apr 29 '21 at 09:01
  • I would like to know why Java has this issue when C# does not. Is Java really that broken? – bikeman868 Jan 04 '23 at 01:30

6 Answers6

314

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

Bugs
  • 4,491
  • 9
  • 32
  • 41
Ben Schulz
  • 6,101
  • 1
  • 19
  • 15
  • 2
    So are we saying that the heap is polluted because it contains references whose types are not what we might expect? (List vs List in your example) – hertzsprung Sep 17 '12 at 16:03
  • 46
    I've found [an explanation of heap pollution on programmers.stackexchange](http://programmers.stackexchange.com/questions/155994/java-heap-pollution) – hertzsprung Sep 17 '12 at 16:05
  • 51
    This answer is a fine explanation of what heap pollution is, but it doesn't really explain why varargs are so particularly likely to cause it as to warrant a specific warning. – Dolda2000 May 20 '17 at 01:20
  • 8
    Me too, I'm missing information how to ensure that my code does not contain this problem (eg. how do I know it's hardened enough to add @SafeVarargs) – Daniel Alder Mar 14 '19 at 08:25
298

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 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:

Gili
  • 86,244
  • 97
  • 390
  • 689
  • 2
    I think I'm understanding better. The danger comes when you cast varargs to `Object[]`. As long as you don't cast to `Object[]`, it sounds like you should be fine. – djeikyb May 22 '14 at 18:17
  • 3
    As an example of a stupid thing you could do: `static void bar(T...args) { ((Object[])args)[0] = "a"; }`. And then call `bar(Arrays.asList(1,2));`. – djeikyb May 22 '14 at 18:18
  • This does not compile since the type of ```bar[0]``` is a List, not T – Eran H. Dec 21 '15 at 08:44
  • @EranH. Fixed (I think). Try now. – Gili Dec 21 '15 at 09:13
  • 1
    @djeikyb if the danger only arises if I cast to `Object[]` why would the compiler trigger a warning if I don't? Should be fairly easy to check this at compile-time, after all (in case I don't pass it on to another function with a similar signature, in which case the other function should trigger a warning). I don't believe this is really the core of the warning ("You're safe if you don't cast"), and I still don't understand in which case I'm fine. – Qw3ry Oct 31 '16 at 13:56
  • 7
    @djeikyb You may do exactly the same stupid thing without parametrized varargs (e.g. `bar(Integer...args)`). So what's the point of this warning then? – Vasiliy Vlasov Jun 07 '17 at 15:30
  • 5
    @VasiliyVlasov This issue is only relevant for parameterized varargs. If you try to do the same thing with non-typed arrays the runtime will prevent you from inserting the wrong type into the array. The compiler is warning you that the runtime will be unable to prevent incorrect behavior because the parameter type is unknown at runtime (by contrast, arrays *do* know the type of their non-generic elements at runtime). – Gili Jun 08 '17 at 17:08
  • 1
    Completely off-topic: you should never use the constructor of `Integer`. It'll bypass the internal caching mechanism of the `Integer` class. Its use is also deprecated in newer Java versions. Use `Integer.valueOf(int)` instead. – MC Emperor Jun 24 '19 at 14:08
11

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

jontro
  • 10,241
  • 6
  • 46
  • 71
7

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.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • 6
    Interesting though it doesn't really answer his question about `@SafeVarargs`. – Paul Bellora Sep 17 '12 at 15:23
  • 2
    Nope. That's not what heap pollution is. "Heap pollution occurs when a variable of a parameterized type refers to an object that is not of that parameterized type." Ref: http://docs.oracle.com/javase/tutorial/java/generics/nonReifiableVarargsType.html#heap_pollution – Doradus Sep 09 '17 at 20:51
2

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.

user1122069
  • 1,767
  • 1
  • 24
  • 52
1

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.

Pavel
  • 4,912
  • 7
  • 49
  • 69