ChatGPT解决这个技术问题 Extra ChatGPT

Possible heap pollution via varargs parameter

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?

I am seeing this in my editor: Possible heap pollution from parameterized vararg type
If you are confused (like me) when you can safely use the annotation, here is a useful explanation to check whether your method is safe to annotate with @SafeVarargs

L
Lii

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.


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)
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.
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)
G
Gili

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:

http://docs.oracle.com/javase/7/docs/technotes/guides/language/non-reifiable-varargs.html

http://docs.oracle.com/javase/tutorial/java/generics/nonReifiableVarargsType.html#heap_pollution


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.
As an example of a stupid thing you could do: static <T> void bar(T...args) { ((Object[])args)[0] = "a"; }. And then call bar(Arrays.asList(1,2));.
@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.
@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?
@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).
j
jontro

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


The additional compiler restrictions on its use don't seem especially relevant.
P
Peter Lawrey

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.


Interesting though it doesn't really answer his question about @SafeVarargs.
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: docs.oracle.com/javase/tutorial/java/generics/…
u
user1122069

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.


P
Pavel

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.