我有一个Java应用程序,它使用本机库来实现它的一些功能。它使用JNI来控制本机库,并从库中接收异步回调。您可以将其视为可以相互通信的Java前端和本机后端。
我正面临内存泄漏。在我开始应用程序后不久,内存缓慢但稳定地增加。所以我试着看看可能导致泄漏的原因。
首先,我尝试用简单的C ++文本接口替换Java前端。这样,应用程序不会以任何方式使用Java - 并且泄漏已停止。所以问题必须在Java前端。
所以我启动了jvisualVM以查看堆是否增加 - 事实证明它没有。 Java堆大小相当稳定。我甚至用xmx32m启动了这个程序,但是在没有任何OutOfMemoryError
s的情况下,内存保持在100m以上。事实上,jvisualVM显示Java堆大约7米。
所以我用WinDbg深入研究了这个程序。我用!heap -s
命令分析了堆模式,我得到了这个:
重新开始一个新的计划:
0:059> !heap -s
LFH Key : 0x382288b9
Termination on corruption : ENABLED
Heap Flags Reserv Commit Virt Free List UCR Virt Lock Fast
(k) (k) (k) (k) length blocks cont. heap
-----------------------------------------------------------------------------
00330000 00000002 2048 1704 2048 22 71 2 0 0 LFH
005b0000 00001002 1088 212 1088 68 3 2 0 0 LFH
00aa0000 00001002 1088 108 1088 15 7 2 0 0 LFH
004f0000 00001002 15424 12876 15424 1372 89 9 0 1 LFH
...
0:059> !heap -stat -h 004f0000
heap @ 004f0000
group-by: TOTSIZE max-display: 20
size #blocks total ( %) (percent of total busy bytes)
2b110 20 - 562200 (60.36)
98 166e - d5150 (9.33)
6cd20 1 - 6cd20 (4.77)
...
已经运行了大约半小时的程序:
0:046> !heap -s
LFH Key : 0x5e47ba72
Termination on corruption : ENABLED
Heap Flags Reserv Commit Virt Free List UCR Virt Lock Fast
(k) (k) (k) (k) length blocks cont. heap
-----------------------------------------------------------------------------
006b0000 00000002 2048 1744 2048 46 92 2 0 0 LFH
00200000 00001002 1088 220 1088 68 3 2 0 0 LFH
00950000 00001002 1088 108 1088 15 7 2 0 0 LFH
001b0000 00001002 47808 31936 47808 1855 102 12 0 0 LFH
...
0:046> !heap -stat -h 001b0000
heap @ 001b0000
group-by: TOTSIZE max-display: 20
size #blocks total ( %) (percent of total busy bytes)
98 59d1 - 355418 (36.67)
2b110 10 - 2b1100 (29.61)
6cd20 1 - 6cd20 (4.68)
...
现在可以清楚地看到泄漏是由越来越多的块大小为98的块引起的。但是当我尝试用!heap -p -a
分析其中一个块时,我得到:
***错误:找不到符号文件。默认导出jvm.dll的符号
没有任何堆栈跟踪。因此块被分配在jvm.dll中的某个地方,并且由于JVM没有pdbs,我无法进一步调试泄漏。
我设法确定了代码中泄漏的位置。 Java前端的所有callbac都通过一个函数:
void callback(JNIEnv *env, int stream, double value, char *callbackName){
jclass jni = env->FindClass("nativ/Callbacks");
jmethodID callbackMethodID = env->GetStaticMethodID(jni, callbackName, "(ID)V");
jvalue params[2];
params[0].i = (long)(stream);
params[1].d = value;
env->CallStaticVoidMethodA(jni, callbackMethodID, params); //commenting this out stops the leaks
}
当我注释掉最后一个命令时,泄漏会停止,但我没有得到反馈给前端。
这可能是JVM错误吗?我怎么知道?
答案 0 :(得分:0)
malloc()在内部调用HeapAlloc()。我猜你需要一个'Release'方法来释放JVM分配的内存,只要你的库保持对JVM内部状态的引用。