关于java:通过varargs参数的潜在堆污染

Potential heap pollution via varargs parameter

我理解当Java 7使用通用类型的VARARGS时会发生这种情况;

但我的问题是……

当Eclipse说"它的使用可能会污染堆"时,它究竟意味着什么?

新的@SafeVarargs注释是如何阻止这种情况的?


堆污染是一个技术术语。它指的是引用的类型不是它们指向的对象的父类型。

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

这可能导致"无法解释"的ClassCastExceptions。

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

@SafeVarargs并不能阻止这一点。然而,有一些方法可以证明不会污染堆,编译器只是无法证明它。以前,这样的API的调用者会收到恼人的警告,这些警告完全没有意义,但必须在每个调用站点都被禁止。现在,API作者可以在声明站点抑制它一次。

但是,如果该方法实际上不安全,则不会再警告用户。


当你申报

编译程序将其转换为

public static void foo(List[] bar)然后到

public static void foo(List[] bar)

这样就会产生这样的危险:您将错误地将不正确的值分配到列表中,编译器将不会触发任何错误。例如,如果TString代码,那么以下代码将无错误地编译,但在运行时会失败:

1
2
3
4
5
6
7
8
9
// 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);

如果您检查了该方法以确保它不包含此类漏洞,那么您可以使用@SafeVarargs对其进行注释以抑制警告。对于接口,使用@SuppressWarnings("unchecked")

如果收到此错误消息:

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

而且你确信你的使用是安全的,那么你应该使用@SuppressWarnings("varargs")。请参见@safevarargs是否是此方法的适当注释?以及https://stackoverflow.com/a/14252221/14731,可以很好地解释第二种错误。

参考文献:

  • http://docs.oracle.com/javase/7/docs/technotes/guides/language/non-reifiable-varargs.html
  • http://DOCS.Oracle .COM/Javase/Tudio/Java/泛型/非ReIFiabl Valasepe。


@SafeVarargs并不能阻止这种情况的发生,但是它要求编译器在编译使用它的代码时更严格。

http://docs.oracle.com/javase/7/docs/api/java/lang/safevarargs.html进一步详细解释了这一点。

堆污染是指当您在通用接口上执行操作时得到一个ClassCastException,并且它包含另一个未声明的类型。


当使用varargs时,它会导致创建一个Object[]来保存参数。

由于转义分析,JIT可以优化这个数组的创建。(这是我发现的少数次)它不能保证被优化掉,但是我不会担心它,除非你在你的内存分析器中看到它的问题。

afaik @SafeVarargs禁止编译器发出警告,不会更改JIT的行为方式。


原因是varargs提供了使用非参数化对象数组调用的选项。所以如果你的类型是list。,也可以使用list[]non varargs类型调用它。

下面是一个例子:

1
2
3
4
5
6
7
8
public static void testCode(){
    List[] b = new List[1];
    test(b);
}

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

正如您所看到的,列表[]b可以包含任何类型的使用者,但这段代码是编译的。如果使用varargs,则可以,但如果在类型擦除之后使用方法定义-void test(list[])-则编译器不会检查模板参数类型。@safevarargs将禁止此警告。