JNI EnsureLocalCapacity -- WHY?
Have a look at the JNI docs here: http://download.oracle.com/javase/1.5.0/docs/guide/jni/spec/functions.html
Specifically, look at what is said in the description for the EnsureLocalCapacity function:
For backward compatibility, the VM allocates local references beyond the ensured capacity. 开发者_开发知识库(As a debugging support, the VM may give the user warnings that too many local references are being created. In the JDK, the programmer can supply the -verbose:jni command line option to turn on these messages.) The VM calls FatalError if no more local references can be created beyond the ensured capacity.
And moreover, look at how PushLocalFrame takes a "capacity" argument. (And by the way it doesn't mention if this is a hard limit, or a soft limit like with EnsureLocalCapacity).
Where exactly is all this nonsense about local reference capacity coming from? The docs say that VM will be willing to allocate references beyond the current formal capacity, so why doesn't it just do so and keep all this capacity clutter out of the API?
To give an analogy to C, it feels like I'm being asked to pre-plan how many malloc() calls I'm going to make, and it feels a bit ridiculous.
Is there something important I'm just not seeing here?
To my understanding local reference table size is predefined, and if you allocate more than that it will just crash. So it's not that VM will give you more local refs forever. I've encountered this many times while working with JNI on Android. It's often difficult to find such memory leak. And calling env->DeleteLocalRef() each time you do something with JNI local references just clutters the code. So instead of cluttering the code with DeleteLocalRef() calls, we can just make sure that we don't create too many references and keep the code quite clean. And even if we plan to allocate much more local references, we can still just call PushLocalFrame()/PopLocalFrame() and avoid many calls to DeleteLocalRef().
So to give you analogy to "C", you are asked to pre-plan your calls to malloc to avoid all necessary calls to free() at the end.
The JNI documentation is squishy on this subject. I have found in practice that at times the number of local refs seems unbounded, but you really can run out of local references, for example when iterating over an array using JNI calls. What is a local reference? It is a jobject (or jarray, jstring, etc) that you get from any of these sources:
- Returned from a raw JNI call like NewObjectV() or GetObjectArrayElement()
- Returned from a method invocation like CallObjectMethodV()
- Passed to your native function as an argument
- Probably others I've forgotten
Of these, you usually need not worry about the references that are passed to you in a method argument, UNLESS you intend to keep the reference around beyond the current method call, in which case you should convert it to global.
It is worth noting that you must not hang on to a local reference outside of the current method scope. If you are going to store it in a C++ object, you must convert it to global. Local references do have two advantages
- They are always released automatically when the current frame goes out of scope.
- They are faster than converting to global
Like most JNI errors, any misuse of references are horrible to diagnose, so you really don't want to get one. Your best defense is a consistent approach. In my experience, the cost of converting local-to-global is fairly small. I would suggest the following, unless you have very strict performance needs.
Given that, my rule is -- ALWAYS convert local refs to global refs, using code like:
jobject localjo = ...
jobject globaljo = env->NewGlobalRef(localjo);
env->DeleteLocalRef(localjo);
At that point, you can know that every ref is global, and when you are done, you need to remember to do env->DeleteGlobalRef(globaljo) on each one. In C++ it is possible to write a wrapper class around JNI references that calls env->DeleteGlobalRef() in the dtor. But since JNI doesn't reference-count the JNI references for you, you may need to build that in as well.
This is my guess. I suspect that growing local reference capacity is expensive and this function allows to request the desired capacity once per native method invocation. I think this is more about performance than about correctness.
精彩评论