Java 垃圾收集和JNI调用

Java 垃圾收集和JNI调用,java,garbage-collection,java-native-interface,Java,Garbage Collection,Java Native Interface,我遇到了一个JNI程序随机内存不足的问题 这是一个32位java程序,它读取文件,进行一些图像处理,通常使用250MB到1GB。然后丢弃所有这些对象,然后程序对通常需要100-250MB的JNI程序进行一系列调用 当以交互方式运行时,我从未看到过问题。但是,在连续对多个文件执行批处理操作时,JNI程序将随机耗尽内存。它可能有一个或两个文件的内存问题,然后在接下来的10个文件中运行正常,然后再次出现故障 在JNI调用之前,我已经转储了大量的可用内存,这些内存到处都是,有时是100MB,有时是800

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

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

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

在JNI调用之前,我已经转储了大量的可用内存,这些内存到处都是,有时是100MB,有时是800MB。我的解释是,Java垃圾收集有时在图像处理之后立即运行,有时不运行。如果不是,那么JNI程序可能没有足够的内存

我已经阅读了所有关于GC是非确定性的、不应该调用它、不会产生任何影响等等的内容。但是在开始JNI调用之前强制GC似乎可以改善这种情况

但是,在继续之前,有没有办法真正确保有一定量的可用内存

为了回答关于另一家公司提供的JNI程序的问题,我对它如何分配内存没有真正的了解。我只知道它是C++,没有垃圾收集。有人告诉我它需要100-250MB的内存,我看到的数字可以证实这一点

也许我应该把问题改写成:如果我要打一个我知道需要250MB内存的JNI电话,我如何保证它有那么多可用内存


当然,一个可能的解决方案是进行64位构建。但是,此批处理操作是32位构建的QA的一部分,因此我想测试真实的内容。

以下假设您使用的是hotspot jvm

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

JVM将预先为托管堆保留一个固定的、可能很大的地址空间,然后在该空间的基础上动态分配一些内部结构,然后可能为DirectByteBuffers或内存映射文件分配更多的地址空间。这会给本机代码留下很少的运行空间

用于确定JVM的各个部分使用了多少,用于检查内存映射文件。然后,在不妨碍您的应用程序的情况下尽量限制它

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

FWIW(我意识到这是一种异端)添加一个调用

System.gc();

在对每个文件进行第一次JNI调用之前,对这种情况进行了重大改进。20%的文件没有出现内存错误,现在的内存错误率不到5%。更好的是,这些错误不再是随机的,而是可以在不同的运行中重复出现,因此可以推测它们是可以被跟踪的。

我自己解决这个问题的方法就是简单地调用
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);
}
#包括
// ...
int my_native_函数(JNIEnv*env,jobject obj){
jclass systemClass=nullptr;
jmethodID systemGCMethod=nullptr;
// ...
//把垃圾拿出去。
systemClass=env->FindClass(“java/lang/System”);
systemGCMethod=env->GetStaticMethodID(systemClass,“gc”和“()V”);
env->CallStaticVoidMethod(systemClass,systemGCMethod);
}

我希望这也适用于您。

您没有向我们显示任何代码。这就像让你的医生在电话里诊断你的间歇性疼痛,甚至不告诉她疼痛的部位。请访问并阅读。增加最大堆大小是一个选项吗?@JimGarrison不一定。他很好地描述了这个问题,以至于代码在一般情况下不会带来太多(即如果他没有在代码中做疯狂的事情)。这是关于JNI调用期间GC和内存使用的一般问题。我不能回答这个问题,但我仍然认为这个问题是好的,我认为至少需要更多关于JNI代码正在做什么,特别是它如何分配内存的细节。GC是一个非常困难的话题,关于具体案例的信息越多越好。问:你所说的“可用内存量”到底是什么意思?问:究竟是什么“内存不足”——JRE,还是您的操作系统?Java堆,还是“其他东西”?问:你的操作系统是什么?你的JRE版本?