[This post is by Elliott Hughes, a Software package Engineer on the Dalvik staff. &mdash Tim Bray]
If you don’t publish native code that utilizes JNI, you can end reading through now. If you do publish native code that employs JNI, you truly need to have to read this.
What’s modifying, and why?
Each and every developer wants a great rubbish
collector. The very best garbage collectors transfer objects about. This allows them offer really low-cost allocation and bulk deallocation, avoids heap fragmentation, and could increase locality. Heading objects about is a difficulty if you’ve handed
out pointers to them to native code. JNI utilizes kinds this sort of as jobject to resolve this problem: rather than handing out direct pointers,
you’re provided an opaque take care of that can be traded in for a pointer when essential. By making use of handles, when the rubbish collector moves an object, it just has to update the handle table to point to the object’s new place. This implies that native
code won’t be left holding dangling pointers every time the garbage collector runs.
In previous releases of Android, we did not use indirect handles we utilised direct pointers. This did not seem to be like a dilemma as prolonged as we didn’t have a rubbish collector that moves objects, but it permit you compose buggy code that nevertheless
seemed to perform. In Ice Cream Sandwich, even though we have not yet applied these a garbage collector, we have moved to indirect references so you can start detecting bugs in your native code.
Ice Cream Sandwich attributes a JNI bug compatibility mode so that as prolonged as your AndroidManifest.xml’s targetSdkVersion is less than Ice Cream Sandwich, your code is exempt. But as soon as you update your targetSdkVersion, your code demands to be appropriate.
been updated to detect and report these problems, and in Ice Cream Sandwich, CheckJNI is on by default if debuggable="correct" in your manifest.
A quick primer on JNI references
In JNI, there are numerous sorts of reference. The two most important kinds are regional references and global references. Any provided jobject can
be either local or world-wide. (There are weak globals as well, but they have a separate sort, jweak, and aren’t exciting here.)
The international/local distinction has an effect on each life time and scope. A worldwide is usable from any thread, using that thread’s JNIEnv*,
and is legitimate till an explicit get in touch with toDeleteGlobalRef(). A local is only usable from the thread it was initially handed to, and
is valid until finally possibly an explicit phone to DeleteLocalRef() or, much more commonly, until you return from your native technique. When
a native technique returns, all neighborhood references are automatically deleted.
In the outdated technique, where neighborhood references have been direct pointers, nearby references had been by no means genuinely invalidated. That meant you could use a local reference indefinitely, even if you’d explicitly named DeleteLocalRef() on
it, or implicitly deleted it withPopLocalFrame()!
Though any given JNIEnv* is only valid for use on 1 thread, simply because Android by no means had any per-thread state in a JNIEnv*,
it used to be possible to get absent with making use of a JNIEnv* on the improper thread. Now there is a per-thread regional reference table, it’s
important that you only use a JNIEnv* on the appropriate thread.
These are the bugs that ICS will detect. I’ll go through a couple of typical situations to illustrate these issues, how to spot them, and how to resolve them. It’s critical that you do fix them, since it’s likely that potential Android releases will employ
relocating collectors. It will not be feasible to provide a bug-compatibility mode indefinitely.
Common JNI reference bugs
Bug: Forgetting to phone NewGlobalRef() when stashing a jobject in
a native peer
If you have a native peer (a lengthy-lived native object corresponding to a Java object, normally created when the Java object is created and destroyed when the Java object’s finalizer runs), you should not stash a jobject in
that native object, because it will not be valid subsequent time you attempt to use it. (Equivalent is true of JNIEnv*s. They may be
valid if the following native phone occurs on the exact same thread, but they will not be valid in any other case.)
str_ = s // Error: stashing a reference with no guaranteeing it’s global.
static jlong MyClass_newPeer(JNIEnv* env, jclass)
jstring regional_ref = env->NewStringUTF("hi there, entire world!")
MyPeer* peer = new MyPeer(regional_ref)
// Error: local_ref is no more time valid when we return, but we've saved it in 'peer'.
static void MyClass_printString(JNIEnv* env, jclass, jlong peerAddress)
MyPeer* peer = reinterpret_cast<MyPeer*>(static_cast<uintptr_t>(peerAddress))
// Error: peer->str_ is invalid!
ScopedUtfChars s(env, peer->str_)
std::cout << s.do_str() << std::endl
The correct for this is to only keep JNI worldwide references. Simply because there is in no way any automatic cleanup of JNI worldwide references, it’s critically important that you clean them up oneself. This is created somewhat awkward by the fact that your
destructor will not have a JNIEnv*. The least complicated fix is usually to have an explicit ‘destroy‘ purpose for your native peer, named from
the Java peer’s finalizer:
Bug: Mistakenly assuming FindClass() returns global references
FindClass() returns local references. A lot of folks believe otherwise. In a technique with no course unloading (like Android), you can treat jfieldID
and jmethodID as if they had been global. (They are not actually references, but in a system with course unloading there are equivalent life time concerns.) But jclass is a reference, and FindClass() returns
neighborhood references. A typical bug pattern is “static jclass”. Unless of course you’re manually turning your nearby references into international references, your code is broken. Here’s what appropriate code must seem like:
static jclass gMyClass
static jclass gSomeClass
static void MyClass_nativeInit(JNIEnv* env, jclass myClass)
// ‘myClass’ (and any other non-primitive arguments) are only local references.
gMyClass = env->NewGlobalRef(myClass)
// FindClass only returns nearby references.
jclass someClass = env->FindClass("SomeClass")
if (someClass == NULL)
return // FindClass already threw an exception this kind of as NoClassDefFoundError.
gSomeClass = env->NewGlobalRef(someClass)
If you do have this class of error in your code, the crash will look a thing like this:
JNI ERROR (app bug): try to use stale nearby reference 0x4200001d (must be 0x4210001d)
JNI WARNING: 0x4200001d is not a legitimate JNI reference
in LMyClass.useStashedClass:()V (IsSameObject)
Bug: Calling DeleteLocalRef() and continuing to use the deleted reference
It shouldn’t require to be explained that it is illegal to proceed to use a reference right after callingDeleteLocalRef() on it, but since it utilized
to operate, so you may possibly have produced this error and not recognized. The typical pattern appears to be wherever native code has a long-working loop, and builders consider to clean up each and every simple local reference as they go to steer clear of
hitting the neighborhood reference limit, but they unintentionally also delete the reference they want to use as a return price!
The resolve is trivial: really do not phone DeleteLocalRef() on a reference you’re heading to use (wherever “use” incorporates “return”).
Bug: Calling PopLocalFrame() and continuing to use a popped reference
This is a more delicate variant of the preceding bug. The PushLocalFrame() and PopLocalFrame()calls
permit you bulk-delete regional references. When you get in touch with PopLocalFrame(), you pass in the 1 reference from the body that you’d like
to retain (normally for use as a return worth), or NULL. In the prior, you’d get away with incorrect code like the following:
static jobjectArray MyClass_returnArray(JNIEnv* env, jclass)
jobjectArray array = env->NewObjectArray(128, gMyClass, NULL)
for (int i = i < 128 ++i)
env->SetObjectArrayElement(array, i, newMyClass(i))
env->PopLocalFrame(NULL) // Error: should pass 'array'.
return array // Error: array is no extended legitimate.
The correct is generally to pass the reference to PopLocalFrame(). Notice in the over example that you do not need to preserve references to the
personal array elements as extended as the GC understands about the array alone, it’ll just take treatment of the aspects (and any objects they point to in flip) itself.
If you do have this course of error in your code, the crash will appear some thing like this:
JNI ERROR (app bug): accessed stale nearby reference 0x2d00025 (index 9 in a table of size JNI WARNING: invalid reference returned from native code
Sure, we asking for a little bit much more interest to detail in your JNI coding, which is additional function. But we feel that you will come out forward on the deal as we roll in much better and far more refined memory administration code.