程序员scholar 程序员scholar
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • Web 标准

    • HTML
    • CSS
    • JavaScript
  • 前端框架

    • Vue2
    • Vue3
    • Vue3 + TS
    • 微信小程序
    • uni-app
  • 工具与库

    • jQuery
    • Ajax
    • Axios
    • Webpack
    • Vuex
    • WebSocket
    • 第三方登录
  • 后端与语言扩展

    • ES6
    • Typescript
    • node.js
  • Element-UI
  • Apache ECharts
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • Web 标准

    • HTML
    • CSS
    • JavaScript
  • 前端框架

    • Vue2
    • Vue3
    • Vue3 + TS
    • 微信小程序
    • uni-app
  • 工具与库

    • jQuery
    • Ajax
    • Axios
    • Webpack
    • Vuex
    • WebSocket
    • 第三方登录
  • 后端与语言扩展

    • ES6
    • Typescript
    • node.js
  • Element-UI
  • Apache ECharts
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
npm

(进入注册为作者充电)

  • Java底层 - JVM

    • JVM - Java体系结构
    • JVM - 类加载子系统
    • JVM - 运行时数据区概述及线程
    • JVM - 程序计数器
    • JVM - 虚拟机栈
    • JVM - 本地方法接口
      • 1. 什么是本地方法 (Native Method)?
      • 2. 为什么需要使用本地方法 (Native Method)?
        • 2.1 与 Java 环境外部的交互
        • 2.2 与操作系统的底层交互
        • 2.3 性能考虑 (历史原因及特定场景)
      • 3. 本地方法接口 (JNI) 的现状
    • JVM - 本地方法栈
    • JVM - 堆 (Heap)
    • JVM - 方法区
    • JVM - 对象实例化内存布局
    • JVM - 直接内存管理
    • JVM - 执行引擎
    • JVM - 字符串常量池 (StringTable)
    • JVM - 垃圾回收概述
    • JVM - 垃圾回收相关算法
    • JVM - 垃圾回收相关概念
    • JVM - 垃圾回收器
    • JVM - Class文件结构
    • JVM - 字节码指令集与解析
    • JVM - 类的加载过程详解
    • JVM - 再谈类的加载器
    • JVM - 调优概述
    • JVM - 监控及诊断工具cmd
    • JVM - 监控及诊断工具GUI
    • JVM - 运行时参数
    • JVM - 分析GC日志
  • Java底层
  • Java底层 - JVM
scholar
2024-01-18
目录

JVM - 本地方法接口

# 1. 什么是本地方法 (Native Method)?

简单来说,本地方法 (Native Method) 是 Java 程序调用非 Java 代码(通常是 C 或 C++)的一种接口机制。通过本地方法,Java 代码可以执行由其他语言编写的、已编译成本地机器码的函数。

官方定义精髓:“A native method is a Java method whose implementation is provided by non-java code.” —— 本地方法是一个 Java 方法,但它的具体实现是由非 Java 代码提供的。

核心特征:

  1. native 关键字声明:在 Java 代码中,本地方法使用 native 关键字进行声明,并且没有方法体(没有 {} 代码块),类似于接口方法的定义。其实现细节隐藏在 Java 世界之外。
  2. JNI (Java Native Interface):Java 平台提供了一套标准的编程接口——Java 本地接口 (JNI)——来规范 Java 代码如何与本地代码进行交互。JNI 定义了如何在本地代码中访问 Java 对象、调用 Java 方法、处理异常、管理内存等。
  3. 跨语言调用:本地方法的主要目的是实现 Java 与其他编程语言(主要是 C/C++)的融合,让 Java 程序能够利用其他语言编写的库或直接调用底层系统功能。
  4. 并非 Java 独有:这种调用本地代码的机制并非 Java 首创。许多编程语言都提供了类似的机制,例如 C++ 可以使用 extern "C" 来声明并调用 C 语言编写的函数。

本地方法接口示意图 (上图形象地展示了 Java 通过本地方法接口调用本地方法库中的非 Java 实现)

代码示例:声明本地方法

以下代码展示了如何在 Java 类中声明各种形式的本地方法:

// 文件名: IhaveNatives.java
/**
 * 演示如何声明本地方法 (Native Method)
 */
public class IhaveNatives {

    // 声明一个普通的实例本地方法 Native1,接收一个 int 参数,无返回值
    // 实现由外部非 Java 代码提供
    public native void Native1(int x);

