web-dev-qa-db-ja.com

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

これはJava 7でジェネリック型の可変引数を使用したときに起こることを理解しています。

しかし、私の質問は..

「その使用は潜在的にヒープを汚染する可能性がある」と言うとき、Eclipseは正確にはどういう意味ですか?

そして

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

396
hertzsprung

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

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

これは "説明できない" ClassCastExceptionsにつながる可能性があります。

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

@SafeVarargsはこれをまったく妨げません。しかし、たぶんヒープを汚染しないメソッドがあります。コンパイラはそれを証明できないだけです。以前は、そのようなAPIの呼び出し元は、まったく無意味であったがすべての呼び出しサイトで抑制されなければならなかった厄介な警告を受けるでしょう。これで、API作成者は宣言サイトで一度抑制することができます。

しかし、実際にその方法がnot safeであれば、ユーザーに警告は表示されなくなります。

231
Ben Schulz

宣言したとき

public static <T> void foo(List<T>... bar)コンパイラはそれをに変換します

次にpublic static <T> void foo(List<T>[] bar)

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

その結果、リストに誤った値を誤って割り当ててしまい、コンパイラがエラーを引き起こさないという危険性が生じます。たとえば、TStringの場合、次のコードはエラーなしでコンパイルされますが、実行時に失敗します。

// 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メソッドは、re-reifiable varargsパラメータからヒープ汚染を引き起こす可能性があります

そしてあなたはあなたの使い方が安全であることを確信しているので、あなたは代わりに@SuppressWarnings("varargs")を使うべきです。この2番目の種類のエラーについての良い説明は、 @SafeVarargsはこのメソッドの適切なアノテーションですか? および https://stackoverflow.com/a/14252221/14731 を参照してください。

参考文献:

215
Gili

@SafeVarargsはそれが起こるのを妨げません、しかしそれはそれを使うコードをコンパイルするときコンパイラがより厳密であることを強制します。

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

ヒープ汚染は、一般的なインターフェースで操作を実行したときにClassCastExceptionが返され、宣言された以外の型が含まれている場合です。

7
jontro

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

エスケープ分析により、JITはこの配列作成を最適化することができます。最適化されることは保証されていませんが、メモリプロファイラで問題が発生しない限り、心配はしません。

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

5
Peter Lawrey

その理由は、可変引数には、パラメータ化されていないオブジェクト配列を使用して呼び出すオプションがあるためです。あなたの型がList <A> ...だったなら、List [] non-varargs typeで呼び出すこともできます。

これが一例です。

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

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

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

1
user1122069