面试题答案
一键面试可能导致内存泄漏的Native方法使用场景
- 未正确释放资源:在Native方法中分配了内存(如通过C的
malloc
函数),但在Java代码中没有相应地调用释放内存的方法(如C的free
函数)。例如,使用JNI调用C库函数进行图像数据处理,C函数分配了一块大的图像缓冲区,但处理完后没有释放。 - 对象生命周期管理不当:Native方法持有Java对象的引用,但没有正确处理对象的生命周期。比如,Native代码创建了一个全局的Java对象引用,即使Java层面已经不再使用该对象,由于Native方法一直持有引用,垃圾回收器无法回收该对象,导致内存泄漏。
- 资源缓存未清理:在Native方法中实现了缓存机制,但缓存的资源没有在合适的时候清理。例如,缓存了数据库连接对象,在应用结束或数据库配置变更时没有关闭和清理这些连接。
利用MAT(Memory Analyzer Tool)进行排查和定位
- 生成堆转储文件
- 在Java应用运行时,使用
jmap
工具生成堆转储文件(.hprof
文件)。例如,假设Java进程ID为1234
,可以在命令行执行jmap -dump:format=b,file=heapdump.hprof 1234
。
- 在Java应用运行时,使用
- 导入堆转储文件到MAT
- 打开MAT工具,选择
File
->Open Heap Dump
,然后选择生成的.hprof
文件。
- 打开MAT工具,选择
- 分析对象引用关系
- Histogram视图:查看
Histogram
视图,它按类列出堆中的对象数量和大小。关注那些数量异常多或占用内存过大的类,这些类可能与内存泄漏有关。 - Dominator Tree视图:切换到
Dominator Tree
视图,它显示对象之间的支配关系(即如果一个对象被释放,哪些对象也会被释放)。通过这个视图可以找到占用大量内存的对象及其引用链。 - Retained Heap:在上述视图中,关注
Retained Heap
列,它表示对象及其所有被它直接或间接引用的对象所占用的内存大小。通常,内存泄漏对象会有较大的Retained Heap
值。
- Histogram视图:查看
- 定位Native方法相关对象
- 由于是在包含Native方法调用的项目中排查,查找与Native方法相关的类或对象。这可能包括JNI本地接口相关的类,或者在Native代码中创建或引用的Java对象。
- 通过分析引用链,判断是否存在Native方法导致的对象无法被垃圾回收。例如,如果发现某个Java对象的引用链中有Native层的对象一直持有其引用,且该Java对象在Java层面不应再被使用,可能就是内存泄漏点。
- 使用OQL(Object Query Language)查询
- MAT提供了OQL,可以编写查询语句来筛选特定的对象。例如,可以编写查询语句查找特定类的对象,或者根据对象的属性进行筛选,进一步定位可能存在内存泄漏的对象。例如,查询
select * from instanceof com.example.MyNativeRelatedClass
,以找出所有MyNativeRelatedClass
类型的对象及其相关信息。
- MAT提供了OQL,可以编写查询语句来筛选特定的对象。例如,可以编写查询语句查找特定类的对象,或者根据对象的属性进行筛选,进一步定位可能存在内存泄漏的对象。例如,查询