2013-05-04 127 views
1

我有一個Java應用程序使用本機庫的一些功能。它使用JNI來控制本地庫,並從庫中接收異步回調。您可以將它看作是彼此通信的Java前端和本地後端。調試JVM內存泄漏

我正面臨內存泄漏。在我啓動應用程序後不久,內存緩慢但穩步增加。所以我試圖尋找可能導致泄漏的原因。

首先,我嘗試用簡單的C++文本接口替換Java前端。這樣,應用程序不會以任何方式使用Java - 並且泄漏停止。所以問題必須在Java前端。

所以我啓動了jvisualVM來查看堆是否增加 - 事實證明它沒有。 Java堆大小相當穩定。我甚至用xmx32m啓動了這個程序,但是內存一直保持在100m以上,沒有任何的OutOfMemoryErrors。事實上,jvisualVM在7米左右顯示了Java堆。

因此,我用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分析其中一個塊時,我得到:

*** ERROR: Symbol file could not be found. Defaulted to export symbols for jvm.dll

沒有任何堆棧跟蹤。所以這些塊被分配到jvm.dll中的某處,並且因爲沒有用於JVM的pdbs,所以我無法進一步調試泄漏。

我成功地找出了漏洞在我的代碼中發生的位置。所有callbacs到Java前端通過一個功能:

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

Windows堆是一箇舊的API。大多數malloc()實現不使用它們。 – 2013-05-04 21:04:23

+0

@brian請你詳細說明一下嗎?還有哪些其他API被使用?這是如何解釋我在Windbg輸出中看到的增長? – 2013-05-04 21:13:04

+1

我看過的所有malloc()都使用VirtualAlloc()。如果堆增長與進程大小增長相匹配,那麼使用Windows堆的是一些API。 – 2013-05-04 21:56:53

回答

0

malloc()內部調用HeapAlloc()。我想你需要一個'Release'方法釋放由JVM分配的內存,只要你的庫持有引用JVM的內部狀態。