Object reference and memory leak problem in JNI programming of Java _java

Source: Internet
Author: User
Tags garbage collection memory usage

Jni,java Native Interface is the programming interface for Native code. JNI enables Java code programs to interact with native code-call native code in a Java program, and embed Java virtual machines in native code to invoke Java.
JNI programming is widely used in software development, and its advantages can be summed up in the following points:
The platform relevance of native code is used to demonstrate the advantages in platform-related programming.
Code reuse for native.
Native code bottom operation, more efficient.
But everything has two sides, and so does JNI programming. Programmers in the use of JNI should be aware of JNI programming in the following several drawbacks, to avoid weaknesses, can write more complete, high-performance code:
Context switching from the Java environment to native code is time-consuming and inefficient.
JNI programming, if improperly manipulated, can cause a crash of the Java virtual machine.
JNI programming, if improperly manipulated, can cause memory leaks.
Memory leaks in JAVA
Memory leaks in Java programming can be divided into two types from the perspective of the leaking memory location: A memory leak in the Java Heap in the JVM, and a native memory memory leak in the JVM's memory.

Local and global references

JNI exposes instances, array types to opaque references. The native code does not directly examine the context of an opaque reference pointer, but instead accesses the data structure pointed to by an opaque reference by using the JNI function. Because only opaque references are processed, there is no need to worry about the layout of different internal objects caused by different Java VM implementations. However, it is important to understand the different kinds of references in JNI:
1 JNI support opaque references in 3: Local references, global references, and weak global references.
2 Local and global references, have their own different life cycle. Local references can be automatically freed, and global references and global references are valid until they are released by the programmer.
3 a local or global reference so that the referenced object cannot be garbage collected. A weak global reference allows the referenced object to be garbage collected.
4 Not all references can be used in all contexts. For example, it is illegal to use a local reference after a create return reference native method.

So what exactly is a local reference, what is the global reference, and what are their differences?

Local reference

Most JNI functions create local references. For example, the JNI function NewObject creates an instance and returns a local reference to the instance.

A local reference is valid only in the dynamic context of the native method that created it, and only in one invocation of the native method. All local references are valid only during the execution of a native method, which is reclaimed when the method returns.

It is not feasible to use a static variable in the native method to hold a local reference so that the local reference is used in subsequent calls. For example, the following example misused a local reference:
/* This code is illegal * *
Jstring

Mynewstring (jnienv *env, Jchar *chars, Jint len) 
{ 
  static jclass stringclass = NULL; 
  Jmethodid CID; 
  Jchararray Elemarr; 
  jstring result; 
  if (Stringclass = = NULL) { 
    Stringclass = (*env)->findclass (env, "java/lang/string"); 
    if (Stringclass = = null) {return 
      null;/* Exception thrown/ 
    }}/* It is wrong to use the 
  cached str Ingclass here, the 
    because it may invalid. * * 
  cid = (*env)->getmethodid (env, Stringclass, "<init>" ([ C) V "); 
  ... 
  Elemarr = (*env)->newchararray (env, Len); 
  ... 
  result = (*env)->newobject (env, Stringclass, CID, Elemarr); 
  (*env)->deletelocalref (env, Elemarr); 
  return result; 
} 

This is not the correct way to save a local reference because Findclass () returns a local reference to the java.lang.String. This is because when the native code returns from Mynewstring, the VM releases all local references, including references to the class object stored in the Stringclass variable. This can then be followed by a subsequent call to the mynewstring, which may access the illegal address, causing the memory to be corrupted or the system crashing.

Local references fail in two ways: '
1) The system will automatically release local variables.
2 programmers can display the life cycle of local references, such as invoking Deletelocalref.

A local reference may be passed to multiple native methods before being destroyed. For example, in Mynewstring, a string reference created by NewObject is returned, which will be determined by the caller of the NewObject to release the reference. And in the following code:

Jniexport jstring jnicall java_c_f (jnienv *env, jobject this) { 
   char *c_str = ... <pre name= "code" class= "CPP" >
    ... <pre name= "code" class= "CPP" >return mynewstring (C_STR); <pre name= "code" class= "CPP"} 

After the VM receives a local reference from Java_c_f, the underlying string object is passed to the Ava_c_f caller, and then the local reference created by the JNI function NewObject that was originally invoked in mynewstring is destroyed.

Local objects only belong to the thread that created them and are valid only in that thread. A local reference created by one thread trying to invoke another thread is not allowed. It is a bad programming to save a local reference to a global variable and then use it in another thread.

Global reference

You can use a global reference to cross a native method when it is invoked multiple times. A global reference can span multiple threads and is consistent and valid until it is freed by the programmer. As with local references, global references ensure that referenced objects are not garbage collected.

Unlike local references (local variables can be created by most JNI functions), global references can only be created by a JNI function (newglobalref). The following is a mynewstring with a global reference version:
/* This code is OK */
Jstring

