title: JVM author: Gamehu tags: - java categories: - 工作 date: 2022-06-11 10:22:00 --- ### 背景 由于最近在查一个OOM问题,所以借机读起了《深入理解Java虚拟机》第三版。简单做个记录。 ### 垃圾收集算法 #### 分代收集理论 大多数垃圾收集器的都应用的理论。 > 弱分代假说(年轻代):绝大多数朝生夕灭的对象。 > 强分代假说(老年代):熬过越多次垃圾收集就越难消亡。 > 跨代引用假说 Partial GC(部分收集): * Minor GC/Young GC(年轻代收集) * Major GC/Old GC(老年代收集) * Mixed GC(混合收集):JDK 11 ,只有G1有,针对年轻代和部分老年代。 Full GC(整堆收集):整个堆和方法区 ##### 标记-清除算法 标记需要回收的对象,然后清除。这是最基础算法,后续的其它算法都是针对其缺点的改进而来的。所以其实放在这个目录纬度可能有点不太合适,不过不纠结了。 {% asset_img 1.jpeg stackoverflow %} 缺点: 1. 随着对象增加而效率降低。 2. 内存碎片 ##### 标记-复制算法 年轻代内存均分两个区域,一个使用完了,就将还存活的对象复制到另一个。避免内存碎片。 {% asset_img 2.png opengenus %} 缺点: 1. 内存缩小了一半 。 2. 存活对象越多,复制的开销越大,效率降低。 3. 需要担保机制,保证剩下的区域也不够时,也能正常运行,即需要依赖其它内存区域(年老代)。 Appel式回收:优化了标记-复制算法,使内存的分配更加合理,比如分成了1个Eden、2个Survivor,Eden、Survivor默认比例为8:1,一开始使用1个Eden、1个Survivor,即剩下未使用的内存只占10%。 ##### 标记-整理算法 主要针对年老代。标记后,让存活对象往内存的一端移动,然后再清理除此以外的内存。 {% asset_img gc_compact.png opengenus %} 缺点: 1. 移动就意味着需要更新引用地址,增加了回收的复杂度。 所以需要权衡(如果移动则回收更复杂,如果不移动则内存分配更复杂)。比如如果更关注吞吐量则选择“移动”,如果更关注延迟则“不移动”。 #### 算法细节 上面说的是一些理论,现在说一些细节,垃圾收集的前置知识。 可达性分析:追踪通过“根”对象的一系列参考链可以到达哪些对象。算法中定义了几个GC Root对象。这些根对象在GC时不会被JVM回收,然后通过这些对象像分支一样向外扩展。引用的对象表示它们仍在使用中,不会被使用。 ##### 根结点枚举 即找出所有的GC Roots,且该过程是STW。所谓GC根是垃圾收集器专用的对象。垃圾收集器收集那些不是GC根并且不能通过GC根的引用访问的对象。 固定的可做为GC Roots的节点主要在全局性的引用(常量,类静态属性)与执行上下文(栈桢的本地变量表), **OopMap** 快速完成根结点枚举的实现方式:OopMap。 一旦类加载动作完成的时候,HotSpot就会把对象内什么偏移量上是什么类型的数据计算出来, 在即时编译过程中,也会在特定的位置记录下栈里和寄存器里哪些位置是引用。 这样收集器在扫描时就可以直接得知这些信息了,并不需要真正一个不漏地从方法区等GC Roots开始查找 所以OopMap的作用就是存储内存中哪些位置存储了对象引用。 ##### 安全点 > OopMap记录的位置(不是随时随地都记录,不然空间占用又是个大问题) > 控制用户执行程序不能任意位置停顿下来执行垃圾收集。而是强制要求在安全点才能停顿。 这些特定安全点的主要位置如下: * 在方法返回之前 * 调用方法后 * 抛出异常的位置 * 循环结束 * … 咋个保证到了位置就能停呢,HotSpot采用主动式中断方式,即不直接操作线程而是设置一个标志位,线程轮询这个标志位,当为真时,则线程主动中断挂起。 为了使轮询高效,HotSpot使用内存保护陷阱的方式,把轮询操作精简为一条指令,当应该听顿时,设置对应的内存页为只读,则该指令会产生一个自陷异常信号,使线程能挂起。 ##### 安全区域 扩展拉伸了的安全点。用于程序不执行的时候,即没法触发安全点的情况,该区域里引用关系不会发生变化,因此只要在区域内就能进行垃圾收集。 ##### 记忆集与卡表 > 跨代引用的记录方案,避免为了找跨代引用关系时扫描整个老年代。 记忆集:用于记录从非收集区域指向收集区域的指针集合的抽象数据结构。 卡表:记忆集的具体的实现。 ##### 写屏障 为的是维护卡表的元素,比如其它代的对象引用了本区域的对象,即赋值操作,这个时候需要更新卡表,但是怎么保证卡表的更新呢,特别是在已经编译为机器码时。 写屏障就是机器码层面的一个手段,在每个赋值操作形成一个AOP切面,赋值前后都属于写屏障,这个时候就能把更新卡表放在赋值之后了,从而保证卡表的更新。 ##### 并发可达性分析 **三色标记** > 标记对象的状态。用于后续是否被回收的输入条件。 > 白色: 未被GC访问过的对象。 > 灰色: 被GC访问过,但是至少还存在一个引用没有被扫描。 > 黑色: 被GC访问过,且所有引用都被扫描过。 **并发扫描对象消失问题** 即把原本存活的对象标记为已消亡。 解决办法: 1. 增量更新(插入引用) 2. 原始快照(删除引用) #### 垃圾收集器 ##### G1 里程碑式的收集器,源于开创了基于Region的堆内存布局,可对任何区域进行回收即不区分老年代、新生代。 虽然还是遵循分代理论,但是目标范围不再像以前的收集器,分隔新生代、老年代或者整个堆,而是可以对任何区域进行回收,因为Region布局里的每个区域都可作为新生代或者老年代的空间。 即新生代、老年代不再是固定的。而是一系列Region的动态集合(不需要连续)。 G1追求的不是一次扫描整个新生代或者老年代甚至整个堆,G1通过计算只会扫某些Region,理论上来讲只要收集的速度跟上分配的速度那就能完美一直运行。 {% asset_img g1-011.png g1 %} ### JVM Architecture https://docs.oracle.com/javase/specs/jvms/se11/jvms11.pdf ![img](https://www.freecodecamp.org/news/content/images/size/w600/2021/01/image-39.png) ![img](https://www.freecodecamp.org/news/content/images/size/w600/2021/01/image-32.png) JVM中的类的加载器主要有三种:***\*启动类加载器\****,***\*拓展类加载器\****,***\*应用类加载器。\**** **启动类加载器(Bootstrap classLoader):**又称为引导类加载器,由C++编写,无法通过程序得到。主要负责加载JAVA中的一些核心类库,主要是位于/lib/rt.jar中。 **拓展类加载器(Extension classLoader):**主要加载JAVA中的一些拓展类,位于/lib/ext中,是启动类加载器的子类。 **应用类加载器(System classLoader):** 又称为系统类加载器,主要用于加载CLASSPATH路径下我们自己写的类,是拓展类加载器的子类。 https://www.freecodecamp.org/news/jvm-tutorial-java-virtual-machine-architecture-explained-for-beginners/ #### Types of Java Garbage Collectors https://www.cnblogs.com/woshimrf/p/jvm-garbage.html https://javapapers.com/java/types-of-java-garbage-collectors/ ![img](https://i.stack.imgur.com/XHfx0.jpg) ![img](https://static001.infoq.cn/resource/image/75/a4/754f1ab05e6527107cfd8578d98a80a4.png) https://www.infoq.cn/article/3wyretkqrhivtw4frmr3 [Java 如何有效地避免OOM:善于利用软引用和弱引用](https://www.cnblogs.com/dolphin0520/p/3784171.html) `本文引用的内容,如有侵权请联系我删除,给您带来的不便我很抱歉。` Java Platform, Standard Edition HotSpot Virtual Machine Garbage Collection Tuning Guide:https://docs.oracle.com/javase/9/gctuning/garbage-first-garbage-collector.htm#JSGCT-GUID-ED3AB6D3-FD9B-4447-9EDF-983ED2F7A573 Java Garbage Collection handbook :https://plumbr.io/handbook/garbage-collection-algorithms-implementations/g1 Shenandoah GC:https://shipilev.net/talks/javazone-Sep2018-shenandoah.pdf https://stuefe.de/posts/metaspace/what-is-compressed-class-space/ https://shipilev.net/jvm/anatomy-quarks/23-compressed-references/ https://stuefe.de/posts/metaspace/analyze-metaspace-with-jcmd/ [Understanding the Java Memory Model and Garbage Collection](https://www.digitalocean.com/community/tutorials/java-jvm-memory-model-memory-management-in-java) [JVM Architecture](https://www.youtube.com/watch?v=QHIWkwxs0AI&t=212s)