原生资源泄漏指AWT/Swing组件未释放操作系统级资源,导致句柄数暴涨、UI卡顿或崩溃;需通过jstat、lsof/Process Explorer、native memory tracking等定位未dispose的Graphics/Image/Font/Robot;修复须显式dispose且置于finally或try-with-resources中。
这类泄漏属于“原生资源泄漏”,不是 java 堆内存泄漏,而是 awt/swing 组件(如 graphics、bufferedimage、font、toolkit 创建的图像、robot 等)背后持有的操作系统级资源(如 gdi 句柄、x11 pixmap、cairo surface)未释放,导致进程句柄数暴涨、渲染卡顿甚至系统级崩溃。它不触发 outofmemoryerror: java heap space,但会引发 outofmemoryerror: unable to create new native thread 或 ui 冻结、绘图异常等表象。
确认是否为 AWT 原生资源泄漏
先排除堆泄漏干扰,聚焦原生层:
- 用
jstat -gc <pid>观察老年代占用是否稳定——若堆内存平稳但进程 RSS 持续上涨(ps -o pid,rss,comm -p <pid>),高度可疑 - 在 Linux 上执行
lsof -p <pid> | wc -l或cat /proc/<pid>/status | grep 'Threads\|FDSize\|SigQ',看文件描述符/线程数是否缓慢增长;Windows 下用 Process Explorer 查看 “GDI Objects” 和 “User Objects” 计数是否只增不减 - 调用
java.awt.GraphicsEnvironment.isHeadless()为 false 且应用大量使用createGraphics()、getGraphics()、createImage()等方法时,风险极高
定位未 dispose 的关键位置
AWT 资源必须显式 dispose(),不能依赖 finalize(JDK 9+ 已移除 AWT finalize 逻辑):
- 重点扫描所有
Graphics获取点:尤其是component.getGraphics()(严禁用于长期绘图)、image.createGraphics()、bufferStrategy.getDrawGraphics() - 检查
BufferedImage构造中传入的ColorModel或WritableRaster是否来自外部资源池;某些 JDK 版本中,用Graphics2D绘制到BufferedImage后,若该 image 被Toolkit.getDefaultToolkit().createImage()包装过,需额外 dispose 对应的Image - 排查
Font实例:通过GraphicsEnvironment.registerFont()加载的字体,或由Toolkit.createImage()返回的Image关联的Font,都可能持有 native peer - 留意
Robot实例:每次 new Robot() 都分配 native 层输入设备句柄,不用完立刻dispose()会快速耗尽
用工具验证与捕获泄漏路径
标准 JVM 工具对 native 资源无感知,需组合手段:
- JDK 自带
jcmd <pid> VM.native_memory summary(需启动时加-XX:NativeMemoryTracking=summary),观察Internal或Other区域持续增长,尤其配合 AWT 相关操作后突增 - Linux 下用
perf record -e syscalls:sys_enter_close -p <pid>抓 close 系统调用频次,对比正常时段——若 GDI/X11 close 类调用极少,说明资源未释放 - 启用 AWT 日志:
-Dsun.java2d.debugfonts=true -Dsun.java2d.trace=count,可输出 font/image 创建/销毁计数;搭配-Dsun.java2d.opengl.fbobject=false关闭易泄漏的 OpenGL 后端辅助定位 - 在关键代码块前后插入
Toolkit.getDefaultToolkit().getSystemEventQueue().peekEvent()并打点,间接判断 AWT 事件队列压力是否因资源堆积而升高
修复与防御措施
核心原则:谁创建,谁 dispose;且必须放在 finally 或 try-with-resources(需封装)中:
立即学习“Java免费学习笔记(深入)”;
- 所有
Graphics实例必须在使用完毕后立即g.dispose(),禁止缓存复用(除非你完全掌控其生命周期) - 避免
component.getGraphics():改用paint(Graphics)或BufferStrategy+getDrawGraphics(),并在show()后立刻 dispose - 对
BufferedImage,若其Graphics已 dispose,图像本身无需额外 dispose;但若通过Toolkit.getImage()或createImage()获取的Image,必须调用image.flush()(等效于 dispose) - 封装危险操作:写一个
AutoCloseableGraphics包装类,或用try (var g = image.createGraphics()) { ... }(Java 9+)强制约束 - 禁用高风险路径:生产环境设置
-Dsun.java2d.xrender=false(Linux)或-Dsun.java2d.d3d=false(Windows)规避已知驱动层泄漏



