    // 声明一个静态的本地方法 Native2,无参数,返回 long 类型
    // static 和 native 可以一起使用
    native static public long Native2();

    // 声明一个同步的、私有的实例本地方法 Native3,接收一个 Object 参数,返回 float 类型
    // synchronized, private 和 native 也可以组合使用
    native synchronized private float Native3(Object o);

    // 声明一个实例本地方法 Natives,接收一个 int 数组参数,并可能抛出 Exception
    // native 方法也可以声明抛出异常,虽然异常处理主要在 Java 端
    native void Natives(int[] ary) throws Exception;

    // 注意:native 不能与 abstract 一起使用
    // public native abstract void NoAbstractNative(); // 编译错误!
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

关键点:

  • 使用 native 关键字标记方法。
  • 没有方法体,以分号 ; 结束。
  • native 可以与除 abstract 之外的大多数 Java 修饰符(如 public, static, synchronized, private 等)组合使用。为什么不能和 abstract 共用?因为 abstract 意味着方法没有实现,需要子类实现;而 native 意味着方法有实现,只是实现在 Java 外部,两者语义冲突。

# 2. 为什么需要使用本地方法 (Native Method)?

Java 语言以其强大的跨平台能力、丰富的类库和便捷的开发体验著称。然而,在某些特定场景下,单纯使用 Java 可能遇到困难或效率瓶颈,这时本地方法就派上了用场。使用 Native Method 的主要原因包括:

# 2.1 与 Java 环境外部的交互

这是本地方法存在的最核心、最主要的原因。Java 程序运行在 JVM 这个虚拟平台上,有时需要与 JVM 之外的环境进行交互,例如:

  • 访问底层系统资源:调用操作系统的特定 API(如文件系统、网络、进程管理、硬件设备等)。
  • 与硬件交互:直接控制硬件设备,如打印机、传感器、工业控制设备等。
  • 调用已有的非 Java 库:利用现存的大量用 C/C++ 等语言编写的高性能库、算法库、图形库或特定领域的专业库。

本地方法为这种交互提供了一个简洁、标准的接口,使得 Java 开发者无需深入了解 Java 应用之外环境的复杂细节,就能调用底层功能。

# 2.2 与操作系统的底层交互

  • JVM 自身的实现:JVM 本身就是一个复杂的软件,其部分核心功能(如解释器、JIT 编译器、垃圾收集器的一些底层操作)可能就是用 C/C++ 等语言编写的,需要与操作系统紧密协作。
  • Java 运行时库 (JRE) 的实现:JRE 提供了大量的标准类库供 Java 程序使用。这些库虽然大部分是用 Java 实现的,但在涉及到需要直接调用操作系统功能的地方(如线程管理、网络 IO、文件 IO、时间获取等),往往会通过本地方法来调用底层的操作系统 API。
  • 访问操作系统特性:当 Java 标准库没有提供对某个特定操作系统功能的封装时,开发者可以通过编写本地方法来直接调用该功能。

示例:Thread.setPriority() 的实现

以 java.lang.Thread 类中的 setPriority() 方法为例:

// Thread.java (简化示意)
public final void setPriority(int newPriority) {
    // ... (一些 Java 层的检查代码) ...
    setPriority0(newPriority); // 调用私有的本地方法
}

// 私有的本地方法声明,没有方法体
private native void setPriority0(int newPriority);
1
2
3
4
5
6
7
8

这个 setPriority0() 方法的具体实现是由 JVM 提供的(通常用 C 语言编写)。在不同的操作系统平台上,这个本地方法的实现会调用相应的系统 API 来设置线程优先级。例如,在 Windows 平台上,它可能最终调用 Win32 API SetThreadPriority()。

本地方法实现的来源:

