运行Java程序,除了Java虚拟机之外,还需要Java类库的配合。Java虚拟机和Java类库一起构成了Java运行时环境。Java类库主要用Java语言编写,一些无法用Java语言实现的方法则使用本地语言编写,这些方法叫作本地方法。OpenJDK类库中的本地方法是用JNI(Java Native Interface) 编写的
Java类库中的很多类都要注册本地方法,比如Object类就有一个registerNatives()本地方法,用于注册其他本地方法
public class Object {
private static native void registerNatives();
static {
registerNatives();
}
... // 其他代码
}
Java虚拟机规范并没有规定如何实现和调用本地方法我们用Java虚拟机栈执行本地方法,
Java虚拟机规范预留了两条指令,操作码分别是0xFE和0xFF。
下面将使用0xFE指令来达到这个目的:
1.在用classFile里面的方法表解析出method[]的时候, 如果从方法的accessFlags标志位看出来是native方法, 就code[]={0xFE,reture}
2.0xFE指令对应invokeNative字节码, execute()是 用(className,方法名,描述符)去 本地方法注册中心(内部是个map) 拿对应的 本地方法来执行,
本地方法注册中心里面的本地方法,是最开始初始化的时候注册进去的, 写死的
Object本地方法
本地方法的开头都是 :局部变量表[0]拿到this
thisObject = frame.getLocalVars().getRef(0);
最后都是把运行结果入栈
-
()Ljava/lang/Class; getClass
对象.getClass就是它
反射
局部变量表[0]拿到this->拿到this的class->拿到class的对象->入栈
-
-
"()I hashCode
局部变量表[0]拿到this->hash(this)->入栈
使用例子obj1.hashCode()
-
-
()Ljava/lang/Object; clone
局部变量表[0]拿到this->检查是不是实现了clone接口 没实现就异常->克隆 入栈
克隆是复制class引用,一一复制slot, 如果slot里面是引用 那么还在指向原来的
-
Class本地方法
反射
getPrimitiveClass
getName0
装箱拆箱
Integer为例 里面缓存了一些东西 如int 到 char转换:
final static char[] digits = {
'0' , '1' , '2' , '3' , '4' , '5' ,
'6' , '7' , '8' , '9' , 'a' , 'b' ,
'c' , 'd' , 'e' , 'f' , 'g' , 'h' ,
'i' , 'j' , 'k' , 'l' , 'm' , 'n' ,
'o' , 'p' , 'q' , 'r' , 's' , 't' ,
'u' , 'v' , 'w' , 'x' , 'y' , 'z'
};
public static Integer valueOf(int i) {
//有缓存
if (i >= -128 && i <= 127){
return IntegerCache.cache[i + (-IntegerCache.low)];
}
return new Integer(i);
}
↑ Integer.valueOf()方法并不是每次都创建Integer()对象,而是维护了一个缓存池IntegerCache。对于比较小(默认是–128~127)的int变量,在IntegerCache初始化时就预先加载到了池中,需要用时直接从池里取即可。IntegerCache是Integer类的
内部类
IntegerCache在初始化时
需要确定缓存池中Integer对象的上限值,为此它调用了
sun.misc.VM类的getSavedProperty()方法。
String本地方法
这个String如果常量池原来没有,放进去,原来有 返回老的
intern, ()Ljava/lang/String;
System 本地方法
-
arraycopy, (Ljava/lang/Object;ILjava/lang/Object;II)V
StringBuilder.append() 最终会调用这个本地方法
使用例子:
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin); -
initProperties, (Ljava/util/Properties;)Ljava/util/Properties;
准备一些常量 如 "java.class.version", "52.0"
Throwable本地方法
所有异常和错误的父类构造方法里面会调用
fillInStackTrace",(I)Ljava/lang/Throwable;
给这个对象填入 StackTraceElement实例 以便以后显示报错信息
FileOutputStream
有个输出方法`java/io/FileOutputStream, writeBytes'
所有输出流最后都会调用它 以字节为单位输出
VM
调用System.initializeSystemClass 初始化虚拟机需要的东西
会在main之前调用