4.3 Java JNI 机制

news2024/12/22 11:33:37

1 绪论

  JNI 是一个原生编程接口。它允许在 Java 虚拟机(JVM)内运行的 Java 代码与用其它编程语言(如 C、C++ 和汇编)编写的应用程序和库进行互操作。

  JNI 最重要的好处是它对底层 JVM 的实现没有限制。因此,JVM 供应商可以添加对 JNI 的支持,而不会影响 JVM 的其它部分。程序员可以编写一个原生应用程序或库的一个版本,并期望它与支持 JNI 的所有 JVM 一起工作。

  虽然可以完全用 Java 编写应用程序,但在某些情况下,仅靠 Java 无法满足应用程序的需求。程序员使用 JNI 编写 Java 原生方法来处理应用程序不能完全用 Java 编写的情况。

  以下示例说明了何时需要使用 Java 原生方法:

  • 标准 Java 类库不支持应用程序所需的依赖于平台的功能。
  • 已经有一个用另一种语言编写的库,并希望通过 JNI 使 Java 代码可以访问它。
  • 希望用低级语言(如汇编语言)实现一小部分时间关键型代码。

  通过 JNI 编程,您可以使用原生方法:

  • 创建、检查和更新 Java 对象(包括数组和字符串)。
  • 调用 Java 方法。
  • 捕获并抛出异常。
  • 加载类并获取类信息。
  • 执行运行时类型检查。

2 JNI 的设计

2.1 JNI 接口函数和指针

  原生代码通过调用 JNI 函数来访问 JVM 特性。JNI 函数可通过接口指针使用。接口指针是指向指针的指针。此指针指向一个指针数组,每个指针都指向一个接口函数。每个接口函数都位于数组内的预定义偏移处。下图 “接口指针” 说明了接口指针的组织。
在这里插入图片描述
  JNI 接口的组织方式类似于 C++ 虚拟函数表。使用接口表而不是硬连线函数条目的优点是 JNI 名称空间与原生代码分离。VM 可以轻松提供多个版本的 JNI 函数表。例如,VM 可能支持两个 JNI 函数表:

  • 一个执行彻底的非法参数检查,适合调试;
  • 另一个执行 JNI 规范所需的最小检查量并且因此更高效。

  JNI 接口指针仅在当前线程中有效。因此,本机方法不得将接口指针从一个线程传递到另一个线程。实现 JNI 的 VM 可以在 JNI 接口指针指向的区域中分配和存储线程本地数据。

  原生方法接收 JNI 接口指针作为参数。当 VM 从同一 Java 线程对原生方法进行多次调用时,它保证会将相同的接口指针传递给原生方法。但是,可以从不同的 Java 线程调用原生方法,因此可能会接收不同的 JNI 接口指针。

2.2 编译、加载和链接原生方法

  原生方法使用System.loadLibrary方法加载。在以下示例中,类初始化方法加载了一个特定于平台的原生库,其中定义了原生方法f

package p.q.r;

class A {
    native double f(int i, String s);
    static {
        System.loadLibrary("p_q_r_A");
    }
}

  System.loadLibrary的参数是程序员任意选择的库名称。该系统遵循一种标准但特定于平台的方法,将库名称转换为原生库名称。例如,Linux 系统将名称p_q_r_A转换为libp_q_r_A.so,而 Windows 系统将相同的p_q_r_A名称转换为p_q_r_A.dll

  程序员可以使用单个库来存储任意数量的类所需的所有原生方法,只要这些类要用相同的类加载器加载。VM 在内部为每个类加载器维护一个已加载的原生库列表。