  1. JVM 内置:像 setPriority0() 这样与 JVM 核心功能或 JRE 底层实现紧密相关的本地方法,其实现通常直接嵌入在 JVM 内部。
  2. 外部动态链接库 (DLL / SO):更常见的情况是,开发者自己编写 C/C++ 代码来实现本地方法的功能,并将这些代码编译成动态链接库文件(Windows 上的 .dll,Linux/macOS 上的 .so)。然后,Java 程序在运行时通过 System.loadLibrary() 或 System.load() 方法加载这个库,JVM 就能找到并调用其中的本地方法实现了。

# 2.3 性能考虑 (历史原因及特定场景)

在 Java 早期,由于解释执行效率相对较低,对于性能极其敏感的代码段,有时会选择使用 C/C++ 编写本地方法来实现,以获得接近本地代码的执行速度。然而,随着 JVM JIT (Just-In-Time) 编译技术的飞速发展,Java 代码的运行性能已经得到了极大的提升,在许多场景下与 C/C++ 的差距已经很小甚至可以忽略。因此,单纯为了性能而使用本地方法的场景已经大大减少。但在某些特定的计算密集型领域(如科学计算、图像处理、游戏引擎物理计算等),调用高度优化的 C/C++ 库仍然是获取极致性能的有效手段。

# 3. 本地方法接口 (JNI) 的现状

尽管 JNI 曾经在 Java 平台的发展中扮演了重要角色,但目前在常规的企业级应用开发中,直接使用本地方法的场景已经越来越少。

使用减少的原因:

  1. 替代技术的成熟与普及:
    • 分布式系统与微服务:现代应用倾向于通过网络进行异构系统间的通信。使用 Socket、RPC (Remote Procedure Call) 框架(如 gRPC, Dubbo)、Web Services (SOAP/REST API) 等技术可以在不同语言、不同平台的服务之间方便地进行交互,提供了更好的解耦和可伸缩性。
    • 进程间通信 (IPC):如果需要在同一台机器上的 Java 程序与其他语言程序交互,也可以使用管道、共享内存、消息队列等 IPC 机制。
  2. JNI 开发的复杂性和风险:
    • 开发难度:编写和调试 JNI 代码比纯 Java 代码更复杂,需要同时熟悉 Java 和 C/C++,并理解 JNI 的规范和内存管理规则。
    • 内存管理风险:本地代码需要手动管理内存(分配和释放),容易出现内存泄漏或野指针等问题,影响 Java 应用的稳定性。
    • 错误处理复杂:本地代码中的错误和异常处理与 Java 的异常机制不同,需要小心处理两者之间的转换。
    • 线程安全:在本地代码中处理多线程需要格外谨慎,容易引入难以发现的并发问题。
    • 可移植性差:本地代码通常需要针对不同的操作系统和 CPU 架构进行编译,降低了 Java 应用的跨平台性。维护成本高。
  3. Java 性能提升:如前所述,JIT 编译器的进步使得 Java 在大多数场景下性能已足够好,不再需要为了微小的性能提升而引入 JNI 的复杂性。

JNI 仍然适用的场景:

尽管使用频率降低,JNI 在以下场景中仍然有其价值:

  1. 与硬件直接交互:这是 JNI 最难以替代的领域之一,例如编写设备驱动程序、控制专用硬件设备等。
  2. 调用高性能本地库:对于需要极致性能的计算密集型任务,如图形渲染、物理引擎、大规模科学计算、音视频编解码等,调用经过高度优化的 C/C++ 库(如 BLAS, LAPACK, OpenCV, FFmpeg 等)仍然是常见做法。
  3. 集成遗留系统:当需要与无法修改的、用 C/C++ 编写的旧系统或库进行交互时,JNI 是一个直接的集成方案。
  4. 实现 JVM/JRE 自身:JNI 仍然是 JVM 和 Java 核心类库与底层操作系统沟通的基础技术。

总结:

在现代 Java 开发中,应优先考虑使用更高级、更解耦的跨语言通信方式(如网络服务)。只有在确实需要与底层硬件交互、调用高性能本地库或集成无法替代的遗留系统,并且能够承担 JNI 带来的复杂性和风险时,才考虑使用本地方法接口。

核心要点总结

  1. 本地方法 (Native Method) 是 Java 调用非 Java 代码(通常是 C/C++)的接口,使用 native 关键字声明,无方法体。
  2. JNI (Java Native Interface) 是实现本地方法调用的标准规范和接口。
  3. 主要用途:与 Java 环境外部(操作系统、硬件、非 Java 库)交互。JVM 和 JRE 自身也大量使用 JNI。
  4. 现状:由于替代技术成熟、JNI 开发复杂且有风险、Java 性能提升等原因,直接使用 JNI 的场景在企业应用中已显著减少。
  5. 适用场景:主要限于硬件交互、调用高性能本地库、集成遗留系统等特定领域。
编辑此页 (opens new window)
上次更新: 2025/04/05, 20:16:54
JVM - 虚拟机栈
JVM - 本地方法栈

← JVM - 虚拟机栈 JVM - 本地方法栈→

Theme by Vdoing | Copyright © 2019-2025 程序员scholar
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式