The Java environment and language are extremely secure and efficient for application development. However, some applications need to execute some tasks that cannot be completed by pure Java programs, such:
Integrate with old code to avoid rewrite.
Implement the functions missing from the available class libraries. For example, to implement Ping in Java, you may need the Internet Control Message Protocol (ICMP) function, but the basic class library does not provide it.
It is best to integrate with code written in C/C ++ to fully explore performance or other environment-related system features.
Solve special situations that require non-Java code. For example, the implementation of core class libraries may require cross-package calls or bypass other Java security checks.
JNI allows you to complete these tasks. It clearly separates Java code from local code (C/C ++) Execution and defines a clear API for communication between the two. To a large extent, it avoids the Direct Memory Reference of the local code to the JVM, so that the local code only needs to be written once and can be executed across different JVM implementations or versions.
With JNI, the local code can interact with Java objects at will, obtain and design field values, and call methods, without being as limited as the same functions in Java code. This freedom is a double-edged sword: It sacrifices the security of Java code, in exchange for the ability to complete the tasks listed above. JNI provides powerful low-level access to machine resources (memory, I/O, etc.) in your applications, therefore, you are not protected by the security net as normal Java developers do. The flexibility and robustness of JNI brings some programming practices risks, such as poor performance, bugs, and even program crashes. You must pay special attention to the Code in the application and use good practices to ensure the overall integrity of the application.
This article describes the top 10 coding and design errors that JNI users encounter most often. The goal is to help you recognize and avoid them so that you can write secure, efficient, and superior-performance JNI code. This article also describes some tools and techniques used to find these problems in new or existing code and shows how to effectively apply them.
Java JNI programming defects can be divided into two types:
Performance: the code can execute the designed functions, but the whole program may be slowed down or slowed down in some form.
Correctness: the code runs normally sometimes, but cannot provide the required functions reliably. The worst case is that the program crashes or hangs.
Performance Defects
The five major performance defects of programmers when using JNI are as follows:
◆ Method ID, field ID, and class are not cached
◆ Trigger an array copy
◆ Return visit (reaching back) instead of passing Parameters
◆ Identify the boundary between local code and Java code by mistake
◆ Use a large number of local references without notifying JVM
◆ Method ID, field ID, and class are not cached
To access fields of Java objects and call their methods, the local code must call findclass (), getfieldid (), getmethodid (), and getstaticmethodid (). For getfieldid (), getmethodid (), and getstaticmethodid (), the IDS returned for a specific class will not change during the lifetime of the JVM process. However, calling to obtain fields or methods sometimes requires a lot of work in JVM, because fields and methods may be inherited from the superclass, this allows the JVM to traverse the class hierarchy to find them. Because the IDs are the same for a specific class, you only need to find them once and then you can use them again. Similarly, the overhead of searching class objects is large, so they should also be cached.
For example, listing 1 shows the JNI Code required to call a static method:
Listing 1. Calling static methods using JNI
Int val = 1; Jmethodid method; Jclass CLS; CLS = (* env)-> findclass (ENV, "com/IBM/example/testclass "); If (* env)-> exceptioncheck (ENV )){ Return err_find_class_failed; } Method = (* env)-> getstaticmethodid (ENV, CLS, "setinfo", "(I) V "); If (* env)-> exceptioncheck (ENV )){ Return err_get_static_method_failed; } (* Env)-> callstaticvoidmethod (ENV, CLS, method, Val ); If (* env)-> exceptioncheck (ENV )){ Return err_call_static_method_failed; } |
Each time we want to call a method, The lookup class and method ID generate six local calls, instead of the two calls required for the first cache class and method ID.