【JAVA基础】通过Thread.sleep来了解JNI
什么是JNI
听说了 JNI -> 想学 -> 自己做实验,发现确实快 -> 想深入学习 -> Google -> 发现文章只有一点点 -> 看完了,全是垃圾 -> 找官文/爆栈/上知乎提问 -> 自己踩坑 ->
顺便学了一堆附加技能,比如 Rust/定制JVM 等 -> 想写教程,发现内容太多,又懒 -> 放弃写教程,要出wiki ->硬着头皮写吧!(后面是我加的)
Java 环境和语言对于应用程序开发来说是非常安全和高效的。但是,一些应用程序却需要执行纯 Java 程序无法完成的一些任务,比如:
- 与旧有代码集成,避免重新编写。
- 实现可用类库中所缺少的功能。举例来说,在 Java 语言中实现 ping 时,您可能需要 Internet Control Message Protocol (ICMP) 功能,但基本类库并未提供它。
- 最好与使用 C/C++ 编写的代码集成,以充分发掘性能或其他与环境相关的系统特性。
- 解决需要非 Java 代码的特殊情况。举例来说,核心类库的实现可能需要跨包调用或者需要绕过其他 Java 安全性检查。
借助 JNI,本机代码可以随意与 Java 对象交互,获取和设计字段值,以及调用方法,而不会像 Java 代码中的相同功能那样受到诸多限制。
这种自由是一把双刃剑:它牺牲 Java 代码的安全性,换取了完成上述所列任务的能力。在您的应用程序中使用 JNI 提供了强大的、对机器资源
(内存、I/O 等)的低级访问,因此您不会像普通 Java 开发人员那样受到安全网的保护。JNI 的灵活性和强大性带来了一些编程实践上的风险,
比如导致性能较差、出现 bug 甚至程序崩溃。您必须格外留意应用程序中的代码,并使用良好的实践来保障应用程序的总体完整性。
JVM在链接一个native方法的时候会根据JNI方法的命名规范去查找底层的实现函数,这个规范就是C linkage
JNI编程注意项
JNI 编程缺陷可以分为两类:
性能:代码能执行所设计的功能,但运行缓慢或者以某种形式拖慢整个程序。
- 不缓存方法ID、字段ID和类
- 触发数组副本
- 回访(Reaching back)而不是传递参数
- 错误认定本地代码与java代码之间的界限
- 使用大量本地引用,而未通知JVM
正确性:代码有时能正常运行,但不能可靠地提供所需的功能;最坏的情况是造成程序崩溃或挂起。
- 使用错误的JNIEnv
- 为检测异常
- 为检测返回值
- 为正确使用数组方法
- 为正确使用全局引用
通过Thread.sleep再来认识JNI
public static native void sleep(long millis) throws InterruptedException;
这是Thread方法中的sleep函数,我们可以看到他是一个native方法,根据我们对navite关键字的理解,会认为这是一个本地方法,那么什么是本地方法呢?
我个人认为就是:一个非java语言来实现的方法。而JNI就负责java方法与非java方法之间的互相调用
接下来我们就去找sleep对应的本地实现,我用的是OpenJDK7,大家可以下载下来看看,在openjdk\jdk\src\share目录下面的是与平台无关的代码实现,
然后我们可以找到openjdk\jdk\src\share\native\java\lang\Thread.c 但是打开我们发现并没有找到实现代码
先说一下JNINativeMethod这个结构体
接下来我们针对JVM_Sleep这个关键字使用Sublime进行搜索,可以找到在openjdk\hotspot\src\share\vm\prims\jvm.cpp有JVM_Sleep的实现
流程很简单,主要说几个参数
JVM_ENTRY JVM_END都是在openjdk\hotspot\src\share\vm\runtime\interfaceSupport.hpp里面定义的一个宏
JVM_ENTRY is a preprocessor macro that adds some boilerplate code that is common for all functions of HotSpot JVM API. This API is a connection layer between the native code of JDK class library and the JVM.
What JVM_ENTRY macro does:
Gets the pointer to current JavaThread from JNIEnv* argument.
Performs thread state transition from _thread_in_native to _thread_in_vm, blocking at safepoint if necessary..
Cleans JNI local references on the exit from the function.
Takes care of debug tracing and verification in debug builds of JVM.
vmSymbols: openjdk\hotspot\src\share\vm\classfile\vmSymbols.hpp里面定义了各种对java中的映射
HS_DTRACE_PROBE1: HotSpot的动态追踪探针(埋点)
ConvertSleepToYield: sleep转yield。yield会释放CPU资源,让优先级更高(至少是相同)的线程获得执行机会;从代码中可以看到当sleep传入0的时候,如果转换yield成功两者一样的。否则他会sleepMinSleepInterval的时间片
流程:先获取线程当前状态,然后设置成SLEEPING,接下来执行os:sleep方法,如果过程中出现中,会抛出异常。sleep完以后再把线程设置成之前的状态
JDK11的JNI规范