本地方法及实现
什么是本地方法
A native method is a Java method whose implementation is provided by non-java code.
本地方法的实现
在定义一个native method时,java中只提供定义,由非java语言提供实现。
JNI即Java Native Interface,它能在Java层实现对本地方法的调用,一般本地的实现语言主要是C/C++,JVM主要使用C/C++ 和少量汇编编写,在执行Java字节码时如果遇到有某个方法标明为Native的则从JVM中找到对应的C/C++函数,一般本地方法对应的函数会被注册到JVM中。
例子,编写Java类提供本地加密。
- Java类
//编写Java类提供本地加密
package com.seaboat.bytecode;
public class ByteCodeEncryptor {
static{
System.loadLibrary("ByteCodeEncryptor");
}
public native static byte[] encrypt(byte[] text);
}
- 使用jni生成C语言头文件。
javah -jni com.seaboat.bytecode.ByteCodeEncryptor
生成的头文件
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_seaboat_bytecode_ByteCodeEncryptor */
#ifndef _Included_com_seaboat_bytecode_ByteCodeEncryptor
#define _Included_com_seaboat_bytecode_ByteCodeEncryptor
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_seaboat_bytecode_ByteCodeEncryptor
* Method: encrypt
* Signature: ([B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_com_seaboat_bytecode_ByteCodeEncryptor_encrypt
(JNIEnv *, jclass, jbyteArray);
#ifdef __cplusplus
}
#endif
#endif
- 根据头文件编写源文件。
#include "com_seaboat_bytecode_ByteCodeEncryptor.h"
#include "jni.h"
void encode(char *str)
{
unsigned int m = strlen(str);
for (int i = 0; i < m; i++)
{
str[i] = str[i]+4;
}
}
extern"C" JNIEXPORT jbyteArray JNICALL
Java_com_seaboat_bytecode_ByteCodeEncryptor_encrypt(JNIEnv * env, jclass cla,jbyteArray text)
{
char* dst = (char*)env->GetByteArrayElements(text, 0);
encode(dst);
env->SetByteArrayRegion(text, 0, strlen(dst), (jbyte *)dst);
return text;
}
- cl编译并生成动态库
cl /EHsc -ID:\Java\jdk1.8.0_73\include\ -ID:\Java\jdk1.8.0_73\include\win32 -LD com_seaboat_bytecode_ByteCodeEncryptor.cpp -FeByteCodeEncryptor.dll
- Java中如何加载?
System.loadLibrary("ByteCodeEncryptor");
加载动态库,而它其实是通过ClassLoader.loadLibrary()方法来加载,最终也是通过JVM的类加载器本地方法加载。
本地方法栈
这俩都是JVM规范所规定的概念上的东西,并不是说具体的JVM实现真的要给每个Java线程开两个独立的栈。以Oracle JDK / OpenJDK的HotSpot VM为例,它使用所谓的“mixed stack”——在同一个调用栈里存放Java方法的栈帧与native方法的栈帧,所以每个Java线程其实只有一个调用栈,融合了JVM规范的JVM栈与native方法栈这俩概念。
-
本地方法栈和Java方法栈一样都是线程私有的。
-
当带有本地方法的Java类被加载时,相关的DLL并未被加载。当本地方法被调用时,这些DLL才会被加载并设置指向方法的指针,这是通过调用java.system.loadLibrary()实现的。
任何本地方法接口都会使用某种本地方法栈。当线程调用Java方法时,虚拟机会创建一个新的栈帧并压入Java栈。然而当它调用的是本地方法时,虚拟机会保持Java栈不变,不再在线程的Java栈中压入新的帧,虚拟机只是简单地动态连接并直接调用指定的本地方法。
如果某个虚拟机实现的本地方法接口是使用C连接模型的话,那么它的本地方法栈就是C栈。当C程序调用一个C函数时,其栈操作都是确定的。传递给该函数的参数以某个确定的顺序压入栈,它的返回值也以确定的方式传回调用者。同样,这就是虚拟机实现中本地方法栈的行为。
很可能本地方法接口需要回调Java虚拟机中的Java方法,在这种情况下,该线程会保存本地方法栈的状态并进入到另一个Java栈。线程可以在Java方法栈和本地方法栈中跳转。
refs: https://blog.csdn.net/wangyangzhizhou/article/details/74931733
本文地址:https://cheng-dp.github.io/2018/12/14/mative-method-stack/