概念
native是一个计算机函数,一个Native Method就是一个Java调用非Java代码的接口。方法的实现由非Java语言实现,比如C或C++。
JDK中如何运行native方法
首先举个例子看一下在JDK中如何运行native方法的
- java源码中的native方法是不能直接在jdk中看到的,因为jdk不是开源的,要看到的话需要sun授权才行,现在只有openjdk是被sun公司授权,所以要查看的话,下载完整的OpenJDK源码包
- 下载或查看完整的OpenJDK源码
官网:OpenJDK
image.png
image.png
image.png
image.png
- 下载或查看完整的OpenJDK源码
- 找一个JDK中使用native方法的实例
TimeZone.getDefault();//Java读取默认时区 java.util.TimeZone
class文件中的源码为
public static TimeZone getDefault() {
return (TimeZone) getDefaultRef().clone();// 第一步
}
...
static TimeZone getDefaultRef() {
TimeZone defaultZone = defaultTimeZone;
if (defaultZone == null) {
// Need to initialize the default time zone.
defaultZone = setDefaultZone();// 如果没有设置时区的话 第二步
assert defaultZone != null;
}
// Don't clone here.
return defaultZone;
}
private static synchronized TimeZone setDefaultZone() {
TimeZone tz;
// get the time zone ID from the system properties
String zoneID = AccessController.doPrivileged(
new GetPropertyAction("user.timezone"));
// if the time zone ID is not set (yet), perform the
// platform to Java time zone ID mapping.
if (zoneID == null || zoneID.isEmpty()) {
String javaHome = AccessController.doPrivileged(
new GetPropertyAction("java.home"));
try {
zoneID = getSystemTimeZoneID(javaHome);// 如果没有设置时区的话 第三步
if (zoneID == null) {
zoneID = GMT_ID;
}
} catch (NullPointerException e) {
zoneID = GMT_ID;
}
}
// Get the time zone for zoneID. But not fall back to
// "GMT" here.
tz = getTimeZone(zoneID, false);
if (tz == null) {
// If the given zone ID is unknown in Java, try to
// get the GMT-offset-based time zone ID,
// a.k.a. custom time zone ID (e.g., "GMT-08:00").
String gmtOffsetID = getSystemGMTOffsetID();
if (gmtOffsetID != null) {
zoneID = gmtOffsetID;
}
tz = getTimeZone(zoneID, true);
}
assert tz != null;
final String id = zoneID;
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
System.setProperty("user.timezone", id);
return null;
}
});
defaultTimeZone = tz;
return tz;
}
...
/**
* Gets the platform defined TimeZone ID.
**/
private static native String getSystemTimeZoneID(String javaHome);
第三步中调用的getSystemTimeZoneID(String javaHome)这个方法就是native方法.
-4. 通过OpenJDK查看下getSystemTimeZoneID这个方法是什么
![](https://img.haomeiwen.com/i25399192/a7f34255ccd954e9.png)
![](https://img.haomeiwen.com/i25399192/9a6e4f8ed0ba1df0.png)
![](https://img.haomeiwen.com/i25399192/aab2edb81f822363.png)
然后根据java.util.TimeZone的路径找到对应的native包下的信息
![](https://img.haomeiwen.com/i25399192/335f1d314491c0a5.png)
![](https://img.haomeiwen.com/i25399192/6d46a774765c5e89.png)
在java层面我们仅仅须要dll文件,.c文件的目的仅仅是为了生成dll文件.
这个时候回头看下整个流程:
- 执行TimeZone.getDefault();
- JVM加载TimeZone字节码到内存,同时因为getSystemTimeZoneID(String javaHome)方法描述符内有native,这个描述符块将有一个指向该方法的实现的指针;
- src/share/native/java/util/TimeZone.c文件编译后生成DLL文件,而方法的实现就在这个DLL文件中;
- 在执行getSystemTimeZoneID(String javaHome)方法的同时DLL文件会被操作系统加载到java程序的地址空间;
- JVM完成对Java外的环境交互.
总结:
JVM如何运行Native方法
一个类第一次被使用到时,这个类的字节码会被加载到内存,并且只会加载一次。在这个被加载的字节码的入口维持着一个该类所有方法描述符的list,这些方法描述符包含这样一些信息:方法代码存于何处,它有哪些参数,方法的描述符(public之类)等等。
如果一个方法描述符内有native,这个描述符块将有一个指向该方法的实现的指针;这些实现在一些DLL文件内,但是它们会被操作系统加载到java程序的地址空间;
当一个带有本地方法的类被加载时,其相关的DLL并未被加载,因此指向方法实现的指针并不会被设置。当本地方法被调用之前,这些DLL才会被加载,这是通过调用java.system.loadLibrary()实现的。
为什么需要Native方法呢
- 有些功能Java实现不易.比如读取默认时区,使用Java实现是不太容易的.
- 使用C语言或者C++等其他语言执行效率会更高
网友评论