クラス自体がガベージ コレクションされる場合、静的変数はガベージ コレクションされる必要があります。これは、そのクラス ローダーがガベージ コレクションされる場合です。
クラス (またはクラスのインスタンス) への参照を持つアプリケーションのクラスローダーによってロードされなかったものをすべて持つことで、メモリ リークを簡単に作成できます。適切に削除しなかったコールバック リスナーなどを探します (内部/匿名クラスは見過ごされがちです)。
クラスの 1 つを 1 回参照すると、そのクラス ローダーが防止され、そのクラス ローダーによってロードされたすべてのクラスがガベージ コレクションの対象になります。
編集、すべてのクラスの GC を妨げるオブジェクトのリークの例:
MemoryMXBean mx = ManagementFactory.getMemoryMXBean();
NotificationListener nl = new NotificationListener() { ... };
((NotificationEmitter) mx).addNotificationListener(nl, ..., ...);
アプリケーション スコープの外に存在するオブジェクト (ここでは MemoryMXBean) にリスナー (ここでは NotificationListener) を登録すると、リスナーは明示的に削除されるまで「ライブ」のままになります。リスナー インスタンスは ClassLoader (アプリケーション クラスローダー) への参照を保持しているため、クラスローダーの GC を防止する強力な参照チェーンが作成されました。これにより、ロードされたすべてのクラス、およびそれを介して、それらのクラスが保持するすべての静的変数が防止されます。
Edit2:基本的に、この状況を回避する必要があります:
[Root ClassLoader]
|
v
[Application ClassLoader]
|
v
(Type loaded by Root).addSomething()
アプリケーション サーバーを実行している JVM は、ルート クラス ローダー (および場合によってはアプリケーション サーバーも) を介して JRE をロードしました。つまり、それらのクラスの一部へのライブ参照が常に存在するため、それらのクラスが GC の対象になることは決してありません。アプリケーション サーバーはアプリケーションを別のクラス ローダーにロードし、アプリケーションが再デプロイされると (または少なくとも再デプロイする必要があります)、参照を保持しなくなります。ただし、アプリケーションは、少なくとも JRE のすべてのクラスをアプリケーション サーバーと共有します (少なくとも JRE ですが、通常はアプリケーション サーバーも共有します)。
アプリケーション サーバーが別のクラス ローダー (親なし、実際には 2 番目のルート クラス ローダー) を作成し、JRE をもう一度 (アプリケーションのプライベートとして) ロードしようとした場合、多くの問題が発生します。 . シングルトンとして意図されたクラスが 2 つ存在し、2 つのクラス階層が他方の参照を保持できなくなります (JVM の異なるタイプである異なるクラス ローダーによってロードされた同じクラスが原因)。それぞれの「他の」クラス・ローダー・オブジェクトの参照型として java.lang.Object を使用することさえできませんでした。