2.2.1 解析原生方法名称

  JNI 定义了一个 1:1 的映射,从 Java 中声明的原生方法的名称到驻留在原生库中的原生方法的名称。VM 使用此映射将原生方法的 Java 调用动态链接到原生库中的对应实现。

  映射通过连接从原生方法声明派生的以下组件来生成原生方法名称:

  1. 前缀Java_
  2. 给定声明原生方法的类的内部形式的二进制名称:转义名称的结果。
  3. 下划线(_
  4. 转义的方法名
  5. 如果原生方法声明重载:两个下划线(__)后跟方法声明的转义参数描述符(JVMS 4.3.3)。

  转义使每个字母数字 ASCII 字符(A-Za-z0-9)保持不变,并用相应的转义序列替换下表中的每个UTF-16代码单元。如果要转义的名称包含代理项对,则高代理项代码单元和低代理项代码单位将分别转义。转义的结果是一个仅由ASCII字符a-Za-z0-9和下划线组成的字符串。

  转义是必要的,原因有二。首先,确保 Java 源代码中的类和方法名(可能包含 Unicode 字符)在 C 源代码中转换为有效的函数名。其次,确保使用[字符对参数类型进行编码的原生方法的参数描述符可以用 C 函数名进行编码。

  当 Java 程序调用原生方法时,VM 首先通过查找原生方法名称的短版本(即没有转义参数签名的名称)来搜索原生库。如果找不到具有短名称的原生方法,则 VM 会查找原生方法名称的长版本,即包含转义参数签名的名称。

  首先查找短名称可以更容易地在原生库中声明实现。例如,给定 Java 中的原生方法:

package p.q.r;

class A {
    native double f(int i, String s);
}

对应的 C 函数可以命名为Java_p_q_r_A_f,而不是Java_p_q_r_A_f__ILjava_lang_String_2

  只有当类中的两个或多个原始呢个方法具有相同名称时,才需要在原生库中声明具有长名称的实现。例如,给定 Java 中的这些原生方法:

package p.q.r;

class A {
    native double f(int i, String s);
    native double f(int i, Object s);
}

对应的 C 函数必须命名为Java_p_q_r_A_f__ILjava_lang_String_2Java_p_q_r_A_f__ILjava_lang _Object_2,因为原生方法已重载。

  如果 Java 中的原生方法仅被非原生方法重载,则原生库中的长名称是不必要的。在下面的示例中,原生方法g不必使用长名称链接,因为另一个方法g不是原生方法,因此不驻留在原生库中。

package p.q.r;

class B {
    int g(int i);
    native int g(double d);
}

  请注意,转义序列可以安全地以_0_1等开头,因为 Java 源代码中的类名和方法名从不以数字开头。然而,在不是从 Java 源代码生成的类文件中,情况并非如此。为了保留与原生方法名称的 1:1 映射,VM 按如下方式检查结果名称。如果从原生方法声明(类或方法名,或参数类型)中转义任何前体字符串的过程导致前体字符串中的“0”、“1”、“2”或“3”字符在结果中紧随下划线之后或转义字符串的开头(它将在完全组装的名称中的下划线之后)保持不变,则称转义过程“失败”。在这种情况下,不会执行原生库搜索,尝试链接原生方法调用将抛出UnsatisfiedLinkError。有可能将目前的简单映射方案扩展到涵盖此类情况,但复杂性成本将超过任何收益。

  原生方法和接口API都遵循给定平台上的标准库调用约定。例如,UNIX系统使用C调用约定,而Win32系统使用__stdcall

  原生方法也可以使用RegisterNatives函数显式链接。请注意,RegisterNatives可以通过更改给定原生Java方法要执行的原生代码来更改JVM的记录行为(包括加密算法、正确性、安全性、类型安全性)。因此,请谨慎使用具有使用RegisterNatives函数的原生库的应用程序。

2.2.2 原生方法参数

  JNI 接口指针是原生方法的第一个参数。JNI 接口指针的类型为JNIEnv。第二个参数因原生方法是静态的还是非静态的而异。非静态原生方法的第二个参数是对对象的引用。静态原生方法的第二个参数是对其Java类的引用。

  其余的参数对应于常规的Java方法参数。原生方法调用通过返回值将其结果传递回调用例程。

  以下代码示例说明了如何使用 C 函数实现原生方法f。原生方法的声明如下:

package p.q.r;

class A {
    native double f(int i, String s);
    // ...
}

长名称为Java_p_q_r_A_f__ILjava_lang_String_2的 C 函数实现了原生方法f

jdouble Java_p_q_r_A_f__ILjava_lang_String_2 (
     JNIEnv *env,        /* interface pointer */
     jobject obj,        /* "this" pointer */
     jint i,             /* argument #1 */
     jstring s)          /* argument #2 */
{
     /* Obtain a C-copy of the Java string */
     const char *str = (*env)->GetStringUTFChars(env, s, 0);

     /* process the string */
     ...

     /* Now we are done with str */
     (*env)->ReleaseStringUTFChars(env, s, str);

     return ...
}

请注意,我们总是使用接口指针env来操作Java对象。使用C++,您可以编写一个稍微干净的代码版本,如下面的代码示例所示:

extern "C" /* specify the C calling convention */

jdouble Java_p_q_r_A_f__ILjava_lang_String_2 (

     JNIEnv *env,        /* interface pointer */
     jobject obj,        /* "this" pointer */
     jint i,             /* argument #1 */
     jstring s)          /* argument #2 */

{
     const char *str = env->GetStringUTFChars(s, 0);

     // ...

     env->ReleaseStringUTFChars(s, str);

     // return ...
}

在C++中,额外的间接层和接口指针参数从源代码中消失了。然而,底层机制与C完全相同。在C++中,JNI函数被定义为可扩展到C对应函数的内联成员函数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2240859.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

influxDB 时序数据库安装 flux语法 restful接口 nodjsAPI

安装 Install InfluxDB | InfluxDB OSS v2 Documentation Debian和Ubuntu用户可以用apt-get包管理来安装最新版本的InfluxDB。 对于Ubuntu用户,可以用下面的命令添加InfluxDB的仓库,添加之后即可apt-get 安装influxdb2 wget -q https://repos.influx…

7z 解压器手机版与解压专家:安卓解压工具对决

7z 解压器手机版和解压专家都是在安卓设备上广受欢迎的解压软件。7z 解压器手机版由深圳乡里云网络科技有限公司开发,大小为 32.8M,支持多种常见的压缩文件格式,如.zip、.rar、.7z 等。 它对安卓操作系统的特性和用户习惯进行了优化&#xf…

亮数据——助力全球数据抓取的高效代理平台

目录 实际案例:利用代理服务抓取企业信息完整代码运行结果 亮数据的技术优势与应用场景产品更新:简化注册流程与智能助手升级立即注册,开启您的数据抓取之旅! 在如今的大数据时代,企业决策越来越依赖于数据分析&#x…

设计模式之责任链模式(Chain Of Responsibility)

一、责任链模式介绍 1、责任链模式介绍 职责链模式(chain of responsibility pattern) 定义: 避免将一个请求的发送者与接收者耦合在 一起,让多个对象都有机会处理请求。将接收请求的对象连接成一条链,并且沿着这条链 传递请求,直到有一个对…

【月之暗面kimi-注册/登录安全分析报告】

前言 由于网站注册入口容易被机器执行自动化程序攻击,存在如下风险: 暴力破解密码,造成用户信息泄露,不符合国家等级保护的要求。短信盗刷带来的拒绝服务风险 ,造成用户无法登陆、注册,大量收到垃圾短信的…

低代码牵手 AI 接口:开启智能化开发新征程

一、低代码与 AI 接口的结合趋势 低代码开发平台近年来在软件开发领域迅速崛起。随着企业数字化转型的需求不断增长,低代码开发平台以其快速构建应用程序的优势,满足了企业对高效开发的需求。例如,启效云低代码平台通过范式化和高颗粒度的可配…

安培环路定理

回忆 静电场中的回路定理:→静电场是保守场 安培环路定理 1、圆形回路包围无限长载流直导线 (1)回路逆时针 (2)回路顺时针 规定: 回路正向由右手螺旋定则判断(根据回路绕行方向,…

IDEA 2024.3正式版发布,速览新功能!

0 前言 IntelliJ IDEA 2024.3 引入了一系列可以提升您的开发体验的强大新功能。 IDE 现在提供代码逻辑结构的表示,简化了 Kubernetes 应用程序的调试体验,引入了集群范围的 Kubernetes 日志访问。 1 关键亮点 1.1 Structure工具窗口中的 Logical代码结…

LabVIEW 实现 find_nearest_neighbors 功能(二维平面上的最近邻查找)

1. 背景介绍 在数据分析和图像处理领域,经常需要查找给定点的最近邻居点。在LabVIEW中,计算二维平面上多个点之间的欧氏距离,并返回距离最近的几个点是一种常见操作。find_nearest_neighbors 函数用于实现这个功能。 2. 欧氏距离计算 在二维…

LeetCode 单调栈 下一个更大元素 I

下一个更大元素 I nums1 中数字 x 的 下一个更大元素 是指 x 在 nums2 中对应位置 右侧 的 第一个 比 x 大的元素。 给你两个 没有重复元素 的数组 nums1 和 nums2 &#xff0c;下标从 0 开始计数&#xff0c;其中nums1 是 nums2 的子集。 对于每个 0 < i < nums1.length…

vue的组件使用

1.安装element plus组件库 npm install element-plus --save

2024-11-14 算法学习及论文辅导(每日更新,随时联系)

看看学习小群的学习氛围&#x1f447;&#x1f3fb; 很多同学自己学习遇到问题没人解决&#xff0c;最终消耗了时间&#xff0c;精力同时大大消耗了自己对学习的信心&#x1f627; &#x1f973;来看看跟班学习&#xff0c;大家遇到问题的时候是怎么解决的&#xff1a; 首先…

开源三代示波器的高速波形刷新方案开源,支持VNC远程桌面,手机,Pad,电脑均可访问(2024-11-11)

说明&#xff1a; 1、本来这段时间是一年一度Hackaday硬件设计开源盛宴&#xff0c;但hackaday电子大赛在去年终结了。所以我开源个我的吧。 2、三代示波器的高速波形刷新方案&#xff0c;前两年就做好了&#xff0c;这两年忙H7-TOOL的更新比较多&#xff0c;三代示波器的更新…

B-树特点以及插入、删除数据过程

B树&#xff08;B-Tree&#xff09;是一种自平衡的多路查找树&#xff0c;它广泛应用于数据库索引和文件系统中&#xff0c;尤其适用于外部存储设备&#xff08;如磁盘&#xff09;。B树的设计使得它能够高效地存储大量数据并支持高效的插入、删除和查询操作。以下是B树的主要特…

微信小程序自定义tabbar;禁用某个tab;修改某个tab的样式

微信小程序自定义tabbar&#xff1b;禁用某个tab&#xff1b;修改某个tab的样式 原本使用本身的tabBar就已经很舒服了&#xff0c;很合适了的&#xff0c;但是总有一些脑洞大开的产品和客户&#xff0c;给你搞点多样式&#xff0c;没办法牛马就得去做咯&#xff0c;现在就给大…

操作系统——内存段式和段页式管理

目录 一、为什么要有段式管理&#xff1f; 二、段式管理的实现原理 1、段式虚拟空间 2、段式管理的内存分配与释放 3、段式管理的地址变换 &#xff08;1&#xff09;段表 &#xff08;2&#xff09;动态地址变换 4、段的共享与保护 &#xff08;1&#xff09;共享 &a…

【C#设计模式(10)——装饰器模式(Decorator Pattern)】

前言 装饰器模式可以在运行时为对象添加额外的功&#xff0c;而无需修改原始对象的代码。这种方式比继承更加灵活。 代码 //蛋糕类&#xff08;抽象类&#xff09; public abstract class Cake {public abstract void Create(); } //奶油蛋糕类 public class CreamCake : Cak…

千图网 AI 绘画平台——智能图像创作工具

抖知书老师推荐&#xff1a; ​千图网的AI图像处理工具已经上线有一段时间了&#xff0c;随着AI技术的不断提升&#xff0c;越来越多的设计师和创意工作者开始接受并使用这个高效的工具。最初对于AI会影响创作行业的担忧&#xff0c;现在也逐渐消散了。设计师们依然在创造&…

【前端】技术演进发展简史

一、前端 1、概述 1990 年&#xff0c;第一个web浏览器诞生&#xff0c;Tim 以超文本语言 HTML 为基础在 NeXT 电脑上发明了最原始的 Web 浏览器。 1991 年&#xff0c;WWW诞生&#xff0c;这标志着前端技术的开始。 前端&#xff08;Front-end&#xff09;和后端&#xff08;…

【C#设计模式(4)——构建者模式(Builder Pattern)】

前言 C#设计模式(4)——构建者模式(Builder Pattern) 运行结果 代码 public class Computer {private string part1 "CPU";private string part2 "主板";private string part3 "内存";private string part4 "显卡";private st…