Reputation: 20923
I created a DLL that contains a JNI function which is called from C# code. The function is called many times by the C# application which is a long-running GUI application.
My JNI function calls JNI_GetCreatedJavaVMs. If there are zero VMs, then one is created via function JNI_CreateJavaVM(), otherwise my function calls AttachCurrentThread().
Then my function calls FindClass() and loads a class from a JAR file.
Finally I call a method on that class.
In the java code of the invoked method, I first call method totalMemory() and write the result to a log file. The method then performs some other operations.
Usually method totalMemory()
returns a value of around 66 million, i.e. approximately 64 MB, but sometimes it returns around two million, i.e. 2 MB. When totalMemory()
is only 2 MB, my java method throws OutOfMemoryError (java heap space) because there isn't enough memory to allocate the object that my java method needs to do.
Note that my java method contains catch (Throwable)
so that the C# application can continue. After the OutOfMemoryError
, for a subsequent call to my JNI function, totalMemory()
will again return a value of 64 MB.
What would cause the total memory allocated to the JVM to drop from 64 MB to 2 MB ?
Note that I am referring to the total memory and not the free memory. I assume that the total memory should not change. I believe that my log file proves my assumption since, as I wrote above, it is almost always 64 MB. It just sometimes drops to 2 MB, hence this question. Why does total memory sometimes drop to 2 MB ?
My (stripped-down) JNI code.
#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);
}
My (stripped-down) java method that is invoked in the above JNI code.
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;
}
Upvotes: 2
Views: 208
Reputation: 20923
My suspicion appears to have been proven correct. Sometimes error messages can be misleading. I had a gut feeling from the start that the problem was not really a memory problem and like I said, it appears I was correct.
My code uses java logging to write log files in the Windows temporary folder. It turns out that another, non-related process also writes log files to the same folder. In fact it literally writes thousands of files to that folder and those files are never deleted. Once we deleted those files, the problem went away.
So the problem was the fact that the temporary folder was full to bursting with files - none of which were written by my code. Something to check if anyone experiences a similar problem and winds up here due to an Internet search.
Upvotes: 0
Reputation: 4259
As far as I understand the JVM memory model:
maxMemory
is the maximum amount the JVM can use (as defined by -Xmx)
maxMemory
= totalMemory
+ not allocated memory
totalMemory
- freeMemory
= used
While its rarely (afaik) seen that the JVM releases memory back to the OS, its possible and would explain what you are experiencing.
totalMemory
should not drop below the value that is defined by -Xms
,
so running your jvm with a fixed memory value, might solve your issue.
For example: -Xms64m -Xmx64m
Upvotes: 2