我在调用 BufferedImage.getGraphics() 方法时遇到一些框架 API 的问题,从而导致内存泄漏。这个方法的作用是它总是调用 BufferedImage.createGraphics()。在 Windows 机器上, createGraphics() 由 Win32GraphicsEnvironment 处理,Win32GraphicsEnvironment在其字段displayChanger中保留一个侦听器列表。当我在 BufferedImage someChart上调用 getGraphics 时,someChart的 SurfaceManager(它保留对someChart的引用)被添加到 Win32GraphicsEnvironment 中的侦听器映射中,从而防止someChart被垃圾收集。之后什么都不会删除someChart的 SurfaceManager听众地图。
通常,一旦调用 getGraphics,阻止 BufferedImage 被垃圾收集的总结路径如下:
GC Root -> localGraphicsEnvironment(Win32GraphicsEnvironment) -> displayChanger(SunDisplayChanger) -> listeners(Map) -> key (D3DChachingSurfaceManager) -> 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 都变成了垃圾。
我考虑过SurfaceManager,作为displayListeners的key,可以共享或者重用,但是我的实验排除了这种可能性。我还考虑过缓存,我故意通过使每次重绘调用都独一无二来防止缓存发生。尽管如此,我仍然不知道这是如何发生的以及如何解决泄漏。