【Java基础】JNI机制开发指南—认识JNI原理及如何用 Java 调用 C 的动态链接库

今天在看java多线程编程的时候,发现Thread这个类中有多个native方法,以前从来没有见过这种方法,因此对于比较好奇,查阅了一些资料,现在整理一下,以作备忘。

一.知识拓展

1. *.c、*.cpp、*.h是什么?

  • *.c:用C编写的源代码文件的后缀名
  • *.cpp : 用C++编写的源代码文件的后缀名
  • *.h: 用C++/C语言编写的头文件的后缀名

2.动态库、静态库文件有几种

windows平台

  • .dll : 动态链接库,作为共享函数库的可执行文件.
  • .obj : 目标文件,相当于源代码对应的二进制文件,未经过重定义.
  • .lib : (静态链接库)可理解为多个 obj 的集合,本质与 .obj 相同.

linux平台

  • .so:(share object)动态链接库,和windows 的 dll 类似
  • .o : 目标文件,相当于源代码对应的二进制文件 类似 windows 下 obj.
  • .a : (静态链接库)与 .o 类似,多个 .o 的集合 类似 windows 下 lib.

什么是dll?

  • Windows中的动态链接库(共享库)以 *.dll (Dynamic Link Library)为后缀名,即编译好的可以供其他程序使用的代码和数据。但它们不能直接执行。它们与Linux.so文件类似。DLL文件一般被存放在C:\Windows\System32目录下。

    在Windows中,许多应用程序并不是一个完整的可执行文件,它们被分割成一些相对独立的动态链接库,即DLL文件,放置于系统中。当我们执行某一个程序时,相应的DLL文件就会被调用。一个应用程序可有多个DLL文件,一个DLL文件也可能被几个应用程序所共用,这样的DLL文件被称为共享DLL文件

什么是so?

  • Linux中的动态链接库(共享库)以 *.so (share object)为后缀名,即编译好的可以供其他程序使用的代码和数据。但它们不能直接执行。它们与windows.dll文件类似。

3.Linux 下的静态链接库与动态链接库区别

静态库 .a

  • 文件的命名方式:“libxxx.a”,库名前加“lib”,后缀是“.a”,库名是“xxx”,完整命名为 ‘libjvm.so’
  • 链接时间:在编译的时把库中代码复制进工程中,导致工程变大,但是速度快
  • 链接方式:静态库的链接是将整个函数库的所有数据都合成进了目标代码
    • 优点是编译后的执行程序不需要提供对应的函数库(库已经被打包到可执行程序里,库的加载速度快),程序在运行时与函数库再无瓜葛,移植方便
    • 缺点是浪费空间和资源,因为所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件,如果所使用的静态库发生更新改变,则需要重新编译生成静态库(全量更新)。

动态库 .so

  • 文件命名方式:“libxxx.so”,库名前加“lib”,后缀是“.so”,库名是“xxx”,完整命名为 ‘libjvm.so’

  • 链接时间:动态库在编译时并没有被编译进工程中(内存中只有一份拷贝。工程中只保留链接到这份代码的引用),而是当你的程序执行到相关函数的时候将库文件编译进工程里面。

    • 缺点是函数库并没有整合进程序,加载速度相对较慢,而且程序的运行环境必须提供相应的库;
    • 优点是执行程序体积小,动态库更新了,不需要重新编译程序(前提函数接口没变).不同的应用程序如果调用相同的库,那么在内存中只需要有一份该共享库的实例,规避了空间浪费问题。动态库在程序运行时才被载入,函数库有改变,用户只要更新对应的函数库即可增量更新

二者的区别:同一个程序分别使用静态库和动态库生成两个可执行文件时,静态库链接所生成的那个可执行文件要比动态库链接所生成的可执行文件占用的空间大
在这里插入图片描述
(windows原理类似)

4.windows下使用可以调用LI.so文件吗

Window与Linux执行文件格式不同,在创建动态链接库的时候有一些差异。

  • 在Windows系统下的执行文件格式是PE格式,动态库需要一个DllMain函数做出初始化的入口,通常在导出函数的声明时需要有_declspec(dllexport)关键字

  • Linux下gcc编译的执行文件默认是ELF格式不需要初始化入口,亦不需要函数做特别的声明,编写比较方便。

