BufferedImage.getGraphics() メソッドを呼び出すフレームワーク API に問題があり、メモリ リークが発生しています。このメソッドが行うことは、常に BufferedImage.createGraphics() を呼び出すことです。Windows マシンでは、 createGraphics() は Win32GraphicsEnvironment によって処理され、フィールドdisplayChanger内にリスナーリストが保持されます。BufferedImage someChartで getGraphics を呼び出すと、someChartの SurfaceManager ( someChart への参照を保持する) がWin32GraphicsEnvironmentのリスナーマップに追加され、someChartがガベージ コレクションされるのを防ぎます。その後、someChartの SurfaceManager をリスナーマップ。
一般に、getGraphics が呼び出されると、BufferedImage がガベージ コレクションされるのを停止する要約されたパスは次のとおりです。
GC ルート -> localGraphicsEnvironment(Win32GraphicsEnvironment) -> displayChanger(SunDisplayChanger) -> listeners(Map) ->キー(D3DCachingSurfaceManager) -> bImg(BufferedImage)
フレームワークのコードを変更して、BufferedImage.getGraphics() を呼び出すたびに BufferedImage の SurfaceManager への参照を保持するようにすることもできました。次に、localGraphicsEnvironment を取得して Win32GraphicsEnvironment にキャストし、BufferedImage の SurfaceManager への参照を使用して removeDisplayChangedListener() を呼び出します。しかし、これは問題を解決するための適切な方法ではないと思います。
誰かがこの問題について私を助けてくれますか? どうもありがとう!
詳細と調査結果
UI に追加しようとしているコンポーネントは、再描画されるたびに BufferedImage.getGraphics() を呼び出します。その結果、displayChanger ( SunGraphicsEnvironment内) によって保持されるガベージの数は、コンポーネントが再描画されるにつれて増加するはずです。
ただし、物事は十分に奇妙に動作します。
確実に再描画をトリガーする UI でのアクションをカウントし、 displayChanger内のガベージ リスナーの数をカウントに対してチェックすると、それらは一致しません。(例: クリックする前に 8 人のリスナーがいて、60 回クリックしました。結局のところ、リスナーは 18 人しかいません。)
一方、ブレークポイントをオンにして、displayListenersに何かを追加するプロセスに進むと、クリックするたびに、 displayListenersに新しいエントリが作成されます。したがって、displayListenersが保持するすべての BufferedImageはガベージになります。
displayListeners のキーとして使用される SurfaceManager が共有または再利用される可能性があると考えましたが、私の実験ではその可能性は否定されました。キャッシングも検討し、repaint のすべての呼び出しを一意にすることで、意図的にキャッシングが発生しないようにしました。それでも、これがどのように発生するのか、またリークを解決する方法はわかりません。