最近在学Jvm的知识,把之前学过的东西做一下总结,尽量自己复现出来
Java虚拟机主要由两大系统和两大组件构成,两大系统分别是类加载子系统和执行引擎,两大组件分别是运行时数据区和本地方法接口,这里主要对运行时数据区做一下总结
运行时数据区分为五个内存区域,分别是堆,方法区,虚拟机栈,本地方法栈,程序计数器,类加载子系统将字节码文件的不同数据加载到不同的区域,执行引擎在执行命令时与运行时数据区的数据进行动态交互.
堆:
堆是JVM内存中最大的一块区域,是GC的主要工作区域,可以位于物理上不连续,但逻辑上连续的空间,主要用来存放对象实例和数组,线程共享,当内存中没有足够的空间分配给新的实例,又无法继续扩展时就会出现OutOfMemoryError,堆根据垃圾回收又可再分为新生代和老年代,新生代又可再分为Eden空间,from survivor空间,to survivor空间
方法区:
线程共享,主要存储类信息,静态变量,常量以及编译后的方法对应的二进制指令集等数据,同时方法区内部还包含运行时常量池(类加载的解析阶段将字节码中常量池中的符号引用替换成直接引用后,常量池就变成了运行时常量池),内存有限,在内存不足又无法继续申请时出现OutOfMemoryError,可以申请不实现垃圾回收
虚拟机栈:
线程私有,虚拟机栈是一个栈结构的空间,当每一个方法开始执行的时候都会在虚拟机栈中来开辟一个栈桢,栈帧主要存储方法执行时的局部变量表,操作数栈,动态链接以及方法出口等,当方法执行完毕后栈帧会被回收,一个方法从开始执行到执行结束对应了,栈帧从入栈到出栈的过程,当线程申请一个超出虚拟机规定上限的栈空间时就会出现StackOverFlowError,当栈无法再为新的方法分配栈帧又无法继续扩展时则出现OutOfMemoryError.
本地方法栈:
线程私有,和虚拟机栈类似,区别是本地方法栈为本地方法服务,本地方法是指用native关键字修饰,用C语言实现的方法,主要是用于弥补Java不方便的实现的一些功能,可以直接和系统对接.同样会出现上面两种error
程序计数器:
线程私有,主要存储虚拟机下一条要执行的指令地址,当执行的是本地方法是则是undefined,字节码解释器就是通过改变程序计数器的值来选取下一条要执行的指令,分支,循环,跳转,异常处理都要依赖程序计数器来实现,如果正在执行的方法为本地方法,则程序计数器的值为null,并且本区域是虚拟机唯一没有规定OutOfMemoryError的区域
以上线程共享区域生命周期随着虚拟机的创建而创建,随着虚拟机的消亡而消亡.线程私有的区域随着线程创建而创建,随着线程的消亡而消亡.
执行引擎
我们这里再说一下执行引擎,执行引擎主要由解释器,即时编译器以及垃圾回收器组成,其中垃圾回收器是进行垃圾回收用的,这里我们就不详细说明.
首先大概说一下方法的执行流程,当我们执行一个方法时,会在虚拟机栈中开辟一个栈桢,这个栈帧中会存储一些方法执行需要的数据,但是方法的执行逻辑其实是在方法区中以指令集的形式存储的,我们的栈帧中会存储一个动态链接,这个动态链接就会持有指向我们方法区中的指令集的指针,当我们执行方法是就是根据动态链接的指针,找到对应的指令集,我们的执行引擎主要就是配合虚拟机栈内的数据以及程序计数器去执行这些指令集.
-
解释器 由于我们的指令集是字节码的形式存储在方法区中的,不能直接被我们的虚拟机执行,那么解释器就是用来将我们的指令集解释成虚拟机可以执行的形式然后再执行,但是由于我们的解释器是一边解释一边执行的,那么效率本身就不高,那么对于一些重复的逻辑可能要去重复解释再执行(比如循环),效率就会很低,所以执行引擎里面就有了即时编译器.
-
即时编译器 主要负责识别一些反复执行的代码,也就是热点代码,会直接将这些指令集编译成编译成一种本地指令,那么在下次执行时就不用解释器解释再执行,而是直接执行本地指令,可以提高我们的执行效率.