所以得出结论

  • .dll文件和.so文件在调用时是需要编译链接的,这就要求系统必须要有相对应的环境,由于编译器不同,生成的库文件不同。因此是不能在Windows下调.so文件的,也不能在Linux下调.dll文件。

解决笨办法:

  • 将.so文件对应的C/C++源码拿到Windows下编译链接,生成.dll文件,替换原来的.so文件即可。(如果拿不到源码就没办法了)
  • 将开发环境换成Linux下的,这样一定可以成功调用.so文件。

二.认识 JNI

简单介绍

java是一种跨平台的语言,那么它为什么能做到跨平台,实际上是因为不同的平台(windows、linux、mac-os)java有不同的解释器,对应java不同的虚拟机(JVM),这样,就可以做到源代码文件(.java)只需一次编译成字节码文件(.class),就可以在不同的平台运行 。而java不是万能的,有些操作需要调用操作系统平台API实现,JNI就是Java用于和其他语言进行互操作的API,称为Java Native Interface (Java本地接口)

JAVA无法对操作系统底层进行操作,但可以通过jni(java native interface)调用其他语言来实现底层的访问。
在这里插入图片描述

也许不少人觉得Java已经足够强大,为什么要需要JNI这种东西呢?

  • 我们知道Java是一种平台无关性的语言,平台对于上层的java代码来说是透明的,所以在多数时间我们是不需要JNI的,但是假如你遇到了如下的三种情况之一呢?
    1. 你的Java代码,需要底层的操作系统的某个特性。但是你找遍了JDK帮助文档也找不到相关的API。
    2. 在本地还有一个别的系统,不过他不是Java语言实现的,这个时候你的老板要求你把两套系统整合到一起。
    3. 你的Java代码中需要用到某种算法,不过算法是用C实现并封装在动态链接库文件(DLL)当中

JNI的缺点:

  1. 当在某个操作系统下使用JNI本地代码编译生成了动态链接库后,如果要将这个程序移植到其他操作系统,需要在新的平台重新编译代码生成动态链接库
  2. 对其他语言的不正确使用可能会造成程序出现错误,例如使用c语言进行内存操作时未及时回收内存可能引起的内存泄漏
  3. 对其他语言的依赖过高,会提高了java和其他语言的耦合性,也提高了对项目代码的维护成本

环境需求

1.JNI 最常见的两个应用

  1. 从Java程序调用C/C++(常用,本文也是主要介绍这个)
  2. 从C/C++程序调用Java代码

JNI不局限于c、c++,也可以和其他语言进行交互

2.需要下列工具与组件

  1. javac.exe: Java 编译器:随JDK一起提供的
  2. java.exe: Java 虚拟机(JVM):随 JDK一起提供的 。
  3. javah.exe: 本机方法 C 文件生成器:随JDK一起提供的 。

3.JDK提供定义JNI的库文件和本机头文件

  • jni.h (C 头文件)jvm.lib jvm.dll (window下)或 libjvm.so 文件(linux下),这些文件都是随 JDK 一起提供的

Java为不同的操作系统平台提供了各自相适应的运行时环境以及根据不同的编译器提供了 "JNI头文件"

JNI头文件一般由2个组成:

  • jni_md.h提供了依赖于平台的头文件;jni.h提供了jni所需要的接口声明以及各种类型的定义。
  • 这2个头文件都可以在JDK的include目录中找到。我们在创建一个JNI动态库的工程时应该将工程的输出目标设置为动态连接库(windows为.dll,Unix-like系统下为.so,OS X下为.dylib)。我们在创建工程时可以将这两个头文件导入到工程中。

4.能够创建共享库的 C 和 C++ 编译器

java 加载库文件的方法

//第一种,参数为库文件名称
System.loadLibrary(String libname);

//第二种,参数为库文件路径和名称
System.load(String filename);
//Runtime.getRuntime().loadLibrary(libname)
//Runtime.getRuntime().load(libFilePath)。
  • 方式1只需要指定动态库的名字,不需要加lib前缀,也不要加.so、.dll和.jnilib后缀
  • 方式2:指定动态库的绝对路径,需要加上前缀和后缀

加载库示例

