《JVM第6课》本地方法栈

1 什么是本地方法

首先要知道什么是本地方法,本地方法并不是 JVM 自己的方法,也不是 jre 里面的方法,而是指那些操作系统自己的方法(如C/C++方法),它们在操作系统目录里。可以这么理解,本地方法就是计算机操作系统对外提供的方法,JVM 通过调用这些方法可以实现 Java 程序和计算机的交互。

1.1 本地方法的好处

  1. 访问操作系统资源:直接调用操作系统的API,例如文件系统、网络接口、图形用户界面等。
  2. 性能优化:对于某些计算密集型任务,使用C或C++等语言实现可以显著提高性能。
  3. 使用现有库:利用已经存在的C/C++库,避免重复开发和维护。
  4. 硬件访问:直接访问硬件设备,例如摄像头、传感器等。

所以如果我们想优化计算密集型任务的性能,或是调用 Java 中没有实现的计算机功能,我们可以自己实现一个本地方法。

1.2 声明本地方法

在Java中,本地方法通过native关键字声明。例如:

java 复制代码
public class MyClass {
    // 声明本地方法
    public native void nativeMethod();

    // 静态块中加载本地库
    static {
        System.loadLibrary("mylib"); // 加载名为mylib的本地库
    }

    public static void main(String[] args) {
        new MyClass().nativeMethod(); // 调用本地方法
    }
}

1.3 实现本地方法

本地方法的实现通常使用JNI(Java Native Interface)或JNA(Java Native Access)来完成。以下是使用JNI实现本地方法的步骤:

1. 生成头文件

使用javah工具生成包含本地方法签名的C头文件。假设上面的Java类保存为MyClass.java,编译后生成MyClass.class,然后运行:

shell 复制代码
javah -jni MyClass

这将生成一个名为MyClass.h的头文件,内容如下:

c 复制代码
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class MyClass */

#ifndef _Included_MyClass
#define _Included_MyClass
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     MyClass
 * Method:    nativeMethod
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_MyClass_nativeMethod
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

2. 编写C语言实现

根据生成的头文件,编写C语言实现。例如:

c 复制代码
#include <jni.h>
#include <stdio.h>
#include "MyClass.h"

// 实现本地方法
JNIEXPORT void JNICALL Java_MyClass_nativeMethod(JNIEnv *env, jobject obj) {
    printf("Hello from native method!\n");
}

3. 编译C代码

将C代码编译成动态链接库。假设C文件名为mylib.c,编译命令如下:

在Linux上:

shell 复制代码
gcc -shared -o libmylib.so -I/usr/lib/jvm/java-8-openjdk-amd64/include -I/usr/lib/jvm/java-8-openjdk-amd64/include/linux mylib.c

在Windows上:

shell 复制代码
cl -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" -LD mylib.c -Fe mylib.dll

4. 运行Java程序

确保动态链接库在Java程序的库路径中,然后运行Java程序:

shell 复制代码
java -Djava.library.path=. MyClass

1.4 使用JNA

JNA是一种更简单的方式来调用本地库,不需要编写C代码。以下是一个使用JNA的示例:

  1. 添加JNA依赖 :在项目中添加JNA的依赖。如果你使用Maven,可以在pom.xml中添加:
java 复制代码
<dependency>
    <groupId>net.java.dev.jna</groupId>
    <artifactId>jna</artifactId>
    <version>5.8.0</version>
</dependency>
  1. 定义接口:定义一个接口来映射本地库中的函数。
c 复制代码
import com.sun.jna.Library;
import com.sun.jna.Native;

public interface MyLib extends Library {
    MyLib INSTANCE = Native.load("mylib", MyLib.class);

    void nativeMethod();
}
  1. 调用本地方法
java 复制代码
public class MyClass {
    public static void main(String[] args) {
        MyLib.INSTANCE.nativeMethod(); // 调用本地方法
    }
}

1.5 总结

本地方法是Java程序中的一种特殊方法,其声明在Java代码中,但实现由非Java语言编写。通过本地方法,Java程序可以访问操作系统资源、优化性能、使用现有库和直接访问硬件设备。常见的实现方式包括JNI和JNA。

2 本地方法栈

2.1 特点

  1. 线程私有:每个线程都有自己的本地方法栈,与Java虚拟机栈一样,本地方法栈也是线程私有的。
  2. 存储结构:本地方法栈中的每个栈帧(Frame)对应一次本地方法的调用。栈帧中包含本地方法的参数、局部变量、操作数栈等信息。
  3. 调用机制:当Java代码调用一个本地方法时,JVM会创建一个新的栈帧并将其压入本地方法栈。本地方法执行完毕后,栈帧会被弹出并丢弃。

2.2 本地方法栈与Java虚拟机栈的区别

  • 用途不同:Java虚拟机栈用于支持Java方法的执行,而本地方法栈用于支持本地方法的执行。
  • 实现方式:Java虚拟机栈的实现由JVM规范规定,而本地方法栈的实现通常依赖于具体的JVM实现和操作系统的ABI(Application Binary Interface)。
  • 数据类型:Java虚拟机栈主要处理Java类型的值,而本地方法栈可能涉及更广泛的C/C++类型或其他原生类型。

2.3 本地方法栈的工作流程

  1. 方法调用 :当Java代码中调用一个声明为native的方法时,JVM会查找该方法的本地实现。
  2. 栈帧创建:JVM为本地方法创建一个新的栈帧,并将其压入本地方法栈。
  3. 参数传递:调用本地方法所需的参数会被从Java虚拟机栈复制到本地方法栈的栈帧中。
  4. 方法执行:本地方法在本地方法栈中执行,可以访问操作系统资源、硬件设备等。
  5. 结果返回:本地方法执行完毕后,结果会被从本地方法栈复制回Java虚拟机栈,然后继续执行Java代码。
  6. 栈帧弹出:本地方法栈中的栈帧被弹出并丢弃。

2.4 总结

本地方法栈是JVM中用于支持本地方法调用的重要数据结构。通过本地方法栈,Java程序可以调用用其他语言编写的代码,从而实现更广泛的功能和更高的性能。

相关推荐
流星52112213 小时前
GC 如何判断对象该回收?从可达性分析到回收时机的关键逻辑
java·jvm·笔记·学习·算法
JanelSirry14 小时前
我的应用 Full GC 频繁,怎么优化?
jvm
JH307314 小时前
jvm,tomcat,spring的bean容器,三者的关系
jvm·spring·tomcat
DKPT18 小时前
JVM直接内存和堆内存比例如何设置?
java·jvm·笔记·学习·spring
siriuuus18 小时前
JVM 垃圾收集器相关知识总结
java·jvm
小满、20 小时前
什么是栈?深入理解 JVM 中的栈结构
java·jvm·1024程序员节
百花~1 天前
JVM(Java虚拟机)~
java·开发语言·jvm
每天进步一点点dlb1 天前
JVM中的垃圾回收算法和垃圾回收器
jvm·算法
漫漫不慢.2 天前
蓝桥杯-16955 岁月流转
java·jvm·蓝桥杯
boy快快长大3 天前
【JVM】线上JVM堆内存报警,占用超90%
jvm