JVM基础篇1 - Class的加载
JVM基础篇2 - 指令集
JVM进阶篇1 - 内存模型
JVM进阶篇2 - GC垃圾回收
JVM总览- JVM架构
java运行时数据区(RuntimeDataArea)
-
JVM stacks: 每一个线程对应一个栈,一个方法对应一个栈帧(frame)
栈帧的组成:
-
局部变量表 :局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。在Java程序被编译成Class文件时,就在方法的Code属性的max_locals数据项中确定了该方法所需要分配的最大局部变量表的容量。
-
操作数栈 :操作数栈又叫做操作栈,是一个后入先出的栈。同局部变量表一样,操作数栈的最大深度也在编译的时候被写入到Code属性的max_stacks数据项中
-
动态连接:每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了方法调用过程中的动态连接。Class文件中的常量池中存有大量的符号引用,字节码中的方法调用指令就是以常量池里指向方法的符号引用作为参数的,这些符号的一部分会在类加载阶段或者第一次使用的时候被转化为直接引用,这种转化被称为静态解析。另一部分在每一次运行期间转化为直接引用,被称为动态连接(因为方法的执行对应着栈帧的入栈出栈,所以存放在栈帧中)
-
方法返回值:当一个方法开始执行后,只有两种方法可以退出这个方法。第一种方法是执行引擎遇到任何一个方法返回的字节码指令,这时候可能会有返回值传递给上层的方法调用者,是否有返回和返回值的类型将根据遇到何种方法返回指令来决定,这种退出方法的方式称为正常完成出口。另外一种退出方法是,在方法执行过程中遇到异常,并且这个异常没有在方法体内得到处理,就会导致方法退出,这种退出方法的方式称为异常完成出口。
无论采用哪种退出方式,在方法退出之后,都需要返回到方法被调用的位置,程序才能继续执行。一般来说,方法正常退出时,调用者的PC计数器可以作为返回地址,栈帧中很可能会保存这个计数器值。而方法异常退出时,返回地址是要通过异常处理器表来确定的,栈帧中一般不会保存这部分信息。
方法退出的过程实际上就等同于把当前栈帧出栈,因此退出时可能执行的操作有:恢复上层方法的局部变量表和操作数栈,把返回值压入调用者栈帧的操作数栈中,调整PC计数器的值以指向方法调用指令后面的一条指令等。
-
-
paogramCounter(PC) 程序计数器,存放下一个指令的地址
//模拟虚拟机的运行 while(not end){ 1.取PC中的位置,找到对应位置的指令 2.执行该指令 3.PC++ }
-
Direct Memory: 直接内存,操作系统管理, 原理:NIO (实现zero copy) 用户空间直接访问内核空间。
-
native method stacks : 本地方法栈
-
Method Area(逻辑概念) : 方法区,装class指令
具体实现:
-
perm Space (jdk < 1.8)
字符串常量位于PermSpace,FGC不会回收(可能会OOM)
-
Meta Space (jdk > 1.8)
字符串常量位于堆,会触发FGC清理
-
线程共享区域: Heap 和 Method Area
为什么每个线程都要有自己的PC计数器?
CUP线程切换,CPU切换回来的时候才能知道上一个线程执行到哪了
栈帧指令:
常用指令:
-
load 压栈
-
pop 弹栈
-
add 加法
-
mul 乘法
-
sub 减法
-
invoke
- InvokeStatic 调用静态方法
- InvokeVirtual调用可以多态的方法
- InvokeInterface调用接口方法
- InvokeSpecial调用可以直接定位,不需要多态的方法如private 方法、构造方法
- InvokeDynamic调用lambda表达式或者反射或者其他动态语言scala kotlin,或者CGlib ASM,动态产生的类会用到的指令
InvokeDynamic调用lambda表达式或者反射或者其他动态语言scala kotlin,或者CGlib ASM,动态产生的类会用到的指令