Java应用程序通过JNI调用C++ DLL;如何最好地分配内存?

10

问题的基本总结如下: 如何最佳优化我的内存分配,以尽可能多地为我通过JNI访问的DLL提供内存? 我应该最小化什么,最大化什么等。

系统: 在具有4GB RAM的32位系统中作为Windows 32服务运行JBoss 6。我确实了解Java Heap的内存有最大限制。JVM是JRE1.6.0_26

服务: 安装在JBoss下的Web应用程序接收来自客户端的请求; 每个请求通过JNI调用C++构建的DLL以某种方式处理图像文件。

问题: 偶尔,在较大或某些(不是全部)LZW压缩的图像上,调用Java类会收到消息,指示DLL经历了全局内存耗尽并未能完成所请求的过程。

除了基本的Windows进程外,服务器上没有其他正在运行的内容。

当前的JBOSS应用程序服务器内存设置如下,但可能过多:

-Xms1024m -Xmx1024m -Xss1024k -XX:MaxPermSize=128m

我正在尝试确定最佳的内存设置,以便尽可能多地为JNI DLL提供资源,因为我了解JNI不使用分配给Java Heap的任何内存。

我已阅读以下内容,但没有找到有助于回答我的问题:

Java JNI:内存分配/分区

可以使用jconsole识别JNI C ++对象中的内存泄漏吗?

目前提供的两个答案都未解决基本问题。

JVM参数设置为上述值后一周后JBoss服务器的当前内存(任务管理器显示java.exe进程为750,672k)

Total Memory Pools: 5

Pool: Code Cache (Non-heap memory)

    Peak Usage : init:2359296, used:7317312, committed:7438336, max:50331648
    Current Usage : init:2359296, used:7306496, committed:7438336, max:50331648


        |---------| committed:7.09Mb
        +---------------------------------------------------------------------+
        |/////////| | max:48Mb
        +---------------------------------------------------------------------+
        |---------| used:6.97Mb


Pool: PS Eden Space (Heap memory)

    Peak Usage : init:268500992, used:354811904, committed:354811904, max:355991552
    Current Usage : init:268500992, used:270153472, committed:354091008, max:354156544


        |--------------------------------------------------------------------| committed:337.69Mb
        +---------------------------------------------------------------------+
        |///////////////////////////////////////////////////// || max:337.75Mb
        +---------------------------------------------------------------------+
        |----------------------------------------------------| used:257.64Mb


Pool: PS Survivor Space (Heap memory)

    Peak Usage : init:44695552, used:44694896, committed:78643200, max:78643200
    Current Usage : init:44695552, used:0, committed:1835008, max:1835008


        |---------------------------------------------------------------------| committed:1.75Mb
        +---------------------------------------------------------------------+
        | | max:1.75Mb
        +---------------------------------------------------------------------+
        | used:0b


Pool: PS Old Gen (Heap memory)

    Peak Usage : init:715849728, used:123671968, committed:715849728, max:715849728
    Current Usage : init:715849728, used:104048648, committed:715849728, max:715849728


        |---------------------------------------------------------------------| committed:682.69Mb
        +---------------------------------------------------------------------+
        |////////// | max:682.69Mb
        +---------------------------------------------------------------------+
        |---------| used:99.23Mb


Pool: PS Perm Gen (Non-heap memory)

    Peak Usage : init:16777216, used:91989664, committed:134217728, max:134217728
    Current Usage : init:16777216, used:90956472, committed:90963968, max:134217728


        |----------------------------------------------| committed:86.75Mb
        +---------------------------------------------------------------------+
        |//////////////////////////////////////////////| | max:128Mb
        +---------------------------------------------------------------------+
        |----------------------------------------------| used:86.74Mb
1个回答

16

由JNI封装的本地代码分配的内存分配给JVM进程,但不受您的Java代码控制。 它不是堆的一部分,并且不能通过JVM参数进行调整。 基本上,使用本地malloc分配的任何内容都必须由该本地代码管理。 如果您控制使用的库,则必须检查资源泄漏。 如果这在长时间运行的过程中使用,这尤其重要。

根据我的经验,最好的方法是通过提取JVM公开的JMX统计信息来检查实际的内存使用情况。 一旦你知道你的Java应用程序消耗了多少内存,你就会更好地了解在哪里设置最大堆设置。 Permgen空间用于类定义等,因此除非正在执行大量动态类加载,否则您通常不需要太多内存。

虽然您无法调整JNI库可用的内存,但调整为堆保留的内存可能会释放可以供库使用的资源。

预期的是,将堆内存峰值相加,得到约1022.19(堆的最大大小)。 当堆用完时,将启动完整GC运行并回收脏堆。 根据您提供的数字,我建议从Xmx512m开始。 这将为您的JNI代码提供足够的空间。

如果发现由于过多的垃圾回收而导致JVM挣扎,这意味着您很快就会用完Java堆,则可以增加该分配量。 但是,如果它正在迅速占用512mb以致引起明显的性能影响,则短期内除了显着增加外,几乎不会有任何效果。 这完全取决于您的程序,它有多快消耗Java堆以及完整GC运行的效果有多好。


1
如果我没有明确说明,你真的无法设置JNI库可以请求的内存量的最大限制。 - allingeek
那么您的意思是要尽量减少分配给JVM进程的总内存吗?我是否可以从JMX给出的总内存值中减去JVM进程消耗/分配的值,以确定DLL使用的数量? - JoshDM
1
是的,大约如此。在32位架构上,进程堆(包含Java堆和其他所有内容)受到约2GB的限制。在64位架构上,限制要大得多。如果您将几个G的内存专门分配给Java堆和程序内存的其他Java特定组件,则不会留下太多空间用于JNI内存分配(该内存分配针对进程堆进行)。 - allingeek
2
进程堆的“其他所有”部分包括:永久空间、代码生成、套接字缓冲区、线程堆栈、直接内存空间、JNI 代码、垃圾回收和 JNI 分配的内存。因此,虽然减法可以给出此空间的总量,但它不仅仅是 JNI 分配的空间(尽管这可能是其最大的消耗者)。 - allingeek
好的,那么让我们看看我这里有什么:-Xms1024m -Xmx1024m -Xss1024k -XX:MaxPermSize=128m。假设我不需要这些值像它们现在这样高(我受到32位架构的限制),我应该尽可能地降低Xms、Xmx和MaxPermSize的值以释放JNI,对吗?是否存在其他运行时参数,我可能要考虑包括,以可能增强进程堆分配?JVM是JRE1.6.0_26 - JoshDM
2
好的,还有其他参数可以帮助您调整Java堆使用情况,但这些是最重要的。我建议您运行一下代码,确定典型的堆占用情况,并将其分配30-40%,并将其设置为最大堆大小。我建议保留Xms默认值,因为您只想使用所需的内存,除非您看到特定运行时Permgen空间不足的错误,否则也不要更改它。除非您在此处运行大规模多线程应用程序,否则也不要更改Xss。最重要的是Xmx(最大堆大小)。 - allingeek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接