通过varargs参数潜在的堆污染

her*_*ung 410 java eclipse generics variadic-functions

我理解当使用具有泛型类型的varargs时,Java 7会发生这种情况;

但我的问题是......

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

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

Ben*_*ulz 240

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

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

这可能导致"无法解释" ClassCastException.

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

@SafeVarargs根本不会阻止这一点.但是,有些方法可以证明不会污染堆,编译器就是无法证明它.以前,这些API的调用者会得到令人讨厌的警告,这些警告完全没有意义,但必须在每个呼叫站点被抑制.现在API作者可以在声明站点禁止它一次.

但是,如果方法其实并不安全,用户将不再被警告.

  • 我发现[程序员堆栈污染的解释.stackexchange](http://programmers.stackexchange.com/questions/155994/java-heap-pollution) (36认同)
  • 这个答案很好地解释了堆污染是什么,但它并没有真正解释为什么varargs特别有可能导致它需要特定的警告. (23认同)
  • 我也是,我缺少有关如何确保我的代码不包含此问题的信息(例如,我如何知道它已经足够坚固以添加@SafeVarargs) (3认同)
  • 那么我们是否说堆被污染了,因为它包含的引用的类型不是我们所期望的?(在您的示例中列出<A> vs List <B>) (2认同)

Gil*_*ili 226

当你申报时

public static <T> void foo(List<T>... bar) 编译器将其转换为

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

public static void foo(List[] bar)

然后出现危险,您将错误地将错误的值分配给列表,并且编译器不会触发任何错误.例如,如果T是a,String则以下代码将在没有错误的情况下编译但在运行时将失败:

// 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);
Run Code Online (Sandbox Code Playgroud)

如果您查看了该方法以确保它不包含此类漏洞,则可以使用它@SafeVarargs来对其进行注释以禁止显示警告.对于接口,请使用@SuppressWarnings("unchecked").

如果您收到此错误消息:

Varargs方法可能会导致不可恢复的varargs参数的堆污染

并且您确定您的使用是安全的,那么您应该使用@SuppressWarnings("varargs").请参阅@SafeVarargs是否适合此方法?/sf/answers/997655501/对第二种错误的一个很好的解释.

参考文献:

  • @djeikyb如果没有参数化的变量,你可能会做同样愚蠢的事情(例如`bar(Integer ... args)`).那么这个警告的重点是什么呢? (4认同)
  • 作为一个愚蠢的事情你可以做的例子:`static <T> void bar(T ... args){((Object [])args)[0] ="a"; }`.然后调用`bar(Arrays.asList(1,2));`. (3认同)
  • @VasiliyVlasov此问题仅与参数化varargs相关.如果您尝试对非类型化数组执行相同操作,则运行时将阻止您将错误类型插入到数组中.编译器警告您运行时将无法阻止不正确的行为,因为参数类型在运行时是未知的(相反,数组*在运行时*知道它们的非泛型元素的类型). (3认同)
  • 我想我的理解会更好.当你将varargs转换为`Object []`时会出现危险.只要你不转换为`Object []`,听起来你应该没问题. (2认同)

jon*_*tro 8

@SafeVarargs 并不阻止它发生,但它要求编译器在编译使用它的代码时更严格.

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

堆污染是指ClassCastException在通用接口上执行操作时,它包含的另一种类型不是声明的.


Pet*_*rey 5

当您使用varargs时,它可以导致创建一个Object[]来保存参数.

由于逃逸分析,JIT可以优化此阵列创建.(我发现它的少数几次之一)它不能保证被优化掉,但除非你在内存分析器中看到它的问题,否则我不会担心它.

AFAIK会@SafeVarargs禁止编译器发出警告,并且不会更改JIT的行为方式.

  • 有趣但虽然它没有真正回答他关于`@SafeVarargs`的问题. (6认同)
  • 没有。这不是堆污染。“当参数化类型的变量引用不属于该参数化类型的对象时,就会发生堆污染。” 参考:http://docs.oracle.com/javase/tutorial/java/generics/nonReifyingVarargsType.html#heap_pollution (2认同)