static {
    System.loadLibrary("native-lib");
    //用这种方式加载so库和System.loadLibrary函数加载库的效果是一样的
    //Runtime.getRuntime().loadLibrary("native-lib");
    //String soLibFilePath;
    //用这种方式加载so库需要指定完整的库路径
    //Runtime.getRuntime().load(soLibFilePath);
}

如果使用方式1,java会去java.library.path系统属性指定的目录下查找动态库文件,如果没有找到会抛出java.lang.UnsatisfiedLinkError异常

Exception in thread "main" java.lang.UnsatisfiedLinkError: no HelloJNIin java.library.path
	at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1860)
	at java.lang.Runtime.loadLibrary0(Runtime.java:845)
	at java.lang.System.loadLibrary(System.java:1084)
	at com.study.jnilearn.HelloWorld.<clinit>(HelloWorld.java:13)

从异常中可以看出来,他是在java.library.path中查找该名称对应的动态库,如果在mac下找libHelloJNI.jnilib文件,linux下找libHelloJNI.so文件,windows下找HelloJNI.dll文件,可以通过调用System.getProperties("java.library.path")方法获取查找的目录列表

System.getProperty("java.library.path");
//默认情况下,Windows平台下包含下面的路径:
//1)和jre相关的目录
//2)程序当前目录
//3)Windows目录
//4)系统目录(system32)
//5)系统环

有2种方式可以让java从java.library.path找到动态链接库文件

  • 方式1:将动态链接库拷贝到java.library.path目录下

  • 方式2:给jvm添加“-Djava.library.path=动态链接库搜索目录”参数,指定系统属性java.library.path的值

    java -Djava.library.path=/Users/yangxin/Desktop
    

    Linux/Unix环境下可以通过设置LD_LIBRARY_PATH环境变量,指定库的搜索目录。

三.用 Java 调用 C 的动态链接库

1.搭建GCC编译环境

既然使用的了JNI,那就不可避免地需要将C/C++文件编译成dll(windows)so(Linux)文件。因为我是在Windows平台下开发,可以有如下选择:

  1. 使用VC(或VS)编译成dll
  2. 使用GCC编译成dll

在window环境下,如果你不希望为了生成一个dll就去下载体积庞大的的Visual Studio的话,MinGW是一个不错的选择,简单的说它就是一个windows版本下的gcc。那么估计有的同学又要问了,gcc是什么?简单的来说就是linux系统下C/C++的编译器,通过它可以将源代码编译成可执行程序。

  • 因为开发也可能需要编译成Linux平台的so文件,因此,为了后面程序的兼容,使用GCC编译器比较好。而Windows平台下的GCC又可以有如下选择:
    1. 使用MinGW(我选择了MinGW)
    2. 使用Cygwin

注意:搭建GCC编译环境时,一定要选择正确的GCC编译版本(32位和64位)。如果你本地安装的JDK是64位的,那么选择64位GCC,否则选择32位。这是为了使得编译后的库文件跟JVM的位一致,否则后面JVM无法调用dll(或so)。

-------------------------查看搭建MinGW教程-------------------------

2.开始编码

JNI开发流程主要分为以下6步:

  1. 编写声明了native方法的Java类
  2. 将Java源代码编译成class字节码文件
  3. javah -jni命令生成.h头文件(javah是jdk自带的一个命令,-jni参数表示将class中用native声明的函数生成jni规则的函数)
  4. 用本地代码(c、c++、其他语言)实现.h头文件中的函数
  5. 将本地代码编译成动态库(windows:*.dll,linux/unix:*.so,mac-os x:*.jnilib)
  6. 拷贝动态库至 java.library.path 本地库搜索目录下,或设置jvm参数-Djava.library.path=链接库所在目录,并运行Java程序

2.1.创建本地方法类

1. 在包jni.demo下编写HelloJNI类

package jni.demo;
public class HelloJNI {
    // 声明所调用的库名称
    static {
        // hello.dll (Windows) or libhello.so (Unixes)
        System.loadLibrary("HelloJNI");
    }
	
	//带参数,带返回值的本地方法
    private native String sayHello(String name);

