Reputation: 33920
#define PRINTF(...) ((void)__android_log_print(ANDROID_LOG_INFO, "yaui", __VA_ARGS__))
jfindViewById = (Env)->GetMethodID(cls, "findViewById", "(I)Landroid/view/View;");
for (int i = 0; i < 1000; i++) {
PRINTF("%i ", i);
view = (jobject) (Env)->CallObjectMethod(Obj, jfindViewById, N);
}
The loop will execute about 500 times, then program will crash. I am having trouble understanding why. Must be a memory leak or a resource leak, but what can possibly leak here?
In real life I do not need to execute this function 1000 times at once like that. This is the minimal loop I created in search for the problem.
Upvotes: 1
Views: 659
Reputation: 3973
Apparently your findViewById
method returns Java objects and you are storing those jobject
references in view
and always overwriting them. However, that's a problem for the JVM: How can the garbage collection know about native code that is holding jobject
references to objects? I know that some JavaScript engines just walk through the entire native stack and check if there is any value that would be a valid object reference. Dirty. The JVM uses a different approach: When a thread enters native code, a local reference frame is created in the JVM. Whenever the native code is given a local jobject
reference, be it through the method arguments, by calling methods that return objects or by reading object values from fields, that reference is added to that JVM stack frame. Now the GC can just take a look at the stack frames and immediately see which objects are being referenced. When the native code then returns, the special stack frame is removed and therefore, the local references are released. According to the latest JNI documentation, the JVM allocates space for 16 local references in that stack frame by default, but to preserve backwards compatibility, it can allocate more than that (it'll probably resize the buffer or something like that). The error message suggests that the JVM won't do this infinitely (max=512
) and your 1000 references (one per loop iteration) obviously exceed that limit.
Now you have several options:
DeleteLocalRef
method will free local references, allowing you to reuse space in the reference buffer.EnsureLocalCapacity
verifies that the current thread can hold a given number of references. It will return a negative number and throw an OutOfMemoryError
if that's not the case.PushLocalFrame
to create a new local reference frame with a capacity of your choice. When you're done, you can release all references within that frame at once by calling PopLocalFrame
.If you can, you should of course delete the local references as soon as you no longer need them.
Upvotes: 2
Reputation: 12998
Looking at
int __android_log_print(int prio, const char *tag, const char *fmt, ...);
So your #define should have two logical parameters: One for the format string, and the remaining ones are the parameters:
#define PRINTF(format, ...) \
((void)__android_log_print(ANDROID_LOG_INFO, "yaui", format, __VA_ARGS__))
The format string is not really part of the varargs. But from a logical point of view, it is used to detect the number of the expected parameters which follow on the stack.
Upvotes: 0