【问题标题】:Debugging JVM memory leak调试JVM内存泄漏
【发布时间】:2013-05-04 20:56:09
【问题描述】:

我有一个 Java 应用程序,它使用本机库来实现其某些功能。它使用 JNI 来控制本机库,并从库中接收异步回调。您可以将其视为相互通信的 Java 前端和本机后端。

我正面临内存泄漏。在我启动应用程序后不久,内存缓慢而稳定地增加。因此,我试图查看可能导致泄漏的原因。

首先,我尝试用一​​个简单的 C++ 文本界面替换 Java 前端。这样一来,应用程序就不会以任何方式使用 Java - 并且泄漏停止了。所以问题一定出在Java前端。

所以我启动了 jvisualVM 以查看堆是否增加 - 结果并没有。 Java 堆大小相当稳定。我什至用 xmx32m 启动了这个程序,但是内存一直增加到超过 100m,没有任何OutOfMemoryErrors。事实上,jvisualVM 显示 Java 堆大约为 7m。

因此,我使用 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 的 pdb,我无法进一步调试泄漏。

我设法查明了代码中发生泄漏的位置。 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 错误吗?我怎么知道?

【问题讨论】:

  • Windows 堆是旧的 API。大多数 malloc() 实现不使用它们。
  • @brian 你能详细说明一下吗?然后使用了哪些其他 API?这如何解释我在 Windbg 输出中看到的增加?
  • 我看过的所有 malloc() 都使用 VirtualAlloc()。如果堆增长与进程大小增长相匹配,那么就是某些 API 使用 Windows 堆。

标签: java c++ memory-leaks jvm java-native-interface


【解决方案1】:

malloc() 在内部调用 HeapAlloc()。我猜你需要一个“释放”方法来释放 JVM 分配的内存,只要你的库持有对 JVM 内部状态的引用。

【讨论】:

    猜你喜欢
    • 2010-12-01
    • 2021-01-10
    • 2010-11-23
    • 2018-11-22
    • 2016-11-04
    • 1970-01-01
    • 2013-09-28
    • 2010-10-14
    • 1970-01-01
    相关资源
    最近更新 更多