次のような非静的内部クラスのオブジェクトで初期化されるスレッドローカルオブジェクトがあります。
public class StressTestThreadLocal {
private final ThreadLocal<TObject> tObjectThreadLocal = ThreadLocal.withInitial(
() -> new TObject(1000));
private static ExecutorService executorService = Executors.newFixedThreadPool(4);
private void startThread() {
executorService.submit(tObjectThreadLocal::get);
}
public class TObject {
List<Integer> test;
TObject(int n) {
test = new ArrayList<>();
for (int i = 0; i < n; i++) {
test.add(i);
}
System.out.println("Done making TObject " + UUID.randomUUID());
}
}
public static void main(String[] args) {
for (int i = 0; i < 100000; i++) {
StressTestThreadLocal testThreadLocal = new StressTestThreadLocal();
testThreadLocal.startThread();
}
while (true) {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
このプログラムを実行し、jconsoleを接続して、GCを複数回トリガーしましたが、メモリ使用量は減少しませんでした。次に、ヒープダンプを取得し、TObject
クラスで作成されたオブジェクトの数を分析しました。100,000個のオブジェクトすべてがメモリ内で使用可能であることが示されました。
ヒープダンプのスクリーンショット、オブジェクト数を確認してください
内部クラスを静的にしたので、外部クラスオブジェクトを強く参照しなくなり、同じコードを再度実行しました。ここでGCをトリガーすると、メモリ使用量が大幅に減少し、メモリ内のオブジェクトの数は約3000にすぎませんでした。
3000個のオブジェクトのみを含むヒープダンプのスクリーンショット
私が理解しているかどうかわからないのはこれです:
最初のケースでは、outerObjectとinnerObjectは相互に強い参照を保持していますが、他の場所から強く参照されているものはありません。threadlocalmapにthreadlocal変数(TObject
)への弱参照のみが含まれていて、外部オブジェクトへの参照をStressTestThreadLocal
他の場所に保存しなかった場合、threadlocalオブジェクトがガベージコレクションの対象にならないのはなぜですか?そして、なぜ内部クラスを静的にすることでこの問題が自動的に解決されたのでしょうか?
スレッドローカルマップには、実際の値(この場合はTObject)への強い参照があります。マップのキー(ThreadLocal)のみが弱参照です。キーを弱参照にする理由は、キーが参照されなくなったときにマップからエントリを消去するためです。http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/lang/ThreadLocal.java?av=f#298
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
内部クラスが非静的である場合、オブジェクトには、StressTestThreadLocalへの強力な参照があり、tObjectThreadLocal(スレッドローカルマップのキー)への強力な参照があります。したがって、エントリがガベージコレクションされることはありません。
TLM-> TLM.Entry.Value(TObject)-> StressTestThreadLocal-> TLM.Entry.Key(ThreadLocal)
ここで、TLM =スレッドローカルマップ
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加