Javaはどのようにネイティブメソッドを呼び出しますか?ホットスポットのソースコード分析にはスキルが必要です

JDKソースコード(並行並行パッケージ、スレッド関連のソースコードなど)を学習するときは、メソッドをレイヤーごとに入力します。通常、最下層を見るとネイティブの変更メソッドが表示されます。

JDKソースコードを見るときにネイティブメソッドがないのはなぜですか?ネイティブメソッドは何をしますか?ネイティブメソッドはどこで確認できますか?Javaはどのようにネイティブメソッドを呼び出しますか?今日、実際のシミュレーションを通じて、Javaがネイティブメソッドを呼び出す方法を確認してください。

このテストを行うために、私は2泊かかり、さまざまな問題に遭遇しました。これらの問題を解決するために、私は何本のタバコを吸い、何本の髪を失ったのかわかりません。

上のテキスト。

1.ネイティブメソッドがあるのはなぜですか

Javaは高レベルのコンピューター言語であり、最終的には基盤となるオペレーティングシステムで実行する必要がありますが、Javaはオペレーティングシステムを直接操作することはできません。これには、Javaとオペレーティングシステム間での言語エスケープと同様のプロセスが必要です。

C言語とC ++言語はオペレーティングシステムと直接対話できることを私たちは知っています。JDKのネイティブメソッドは、Java操作命令をCおよびC ++に変換して、基盤となるオペレーティングシステムとの相互作用を実現できます。Java操作をCおよびC ++に変換するプロセスは、JVMによって実行されます。jvmのソースコード(ホットスポットなど)には、JDKのネイティブメソッドの特定の実装を含む大量のCおよびC ++コードが含まれています。

ここでは、JDK、JRE、およびJVMの関係を確認したいと思います。JDKは、Javaランタイム環境JRE、Javaツール、Java基本クラスライブラリを含むJava全体のコアであるJava開発キットです。JREはJDKプロジェクトの一部であり、JVM標準およびJavaコアクラスライブラリの実装を含む、Javaのランタイム環境です。JVMはJava仮想マシンであり、クロスプラットフォームのJava実装全体の中核部分であり、Java言語で記述されたソフトウェアプログラムを実行できます。したがって、JVMはJava言語とオペレーティングシステムを接続するブリッジです。Javaの「一度コンパイルしてどこでも実行」は、JVMモジュールでは、同じネイティブメソッドのオペレーティングシステムの実装が異なるため、JVMが異なるオペレーティングシステム間の違いを保護することを意味します。さまざまなオペレーティングシステムの要件を満たすため。したがって、ネイティブメソッドの特定の実装を理解したい場合は、JVMコードを確認する必要があります。JVMのソースコードはどこにありますか?もちろん、それはJDKのソースコードにあります。ここでは、OpenJdkのさまざまなバージョンのコードを表示できます。openJdk内にはさまざまなバージョンのホットスポット実装があります。

今日はJDKのソースコードに焦点を当てていないので、ここでは詳しく説明しません。

cまたはc ++で記述されたネイティブメソッドを呼び出すJavaをシミュレートするテクノロジは、JNI(Java Native Interface)と呼ばれます。JNIは、コードがさまざまなプラットフォームに簡単に移植されることを保証できます。

2つ目は、単純なJavaオブジェクトを作成することです。

ここに簡単なjavaクラスを記述し、javacを使用してコンパイルし、javapを使用してヘッダーファイルを生成し、javaコマンドを使用して実行します。