    public static void main(String[] args) {
     String result =    new HelloJNI().sayHello("zhangsan");  // invoke the native method
        System.out.println("result:"+result);
    }
}
  • 静态代码块中,调用loadLibrary()方法加载本地的动态链接库,参数为不包含后缀名的动态链接库库文件名。在window平台下会加载dll文件,在linux平台下会加载so文件,在mac os下会加载jnilib文件
  • native关键字将方法sayHello()声明为本地方法,负责通知jvm这里调用方法的是本地方法,该方法在外部由其他语言实现(c++、c)。具体的实现就在HelloJNI.dll(Windows平台)或libHelloJNI.so(Linux平台)

2.2.生成JNI头文件

2.2.1.手动输入javah指令
  • 使用c/c++来实现本地方法时,要先创建.h头文件。简单的说,c/c++程序通常由 头文件(.h)和源文件(.c或.cpp)组成头文件包含了功能函数、数据接口的声明,而源文件用于书写程序的实现

  • 在Java8中可以直接使用javac -h指令生成c/c++语言中的头文件。Java8前的版本,通过执行javac编译完成class文件后,再执行javah -jni生成c/c++风格的头文件(在jdk10的新特性中已经删除了javah这一指令)。

  • 目前我使用的是Java8,JNI生成头文件是通过JDK中提供的javah来完成,javah在 {JAVA_HOME}/bin目录中。用法如下:

javah -jni -classpath (class文件搜寻目录) -d (头文件输出目录) (类全限定名)

例如,将E:\04_resource_study\study-jni\src\main\java\jni\demo
目录中的HelloJNI.java生成头文件,并放入到E:\04_resource_study\study-jni中:

javah -jni -classpath E:\04_resource_study\study-jni\target\classes -d ./jni/ jni.demo.HelloJNI

需要注意的是:使用javah来生成头文件(.h)时-classpath指定的是编译后的java文件(.class)的目录,而不是源文件(.java)的目录,因此在使用javah指令之前,先build一下项目(或直接运行一下)。所有编译后的文件都会存放target目录中。

执行上面命令是要求在最外层目录(study-jni),因为./jni/ 表示要在当前目录下面生成一个jni的目录保存头文件
在这里插入图片描述

接下来,右键项目 ,点击 open in Terminal 直接在IDEATerminal窗口运行javah
在这里插入图片描述

注意:然后定位到项目的最外层目录(study-jni),然后执行javah命

-classpath ./target/classes:是 class文件保存位置的相对路径
-d: 是执行javah输出的文件的保存目录
jni.demo.HelloJNI: ./target/classes 下面 jni.demo.HelloJNI的类文件
在这里插入图片描述

javah -jni -classpath ./target/classes -d ./jni/ jni.demo.HelloJNI

时在study-jni/jni目录中生成了头文件jni_demo_HelloJNI.h
在这里插入图片描述
头文件内容如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class jni_demo_HelloJNI */

