varargsパラメーターによるヒープ汚染の可能性


433

これは、Java 7でジェネリック型のvarargsを使用するときに発生することを理解しています。

しかし、私の質問です。

「使用するとヒープが汚染される可能性がある」とEclipseが正確に意味するのは何ですか?

そして

新しい@SafeVarargsアノテーションはどのようにこれを防ぎますか?




私はこれを私のエディターで見ています:Possible heap pollution from parameterized vararg type
Alexander Mills

回答:


252

ヒープ汚染は専門用語です。参照先のオブジェクトのスーパータイプではないタイプを持つ参照を参照しています。

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

これは、「説明できない」につながる可能性がありClassCastExceptionます。

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

@SafeVarargsこれをまったく防止しません。ただし、ヒープを汚染しないと思われるメソッドがあり、コンパイラはそれを証明できません。以前は、そのようなAPIの呼び出し元は、完全に無意味な警告を受け取りましたが、すべての呼び出しサイトで抑制する必要がありました。これで、API作成者は宣言サイトで一度抑制できます。

ただし、メソッドが実際に安全でない場合、ユーザーに警告は表示されなくなります。


2
それで、ヒープが汚染されていると言っているのは、そのヒープに、予想とは異なるタイプの参照が含まれているからです。(あなたの例ではList <A> vs List <B>)
hertzsprung 2012


30
この回答は、ヒープ汚染とは何かについての詳細な説明ですが、可変引数が特定の警告を正当化するほど特に原因となる可能性が非常に高い理由を実際に説明しているわけではありません。
Dolda2000

4
私にも、コードにこの問題が含まれないようにする方法に関する情報が不足しています(たとえば、@ SafeVarargsを追加するのに十分に強化されていることをどのように確認できますか)
Daniel Alder

236

宣言するとき

public static <T> void foo(List<T>... bar) コンパイラはそれを

public static <T> void foo(List<T>[] bar) それから

public static void foo(List[] bar)

次に、誤った値をリストに誤って割り当て、コンパイラがエラーをトリガーしないという危険が生じます。例えば、場合Tされ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);

メソッドを見直して、そのような脆弱性が含まれていないことを確認した場合は、注釈を付けて@SafeVarargs警告を抑制できます。インターフェイスには、を使用します@SuppressWarnings("unchecked")

このエラーメッセージが表示された場合:

Varargsメソッドは、再構成不可能なvarargsパラメーターからのヒープ汚染を引き起こす可能性があります

そして、あなたはあなたの使用が安全であると確信しているので、@SuppressWarnings("varargs")代わりに使用するべきです。@SafeVarargsはこのメソッドの適切な注釈ですか?を参照してくださいこの2番目の種類のエラーについては、https://stackoverflow.com/a/14252221/14731参照してください。

参照:


2
私はよく理解していると思います。varargsをにキャストすると危険が生じますObject[]。にキャストしない限り、問題ないように思えObject[]ます。
djeikyb 2014年

3
あなたができる愚かなものの例として:static <T> void bar(T...args) { ((Object[])args)[0] = "a"; }。次にを呼び出しますbar(Arrays.asList(1,2));
djeikyb 2014年

1
@djeikyb危険が発生するのは、キャストした場合にのみ発生し、発生しない場合にObject[]コンパイラが警告をトリガーするのはなぜですか?結局、これをコンパイル時にかなり簡単にチェックできるはずです(同様のシグネチャを持つ別の関数に渡さない場合、他の関数は警告をトリガーするはずです)。これが本当に警告の核心だとは信じていません(「キャストしなくても大丈夫です」)。それでも、私はそれでも問題ありません。
Qw3ry 2016年

5
@djeikybパラメータ化された可変引数なしでまったく同じ愚かなことをするかもしれません(例:)bar(Integer...args)。では、この警告の意味は何でしょうか。
Vasiliy Vlasov 2017年

3
@VasiliyVlasovこの問題は、パラメーター化された可変引数にのみ関係します。型指定されていない配列で同じことを行おうとすると、ランタイムは配列に間違った型を挿入するのを防ぎます。コンパイラーは、パラメータータイプが実行時に不明であるため、ランタイムが不正な動作を防止できないことを警告しています(対照的に、配列実行時に非ジェネリック要素のタイプを知っています)。
Gili

8

@SafeVarargs それが起こるのを防ぐわけではありませんが、それを使用するコードをコンパイルするとき、コンパイラーはより厳格であることを義務付けます。

http://docs.oracle.com/javase/7/docs/api/java/lang/SafeVarargs.htmlでこれについてさらに詳しく説明しています。

ヒープ汚染とはClassCastException、ジェネリックインターフェイスで操作を行うときに、宣言されたものとは別のタイプが含まれている場合です。


その使用に関する追加のコンパイラー制限は、特に関連性があるとは思われません。
ポールベロラ

6

varargsを使用Object[]すると、引数を保持するためのが作成される可能性があります。

エスケープ分析により、JITはこの配列の作成を最適化できます。(私がそれを見つけた数回のうちの1つはそうすることを発見しました)最適化で削除されることは保証されていませんが、メモリプロファイラーに問題がない限り、心配する必要はありません。

AFAIK @SafeVarargsはコンパイラによる警告を抑制し、JITの動作を変更しません。


6
それについての彼の質問には実際には答えませんが、興味深いです@SafeVarargs
ポールベロラ

1
いいえ。それはヒープ汚染とは異なります。「ヒープ汚染は、パラメーター化された型の変数がそのパラメーター化された型ではないオブジェクトを参照するときに発生します。」参考:docs.oracle.com/javase/tutorial/java/generics/...
Doradus

1

その理由は、varargsがパラメーター化されていないオブジェクト配列で呼び出されるオプションを提供するためです。したがって、タイプがList <A> ...の場合、List []非可変引数タイプで呼び出すこともできます。

次に例を示します。

public static void testCode(){
    List[] b = new List[1];
    test(b);
}

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

ご覧のとおり、List [] bには任意のタイプのコンシューマーを含めることができますが、このコードはコンパイルされます。varargsを使用する場合は問題ありませんが、型の消去後にメソッド定義を使用する場合(void test(List []))、コンパイラーはテンプレートパラメーターの型をチェックしません。@SafeVarargsはこの警告を抑制します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.