`
bwlcool
  • 浏览: 20505 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

JNI Local Reference Changes in ICS

    博客分类:
  • ndk
阅读更多

http://android-developers.blogspot.com/2011/11/jni-local-reference-changes-in-ics.html

这个网址fanqiang才能访问。下面是复制的内容

 JNI Local Reference Changes in ICS

 

[This post is by Elliott Hughes, a Software Engineer on the Dalvik team. — Tim Bray]

If you don’t write native code that uses JNI, you can stop reading now. If you do write native code that uses JNI, you really need to read this.

What’s changing, and why?

Every developer wants a good garbage collector. The best garbage collectors move objects around. This lets them offer very cheap allocation and bulk deallocation, avoids heap fragmentation, and may improve locality. Moving objects around is a problem if you’ve handed out pointers to them to native code. JNI uses types such as jobject to solve this problem: rather than handing out direct pointers, you’re given an opaque handle that can be traded in for a pointer when necessary. By using handles, when the garbage collector moves an object, it just has to update the handle table to point to the object’s new location. This means that native code won’t be left holding dangling pointers every time the garbage collector runs.

In previous releases of Android, we didn’t use indirect handles; we used direct pointers. This didn’t seem like a problem as long as we didn’t have a garbage collector that moves objects, but it let you write buggy code that still seemed to work. In Ice Cream Sandwich, even though we haven't yet implemented such a garbage collector, we've moved to indirect references so you can start detecting bugs in your native code.

Ice Cream Sandwich features a JNI bug compatibility mode so that as long 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 needs to be correct.

CheckJNI has been updated to detect and report these errors, and in Ice Cream Sandwich, CheckJNI is on by default if debuggable="true" in your manifest.

A quick primer on JNI references

In JNI, there are several kinds of reference. The two most important kinds are local references and global references. Any given jobject can be either local or global. (There are weak globals too, but they have a separate type, jweak, and aren’t interesting here.)

The global/local distinction affects both lifetime and scope. A global is usable from any thread, using that thread’s JNIEnv*, and is valid until an explicit call to DeleteGlobalRef(). A local is only usable from the thread it was originally handed to, and is valid until either an explicit call to DeleteLocalRef() or, more commonly, until you return from your native method. When a native method returns, all local references are automatically deleted.

In the old system, where local references were direct pointers, local references were never really invalidated. That meant you could use a local reference indefinitely, even if you’d explicitly called DeleteLocalRef() on it, or implicitly deleted it with PopLocalFrame()!

Although any given JNIEnv* is only valid for use on one thread, because Android never had any per-thread state in a JNIEnv*, it used to be possible to get away with using a JNIEnv* on the wrong thread. Now there’s a per-thread local reference table, it’s vital that you only use a JNIEnv* on the right thread.

Those are the bugs that ICS will detect. I’ll go through a few common cases to illustrate these problems, how to spot them, and how to fix them. It’s important that you do fix them, because it’s likely that future Android releases will utilize moving collectors. It will not be possible to offer a bug-compatibility mode indefinitely.

Common JNI reference bugs

Bug: Forgetting to call NewGlobalRef() when stashing a jobject in a native peer

If you have a native peer (a long-lived native object corresponding to a Java object, usually created when the Java object is created and destroyed when the Java object’s finalizer runs), you must not stash a jobject in that native object, because it won’t be valid next time you try to use it. (Similar is true of JNIEnv*s. They might be valid if the next native call happens on the same thread, but they won’t be valid otherwise.)

 classMyPeer{
 public:
   MyPeer(jstring s){
     str_ = s;// Error: stashing a reference without ensuring it’s global.
   }
   jstring str_;
 };

 static jlong MyClass_newPeer(JNIEnv* env, jclass){
   jstring local_ref = env->NewStringUTF("hello, world!");
   MyPeer* peer =newMyPeer(local_ref);
   returnstatic_cast<jlong>(reinterpret_cast<uintptr_t>(peer));
   // Error: local_ref is no longer valid when we return, but we've stored it in 'peer'.
 }

 staticvoidMyClass_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.c_str()<< std::endl;
 }

The fix for this is to only store JNI global references. Because there’s never any automatic cleanup of JNI global references, it’s critically important that you clean them up yourself. This is made slightly awkward by the fact that your destructor won’t have a JNIEnv*. The easiest fix is usually to have an explicit ‘destroy‘ function for your native peer, called from the Java peer’s finalizer:

 classMyPeer{
 public:
   MyPeer(JNIEnv* env, jstring s){
     this->s = env->NewGlobalRef(s);
   }
   ~MyPeer(){
     assert(s == NULL);
   }
   void destroy(JNIEnv* env){
     env->DeleteGlobalRef(s);
     s = NULL;
   }
   jstring s;
 };

You should always have matching calls to NewGlobalRef()/DeleteGlobalRef(). CheckJNI will catch global reference leaks, but the limit is quite high (2000 by default), so watch out.

If you do have this class of error in your code, the crash will look something like this:

    JNI ERROR (app bug): accessed stale local reference 0x5900021(index 8in a table of size 8)
    JNI WARNING: jstring is an invalid local reference (0x5900021)
                 inLMyClass;.printString:(J)V (GetStringUTFChars)
    "main" prio=5 tid=1 RUNNABLE
      |group="main" sCount=0 dsCount=0 obj=0xf5e96410self=0x8215888
      | sysTid=11044 nice=0 sched=0/0 cgrp=[n/a] handle=-152574256
      | schedstat=(15603882460081047) utm=14 stm=2 core=0
      at MyClass.printString(NativeMethod)
      at MyClass.main(MyClass.java:13)

If you’re using another thread’s JNIEnv*, the crash will look something like this:

 JNI WARNING: threadid=8using env from threadid=1
                 inLMyClass;.printString:(J)V (GetStringUTFChars)
    "Thread-10" prio=5 tid=8 NATIVE
      |group="main" sCount=0 dsCount=0 obj=0xf5f77d60self=0x9f8f248
      | sysTid=22299 nice=0 sched=0/0 cgrp=[n/a] handle=-256476304
      | schedstat=(15335857270921848) utm=12 stm=4 core=8
      at MyClass.printString(NativeMethod)
      at MyClass$1.run(MyClass.java:15)

Bug: Mistakenly assuming FindClass() returns global references

FindClass() returns local references. Many people assume otherwise. In a system without class unloading (like Android), you can treat jfieldID and jmethodID as if they were global. (They’re not actually references, but in a system with class unloading there are similar lifetime issues.) But jclass is a reference, and FindClass() returns local references. A common bug pattern is “static jclass”. Unless you’re manually turning your local references into global references, your code is broken. Here’s what correct code should look like:

 static jclass gMyClass;
 static jclass gSomeClass;

 staticvoidMyClass_nativeInit(JNIEnv* env, jclass myClass){
   // ‘myClass’ (and any other non-primitive arguments) are only local references.
   gMyClass = env->NewGlobalRef(myClass);

   // FindClass only returns local references.
   jclass someClass = env->FindClass("SomeClass");
   if(someClass == NULL){
     return;// FindClass already threw an exception such as NoClassDefFoundError.
   }
   gSomeClass = env->NewGlobalRef(someClass);
 }

If you do have this class of error in your code, the crash will look something like this:

    JNI ERROR (app bug): attempt to use stale local reference 0x4200001d(should be 0x4210001d)
    JNI WARNING:0x4200001disnot a valid JNI reference
                 inLMyClass;.useStashedClass:()V (IsSameObject)

Bug: Calling DeleteLocalRef() and continuing to use the deleted reference

It shouldn’t need to be said that it’s illegal to continue to use a reference after calling DeleteLocalRef() on it, but because it used to work, so you may have made this mistake and not realized. The usual pattern seems to be where native code has a long-running loop, and developers try to clean up every single local reference as they go to avoid hitting the local reference limit, but they accidentally also delete the reference they want to use as a return value!

The fix is trivial: don’t call DeleteLocalRef() on a reference you’re going to use (where “use” includes “return”).

Bug: Calling PopLocalFrame() and continuing to use a popped reference

This is a more subtle variant of the previous bug. The PushLocalFrame() and PopLocalFrame() calls let you bulk-delete local references. When you call PopLocalFrame(), you pass in the one reference from the frame that you’d like to keep (typically for use as a return value), or NULL. In the past, you’d get away with incorrect code like the following:

 static jobjectArray MyClass_returnArray(JNIEnv* env, jclass){
   env->PushLocalFrame(256);
   jobjectArray array = env->NewObjectArray(128, gMyClass, NULL);
   for(int i =0; i <128;++i){
       env->SetObjectArrayElement(array, i, newMyClass(i));
   }
   env->PopLocalFrame(NULL);// Error: should pass 'array'.
   return array;// Error: array is no longer valid.
 }

The fix is generally to pass the reference to PopLocalFrame(). Note in the above example that you don’t need to keep references to the individual array elements; as long as the GC knows about the array itself, it’ll take care of the elements (and any objects they point to in turn) itself.

If you do have this class of error in your code, the crash will look something like this:

  JNI ERROR (app bug): accessed stale local reference 0x2d00025(index 9in a table of size 8)
    JNI WARNING: invalid reference returned fromnative code
                 inLMyClass;.returnArray:()[Ljava/lang/Object;

Wrapping up

Yes, we asking for a bit more attention to detail in your JNI coding, which is extra work. But we think that you’ll come out ahead on the deal as we roll in better and more sophisticated memory management code.

 

 

分享到:
评论

相关推荐

    DELPHI开发JNI必备 jni.pas

    描述中提到的"包含文件 jni.pas JNI_MD.INC",`JNI_MD.INC`是一个平台特定的包含文件,可能包含了与特定操作系统相关的定义,例如指针大小、数据对齐等。这个文件对于确保在不同平台上正确编译和链接JNI代码至关重要...

    jni.zip jni编译jni下载

    JNI,全称Java Native Interface,是Java平台标准的一部分,它允许Java代码和其他语言写的代码进行交互。JNI在很多场景下非常有用,比如当需要利用已有的C或C++库,或者提升性能时,我们可以通过JNI将Java代码与本地...

    ScopedLocalRef.rar_out

    A smart pointer that deletes a JNI local reference when it goes out of scope.

    java jni 传递汉字参数,包括 in out

    java jni 传递汉字参数,包括java传入dll 从dll传出到java 包括全部 java 代码 工程文件 全部vc++6.0 Dll 代码 工程文件 找了好久资料才完成的,传上来省的大家再走弯路。

    JNI之Hello-JNI进阶

    LOCAL_MODULE := hello-jni LOCAL_SRC_FILES := hello-jni.c include $(BUILD_SHARED_LIBRARY) 在根目录下运行:ndk-build xxx@xion-driver:~/android_env/eclipse/Workspace/HelloJni$ndk-build Install : ...

    jni底层jar包

    - `Local Reference`:局部引用只在本地方法的生命周期内有效,当本地方法返回时,局部引用会被自动清除。 - `Weak Global Reference`:弱全局引用不会阻止对象被垃圾回收,但可以在对象被回收后得到通知。 4. **...

    JNI的两个头文件jni.h和jni_md.h

    JNI,全称Java Native Interface,是Java平台标准的一部分,它允许Java代码和其他语言写的代码进行交互。JNI在很多场景下都是必要的,比如调用操作系统本地库、加速性能关键的代码或者利用硬件特性等。本文将深入...

    JNI编程指南与规范.zip

    JNI程序员指南与规范.pdf + 06 JNI编程指南.pdf NDK开发汇总 https://blog.csdn.net/baopengjian/article/details/104615972 THIS book covers the Java™ Native Interface (JNI). It will be useful to you if ...

    jni.h头文件

    3. **JNI常量**:定义了一些预定义的常量,如`JNI_VERSION_1_1`, `JNI_VERSION_1_2`, `JNI_VERSION_1_4`, `JNI_VERSION_1_6`, `JNI_VERSION_1_8`等,用于表示JNI接口的版本。 4. **JNI环境变量**:`JNIEnv`结构体中...

    JNI 完整的案例

    LOCAL_SRC_FILES := JniExample.c include $(BUILD_SHARED_LIBRARY) ``` 运行`ndk-build`命令编译生成`.so`动态链接库。 6. **运行和测试** 在Java代码中,可以通过`System.loadLibrary()`加载本地库,然后...

    AndroidJNI技术实现

    LOCAL_MODULE := hello-jni LOCAL_SRC_FILES := HelloJni.c LOCAL_LDLIBS := -llog include $(BUILD_SHARED_LIBRARY) ``` 3. **实现本地代码**: 创建`HelloJni.c`文件,并实现`GetStringFromJNI`函数,该...

    通过JNI调用第三方动态库(生成两个.so文件)

    在Android开发中,JNI(Java Native Interface)是一个关键的技术,它允许Java代码和其他语言写的代码进行交互。这个技术尤其在需要高效计算或者调用系统底层功能时显得尤为重要。本示例将详细介绍如何通过JNI调用第三...

    JniCallback.zip_Android jni_android_jni android_jni callback_jni

    在Android开发中,JNI(Java Native Interface)是一个关键的技术,它允许Java代码和其他语言写的代码进行交互。JNI在很多场景下都非常有用,比如优化性能、使用现有的C/C++库、或者像在这个“JniCallback.zip”文件...

    jni传递对象数组

    JNI,全称Java Native Interface,是Java平台标准的一部分,它允许Java代码和其他语言写的代码进行交互。JNI在很多场景下非常有用,比如当需要利用已有的C/C++库或者优化性能时。本篇文章将深入探讨如何在JNI中传递...

    jni和jniLibs的压缩包.rar

    JNI(Java Native Interface)是Java平台的一个重要特性,它允许Java代码和其他语言写的代码进行交互。在Android系统中,JNI被广泛应用于实现性能敏感的代码,例如与硬件直接交互、调用C/C++库或者利用已有的C/C++...

    jni.h文件.7z

    JNI,全称Java Native Interface,是Java平台标准的一部分,它允许Java代码和其他语言写的代码进行交互。JNI在很多场景下都是必要的,比如当需要利用已有的C/C++库,或者为了性能优化而需要直接调用硬件接口时。在这...

    Android调用Jni返回自定义对象

    在Android开发中,Java Native Interface (JNI) 是一个关键的工具,它允许Java代码与本地C/C++代码交互。JNI的使用场景广泛,包括优化性能、利用硬件特性、调用已有的C/C++库等。当涉及到Android调用JNI并返回自定义...

    JNI实现示例DEMO

    JNI,全称Java Native Interface,是Java平台标准的一部分,它允许Java代码和其他语言写的代码进行交互。JNI在很多场景下都是必要的,比如调用操作系统API、优化性能关键部分或者利用已有的C/C++库。本DEMO将向你...

    Jni_Ndk开发最简单的例子

    LOCAL_MODULE := hello-jni LOCAL_SRC_FILES := hello-jni.c include $(BUILD_SHARED_LIBRARY) ``` 这里,`LOCAL_MODULE`定义了生成的库名,`LOCAL_SRC_FILES`指定了要编译的源文件。你需要创建一个`hello-jni.c`...

    JNI

    JNI,全称Java Native Interface,是Java平台标准的一部分,它允许Java代码和其他语言写的代码进行交互。JNI在很多场景下都是必要的,比如当Java需要调用已有的C/C++库,或者想要利用硬件特性,或者优化性能时。这篇...

Global site tag (gtag.js) - Google Analytics