jvm结构详解
2022-03-17 21:26:38 10 举报
jvm详解
作者其他创作
大纲/内容
中间代码
系统类加载器
jit编译器
机械无关优化
本地方法接口native interface
自定义类加载器
class加载步骤:1、加载:在硬盘上查找并通过IO读入字节码文件2、连接:执行校验、准备、解析(可选)步骤3、校验:校验字节码文件的正确性4、准备:给类的静态变量分配内存,并赋予默认值5、解析:类装载器装入类所引用的其他所有类6、初始化:对类的静态变量初始化为指定的值,执行静态代码块
本地方法栈native method stack
标记-整理算法
parallel old收集器
机械相关优化
寄存器分配器
方法区||非堆method area
新生代
本地方法栈(线程私有):登记native方法,在Execution Engine执行时加载本地方法库
复制算法
垃圾回收器
算法分为“标记”和“清除”阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。它是最基础的收集算法,效率也很高,但是会带来两个明显的问题:1、效率问题2、空间问题(标记清除后会产生大量不连续的碎片)
方法区(线程共享):类的所有字段和方法字节码,以及一些特殊方法如构造函数,接口代码也在此定义。简单说,所有定义的方法的信息都保存在该区域,静态变量+常量+类信息(构造方法/接口定义)+运行时常量池都存在方法区中,虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做 Non-Heap(非堆),目的应该是与 Java 堆区分开来。
cms收集器
metadata
from(1/10)
全盘负责委托机制:当一个ClassLoader加载一个类时,除非显示的使用另一个ClassLoader,该类所依赖和引用的类也由这个ClassLoader载入双亲委派机制:指先委托父类加载器寻找目标类,在找不到的情况下在自己的路径中查找并载入目标类
方法出口
幸存者区(Survivor space)
Java栈(线程私有): Java线程执行方法的内存模型,一个线程对应一个栈,每个方法在执行的同时都会创建一个栈帧(用于存储局部变量表,操作数栈,动态链接,方法出口等信息)不存在垃圾回收问题,只要线程一结束该栈就释放,生命周期和线程一致 JVM对该区域规范了两种异常:1) 线程请求的栈深度大于虚拟机栈所允许的深度,将抛出StackOverFlowError异常2) 若虚拟机栈可动态扩展,当无法申请到足够内存空间时将抛出OutOfMemoryError,通过jvm参数–Xss指定栈空间,空间大小决定函数调用的深度
g1收集器
字节码解释器
新生区3/1young generation
当前虚拟机的垃圾收集都采用分代收集算法,这种算法没有什么新的思想,只是根据对象存活周期的不同将内存分为几块。一般将java堆分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。 比如在新生代中,每次收集都会有大量对象死去,所以可以选择复制算法,只需要付出少量对象的复制成本就可以完成每次垃圾收集。而老年代的对象存活几率是比较高的,而且没有额外的空间对它进行分配担保,所以我们必须选择“标记-清除”或“标记-整理”算法进行垃圾收集。
双亲委派机制:类模板加载会向上委派,父类处理失败,交回给子类处理
parnew收集器
拓展类加载器
serial old收集器
serial收集器
操作树栈
Parallel Scavenge收集器关注点是吞吐量(高效率的利用CPU)。CMS等垃圾收集器的关注点更多的是用户线程的停顿时间(提高用户体验)。所谓吞吐量就是CPU中用于运行用户代码的时间与CPU总消耗时间的比值。 新生代采用复制算法,老年代采用标记-整理算法。
目标代码生成器
分代收集算法
本地方法库
ParNew收集器其实就是Serial收集器的多线程版本,除了使用多线程进行垃圾收集外,其余行为(控制参数、收集算法、回收策略等等)和Serial收集器完全一样。 新生代采用复制算法,老年代采用标记-整理算法。
1、当一个方法开始执行之后,只有两种方式可以退出这个方法;2、第一种方式是执行引擎遇到任意一个方式返回的字节码指令,这时候可能会有返回值传递给上层的方法调用者,是否有返回值以及返回值类型将根据遇到的方法返回字节码指令来决定,这种退出方式成为正常完成出口。3、另一种退出方式是在方法的执行过程中出现了异常,并且这个异常没有在方法体内得到处理,无论是jvm内部产生的异常处理器,就会导致方法退出,这种退出方式称为异常完成出口。此种情况下,方法是不会给上层调用者返回任何值。4、无论采用何种退出方式,在方法退出之后,都需要返回到方法被调用的位置继续执行,方法返回时可能需要在栈帧中保存一些信息,用来帮助恢复它的上层方法的执行状态。5、方法退出时可能执行的操作有:恢复上层方法的局部变量表和操作树栈、把返回值压入调用者栈帧的操作数栈中,调整pc计数器的值以指向方法调用指令后面的一条指令地址。
tenured space
新生代可使用收集器
启动类加载器
eden(8/10)伊甸区(Eden space)
运行时数据区
parallel scavenge收集器
单线程收集器,在收集垃圾时,会停止用户线程,执行收集线程;用户体验较差。 新生代采用复制算法,老年代采用标记-整理算法。
为了解决效率问题,“复制”收集算法出现了。它可以将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。
老年代
根据老年代的特点特出的一种标记算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象回收,而是让所有存活的对象向一段移动,然后直接清理掉端边界以外的内存。
执行引擎
模块中间连线代表年轻代模块下方连线代表老年代
栈帧1
栈帧2
jvm字节码
动态链接
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。它而非常符合在注重用户体验的应用上使用,它是HotSpot虚拟机第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程(基本上)同时工作。从它的名字就可以看出它是一款优秀的垃圾收集器,主要优点:并发收集、低停顿。但是它有下面三个明显的缺点:对CPU资源敏感(会和服务抢资源);无法处理浮动垃圾(在java业务程序线程与垃圾收集线程并发执行过程中又产生的垃圾,这种浮动垃圾只能等到下一次gc再清理了);它使用的回收算法-“标记-清除”算法会导致收集结束时会有大量空间碎片产生。 初始标记: 暂停所有的其他线程(STW),并记录下直接与root相连的对象,速度很快 ; 并发标记: 同时开启GC和用户线程,用一个闭包结构去记录可达对象。但在这个阶段结束,这个闭包结构并不能保证包含当前所有的可达对象。因为用户线程可能会不断的更新引用域,所以GC线程无法保证可达性分析的实时性。所以这个算法里会跟踪记录这些发生引用更新的地方。 重新标记: 重新标记阶段就是为了修正并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段的时间稍长,远远比并发标记阶段时间短 并发清除: 开启用户线程,同时GC线程开始对未标记的区域做清扫。
类加载器
标记-清除算法
java栈java stack
直接内存(元空间)metadata space
jvm
新生区经过多次GC仍然存活的对象移动到老年区。若老年区也满了,那么这个时候将产生MajorGC(FullGC),进行老年区的内存清理。若老年区执行了Full GC之后发现依然无法进行对象的保存,就会产生OOM异常“OutOfMemoryError”
老年代3/2old generation
to(1/10)
目标代码
栈帧n...
局部变量表
heap堆
程序计数器program counter register
0 条评论
回复 删除
下一页