When I run the code, I get an error "failed adding to JNI local ref table has 512 entries"
This is my code:
jstring pJNIData = pJNIEnv->NewStringUTF ( variables[0].GetStringValue() );
pJNIEnv->CallStaticVoidMethod ( pJNIActivityClass, pJNIMethodIDStartTime, pJNIData ) ;
pJNIEnv->DeleteLocalRef(pJNIData);
I have read several suggestions, but none of them work! In spite of the DeleteLocalRef, it fails to works. The function is used in a profiler that literally calls all the functions...
I have seen this when a JNI method called Java code (in my case, the method was not static). As I understand, unused local references are not automatically deleted when a Java method is called from JNI (I mean, until the top-level JNI function returns).
IIRC either there already was information about memory objects in the log, or I could add some logging; from that information I identified garbage items that I did not mention before. They were two arrays and a class, created in subsequent calls but not garbage-collected.
// in a function that calls a Java method from JNI
jbyteArray srcArray = env->NewByteArray(len);
jclass cls = env->FindClass("com/something/MyClass");
jmethodID mid = env->GetMethodID(cls, "mymethod", "([BI)[B");
jbyteArray resArray = (jbyteArray)env->CallObjectMethod(obj, mid, srcArray, XXXX);
...
env->DeleteLocalRef(cls);
env->DeleteLocalRef(resArray);
env->DeleteLocalRef(srcArray);
// no need to do anything with mid
Note that although these three local references were obtained differently, all of them were hanging around.
Useful link:
http://www.netmite.com/android/mydroid/dalvik/docs/jni-tips.html#local_vs_global_references
(or find the Dalvik VM docs dalvik/docs/jni-tips.html and locate the section "Local vs. Global References")
Every object that JNI returns is a "local reference". This means that it's valid for the duration of the current native method in the current thread. Even if the object itself continues to live on after the native method returns, the reference is not valid. This applies to all sub-classes of jobject, including jclass and jarray. [...] Note: method and field IDs are just 32-bit identifiers, not object references, and should not be passed to NewGlobalRef. The raw data pointers returned by functions like GetStringUTFChars and GetByteArrayElements are also not objects.
I thought I would chip in just in case anyone else runs into this issue. This is a weird case that kept me confused for hours!
Ok so I have an NDK app and the Java code being called is inside an apk that is loaded at runtime. I have no idea if the runtime loading effects this in any way but I thought I should mention it.
Now in a c++ method I use find class and getmethodid to get the constuctor to a HashMap and call it to get a new HashMap instance. I then populate the HashMap from the c++ side using jni calls. So far so good.
I then pass the HashMap to java code and, again, all is working as expected. Once the java code has returned I call DeleteLocalRef on the HashMap. No errors are thrown but the reference is not deleted.
This only came up when I finally ran over 512 local references (from multiple calls to this function) and the error dump showed that the last 10 items in the localref store were nearly all HashMaps. I would understand that the GC would not collect these references at the end of the method as I am make a multithreaded ndk app. However the DeleteLocalRef should have worked.
The Fix:
In the end I found that creating the HashMap from a jni call to a java method I wrote was fine, and the reference was then free'able. It seems crazy to have a java function that literally just returns a new HashMap but it worked so for now I am living with it :)
Related
Have been using java.nio.ByteBuffers on the NDK side for a while now - noticed this article about Android relationship with JNI, GC and future of ICS. Article here http://android-developers.blogspot.com/2011/11/jni-local-reference-changes-in-ics.html
So... here is the concern:
Since the "pointer" that JNI provides seems to actually be a reference that is managed by the JNI internaly - it could be "moved" or deleted by GC at some point if it is not marked as NewGlobalReference() in JNI method before being passed to c++ classes?
In my JNI methods I take the Direct Buffer address and pass it on to classes that use it, without any
env->NewGlobalRef(jobject);
env->NewLocalRef(jobject);
env->DeleteGlobalRef(jobject);
management.
For now it all works - but is it correct?
Thoughts?
P.S - I do use free(ByteBuffer) on exit/destructor in c++
A local reference is only valid for the duration of the JNI method that it is passed to or created in. After that method returns to the JVM, the reference is no longer valid. If you're not breaking that rule, you're OK.
It's a bit unclear what you're asking, so let me try to clarify a few points.
Any jobject type you get in JNI, whether returned from a JNI call like FindClass or passed in as an argument (jobject, jclass, jbyteArray, etc), is a local reference. It has a very short lifespan. If you pass it to NewGlobalRef, you get a global reference in return; this will last until you delete it.
Any JNI function that takes or returns a pointer type is giving you a pointer that's good until something invalidates it. For example, if you call GetStringUTFChars, you get a const char* that's valid until you call ReleaseStringUTFChars.
References are not pointers, and pointers are not references. You can't pass a char* to NewGlobalRef, and you can't dereference a global reference (where "can't" is usually an error or a native crash).
What I assume you're doing is calling GetDirectByteBufferAddress on a ByteBuffer object, which returns a void* that points to the start of the direct byte buffer. This pointer is valid until the storage is freed. How that happens depends upon how you allocated it:
If you allocated the direct byte buffer with ByteBuffer.allocateDirect(), then Dalvik owns the storage. It will be freed when the ByteBuffer becomes unreachable and is garbage collected.
If you allocated the storage yourself, and associated it with a ByteBuffer with the JNI NewDirectByteBuffer call, then it's valid until you free it.
For the allocateDirect() case, it's very important that your native code stops using the pointer before your managed code discards the ByteBuffer. One way to do this would be to retain a global reference to the ByteBuffer in your native code, and invalidate your buffer pointer at the same time you delete the global reference. Depending on how your code is structured that may not be necessary.
See also the JNI Tips page.
I have written a shared library which is accessed via my linux-system and via a JNI-call from java.
That library should have a global ringbuffer which must be available in the JNI-method and in the other native methods.
I thought this won't be a problem, because when I access the SL from different programs, the global variables are always as they should.
But now, in my JNI-method, the global variables seem to be not initialized (they should as the program-flow forces it).
Here is my example:
ringbuf_t ringbuffer;
void internalMethod() {
// this method is first called from system-program
ringbuffer = ringbuf_new(5000);
}
JNIEXPORT jint JNICALL Java_example_read(JNIEnv *env, jobject This) {
// this method is later called via JNI
if (!ringbuffer) {
LOGI("uhhh, why is that buffer not set?!");
}
}
What do I have to do to make the ringbuffer-variable really global so every instance/call to the shared-library access one and the same instance of that variable?
From the comments it seems you want to allocate your memory in a process and use it in another.
For this purpose it might be a good idea to look at shared memory: 'man shmget' should be a good start.
Note that this is not specifically related to JNI and is a restriction from the OS.
EDIT:
I would suggest you:
read up on shared memory - you should understand the concepts of how this works.
first try sharing memory between 2 simple applications
only then implement in your JNI application
I use regular std::map to map jobject's to c++ objects. The problem with this approach is that it may fail for other types of references, e.g. global references are actually different pointer than regular local references even if they reference the same object. The correct way to compare if two references reference the same object is:
env->IsSameObject(jobj1, jobj2);
So, my question is: what's the correct way to map jobject's to c++ objects? The obvious reply to wrap jobject into some c++ class that overloads operator== and calls IsSameObject isn't the reply that I'm looking for. I'd like to know if there is way to do it without going back and forth between JVM and native c/c++ for every compare operation.
EDIT: global reference is a jni global reference, and has nothing to do with c++ references.
EDIT2: I'd like to clarify what the problem is with this code:
std::map<jobject, void *> jobjs;
jobject obj1, obj2;
... some code that sets these obj1 and obj2 to some Java objects.
jobjs[obj1] = new CppPeer;
CppPeer * = jobjs[obj1]; //OK...
if(objs.find(obj2) == objs.end()){
assert(obj2 != obj1);
//Here's the problem: here a new c++ CppPeer
//created for obj2, but the problem is that
//even if obj1 != ob2 it doesn't mean that
//they actually reference different java objects
//On the next line error might happen
jobjs[obj2] = new CppPeer; //maybe not OK here...
}
The other problem with IsSameObject is that it makes things pretty nasty and messy. Not only now I'd need to keep a pointer to JVM, but also whenever I need to compare jobjects I'd need to attach thread etc to get pointer to JNIEnv to be able to check jobject
EDIT3: Be aware, that according to android docs you cannot even assume that two references reference the same object if they are equal. I don't know how it's possible, but there's the part from Android's JNI tips page:
One consequence of this is that you must not assume object references
are constant or unique in native code. The 32-bit value representing
an object may be different from one invocation of a method to the
next, and it's possible that two different objects could have the same
32-bit value on consecutive calls. Do not use jobject values as keys.
DISCLAIMER: it smells.
On Android, pointer and jint are of the same size. Consider adding an int field to your Java class to store a pointer to the native conterpart. When in a JNI method, typecast it back to a pointer.
To be on a slightly safer side, do a static assert on datatype matching:
{char a[sizeof(void*) == sizeof(jint) ? 1 : -1]};
If it's compiled on a system where it's not, you'll get a compilation error.
For slightly less smell, use a static/global map from int to your objects, use an ID generator, and instead of object pointer store relatively unique (within process lifetime) objects IDs within the Java object.
I am writing the C++/Native side of a text/VoIP program that utilizes JNI. Currently there are three functions on the C++ side that will likely be called by the Java code regularly. The frequency at which the functions are called will depend on how often the user sends/receives text messages and how active their friends are in changing their presence/status. Each of the three functions follow the pseudo code below and I am not sure if it would be wise (or even possible) to "globalize" anything within the functions.
JNIEnv *env;
if (jvm_->GetEnv((void**) &env, JNI_VERSION_1_6) < 0)
{
[print error and return]
}
jclass stringclass = env->FindClass("java/lang/String"); // Same for all 3
jstring passinfo = env->NewStringUTF([str-info-to-pass]); // Different for all 3
jclass cls = env->FindClass([directory to calling Java function class]); // Same for all 3
[check if cls found, print error if not found and return]
jmethodID methID = env->GetStatisMethodID([arguments for the function]); // Different for all 3
[check if methID found, print error if not found and return]
jobjectArray args = env->NewObjectArray([arguments to build argument structure being passed to function]);
[call Java function, passing right arguments]
I was thinking that I should/would be able to move the jclass stringclass, jclass cls and JNIEnv *env out of the functions and make them globals that were set during the JNI_OnLoad function call. Is this possible/advisable? Will these values be somehow different from the OnLoad function to these functions? Should I just instantiate them as globals and still set them each time in the function call? And, will doing any of these really increase performance in any noticeable amount?
Possible, yes. Wise, it depends. Define for yourself what is a "noticeable amount". What cadence of JNI calls you expect? I think you could see some measurable impact of caching starting at 10+ calls per second. Is that a case? Anyway, in my personal experience, nicely done caching cleans up the code, makes it shorter and more readable. So i will give some advice anyway.
Caching results of FindClass, yes absolutely. Generally anything reusable and "static", what you are looking up by a symbolic name - which introduces a penalty of symbolic lookup, contrary to simply using an already cached pointer. Liang JNI book talks also about field and method ids, so your GetStaticMethodID applies. jmethodID is simpler in the sense that the result of Get(Static)MethodID can be directly cached: it is valid for any further calls. Unlike FindClass which returns a local reference, so for valid caching (and to make jmethodID really persistent) you must create NewGlobalRef from it - and delete it when appropriate. A nice comprehensive example is found in the JNI_OnLoad tutorial. On the other hand, there is no point in caching JNIEnv, you get it again with every JNI call. Cached JNIEnv would be also invalid if you happen to call the JNI methods from another thread than the one from which you cached.
You should not worry about performance.
Currently I develop a voip client where i used native codec. This means that the JNI is called for every rtp packet (two times in every 20 msec because both encoding and decoding) also passing the JNIEnv with each call. This variable is just a pointer. Consumed CPU usage for JNI calls are below 1% on a low end device for me in these circumstances so you should not worry at all if your only need is texting.
I asked this question the other day, but wasn't too specific, so I want to re-clarify.
I am creating an Android Application which uses an existing library in C using the NDK. The problem I have run into is that the C code uses a lot of things java doesn't ( function pointers as parameters is the big problem ).
Anyway, I was wondering if I could write functions in my Java code that the C code calls. Now from what I can tell, you can do it, so I would appreciate it if no one just answered 'Yes you can, LINK." I have been looking into it but its very over my head as to what actually needs to be done.
Can anyone try to explain the process? I know it involves creating a JVM in the C code; any information that will help a newbie get on his feet will be greatly appreciated.
Thanks
EDIT :
So, I don't know what to do for these three steps.
To call a specific Java function from C, you need to do the following:
Obtain the class reference using the FindClass(,,) method.
Obtain the method IDs of the functions of the class that you want to call using the
GetStaticMethodID and GetMethodID function calls.
Call the functions using CallStaticVoidMethod, CallStaticIntMethod, and CallStaticObjectMethod.
This isn't explained too much and I have literally no experience in C. Is FindClass a C method?
Every C function that is callable from Java via JNI has a first parameter of type JNIEnv*. On the C end, this is a pointer to a pointer to a structure with a bunch of pointers to functions. Those functions are your interface to the Java world. FindClass, GetMethodID and the rest are among them.
So when you want to call FindClass from the C side, here's how you do it:
void Java_com_mypackage_MyClass_MyMethod(JNIEnv *jniEnv, jobject thiz)
{
jclass *clazz = (*(*jniEnv)->FindClass)(jniEnv, "com/mypackage/SomeClass");
jmethodID MethodID = (*(*jniEnv)->GetStaticMethodID)(jniEnv, clazz, "SomeMethod", "(I)I");
int result = (*(*jniEnv)->CallStaticIntMethod)(jniEnv, clazz, MethodID, 18);
And so forth. The line dereferences the jniEnv parameter, gets a function pointer and calls the function through it. Class and method names are completely bogus, naturally. How would I know yours.
Note: I'm talking of function pointers here, but not in the same sense as you do; those are function pointers to functions that JNI provides, not to your functions.
The verbosity of call syntax has to do with the limitations of C; in C++, you can write instead
jclass *cl = jniEnv->FindClass("com/mypackage/SomeClass");
as C++ supports function table pointers of this sort natively via virtual functions.
You can probably take some shortcuts along the way. If you're calling methods in the same class as your C point of entry, and it happens to be static, your second parameter already is a class object pointer. If you have a this pointer to the object you want to invoke a method on, you can use GetObjectClass.