JVM(5)—GC垃圾回收

JVM(5)—GC垃圾回收

Java不需要C语言写析构函数手动释放对象。Java会自动对无用对象进行垃圾回收GC,释放占用的内存空间。

垃圾发现算法

  • 什么条件下对象才算是垃圾?

当对象没有任何引用的时候,就代表对象无用了

引用链路断开,后续对象都会标记为垃圾。

  • GC怎么知道对象没有引用?
  1. 引用计数算法

用计数器记录有多少个对象引用自己,当计数器值为0,表明没有被引用,代表是垃圾。

缺点:无法解决循环引用的情况。堆中对象互相引用。

  1. 可达性分析算法(根搜索算法)

从GC Root 出发,有引用的对象对象不可回收,没有引用的标记为垃圾后回收,是JVM默认算法。

GC Root 可以为:栈帧中的局部变量、静态变量、运行时常量池、JNI本地方法调用指针

垃圾回收算法

  1. 标记清除算法

先使用可达性分析算法标记,再把标记的清除。

内存不连续,碎片化。

  1. 复制算法

将空间分为两块大小相同的区域,先将存活对象从From区复制到To区, 再将From区的对象全部清除。

速度快,无碎片,但浪费了一半的存储空间。

年轻代Minor GC使用。

  1. 标记压缩算法

标记垃圾,清除垃圾,对可用对象进行压缩整理。

无碎片,但效率较低。

老年代Full GC使用。

  1. 分代收集算法

年轻代与老年代的特性决定。

年轻代空间小,用复制算法浪费的空间就也小。

年轻代对象存活率低,复制算法效率高。

老年代对象存活率高,空间大,所以用标记压缩算法

分代GC垃圾收集器

用来执行垃圾发现与垃圾回收算法。

  1. Serial收集器

  • 年轻代收集器。

  • 单线程GC,效率低。

  • 适合单核CPU、小内存(几十mb)场景。

  • 已淘汰。

  1. ParNew收集器

  • 年轻代收集器。
  • 多线程GC,效率不错。
  • 优先降低SWT时间,但GC会多次。
  • 适合多核CPU,n个G内存场景。
  • 适合用户交互场景(用户点一下按钮等待时间少)。
  1. Parallel Scavenge(PS)收集器

  • 年轻代收集器。
  • 吞吐率优先考虑(运行用户代码时间/运行用户代码时间+GC垃圾回收时间),所以GC总时间少。
  • STW时间较长,GC次数少。
  • 多线程GC,效率不错。
  • 适合多核CPU。
  • 适合后台计算任务。
  • 1.8默认年轻代GC收集器。(大多开发服务器,和用户交互不是很多,后台计算任务多)
  1. SerialOld收集器

  • Serial收集器老年代版本
  • 1.8默认老年代收集器
  • 单线程GC,效率低
  • 适合单核CPU
  • 作为CMS备用收集器
  1. Parallel Old收集器

  • PS收集器老年代版本
  • 关注吞吐量
  • STW时间较长,GC次数少。
  • 多线程GC,效率不错。
  • 适合多核CPU。
  • 适合后台计算任务。
  1. CMS收集器

  • 用户线程与GC并行执行,但用户线程CPU效率会降低
  • 超短STW,适合几十G内存
  • 会产生浮动垃圾
  • 会产生标记失败
  • 采用的是标记清除算法,产生大量内存碎片,当没空闲内存时会调用SerialOld进行Full GC(标记压缩算法),效率超低。
  • JDK14被放弃

初始标记:只标记GCRoot第一层关联的对象。所以此STW时间极短。

浮动垃圾:用户线程和GC线程并行执行时,GC刚刚标记完垃圾后,用户又新产生了垃圾对象,此垃圾对象无法被标记。

标记失败:用户线程和GC线程并行执行时,标记为垃圾的对象又有用户进程对它进行了引用,此垃圾对象不能被回收。

重新标记:对浮动垃圾、标记失败的对象重新标记。扫描的数据量小,所以此STW时间短。

设置GC收集器的组合

# 查看当前GC收集器组合
java -XX:+PrintCommandLineFlags -version
# 设置GC收集器组合
java -jar -XX:UseSerialGC xxx.jar

不分代收集器

  1. G1收集器

  • 1.9默认GC
  • 多线程并发标记,并发回收
  • 极短GC时间,单次STW默认最多200ms,有效时间内判断哪一块垃圾比较多,优先争对垃圾多的地方回收。
  • 将堆切分无数小的内存区域(物理分区),根据逻辑分代(Eden、Old、Survivor),不物理隔离新生代与老年代
  • 物理分区的大小只能为1/2/4/8/16/32mb
  • 支持上百G内存

新生代回收Minor GC(复制算法),触发条件:新生代(Eden + Survivor)大小占堆大小的60%。

新生代+老年代回收Mixed GC,触发条件:老年代大小超过堆大小的45%。

初始标记STW:标记GCRoot与第一层的对象。

并发标记:与用户线程并发完成GCRoot引用链,但会产生浮动垃圾与标记失败。

最终标记STW:确认浮动垃圾与标记失败的垃圾。

标记压缩算法回收STW:G1开辟一块最多5%堆空间的内存用于标记压缩的数据交换,回收时在STW200ms内回收10%垃圾最多的区域,回收后检查老年代是否低于堆大小的45%,未达标就再来一遍,最多8遍,8遍还未达标就调用Serial Old进行Full GC。

  1. ZGC低延迟收集器

  • 11出现的实验性GC,只在OracleJDK中使用
  • 小于10ms的STW时间
  • 也像G1使用分区,但不是固定容量,采取了动态容量,小-2MB,中-32MB,大-动态产生
  • 支持T级内存GC
  • 彻底不分代,利用“染色指针”实现对象引用。
  • 当GC“标记压缩”回收对象时,对象内存指针会发生变化,但不需要STW,可以和用户线程并发运行。
  1. Shenandoah低延迟收集器

  • 12出现的实验性GC,OpenJDK中使用
  • 小于10ms的STW时间
  • 彻底不分代,不同与ZGC动态块大小,Shenandoah使用固定块大小装对象
  • 支持T级内存GC
  • 利用读屏障与“转发指针”实现对象动态引用
  • 当GC“标记压缩”回收对象时,对象内存指针会发生变化,但不需要STW,可以和用户线程并发运行。
posted @ 2020-08-25 20:21  Baby丿太依赖  阅读(240)  评论(0)    收藏  举报