Dragonwell21项目中G1BarrierSetC2::emit_stubs内存泄漏问题分析
dragonwell21 项目地址: https://gitcode.com/gh_mirrors/dr/dragonwell21
问题背景
在Dragonwell21项目的开发过程中,开发团队在使用ASAN(AddressSanitizer)进行内存检测时,发现了一个潜在的内存泄漏问题。该问题出现在G1垃圾收集器的C2编译器屏障实现中,具体位置在G1BarrierSetC2::emit_stubs方法中。
问题现象
当使用ASAN工具运行特定测试用例时,检测到以下内存泄漏情况:
- 126字节的内存泄漏,分布在3个对象中
- 102字节的内存泄漏,同样分布在3个对象中
这些内存分配都发生在CodeBuffer::expand方法中,最终追溯到G1BarrierSetC2::emit_stubs方法的调用链路上。
技术分析
调用栈分析
从调用栈可以看出,内存泄漏发生在JVM编译过程中的代码生成阶段:
- 编译器线程启动并执行编译任务
- C2编译器开始编译方法
- 在代码生成阶段,G1屏障需要生成特定的stub代码
- 在生成stub代码时,CodeBuffer需要扩展缓冲区空间
- 在扩展过程中分配的内存没有被正确释放
问题本质
经过分析,这个问题实际上是ASAN工具的一个误报。CodeBuffer在JVM中有其特殊的内存管理机制,这些内存会在JVM生命周期结束时统一释放,而不是在每次使用后立即释放。这种设计是为了性能优化考虑,避免频繁的内存分配和释放操作。
解决方案
对于这类已知的ASAN误报问题,可以采用以下解决方案:
- 添加ASAN抑制规则:在LSAN_OPTIONS环境变量中指定suppressions文件,将CodeBuffer::expand方法添加到抑制列表中
- 抑制文件格式:在抑制文件中添加一行"leak:CodeBuffer::expand",告诉ASAN忽略这个特定函数的内存泄漏报告
最佳实践建议
- 对于JVM这类复杂系统,ASAN报告的内存泄漏需要结合具体上下文分析
- 不是所有ASAN报告的内存泄漏都是真正的bug,有些是设计使然
- 对于已知的误报,建立抑制规则是合理的解决方案
- 在开发过程中,应该定期检查ASAN报告,区分真正的内存泄漏和误报
总结
Dragonwell21项目中发现的这个内存泄漏问题实际上是一个ASAN工具的误报情况。通过分析我们了解到,JVM内部的CodeBuffer有其特殊的内存管理机制,这种看似"泄漏"的内存实际上是设计上的优化选择。开发团队通过添加ASAN抑制规则妥善解决了这个问题,既保证了内存检测的有效性,又避免了误报的干扰。
dragonwell21 项目地址: https://gitcode.com/gh_mirrors/dr/dragonwell21
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考