我创建了一个包含从C#代码调用的JNI函数的DLL。这个功能由长期运行的GUI应用程序C#应用程序多次调用。
我的JNI函数调用JNI_GetCreatedJavaVMs。如果虚拟机为零,则通过函数JNI_CreateJavaVM()创建一个,否则我的函数调用AttachCurrentThread()。
然后我的函数调用FindClass()并从JAR文件中加载类。
最后,我在该类上调用一个方法。
在被调用方法的Java代码中,我首先调用方法totalMemory()并将结果写入日志文件。然后该方法执行其他一些操作。
通常,方法totalMemory()
返回的值约为6600万,即大约64 MB,但有时返回的值约为200万,即2 MB。当totalMemory()
只有2 MB时,我的java方法会抛出OutOfMemoryError(java堆空间),因为没有足够的内存来分配我的java方法需要做的对象。
请注意,我的java方法包含catch (Throwable)
,以便C#应用程序可以继续。在OutOfMemoryError
之后,对于以后对我的JNI函数的调用,totalMemory()
将再次返回64 MB的值。
什么会导致分配给JVM的总内存从64 MB减少到2 MB?
请注意,我指的是总内存,而不是空闲内存。我认为总内存不应更改。我相信我的日志文件证明了我的假设,因为如上所述,它几乎总是64 MB。有时只是下降到2 MB,因此出现了这个问题。为什么总内存有时会下降到2 MB?
我的(精简的)JNI代码。
#include <jni.h>
void main()
{
JavaVM *jvm;
JNIEnv *env;
JavaVMInitArgs vm_args;
jint rslt;
jclass cls;
jmethodID mid;
jobject jObj;
JavaVMOption* options = new JavaVMOption[1];
options[0].optionString = "-Djava.class.path=jni_test.jar";
vm_args.version = JNI_VERSION_1_10;
vm_args.nOptions = 1;
vm_args.options = options;
vm_args.ignoreUnrecognized = false;
rslt = JNI_CreateJavaVM(&jvm, (void**) &env, &vm_args);
cls = env->FindClass("jni_test/JniTest0");
mid = env->GetStaticMethodID(cls, "getReply", "()Ljava/lang/String;");
jObj = env->CallStaticObjectMethod(cls, mid);
}
在上面的JNI代码中调用的我的(简化的)java方法。
public static String getReply() {
long totalMemory = Runtime.getRuntime().totalMemory();
System.out.println("total memory = " + totalMemory);
String reply = "That is the correct answer! Well done!";
return reply;
}
答案 0 :(得分:1)
据我了解的JVM内存模型:
maxMemory
是JVM可以使用的最大数量(由-Xmx定义)
maxMemory
= totalMemory
+未分配的内存
totalMemory
-freeMemory
= used
尽管很少(afaik)看到JVM将内存释放回操作系统, 可能并且会解释您的经历。
totalMemory
不应低于-Xms
定义的值,
因此以固定的内存值运行jvm可能会解决您的问题。
例如:-Xms64m -Xmx64m
答案 1 :(得分:0)
我的怀疑似乎已被证明是正确的。有时错误消息可能会引起误解。从一开始我就感到直觉不是问题,实际上不是内存问题,就像我说的那样,看来我是对的。
我的代码使用Java日志记录将日志文件写入Windows临时文件夹中。事实证明,另一个不相关的进程也将日志文件写入同一文件夹。实际上,它实际上将数千个文件写入该文件夹,并且这些文件永远不会删除。删除这些文件后,问题就消失了。
所以问题出在以下事实:临时文件夹已满,文件爆满-我的代码都不写。可以检查是否有人遇到类似问题并由于互联网搜索而出现在这里。