Java~学习简单了解GC中常见的垃圾回收器

文章目录

      • 了解垃圾回收器
      • Serial收集器
      • ParNew收集器
      • Parallel Scavenge收集器
      • Parallel Old收集器
      • CMS收集器
      • G1(重要)
      • 理解GC日志

了解垃圾回收器

  • 在垃圾回收器里主要做的俩件事就是: 标记(可达性分析) + 回收(标记清除 标记复制 标记整理 对象分代)
  • 如何评价垃圾回收器的好坏
  1. 回收的效率 就是扫一遍 可以扫到多少垃圾
  2. 回收的速度 扫一遍要多长时间
  3. 垃圾回收和应用线程之间是否可以请发执行
  4. 垃圾回收器是否支持多线程
  5. 回收的时间是否是可以预测的 承诺10分钟一定扫完 即使扫的不是很干净 但是最大程度上把一些重要垃圾清理掉
  • Java中Stop-The-World机制简称STW,是在执行垃圾收集算法时,Java应用程序的其他所有线程都被挂起(除了垃圾收集帮助器之外)。Java中一种全局暂停现象,全局停顿,所有Java代码停止,native代码可以执行,但不能与JVM交互;所以这些现象多半是由于GC引起。

Serial收集器

  • 历史最悠久的收集器
  • 给新生代使用 串行回收 使用复制算法 是单线程进行标记+回收

ParNew收集器

  • 给新生代使用 支持多线程进行GC(并行)
  • 回收效率比Serial高

Parallel Scavenge收集器

  • 给新生代使用 支持多线程进行GC(并行)
  • 设计初衷是为了尽量缩短STW的时间 但是这个是以牺牲吞吐量和新生代空间作为代价的 , 相当于承诺用户 在一定时间内就会完成一次GC虽然我删除的不是很干净 但是能够很大程度上清除大部分垃圾了

Parallel Old收集器

  • 老年代收集器 支持多线程执行(并行)
  • ParNew的老年版本
  • 这个收集器使用的是标记整理算法 效率更高 但是消耗的CPU资源更多

CMS收集器

  • 老年代收集器 支持多线程并行 采用标记-清除
  • 特点: 尽肯能缩短STW的时间

a: 初始标记 [STW]
只是把GCRoot直相关的对象先标记起来
b: 并发标记 [耗时 但不涉及STW]
执行整个遍历过程 从GCRoot开始 把所有访问到的对象都遍历一遍 不需要暂停用户线程 虽然耗时但是可以和用户线程并发 并发标记完成后得到的垃圾结果可能就和真实效果存在一定的误差
c: 重新标记 [STW]
修改刚才的误差 由于刚才误差的必经是少数 重新标记代价不是很大 虽然STW了 但是时间很小了
d: 并发清除 [耗时 但不涉及STW]
多线程的方式吧刚才的对象都释放掉 直接清除 当然也可以和应用线程并发执行

  • 优点: 能够让STW时间尽量缩短
  • 缺点: 内存碎片 应用线程是并发执行的 很消耗cpu的资源

G1(重要)

  • 比较新的回收器 Java11开始默认使用的回收器
  • 算是最优秀的垃圾回收器
  • 老少通吃 既可以回收新生代 也能回收老年代
    Java~学习简单了解GC中常见的垃圾回收器_第1张图片

每一个矩形区域称为一个"region"
E代表伊甸区 S代表生存区 T代表老年代 H表示放大对象的区域

G1代回收的时候不一定需要一次性把整个内存都会收完 而是以region为单位进行回收(回收的粒度更加精细)
针对新生代的region同样是使用复制算法进行回收
针对老年代的回收类似CMS 但还是有一定差距

a: 初始标记
和CMS很类似 只去找GCRoot之间关联的对象 时间较短 会涉及STW
b: 并发标记
可并发, 进行可达性分析 遍历所有对象不涉及STW 和CMS不同的就是 如果发现老年代region已经没有对象存活 就之间回收 不等最后一个环节回收了
c: 最终标记
修正b产生的误差
d: 筛选回收

理解GC日志

  • 每一种收集器的日志形式都是由它们自身的实现所决定的,换而言之,每个收集器的日志格式都可以不一样。但虚拟机设计者为了方便用户阅读,将各个收集器的日志都维持一定的共性
[GC [DefNew: 3324K->152K(3712K), 0.0025925 secs] 3324K->152K(11904K), 0.0031680 secs]    
[Full GC [Tenured: 0K->210K(10240K), 0.0149142 secs] 4603K->210K(19456K), [Perm : 2999K- >2999K(21248K)], 0.0150007 secs] [Times: user=0.01 sys=0.00, real=0.02 secs]
  • GC日志开头的“[GC”和“[Full GC”说明了这次垃圾收集的停顿类型,而不是用来区分新生代GC还是老年代GC的。如果有“Full”,说明这次GC是发生了Stop-The-World的。接下来的“[DefNew”、“[Tenured”、“[Perm”表示GC发生的区域,这里显示的区域名称与使用的GC收集器是密切相关的 .
  • 后面方括号内部的“3324K->152K(3712K)”含义是“GC前该内存区域已使用容量-> GC后该内存区域已使用容量 (该内存区域总容量)”。而在方括号之外的“3324K->152K(11904K)”表示“GC前Java堆已使用容量 -> GC后Java堆已使用容量(Java堆总容量)”。
  • 再往后,“0.0025925 secs”表示该内存区域GC所占用的时间,单位是秒。有的收集器会给出更具体的时间数据,如“[Times: user=0.01 sys=0.00, real=0.02 secs]”,这里面的user、sys和real与Linux的time命令所输出的时间含义一致,分别代表用户态消耗的CPU时间、内核态消耗的CPU事件和操作从开始到结束所经过的墙钟时间(WallClock Time)。CPU时间与墙钟时间的区别是,墙钟时间包括各种非运算的等待耗时,例如等待磁盘I/O、等待线程阻塞,而CPU时间不包括这些耗时,但当系统有多CPU或者多核的话,多线程操作会叠加这些CPU时间,所以读者看到user或sys时间超过real时间是完全正常的。

你可能感兴趣的:(java,jvm)