垃圾收集和JNI呼叫

时间:2016-07-31 05:40:11

标签: java garbage-collection java-native-interface

我遇到了JNI程序随机内存不足的问题。

这是一个32位的java程序,它读取文件,进行一些图像处理,通常使用250MB到1GB。然后丢弃所有这些对象,然后程序对JNI程序进行一系列调用,通常需要100-250MB。

以交互方式运行时,我从未见过任何问题。但是,当运行批处理操作,在许多文件上连续执行此操作时,JNI程序将随机耗尽内存。它可能会对一个或两个文件产生内存问题,然后对接下来的10个文件运行正常,然后再次出现故障。

我在JNI调用之前已经释放了大量的可用内存,它已经遍布地图,有时是100MB,有时是800MB。我的解释是Java垃圾收集有时会在图像处理后立即运行,有时则不会。如果不是,则可能没有足够的内存用于JNI程序。

我已经阅读了关于GC不确定性的所有内容,不应该调用它,不会有任何区别等等但是在启动JNI调用之前确实似乎强制GC会改善这种情况。

但有没有办法在继续之前确保有一定数量的可用内存?

回答有关JNI程序的问题,这是由另一家公司提供的,我对如何分配内存没有真正的了解。我所知道的是它是在c ++中,它没有垃圾收集。我被告知它需要100-250MB的内存,而我所看到的数字将证实这一点。

也许我应该重新提出这个问题:如果我要打一个我知道需要250MB内存的JNI电话,我怎么能保证它会有那么多的内存?

当然,一种可能的解决方案是进行64位构建。但是,这个批处理操作是32位构建的QA的一部分,所以我想测试真实的东西。

3 个答案:

答案 0 :(得分:3)

我自己解决此问题的方法只是调用System.gc(),但是从里面调用本机代码:

#include <jni.h>
// ...
int my_native_function(JNIEnv* env, jobject obj) {
    jclass    systemClass    = nullptr;
    jmethodID systemGCMethod = nullptr;
    // ...
    // Take out the trash.
    systemClass    = env->FindClass("java/lang/System");
    systemGCMethod = env->GetStaticMethodID(systemClass, "gc", "()V");
    env->CallStaticVoidMethod(systemClass, systemGCMethod);
}

我希望这也适合你。

答案 1 :(得分:1)

以下假设您正在使用热点jvm。

32位进程不仅受提交内存的限制,更重要的是它们受虚拟内存约束,即保留地址空间。在64位系统上,您只能使用4GB的地址,在32位系统上,它只有2-3GB。

JVM将为托管堆预先保留一个固定的,可能是大量的地址空间,然后在该数量之上动态分配一些内部结构,然后甚至可能更多地用于DirectByteBuffers或内存映射文件。这可以为本机代码留下很小的空间。

使用Native Memory Tracking确定JVM的各个部分正在使用多少,并pmap <pid>检查内存映射文件。然后尝试在不妨碍您的申请的情况下限制它。

或者,您可以生成一个新进程并在那里进行图像处理。

答案 2 :(得分:1)

FWIW(我意识到这有点异端)添加了对

的调用
System.gc();

在每个文件的第一次JNI调用之前,对这种情况做出了显着的改进。它不是在20%的文件上获得内存错误,而是现在小于5%。更好的是,错误不再是随机的,但是从一次运行到另一次运行是可重复的,因此可能会被追踪。