深入理解Java虚拟机——JVM小结

JVM

指以软件的方式模拟具有完整硬件系统功能、运行在一个完全隔离环境中的完整的计算机系统。

JVM主要由三个子系统组成

  • 类装载器子系统
  • 运行时数据区(内存结构)
  • 执行引擎
  • 本地方法接口

深入理解Java虚拟机——JVM小结

结构

深入理解Java虚拟机——JVM小结


堆(Heap)

深入理解Java虚拟机——JVM小结


类加载器(ClassLoader)

深入理解Java虚拟机——JVM小结

双亲委派机制

先托付父类加载器寻找目标,在找不到的情况下在自己的路径中查找并加载目标类。

好处:

防止类重复加载:父类完成加载时,子加载器就没必要再加载
沙箱安全机制:防止核心API库被恶意串改

全盘负责委托机制

如果一个类被一个加载器加载,除非显示的是使用另一个类加载器,那么该类的其他引用和依赖也将被这个类加载器载入。

JVM不会一次性将所有类都加进内存,而是按需加载,在运行期间动态加载。


如何判断对象是否可以被回收?

一、引用计数法
给对象添加一个引用计数器,每当有一个地方引用它,计数器加一,
当引用失效,计数减一,当计数器的值为0时,说明对象就是不可能再被使用的。

这种方法实现简单、效率高。但目前主流虚拟机并不是用此算法来管理内存的。主要原因是很难解决对象间相互循环引用的问题。

二、可达性分析法

选一个GC ROOT作为根节点,从这个节点出发开始向下搜索(循环遍历所有对象),节点走过的路径成为引用链,如果任何一个对象到GC ROOT没有任何引用链的话,则证明这个对象是可回收的。

可作为GC ROOT的对象

  • 类加载器
  • Thread
  • static成员
  • 常量引用
  • 本地方法栈的变量

三、finalize方法最终判断对象是否存活
标记的前提:对象在进行可达性分析后发现没有与GCROOTS相连接的引用链

第一次标记:筛选条件是该对象是否有必要执行finalize方法
当对象没覆盖finalize方法,或者finalize被JVM调用过,都会被JVM视为“没必要回收”

第二次标记:如有必要执行finalize方法,则对象会被放在F-QUEUE队列,会被JVM创建的一条低优先级的finalize线程去执行。这里的“执行”指的是虚拟机会触发这个方法,并不承诺会等待它运行结束。这样做的原因是,如果一个对象finalize()方法中执行缓慢,或者发生死循环(更极端的情况),将很可能会导致F-Queue队列中的其他对象永久处于等待状态,甚至导致整个内存回收系统崩溃。
finalize()方法是对象脱逃死亡命运的最后一次机会,稍后GC将对F-Queue中的对象进行第二次小规模标记,如果对象要在finalize()中成功拯救自己----只要重新与引用链上的任何的一个对象建立关联即可,譬如把自己赋值给某个类变量或对象的成员变量,那在第二次标记时它将移除出“即将回收”的集合。如果对象这时候还没逃脱,那基本上它就真的被回收了。


垃圾回收算法

  • 标记-清除算法[老年代]
    遍历GC Roots,然后将存活的对象标记
    缺点:会导致产生大量不连续的内存碎片,使得大对象无法存储。 /空间问题
    需遍历完所有的内存对象才能标记。/效率问题

  • 复制算法[新生代]
    解决了内存碎片的问题
    将内存分割成大小相等的两块,当其中一块内存用完时,gc后将存活的内存对象复制到另一块,将之前内存的对象全部清除。
    缺点:堆使用效率低、内存减半、如果对象存活率高,复制会花费较多时间。

  • 标记-整理算法
    将可回收对象做标记,让存活对象向前移动一段,按照内存地址依次排列,将末端地址以后的内存全部回收。
    优点:解决的内存碎片问题
    缺点:效率低,不仅要标记存活对象,还要整理所有活对象的引用地址。

  • 分代收集算法
    根据每一代的特点选择适合的垃圾收集算法、是一种思想


垃圾收集器

如果说垃圾收集算法是内存回收的方法论,那么垃圾收集器则是内存回收的具体实现。

没有万能的垃圾收集器,我们能做的就是根据具体应用场景选择适合自己的垃圾收集器

  • Serial
  • ParNew
  • Parallel Scavenge
  • CMS
  • G1

Serial

Serial(串行)是最基本、历史最悠久的垃圾收集器
它是单线程的,意味着他在只会调用一条垃圾回收线程去回收内存、工作时必须暂停其他所有的工作线程(STW)
对于新生代,采用复制算法
对于老年代,采用标志-整理算法

深入理解Java虚拟机——JVM小结


ParNew

本质就是Serial收集器的多线程版本、其余行为跟Serial收集器一致
深入理解Java虚拟机——JVM小结


Parallel Scavenge --JDK8默认的垃圾收集器

类似于ParNew,但它更关注吞吐量,也就是高效率利用CPU

对于新生代,采用复制算法
对于老年代,采用标志-整理算法

深入理解Java虚拟机——JVM小结


CMS[Old区]

第一款真正意义上的JAVA并发收集器
对于老年代,采用**[标记-清除]**算法

面试题:
并行(Parallel) :指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。
并发(Concurrent):应用程序线程与垃圾收集线程同时执行(不一定是并行,可能会交替执行)

整个过程分4个步骤 如图
深入理解Java虚拟机——JVM小结

优点:并发收集、低停顿
缺点:对CPU资源敏感,可能与服务抢占资源
无法处理浮动垃圾,在并发清理的过程中,线程可能会产生一些新的垃圾
“标记-清除”算法导致大量的空间碎片产生


G1

是一种面向服务器的收集器,主要针对高配机器(配备多颗处理器以及大容量内存)

特征:
极高概率满足gc停顿时间要求的同时,还具备高吞吐量的性能特征

基于整体:使用的是标记-整理算法
基于局部:使用的是复制算法

深入理解Java虚拟机——JVM小结

深入理解Java虚拟机——JVM小结

和CMS类似,
核心优势是:

可预测的停顿,根据用户期待gc停顿时间来指定回收计划
内部维护了一个有限列表,根据每次允许的收集时间,优先选择回收价值最大的region

G1不做Full Gc,而是Mixed Gc。→也回收所有Young和部分Old