#ifndef _Included_jni_demo_HelloJNI
#define _Included_jni_demo_HelloJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     jni_demo_HelloJNI
 * Method:    sayHello
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_jni_demo_HelloJNI_sayHello
  (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif

接下来我们只需实现JNIEXPORT void JNICALL Java_jni_demo_HelloJNI_sayHello (JNIEnv *, jobject);即可。

  • extern "C"告诉编译器,这部分代码使用C语言规则来进行编译
  • JNIEXPORT和JNICALL是jni中定义的两个,使用JNIEXPORT支持在外部程序代码中调用该动态库中的方法,使用JNICALL定义函数调用时参数的入栈出栈约定
  • 函数名称由Java_<包>_<类名>_<函数名> 规则组成,在该方法中有2个参数,通过第1个参数JNIEnv *的对象可以 调用jni.h中封装好的大量函数第2个参数代表着native方法的调用者
  • 当java代码中定义的native方法是静态方法时这里的参数是jclass非静态方法的参数是jobject

JNIEnv作用及实现

  • JNIEnv是JNI指针接口,通常我们通过它来调用操作Java对象,它提供了所有的操作java对象的api。是在Thread创建的时候创建的,因此它是属于线程的,不能线程共享。
2.2.2.IDEA一键生成头文件

如果目录层次很深,或者是有多个需要生成头文件的class文件,这工作量太大了,可以通过IDEA内置工具来实现。点击File>Settings>Tools>External Tools
在这里插入图片描述
添加一个先的External Tools:
在这里插入图片描述

(名称) Name:Generate Header File
(命令路径) Program: $JDKPath$/bin/javah
(参数) Arguments: -jni -classpath $OutputPath$ -d ./jni/ $FileClass$
(项目名称) Working directory: $ProjectFileDir$

HelloJNI.java文件中点击右键>External Tools>Generate Header File
在这里插入图片描述
会在Terminal打印生成头文件命令
在这里插入图片描述

2.3.编写C文件并编译成dll(或so)文件

2.3.1.手动输入命令生成

jni目录中新建HelloJNI.c文件,如下:
在这里插入图片描述
c文件内容如下:

#include<jni.h>
#include <stdio.h>
#include "jni_demo_HelloJNI.h"

/*
 * Class:     jni_demo_HelloJNI
 * Method:    Ljava
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_jni_demo_HelloJNI_sayHello
  (JNIEnv *env, jclass cls, jstring j_str){
    const char *c_str = NULL;
    char buff[128] = {0};
    c_str = (*env)->GetStringUTFChars(env,j_str,NULL);
    if (c_str == NULL)
    {
        printf("out of memory\n");
        return NULL;
    }
    printf("Java Str:%s\n", c_str);//打印传入的参数
    sprintf(buff,"hello %s",c_str);//拼接hello,然后输出
    (*env)->ReleaseStringUTFChars(env,j_str,c_str);
    return (*env)->NewStringUTF(env,buff);
  }

使用GCC对HelloJNI.c编译,在Terminal窗口输入如下:

E:\04_resource_study\study-jni>gcc -c jni/HelloJNI.c
jni/HelloJNI.c:1:9: fatal error: jni.h: No such file or directory
 #include<jni.h>
         ^~~~~~~
compilation terminated.

发现报错,找不到jni.h头文件,将JDK目录中的include目录加入,即为:

E:\04_resource_study\study-jni>gcc -c -I"E:\dev_tools\jdk\jdk1.8.0_181\include" jni/HelloJNI.c
In file included from jni/HelloJNI.c:1:
E:\dev_tools\jdk\jdk1.8.0_181\include/jni.h:45:10: fatal error: jni_md.h: No such file or directory
 #include "jni_md.h"
          ^~~~~~~~~~
compilation terminated.

又报找不到jni_md.h错误,继续将JDK目录中的include/win32加入,即:

gcc -c -I"E:\dev_tools\jdk\jdk1.8.0_181\include" -I"E:\dev_tools\jdk\jdk1.8.0_181\include\win32" jni/HelloJNI.c

将c文件编译成.o文件在这里插入图片描述

完成编译。此时在项目中会生成HelloJNI.o文件。接下来是将HelloJNI.o转为HelloJNI.dll,即转为windows平台下的动态链接库。在Terminal中输入如下
在这里插入图片描述

gcc -Wl,--add-stdcall-alias -shared -o HelloJNI.dll HelloJNI.o
2.3.2.IDEA一键生成dll

可以将编译成dll的过程命令也加入到External Tools中。前面将c文件编译链接成dll文件分了2个命令,这里我们直接通过一个命令来完成:点击File>Settings>Tools>External Tools

gcc -Wl,--add-stdcall-alias -I"E:\dev_tools\jdk\jdk1.8.0_181\include" -I"E:\dev_tools\jdk\jdk1.8.0_181\include\win32" -shared -o ./lib/HelloJNI.dll ./jni/HelloJNI.c

上面命令将c文件编译成了dll,然后生成的dll文件加入到了lib目录中,而不是像之前那直接放到项目底下。因此在java.library.path应该指定目录为lib。(要先创建lib目录

在这里插入图片描述

添加一个先的External Tools:
在这里插入图片描述

(名称) Name:Generate DLL
(命令路径,指定GCC路径) Program: E:\dev_tools\MinGW64\bin\gcc.exe
(参数) Arguments: -Wl,--add-stdcall-alias -I"$JDKPath$\include" -I"$JDKPath$\include\win32" -shared -o ./lib/$FileNameWithoutExtension$.dll ./jni/$FileNameWithoutExtension$.c
(项目名称) Working directory: $ProjectFileDir$

HelloJNI.c文件中点击右键>External Tools>Generate DLL
在这里插入图片描述

会在Terminal打印生成DLL命令
在这里插入图片描述

2.4.运行

直接运行,结果报错,原因为扫描java.library.path下面的目录没有找到名为HelloJNI的动态链接库
在这里插入图片描述
解决办法

这里通过第二种方法进行测试
在这里插入图片描述

2.5.可能出现的错误

2.5.1.java.library.path找不到dll的错误
java.lang.UnsatisfiedLinkError: no HelloJNI 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 jni.demo.HelloJNI.<clinit>(HelloJNI.java:5)
Exception in thread "main" 

为扫描java.library.path下面的目录没有找到名为HelloJNI.dll的动态链接库。

  • 因为在Windows中JVM的 java.library.path属性即为环境变量Path指定的目录,而我们生成的dll并未放入到Path指定的任何一个目录中,因此我们需要告诉JVM,dll文件在哪个目录中。

解决办法1:.将dll放到java.library.path下面的某一个目录中

    public static void main(String[] args) {
       Arrays.stream(System.getProperty("java.library.path").split(";")).forEach(System.out::println);
    }

获取结果输出为

E:\dev_tools\jdk\jdk1.8.0_181\bin
C:\Windows\Sun\Java\bin
C:\Windows\system32
C:\Windows
E:\dev_tools\Xshell6\
E:\dev_tools\xshell7\
E:\dev_tools\Xftp6\
C:\Windows\system32
C:\Windows
C:\Windows\System32\Wbem
C:\Windows\System32\WindowsPowerShell\v1.0\
C:\Windows\System32\OpenSSH\
E:\dev_tools\Git\cmd
E:\dev_tools\TortoiseSVN\bin
E:\dev_tools\jdk\jdk1.8.0_181\bin
E:\dev_tools\apache-maven-3.3.9\bin
C:\Program Files (x86)\Pandoc\
C:\Program Files\nodejs\
E:\05_resource_node\node_global\
C:\Program Files\OpenVPN\bin
C:\Program Files\MySQL\MySQL Server 5.7\bin
E:\dev_tools\erl7.1\bin
C:\Users\87772\AppData\Local\Microsoft\WindowsApps
E:\dev_tools\IntelliJ IDEA 2019.3.4\bin
E:\dev_tools\Diffuse
C:\Users\87772\AppData\Roaming\npm
.
  • 说明java运行时就会从这些路径下寻找动态链接库

解决办法2:通过设置 VM options 指定 java.library.path
在这里插入图片描述

-Djava.library.path=E:\04_resource_study\study-jni\lib
2.5.2.无法识别__int64类型错误
 error: unknown type name '__int64'
 typedef __int64 jlong;

出现这个错误的人一般是使用Cygwin GCC的人,这是因为Cygwin GCC不认识__int64类型,找到<JDK_HOME>/include/win32/jni_md.h,找到typedef __int64 jlong,并修改为:

#ifdef __GNUC__
typedef long long jlong;
#else
typedef __int64 jlong;
#endif

或者是编译时将__int64加入,如下:

gcc-3 -D __int64="long long" -mno-cygwin -Wl,--add-stdcall-alias 
  -I"<JAVA_HOME>\include" -I"<JAVA_HOME>\include\win32" -shared -o hello.dll HelloJNI.c
2.5.3.64-bit mode not compiled
HelloJNI.c:1:0: sorry, unimplemented: 64-bit mode not compiled in
 #include <jni.h>

出现这个错误是因为JDK的版本与GCC编译器的版本不一致,如:JDK版本是64位,而GCC编译器编译出的dll(或so)是32位,只需换个64位版本的GCC即可。

2.6.JNI 调用 C 流程图

在这里插入图片描述
在这里插入图片描述

windows、linux、mac-os版本等等,在这些jdk中,针对不同系统有着不同的jvm实现。而java语言的跨平台性恰好是和它底层的jvm密不可分的,正是依靠不同的操作系统下不同版本jvm的“翻译”工作,才能使编译后的字节码在不同的平台下畅通无阻的运行。
在这里插入图片描述

在不同操作系统下,c/c++或其他代码生成的动态链接库也会有差异,例如在window平台下会编译为dll文件,在linux平台下会编译为so文件,在mac os下会编译为jnilib文件。而不同平台下的jvm,会“约定俗成”的去加载某个固定类型的动态链接库文件,使得依赖于操作系统的功能可以被正常的调用,
在这里插入图片描述

参考资料

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

墩墩分墩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值