Mynewstring (jnienv *env, Jchar *chars, Jint len) 
{ 
  static jclass stringclass = NULL; 
  ... 
  if (Stringclass = = NULL) { 
    Jclass localrefcls = 
      (*env)->findclass (env, "java/lang/string"); 
    if (Localrefcls = null) {return 
      null;/* Exception thrown/ 
    }/ 
    * Create a global reference * * 
    STRINGC Lass = (*env)->newglobalref (env, LOCALREFCLS); 
    /* The local reference is no longer useful 
    /* (*ENV)->deletelocalref (env, LOCALREFCLS); 
    * is the global reference created successfully? * 
    /if (Stringclass = null) {return 
      null;/* out of memory exception thrown/ 
    } 
  }} 
 


Weak global reference


Weak global references appear in Java 2 SDK1.2. It is created by the Newgolableweakref function and is destroyed by the DELETEGLOABLWEAKREF function. As with global references, it can be invoked across native methods, or across different threads. However, unlike a global reference, it does not prevent garbage collection on the underlying object. The following is a weak global reference version of Mynewstring:

Jniexport void Jnicall

Java_mypkg_mycls_f (jnienv *env, jobject self) 
{ 
  static jclass myCls2 = NULL; 
  if (myCls2 = = NULL) { 
    Jclass mycls2local = 
      (*env)->findclass (env, "MYPKG/MYCLS2"); 
    if (mycls2local = = NULL) {return 
      ;/* can ' t find 
    class 
    /} MYCLS2 = Newweakglobalref (env, mycls2local); 
    if (myCls2 = = NULL) {return;/* out of 
      memory/ 
    } 
  } .../* use 
  MYCLS2 * * 
} 

Weak global references are useful when a reference that is cached by native code does not prevent the underlying object from being garbage collected. As the above example, mypkg. MYCLS.F need to cache mypkg.mycls2 references. and through will mypkg. MYCLS2 Cache to weak references, the ability to implement MYCLS2 classes can still be uninstalled.


In the code above, we assume that the lifecycle of the Mycls class and the MyCls2 class is the same (for example, loaded and unloaded in the same class). Therefore, it is not considered that the MYCLS2 is unloaded and then reloaded when the implementation of the class Mycls and native method Java_mypkg_mycls_f is continued to be used. For situations where this MYCLS2 class may be unloaded and reloaded, it is useful to check whether the weak global reference is still valid. How to check, which will be mentioned below.

Comparison references

You can use the JNI function issameobject to check whether a given two local, global, or weak global references are pointing to the same object.
(*env)->issameobject (env, OBJ1, OBJ2)
The return value is:
Jni_true, representing two objects consistent, is the same object.
Jni_false, which indicates that two objects are inconsistent, not the same object.


NULL is a null reference in the Java VM.
If an object obj is a local or global reference, you can check that it points to a null object:

(*env)->issameobject (env, obj, NULL) 

Or:

NULL = obj 


For weak global references, the above rules need to change:
We can use this function to determine whether the object pointed to by a 0 weak global reference wobj is still alive (still valid).

(*env)->issameobject (env, wobj, NULL) 

return value:
Jni_true, indicating that the object has been reclaimed.
Jni_false, which represents the object that Wobj points to, is still valid.

Releasing references
each JNI reference itself consumes a certain amount of memory, except that the referenced object consumes memory. As a JNI programmer, you should be aware of the number of references that the program will use for a given period of time. In particular, although the local references created by the program will eventually be automatically released by the VM, you still need to know the maximum number of local references that are created at any time during the execution of the program. Creating too many references, even if they are instantaneous and ephemeral, can result in memory exhaustion.

Free local references
In most cases, when you execute a native method, you do not have to worry about releasing the local reference, and the Java VM is released when the native method returns to the caller. However, sometimes it takes a JNI programmer to display a free local reference to avoid excessive memory usage. So when do I need to show the release, and look at the situation:
1 in a single native method invocation, a large number of local references are created. This may cause a JNI local reference table to overflow. It is necessary to delete the local references that are no longer in use at this time. For example, in the following code, it is possible to create a huge array of strings each time in the loop. After each iteration, the native code needs to display a partial reference to the string element:

for (i = 0; i < len; i++) { 
  jstring jstr = (*env)->getobjectarrayelement (env, arr, i); 
  ... * * Process JSTR/ 
  (*env)->deletelocalref (env, JSTR); 
} 

2 You may want to create a tool function that will be invoked by an unknown context. For example, as mentioned earlier, this example of mynewstring, which is owed by the caller each time it is returned, releases the local reference in a timely manner.


3) The native method may not return (for example, a method that may go into a loop of infinite event distribution). Releasing a local reference in a loop at this point is critical in order to not accumulate indefinitely, resulting in a memory leak.


4 The native method may access a large object, so a local reference to that object is created. The native method performs an extra calculation before returning the caller, in addition to accessing the object. A local reference to this large object will include the object in case it is garbage collected. This behavior lasts until the native method returns to the caller, and even if the object is no longer used, it is still protected. In the following example, because the deletelocalref is called before Lengthycomputation (), the garbage collector has the opportunity to release the object that Lref points to.

/* A Native method Implementation * * 
jniexport void Jnicall 
java_pkg_cls_func (jnienv *env, jobject this) 
C4/>lref = ...       /* A large Java object */ 
  ...           /* Last use of Lref * * 
  (*ENV)->deletelocalref (env, lref); 
  Lengthycomputation ();  /* may take some time * 
  /return;         * All local refs are freed */ 
} 

The essence of this scenario is to allow the Java garbage collection mechanism to recycle objects that are not accessed by the native code during the execution of the native method.

Authority Department Reference
do not know how Java 7, should be more powerful bar, have time, to see, here and according to the characteristics of JAVA2.
SDK1.2 provides a set of additional functions to manage the lifecycle of a local reference. They are ensurelocalcapacity, Newlocalref, Pushlocalfram and Poplocalfram.
The specification of JNI requires the VM to automatically ensure that each native method can create at least 16 local references. Experience shows that this capacity is sufficient for most native methods if the native method does not contain complex interoperability with the Java VM's objects. If this is not enough, you need to create more local references, then the native method can invoke ensurelocalcapacity to ensure that there is enough space for these local references.

/* The number of local references to was equal to the 
  length of the array. * 
/if ((*env)->ensurelocal Capacity (env, Len)) < 0 {.../* out of   memory * 
/} for (i = 0; i < len; i++) { 
  jstring jstr = (*env)-& Gt Getobjectarrayelement (env, arr, i); 
  ... * * Process JSTR *////   * Deletelocalref is no longer necessary * * 

By doing this, the memory consumed will naturally be more than the previous version.


In addition, the Pushlocalfram\poplocalfram function allows programmers to create local references to nested scopes. The following code:

#define N_REFS./* The maximum number of local references used in each 
 iteration/for 
(i = 0; i < Len; i++ { 
  if (*env)->pushlocalframe (env, n_refs) < 0) {.../ 
    * out of Memory */ 
  } 
  jstr = (*env)->getob Jectarrayelement (env, arr, i); 
  .../* Process JSTR/ 
  (*env)->poplocalframe (env, NULL); 
 

Pushlocalfram creates a new scope for a specified number of local references, Poplocalfram destroys the top-level scope, and frees all local references in the domain.


The benefit of using these two functions is that they can be referenced by the authority's lifecycle without having to relate to each individual local reference that might be created during execution. example, if the process of processing JSTR creates additional local references, they will be released immediately after Poplocalfram.


The Newlocalref function is useful when you are writing a tool function. This will be in the following section--manage the referenced rules, concrete analysis.

Native code may create scopes that exceed 16 local references, or they may be saved in Pushlocalfram or ensurelocalcapacity calls, and the VM allocates the required memory for a local reference. However, the adequacy of these memory is not guaranteed. If the memory allocation fails, the virtual machine exits.

Releasing global references


When native code no longer needs to access a global reference, you should call Deleteglobalref to release it. If the call to this function fails, the Java VM will not reclaim the corresponding object.


When the native code does not need to access a weak global reference, you should call Deleteweakglobalref to release it. If the call to this function fails, the Java VM will still reclaim the underlying object, but the memory consumed by the weak reference itself will not be reclaimed.


rules for managing references
The purpose of administrative references is to purge unwanted memory usage and object retention.

In general, there are only two types of native code: functions that implement the native method directly, the tool function that is used in the binary context.

When writing the implementation of the native method, it is necessary to be careful about creating a local reference in the loop, as well as being created in the native method, but not returning the local reference to the caller. It is acceptable to leave 16 local references in use when the native method method returns and give them to the Java VM for release. However, calls to the native method should not result in the accumulation of global references and weak global references. These references should not be automatically freed after the native method returns.


When writing a tool function, you must be careful not to divulge any local references or to execute beyond that function. Because a tool function may be repeatedly invoked in unexpected contexts. Any unwanted reference creation can lead to memory leaks.
1 when a tool function that returns an underlying type is invoked, it must have no local reference, if the global reference is cumulative.
2 when a tool function that returns a reference type is invoked, it must have no local, global, or cumulative reference, except as a reference to the return value.


A tool function creates global or weak global references for capturing purposes, which is acceptable because they are created only at the very beginning.


If a tool function returns a reference, you should make the type of reference returned (such as local reference, global reference) as part of the function specification. It should be consistent instead of sometimes returning a local reference, sometimes returning a global reference. The caller needs to know the type of reference returned by the tool function to properly manage its own JNI reference. The following code repeatedly calls a tool tool function (getinfostring). We need to know the type of reference returned by Getinfostring in order to release the reference:

while (jni_true) { 
  jstring infostring = getinfostring (info); 
  ... * * Process infostring */ 
  ???/* We need to call Deletelocalref, Deleteglobalref, 
 or deleteweakglobalref depend ing on the type of 
 reference returned by getinfostring. */ 
} 

In Java2 SDK1.2, the Newlocalref function can be used to ensure that a tool function always returns a local reference. To illustrate this issue, we made some changes to mynewstring, which caches a frequently requested string ("Commonstring") to a global reference:

Jstring

Mynewstring (jnienv *env, Jchar *chars, Jint len) 
{ 
  static jstring result; 
  /* WSTRNCMP compares two Unicode strings 
  /if (wstrncmp ("commonstring", chars, Len) = 0) {/* refers to the glob 
    Al ref caching "commonstring" 
    /static jstring cachedstring = NULL; 
    if (cachedstring = = NULL) {/* Create cachedstring for the "the" the "the"/ 
      jstring cachedstringlocal = ...; 
      * Cache The result in a global reference * 
      /cachedstring = 
        (*env)->newglobalref (env, cachedstringlocal); c13/>} return 
    (*env)->newlocalref (env, cachedstring); 
  } 
  .../* Create the string as a local reference and store in result as 
 a local reference * * result 
  ; 
 

A local reference when the normal process returns. As explained earlier, we have to save the cached character to a global reference so that it can be accessed when the native method is invoked in multiple threads.

Return (*ENV)->newlocalref (env, cachedstring); 

This statement creates a local reference that points to the unified object to which the global reference is cached. As part of the Convention with the caller, mynewstring always returns a local reference.


The Pushlocalfram, Poplocalfram function is used to refer to the life cycle of the Authority department particularly conveniently. You only need to call Pushlocalfram at the entrance of the native function, call Poplocalfram when the function exits, and the local variable will be freed.

Jobject F (jnienv *env, ...) 
{ 
  jobject result; 
  if ((*env)->pushlocalframe (env, a) < 0) {/ 
    * frame not pushed, no poplocalframe needed/return 
    NULL; 
  } 
  ... 
  result = ...; 
  if (...) {/ 
    * Remember to pop the local frame before return * 
    /result = (*env)->poplocalframe (env, result); 
    return result; 
  } 
  ... 
  result = (*env)->poplocalframe (env, result); 
  /* Normal return * * result 
  ; 
 

When a Poplocalfram function call fails, undefined behavior, such as VM crashes, can result.

Memory leak problem
Memory leaks in Java Heap
Java objects are stored in the Java Heap in the JVM's process space, and the Java Heap can change dynamically during the JVM's operation. If there are more Java objects and more space to occupy the Java Heap, the JVM expands the capacity of the Java Heap at run time. If the Java Heap capacity expands to the upper limit, and there is still not enough space to allocate a new Java object after the GC, an out of memory exception is thrown, causing the JVM process to crash.
There are two reasons for the occurrence of an out of memory exception in the Java Heap--① The program is too large, causing too many Java objects to exist at the same time; errors written by the ② program cause Java Heap memory leaks.
A variety of reasons may cause Java Heap memory leaks. JNI programming errors can also cause memory leaks in the Java Heap.
Native memory memory leaks in the JVM
From an operating system perspective, the JVM is not fundamentally different from the other processes at run time. At the system level, they have the same scheduling mechanism, the same memory allocation, the same pattern of memory.
In the JVM process space, the memory space outside of the Java Heap is called the JVM's native memory. Much of the process's resources are stored in the JVM's native memory, such as the loaded code image, the thread stack, the thread's management control block, the JVM's static data, global data, and so on. Also includes the resources assigned to native code in the JNI program.
In the JVM operation, most of the process resources are dynamically allocated from the native memory. When more and more resources are allocated in the native memory, and more and more native memory space is reached and the native memory upper bound, the JVM throws an exception that causes the JVM process to exit abnormally. At this time, Java Heap often not reached the upper limit.
Multiple causes can cause a native memory memory leak in the JVM. For example, the JVM is running too many threads to be created and running at the same time. The resources allocated by the JVM to the thread may deplete the capacity of the native memory.
JNI programming errors can also lead to native memory memory leaks. The discussion on this topic is the focus of this paper.

JNI programming implements the interaction between native code and Java programs, so JNI code programming follows the programming rules of the native code programming language while also complying with the documentation specification for JNI programming. In memory management, the memory management mechanism of the native code programming language itself remains to be followed, as well as to consider the memory management of JNI programming.
This chapter provides a brief overview of the obvious memory leaks in JNI programming. From the native code programming language of its own memory management, and JNI specification additional memory management two aspects of the elaboration.
memory leaks in the Native Code itself
JNI programming is first a specific programming language, or C language, or C + +, or a compilation, or other native programming language. Each programming language environment implements its own memory management mechanism. Therefore, the JNI program developer should follow the memory management mechanism of the native language itself to avoid causing the memory leak. In C, for example, when a malloc () is used to dynamically allocate memory in the process heap, the JNI program should call Free () to release the memory when it is exhausted. All in all, the memory leak rules that should be noted in native language programming are still adaptable in JNI programming.
The memory leak introduced by the Native language itself causes Native memory memory, which in some cases causes Native memory out of the memory.
Memory leaks introduced by Global Reference
JNI programming also follows JNI's normative standards, and the JVM attaches a memory management mechanism specific to JNI programming.
The local Reference in JNI exists only when native method executes and automatically expires when native method is executed. This automatic invalidation makes the use of the local Reference relatively simple, and when native method finishes, the Reference count of the Java objects they reference will be reduced by 1. does not cause memory leaks of Java objects in Java Heap.
The Global Reference reference to Java objects is always valid, so the Java objects they reference will always exist in the Java Heap. Programmers need to carefully maintain the use of the global Reference when using global Reference. If you must use the Global Reference, be sure to remove it when you are not using it. Just as in C, calling free () is the same as malloc () dynamically allocating a piece of memory. Otherwise, the Java object referenced by Global Reference will stay in the Java Heap forever, causing a memory leak in the Java Heap.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.