How does java call native methods? Hotspot source code analysis must have skills

When learning JDK source code (concurrent concurrent package, Thread related source code, etc.), enter the method layer by layer, and you will usually see a native modification method when you see the bottom layer.

Why is there no native method when looking at the JDK source code? What does the native method do? Where can I see the native method? How does java call native methods? Today, through actual simulation, see how java calls native methods.

In order to do this test, it took me two nights and encountered various problems. In order to solve these problems, I don’t know how many cigarettes I smoked and how many hair I lost.

Upper text.

1. Why is there a native method

Java is a higher-level computer language, and ultimately needs to be executed on the underlying operating system, while java cannot directly operate the operating system. This requires a process similar to language escaping between Java and the operating system.

We know that C language and C++ language can directly interact with the operating system. The native method in JDK can convert java operation instructions into C and C++, so as to realize the interaction with the underlying operating system. The process of converting Java operations into C and C++ is done by JVM. The source code of jvm (such as hotspot) contains a large amount of C and C++ code, which contains the specific implementation of the native method in JDK.

Here I want to review the relationship between JDK, JRE, and JVM. JDK is the Java development kit, the core of the entire Java, including the Java runtime environment JRE, Java tools and Java basic class libraries. JRE is a part of the JDK project and is the runtime environment of Java, including the implementation of the JVM standard and the Java core class library. JVM is the Java virtual machine, which is the core part of the entire Java implementation of cross-platform, and can run software programs written in Java language. Therefore, the JVM is a bridge connecting the Java language and the operating system. Java’s "compile once and run everywhere" means that the JVM shields the differences between different operating systems, because in the JVM module, the same native method will be implemented by different operating systems. To meet the requirements of different operating systems. Therefore, if you want to understand the specific implementation of the native method, you must look at the JVM code. Where is the source code of the JVM? Of course it is in the source code of the JDK. Here you can view the code of different versions of OpenJdk, there are different versions of hotspot implementation inside openJdk.

Today's focus is not on the source code of the JDK, so I won't go into details here.

The technology that simulates Java calling native methods written in c or c++ is called JNI (Java Native Interface). JNI can ensure that the code is easily transplanted on different platforms.

Two, write a simple java object

Write a simple java class here, use javac to compile, javap to produce header files, and use java commands to execute.

/**
 * Description: java调用C
 * java方法中有很多native方法,这些方法都是hotspot中用C或者C++实现的。
 * 下面模拟一个java调用C的过程
 * @author 诸葛小猿
 * @date 2020-11-11
 */
public class JavaCallC {
    
    

    static {
    
    
        // 使用文件名加载自定义的C语言库
        System.load("/root/java-learn/libJavaCallC.so" );
    }

    public static void main(String[] args) {
    
    

        JavaCallC javaCallC =new JavaCallC();
        
        // 调用本地方法
        javaCallC.cMethod();
    }

    // 使用C语言实现本地方法
    private native void cMethod();
}

A few pits:

  • In order not to show all kinds of moths later, it is recommended not to add the package name.

  • The library file on line 12 of the code will be generated later, pay attention to the file name and path. The library file can also be System.loadLibrary( "JavaCallC" )loaded using the method. When loading in this way, pay attention to the name of the library;

  • The 24th line of the code defines a native method. I will use the c language to simulate the implementation later.

Three, get the JavaCallC.class file

Upload the above file to Centos and use the following command to compile it.

File upload path: /root/java-learn

Execute the compilation command in this path: java JavaCallC.java

A class file will be generated under this path:JavaCallC.class

Fourth, get the JavaCallC.h file

In /root/java-learnthe path, the header file to generate commands using the javah

In the execution path: javah JavaCallC. Be careful not to put a suffix.

The header file will be generated in this path:JavaCallC.h

The above execution process:

[root@iZuf61pdvb2o7cf4mu9ccyZ java-learn]# 
[root@iZuf61pdvb2o7cf4mu9ccyZ java-learn]# pwd
/root/java-learn
[root@iZuf61pdvb2o7cf4mu9ccyZ java-learn]# ll
total 4
-rw-r--r-- 1 root root 635 Nov 12 23:45 JavaCallC.java
[root@iZuf61pdvb2o7cf4mu9ccyZ java-learn]# javac JavaCallC.java 
[root@iZuf61pdvb2o7cf4mu9ccyZ java-learn]# 
[root@iZuf61pdvb2o7cf4mu9ccyZ java-learn]# ll
total 8
-rw-r--r-- 1 root root 476 Nov 12 23:46 JavaCallC.class
-rw-r--r-- 1 root root 635 Nov 12 23:45 JavaCallC.java
[root@iZuf61pdvb2o7cf4mu9ccyZ java-learn]# 
[root@iZuf61pdvb2o7cf4mu9ccyZ java-learn]# javah JavaCallC
[root@iZuf61pdvb2o7cf4mu9ccyZ java-learn]# 
[root@iZuf61pdvb2o7cf4mu9ccyZ java-learn]# ll
total 12
-rw-r--r-- 1 root root 476 Nov 12 23:46 JavaCallC.class
-rw-r--r-- 1 root root 376 Nov 12 23:46 JavaCallC.h
-rw-r--r-- 1 root root 635 Nov 12 23:45 JavaCallC.java
[root@iZuf61pdvb2o7cf4mu9ccyZ java-learn]# 

Open the header file to view the specific content:

[root@iZuf61pdvb2o7cf4mu9ccyZ java-learn]# cat  JavaCallC.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class JavaCallC */

#ifndef _Included_JavaCallC
#define _Included_JavaCallC
#ifdef __cplusplus
extern "C" {
    
    
#endif
/*
 * Class:     JavaCallC
 * Method:    cMethod
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_JavaCallC_cMethod # 这里就是java文件中cMethod方法的签名。
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif
[root@iZuf61pdvb2o7cf4mu9ccyZ java-learn]# 

Lines 16-17 of the header file are very important. It is the signature of the cMethod method of the java file above. When implementing this method in the following C language, the signature of the method must be consistent with this method .

Five, use C language to simulate a native method

Simulate a c code, file name Cclass.c:

#include <stdio.h> //头文件
#include "JavaCallC.h" // java文件头,这里一定要加上上面java语言的头文件

// 这就是上面头文件中的cMethod方法的具体实现,注意方法签名不能变,一定要和头文件一样。
JNIEXPORT void JNICALL Java_JavaCallC_cMethod(JNIEnv *env, jobject c1) 
{
    
    
    // 如果java调用cMethod方法成功,则会打印这句话
    printf("Java_JavaCallC_cMethod call succ \n");
}

// 以下所有的内容的内容是测试Cclass.c的语法的,可以省掉。
// 先声明 后调用
void test(){
    
     printf("main C \n");}

//main方法,程序入口,用于测试
int main(){
    
     test();}

The same will be Cclass.cuploaded to the Centos, file upload path: /root/java-learn.

The following use Cclass.cto generate dynamic link library file: libJavaCallC.so.

[root@iZuf61pdvb2o7cf4mu9ccyZ java-learn]# gcc  -fPIC -I /opt/jdk1.8.0_211/include  -I /opt/jdk1.8.0_211/include/linux   -shared -o libJavaCallC.so Cclass.c

Many pits:

  • The name and path of the generated library file must be the same as those loaded in the java file above. Among them -o libJavaCallC.sois the name of the generated library file. If you use System.loadLibrary()a library file loaded using the method, the library name used is: "JavaCallC" instead of "libJavaCallC" or "libJavaCallC.so".
  • JavaCallC.javaWhen the native method cMethod() in the Cclass.cfile is implemented in the file, it must be JavaCallC.hconsistent with the signature of cMethed() in the header file and must be used JNIEXPORT void JNICALL Java_JavaCallC_cMethod(JNIEnv *env, jobject c1).
  • Cclass.cBe sure to #include "JavaCallC.h"include the header file in the file header , otherwise it will not be found during compilation and execution Java_JavaCallC_cMethod.
  • When compiling with gcc, because Cclass.cit contains the JavaCallC.hheader file, and JavaCallC.hthe second line of the #include <jni.h>header file contains the header file, which jni.halso contains other header files, when gcc is compiled, the location of these header files must be specified. These header files are all in the directory where jdk is located, and the location of these directories should -Ibe specified with parameters .

After running a shared library (DLL) file: libJavaCallC.so.

After compilation, the shared library directory where the file was added to the environment variable library file LD_LIBRARY_PATHin. LD_LIBRARY_PATHIt is the name of the Linux environment variable, which is mainly used to specify paths other than the default path when searching for shared libraries (dynamic link libraries).

[root@iZuf61pdvb2o7cf4mu9ccyZ java-learn]# export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/root/java-learn

Six, execute java

Through the above operation, /root/java-learnthere will be the following 5 files in the directory:

[root@iZuf61pdvb2o7cf4mu9ccyZ java-learn]# ll
total 24
-rw-r--r-- 1 root root  594 Nov 12 22:39 Cclass.c
-rw-r--r-- 1 root root  852 Nov 12 22:05 JavaCallC.class
-rw-r--r-- 1 root root  376 Nov 12 22:05 JavaCallC.h
-rw-r--r-- 1 root root 1108 Nov 12 22:04 JavaCallC.java
-rwxr-xr-x 1 root root 6179 Nov 12 22:39 libJavaCallC.so
[root@iZuf61pdvb2o7cf4mu9ccyZ java-learn]# 

Use the java JavaCallCcommand below to execute our java program in the current directory:

[root@iZuf61pdvb2o7cf4mu9ccyZ java-learn]# 
[root@iZuf61pdvb2o7cf4mu9ccyZ java-learn]# java JavaCallC
Java_JavaCallC_cMethod call succ
[root@iZuf61pdvb2o7cf4mu9ccyZ java-learn]# 

It Java_JavaCallC_cMethod call succcan be seen by executing the printing result that java calls the native method, executes the method body in the C file, and prints out the successful execution.

Seven, the problems encountered

When doing this test, I encountered various problems. Listed here:

[root@iZuf61pdvb2o7cf4mu9ccyZ java-learn]# java com.wuxiaolong.LB.Demo.Lesson1.JavaCallC
Error: Could not find or load main class com.wuxiaolong.LB.Demo.Lesson1.JavaCallC
[root@iZuf61pdvb2o7cf4mu9ccyZ java-learn]# 
## 这个问题是因为最开始使用了包名,执行时报错,可以通过相关的配置解决,测试中我去掉了包名。

[root@iZuf61pdvb2o7cf4mu9ccyZ java-learn]# java JavaCallC
Exception in thread "main" java.lang.UnsatisfiedLinkError: no JavaCallC in java.library.path
        at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1867)
        at java.lang.Runtime.loadLibrary0(Runtime.java:870)
        at java.lang.System.loadLibrary(System.java:1122)
        at JavaCallC.<clinit>(JavaCallC.java:16)
[root@iZuf61pdvb2o7cf4mu9ccyZ java-learn]# 
## 这是因为加载时使用的时System.loadLibrary(),而库名写错了

[root@iZuf61pdvb2o7cf4mu9ccyZ java-learn]# gcc  -fPIC -I /opt/jdk1.8.0_211/include -I /opt/jdk1.8.0_211/include/linux  -shared -o libJavaCallC.so Cclass.c
Cclass.c:2:53: error: Java_JavaCallC_cMethod.h: No such file or directory
[root@iZuf61pdvb2o7cf4mu9ccyZ java-learn]# 
## 这好像是因为Cclass.c文件中没有使用: #include "JavaCallC.h"

[root@iZuf61pdvb2o7cf4mu9ccyZ java-learn]# java JavaCallC       
Exception in thread "main" java.lang.UnsatisfiedLinkError: JavaCallC.cMethod()V
        at JavaCallC.cMethod(Native Method)
        at JavaCallC.main(JavaCallC.java:25)
[root@iZuf61pdvb2o7cf4mu9ccyZ java-learn]# 
## 这是因为Cclass.c文件方法的签名和JavaCallC.h头文件中的不一致


[root@iZuf61pdvb2o7cf4mu9ccyZ java-learn]# gcc  -fPIC -I /opt/jdk1.8.0_211/include  -shared -o libJavaCallC.so Cclass.c
In file included from JavaCallC.h:2,
                 from Cclass.c:2:
/opt/jdk1.8.0_211/include/jni.h:45:20: error: jni_md.h: No such file or directory
In file included from JavaCallC.h:2,
                 from Cclass.c:2:
/opt/jdk1.8.0_211/include/jni.h:63: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘jsize’
/opt/jdk1.8.0_211/include/jni.h:122: error: expected specifier-qualifier-list before ‘jbyte’
/opt/jdk1.8.0_211/include/jni.h:220: error: expected specifier-qualifier-list before ‘jint’
/opt/jdk1.8.0_211/include/jni.h:1869: error: expected specifier-qualifier-list before ‘jint’
/opt/jdk1.8.0_211/include/jni.h:1877: error: expected specifier-qualifier-list before ‘jint’
/opt/jdk1.8.0_211/include/jni.h:1895: error: expected specifier-qualifier-list before ‘jint’
/opt/jdk1.8.0_211/include/jni.h:1934: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘jint’
/opt/jdk1.8.0_211/include/jni.h:1937: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘jint’
/opt/jdk1.8.0_211/include/jni.h:1940: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘jint’
/opt/jdk1.8.0_211/include/jni.h:1944: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘jint’
/opt/jdk1.8.0_211/include/jni.h:1947: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘void’
In file included from Cclass.c:2:
JavaCallC.h:15: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘void’
Cclass.c:11: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘void’
[root@iZuf61pdvb2o7cf4mu9ccyZ java-learn]# 
## 这是因为编译时少了参数 : -I /opt/jdk1.8.0_211/include/linux

[root@iZuf61pdvb2o7cf4mu9ccyZ java-learn]# gcc  -fPIC -I /opt/jdk1.8.0_211/include -I /opt/jdk1.8.0_211/include/linux  -shared -o libJavaCallC.so Cclass.c
Cclass.c:19: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘void’
[root@iZuf61pdvb2o7cf4mu9ccyZ java-learn]# 
## 这好像是因为Cclass.c文件方法的签名和JavaCallC.h头文件中的不一致

[root@iZuf61pdvb2o7cf4mu9ccyZ java-learn]# gcc  -fPIC -I /opt/jdk1.8.0_211/include  -I /opt/jdk1.8.0_211/include/linux   -shared -o libJavaCallC.so Cclass.c
Cclass.c: In function ‘Java_JavaCallC_cMethod’:
Cclass.c:12: error: expected declaration specifiers before ‘printf’
Cclass.c:13: error: expected declaration specifiers before ‘}’ token
Cclass.c:13: error: expected ‘{
    
    ’ at end of input
[root@iZuf61pdvb2o7cf4mu9ccyZ java-learn]#
## 这好像是因为Cclass.c文件方法的签名和JavaCallC.h头文件中的不一致

Follow the official account and enter " java-summary " to get the source code.

Finished, call it a day!

[ Dissemination of knowledge, sharing of value ], thank you friends for your attention and support. I am [ Zhuge Xiaoyuan ], an Internet migrant worker struggling in hesitation.

Guess you like

Origin blog.csdn.net/wuxiaolongah/article/details/109685475