/**
 * 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();
}

いくつかの落とし穴:

  • 後ですべての種類の蛾を表示しないようにするために、パッケージ名を追加しないことをお勧めします。

  • コードの12行目のライブラリファイルは後で生成されます。ファイル名とパスに注意してください。ライブラリファイルはSystem.loadLibrary( "JavaCallC" )、このメソッドを使用してロードすることもできます。この方法でロードするときは、ライブラリの名前に注意してください。

  • コードの24行目は、ネイティブメソッドを定義しています。後で実装をシミュレートするためにc言語を使用します。

3つ目は、JavaCallC.classファイルを取得することです。

上記のファイルをCentosにアップロードし、次のコマンドを使用してコンパイルします。

ファイルのアップロードパス: /root/java-learn

次のパスでコンパイルコマンドを実行します。 java JavaCallC.java

クラスファイルは、次のパスで生成されます。JavaCallC.class

第四に、JavaCallC.hファイルを取得します

/root/java-learnパス、ヘッダファイルのjavahを使用してコマンドを生成します

実行パス内:javah JavaCallC接尾辞を付けないように注意してください。

ヘッダーファイルは次のパスで生成されます。JavaCallC.h

上記の実行プロセス:

[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]# 

ヘッダーファイルを開いて、特定のコンテンツを表示します。

[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]# 

ヘッダーファイルの16〜17行目は非常に重要です。これは、上記のJavaファイルのcMethodメソッドの署名です。このメソッドを次のC言語で実装する場合、メソッドのシグネチャはこのメソッドと一致している必要があります

5つ目は、C言語を使用してネイティブメソッドをシミュレートする

cコード、ファイル名をシミュレートします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();}

同じことがCclass.cCentosのファイルアップロードパスにアップロードされます:/root/java-learn

Cclass.cダイナミックリンクライブラリファイルを生成するには、次のように使用します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

多くのピット:

  • 生成されたライブラリファイルの名前とパスは、上記のJavaファイルにロードされたものと同じである必要があります。その中に-o libJavaCallC.soは、生成されたライブラリファイルの名前があります。System.loadLibrary()メソッドを使用してロードされたライブラリファイルを使用する場合、使用されるライブラリ名は「libJavaCallC」または「libJavaCallC.so」ではなく「JavaCallC」です。
  • JavaCallC.javaCclass.cファイル内のネイティブメソッドcMethod()がファイルに実装されている場合JavaCallC.h、ヘッダーファイル内のcMethed()の署名一致している必要があり、使用する必要がありますJNIEXPORT void JNICALL Java_JavaCallC_cMethod(JNIEnv *env, jobject c1)
  • Cclass.c必ず#include "JavaCallC.h"ヘッダーファイルをファイルヘッダーにインクルードしてください。そうしないと、コンパイルおよび実行中にヘッダーファイルが見つかりませんJava_JavaCallC_cMethod
  • ため、gccでコンパイルする場合Cclass.c、それが含まれているJavaCallC.hヘッダファイルを、およびJavaCallC.hの2行目#include <jni.h>のヘッダファイルが含まれているヘッダファイル、jni.h他のヘッダファイルが含まれているが、GCCがコンパイルされるとき、これらのヘッダファイルの場所を指定する必要があります。これらのヘッダーファイルはすべてjdkが配置されているディレクトリにあり、これらのディレクトリの場所はパラメータ-Iで指定する必要あります

共有ライブラリ(DLL)ファイルを実行した後:libJavaCallC.so

コンパイル後、ファイルがの環境変数ライブラリファイルLD_LIBRARY_PATH追加された共有ライブラリディレクトリLD_LIBRARY_PATHこれはLinux環境変数の名前であり、主に共有ライブラリ(ダイナミックリンクライブラリ)を検索するときにデフォルトパス以外のパスを指定するために使用されます。

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

6、Javaを実行します

上記の操作により、/root/java-learnディレクトリには次の5つのファイルがあります。

[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]# 

以下のjava JavaCallCコマンドを使用して、現在のディレクトリでJavaプログラムを実行します。

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

印刷結果Java_JavaCallC_cMethod call succ実行すると、Javaがネイティブメソッドを呼び出し、Cファイルのメソッド本体を実行し、正常に実行されたものを出力していることがわかります。

セブン、遭遇した問題

このテストを行っているときに、さまざまな問題が発生しました。ここにリストされています:

[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头文件中的不一致

公式アカウントをフォローし、「java-summaryと入力してソースコードを取得します。

終わった、それを一日と呼んでください!

[知識の普及、価値の共有]、あなたの注意とサポートに感謝します、私は[ Zhuge Xiaoyuan ]、ためらいに苦しんでいるインターネット移民労働者です。

おすすめ

転載: blog.csdn.net/wuxiaolongah/article/details/109685475