如何查找 jdk 中的 native 实现
jdk 中有很多 native 方法,比如 Object 类的 registerNatives 方法、String 类的 intern 方法等。这些方法在 java 层面只有接口定义,具体的方法实现则是在 jdk 中,采用 c/c++ 实现。本文主要讲下如何找到 native 方法的实现。
查找的思路
对于自定义的 native 方法,对应的实现位于自定义的类库中。而对于 jdk 中的 native 方法,对应的实现则位于 jdk 源码中,要查看方法实现,首先需要下载 jdk 源码。本文使用的是 openjdk 8 的源码,可以从 https://github.com/gorden5566/jdk8u_jdk.git 下载,如果速度慢也可以从 https://gitee.com/gorden5566/jdk8u_jdk.git 下载。
有了源码后,下一个问题就是如何找到对应的实现。因为 jdk 中代码非常多,不可能去一个文件一个文件地查看,我们需要有更好的搜索方法。在 JNI入门之详细介绍 有提到,native 方法的本地方法名是遵循一定的规则生成的。因此可以先生成对应的本地方法名,然后再到源码中搜索。
生成本地方法名
以 String 类的 intern 方法为例。String 类的源码如下
1 |
package java.lang; |
按照生成规则生成本地方法名
拼接后结果为
Java_java_lang_String_intern
使用工具生成
自己按照规则拼写本地方法名容易出错,一个更简单的方法是通过工具生成。在
JNI入门之HelloWorld
里,使用了
javah
命令,根据 class 文件自动生成本地方法的头文件。
同样,我们也可以使用
javah
命令生成
String
类的头文件。这里有个背景知识是:java 类加载遵循双亲委派机制,它确保了基础的类不会被用户自定义类覆盖。因此,我们只需要定义一个空的
String
类,确保包路径与 jdk 中的
String
类的路径一致,然后就可以生成所需的头文件。
String
类代码如下:
1 |
package java.lang; |
-
执行
javac String.java -d .
生成 class 文件 -
执行
javah java.lang.String
生成头文件
相关文件如下:
1 |
. |
打开
java_lang_String.h
1 |
/* DO NOT EDIT THIS FILE - it is machine generated */ |
其中
Java_java_lang_String_intern
正是生成的本地方法名。
搜索源码
下一步就是在 jdk 源码中搜索关键字,推荐使用
grep
命令,简洁高效。
1 |
grep -nr "Java_java_lang_String_intern" . |
1 |
./src/share/native/java/lang/String.c:30:Java_java_lang_String_intern(JNIEnv *env, jobject this) |
1 |
|
这只是一个入口,它的实现与虚拟机相关,因此需要到 HotSpot 目录下查找。相关源码可到 https://github.com/gorden5566/jdk8u_hotspot.git 或 https://gitee.com/gorden5566/jdk8u_hotspot 下载。
到 hotspot 目录下搜索
JVM_InternString
1 |
grep -nr "JVM_InternString" . |
找到如下信息:
1 |
./src/share/vm/prims/jvm.cpp:4060:JVM_ENTRY(jstring, JVM_InternString(JNIEnv *env, jstring str)) |
其实现如下:
1 |
JVM_ENTRY(jstring, JVM_InternString(JNIEnv *env, jstring str)) |
源码跟踪
解析字符串
JNIHandles::resolve_non_null
是一个内联方法,它用于把 jobject 类型的 handle 解析为 oop,并且保证返回结果不为空。代码如下:
1 |
inline oop JNIHandles::resolve_non_null(jobject handle) { |
处理逻辑
StringTable
类也在 hotspot 项目下,路径为
./src/share/vm/classfile/symbolTable.cpp
,intern 方法代码如下:
1 |
oop StringTable::intern(oop string, TRAPS) |
生成局部引用
1 |
jobject JNIHandles::make_local(JNIEnv* env, oop obj) { |
其他 native 实现
在
./src/share/native
目录下还有其他的 native 实现
1 |
➜ native git:(master) ls |
执行
tree java
查看 java 目录下的文件,可以发现文件名与 java 中的类名一致。
1 |
java |
-
本文通过编写一个简单的 HelloWorld 带你熟悉下 JNI,包括如何编写一个 JNI 方法,如何打包成动态链接库,如何加载并调用等。
-
2019-12-20
背景:最近在做一个功能,修改了一个原有对象的属性值,这个对象的值最终会落到数据库里。测试下来发现,最终记录到数据库的值总是一个最大值,和预期的结果不一样,正常情况下应该更新为后续的一个计算结果。
-
2017-10-15
Java 虚拟机所管理的内存包括多个运行时数据区域,每个区都有自己的特点。
-
2019-09-30
本文主要介绍下 JNI 相关的概念和一些原理,以便对 JNI 有一个整体认识。
-
2021-01-30
Mac 下编译 netty 报错,提示
Netty/Transport/Native/Unix/Common
模块编译失败,到网上搜索一下,并未发现有人遇到过类似问题,因此做下记录。