简体   繁体   English

BufferedImage.getGraphics()导致内存泄漏,是否有修复?

[英]BufferedImage.getGraphics() resulting in memory leak, is there a fix?

I'm having problem with some framework API calling BufferedImage.getGraphics() method and thus causing memory leak. 我遇到一些框架API调用BufferedImage.getGraphics()方法的问题,从而导致内存泄漏。 What this method does is that it always calls BufferedImage.createGraphics(). 这个方法的作用是它总是调用BufferedImage.createGraphics()。 On a windows machine, createGraphics() is handled by Win32GraphicsEnvironment which keeps a listeners list inside its field displayChanger . 在Windows机器上,createGraphics()由Win32GraphicsEnvironment处理,它将侦听器列表保存在其字段displayChanger中 When I call getGraphics on my BufferedImage someChart , someChart 's SurfaceManager(which retains a reference to someChart ) is added to the listeners map in Win32GraphicsEnvironment, preventing someChart to be garbage collected. 当我在BufferedImage someChart上调用getGraphics时, someChart的SurfaceManager(保留对someChart的引用)被添加到Win32GraphicsEnvironment中的侦听器映射中,从而防止someChart被垃圾收集。 Nothing afterwards removes someChart 's SurfaceManager from the listeners map. 之后没有任何东西从侦听器地图中删除someChart的SurfaceManager。

In general, the summarized path stopping a BufferedImage from being garbage collected, once getGraphics is called, is as follows: 通常,一旦调用getGraphics,阻止BufferedImage被垃圾收集的汇总路径如下:

GC Root -> localGraphicsEnvironment(Win32GraphicsEnvironment) -> displayChanger(SunDisplayChanger) -> listeners(Map) -> key (D3DChachingSurfaceManager) -> bImg(BufferedImage) GC Root - > localGraphicsEnvironment(Win32GraphicsEnvironment) - > displayChanger(SunDisplayChanger) - > listeners(Map) - > key (D3DChachingSurfaceManager) - > bImg(BufferedImage)

I could have changed the framework's code so that after every called to BufferedImage.getGraphics(), I keep a reference to the BufferedImage's SurfaceManager. 我本可以更改框架的代码,以便在每次调用BufferedImage.getGraphics()之后,我保留对BufferedImage的SurfaceManager的引用。 Then, I get hold of localGraphicsEnvironment, cast it to Win32GraphicsEnvironment, then call removeDisplayChangedListener() using the reference to the BufferedImage's SurfaceManager. 然后,我获取localGraphicsEnvironment,将其强制转换为Win32GraphicsEnvironment,然后使用对BufferedImage的SurfaceManager的引用调用removeDisplayChangedListener()。 But I don't think this is a proper way to solve the problem. 但我不认为这是解决问题的正确方法。

Could someone please help me with this issue? 有人可以帮我解决这个问题吗? Thanks a lot! 非常感谢!


MORE DETAILS AND FINDINGS 更多细节和发现

The component I'm trying to add to my UI is makes calls to BufferedImage.getGraphics() every time it is repainted. 我试图添加到我的UI的组件每次重新绘制时都会调用BufferedImage.getGraphics()。 As a result, the number of garbage kept by displayChanger (inside SunGraphicsEnvironment ) should grow as the component gets repainted. 因此, displayChanger (在SunGraphicsEnvironment内部)保留的垃圾数量应随着组件重新绘制而增长。

However, things a behaving weirdly enough: 然而,事情表现得非常奇怪:

when I counted my actions on my UI which would surely trigger repaint, then check the number of garbage listeners inside displayChanger against my count, they don't match up. 当我在我的UI上计算我的操作肯定会触发重绘时,然后检查displayChanger中的垃圾监听器的数量与我的计数,它们不匹配。 (eg. There were 8 listeners before my clicks, and I made 60 clicks. After all, there are only 18 listeners.) (例如,在点击之前有8位听众,我点击了60次。毕竟,只有18位听众。)

On the other hand, if I turn on the breakpoint, and step into the process of adding things to displayListeners , every single click resulted in a new entry in displayListeners . 另一方面,如果我打开断点,并进入向displayListeners添加内容的过程,则每次单击都会在displayListeners中生成一个新条目。 And thus, every BufferedImage held by displayListeners become garbage. 因此, displayListeners持有的每个BufferedImage 都会变成垃圾。

I considered the possibility of SurfaceManager, which is used as the key for displayListeners, may be shared or reused, yet my experiment ruled out this possibility. 我认为可以共享或重用SurfaceManager作为displayListeners的键的可能性,但我的实验排除了这种可能性。 I also considered caching and I deliberately prevented caching from happening by making every call to repaint unique. 我也考虑过缓存,我故意通过每次调用重绘独特的方式来防止缓存发生。 Still, I have no clue how this could happen and how to solve the leak. 不过,我不知道如何发生这种情况以及如何解决泄漏问题。

After rendering the BufferedImage , you should invoke dispose() on the graphics context returned by createGraphics() . 渲染BufferedImage ,应该在createGraphics()返回的图形上下文中调用dispose() createGraphics() Here's an example and a list of similar methods. 这是一个示例和类似方法的列表

Addendum: This seems like an object leak called packratting ; 附录:这似乎是一个称为包装的物体泄漏; the listener mismatch sounds like an artifact of using the debugger. 监听器不匹配听起来像使用调试器的工件。 You might get some ideas from the article Plugging memory leaks with soft references , by Brian Goetz. 您可以从Brian Goetz的文章“ 使用软引用插入内存泄漏”中获得一些想法。

当您不再需要图像时,尝试调用flush()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM