GC 垃圾收集器

GC 垃圾回收算法 和 垃圾收集器之间的关系是什么呢 ?

  1. GC算法 (复制 / 标清 / 标整 ) 是内存回收的一种思想 , 垃圾收集器就是 GC算法 的落地实现
  2. 我们知道没有最完美的 GC算法 , 因此目前为止也没有完美的垃圾收集器 , 只是针对不同的情况选择最适合的收集器来进行分代收集

主要垃圾收集器概述

1. 串行垃圾回收器 ( Serial )

它为单线程环境设计 , 且只使用一个线程进行垃圾回收 , 会暂停所有的用户线程 ( STW ) , 所以不适合服务器环境

2. 并行垃圾回收器 ( Parallel )

多个垃圾收集线程并行工作,此时用户线程是暂停的 ( STW ) , 适用于科学计算/大数据后台处理等弱交互场景 (弱交互指的是 : 非立刻响应的场景 )

3. 并发垃圾回收器 ( CMS )

用户线程和垃圾收集线程同时执行 (不一定是并行,可能交替执行),不需要停顿用户线程 , 互联网公司多用它,适用对响应时间有要求的场景

查看默认的垃圾收集器

JVM 参数:

java -XX:+PrintCommandLineFlags -version

默认的使用的并行垃圾收集器

默认的垃圾收集器有哪些 ?

java 的 gc 回收的类型主要有几种 :

UseSerialGC,UseParallelGC,UseConcMarkSweepGC,

UseParNewGC,UseParallelOldGC,UseG1GC

垃圾收集器


垃圾收集器 : 具体实现这些GC算法并实现内存回收。

 

新生区中使用的GC有 :  Serial (串行)    ParNew ( 并行)     Parallel Scavenge ( 吞吐量优先收集器 )

老年代中使用的GC有 :  Serial Old             CMS                        Parallel Old Serial Old

( 即吞吐量 = 运行用户代码时间 /(运行用户代码时间 + 垃圾收集时间 )

G1 横跨新生区以及老年代

他们之间的联系就是 : 比如 :你在新生区使用了串行 ( Serial ) 那么在老年代中你只能使用 Serial Old 

串行 (Serial ) / ( Serial Copying )

一个单线程的收集器 , 在进行垃圾收集时候 , 必须暂停其他所有的工作线程 ( STW ) , 直到它收集结束

 串行收集器组合 Serial + Serial Old

串行收集器是最古老,最稳定以及效率高的收集器 , 只使用一个线程去回收 , 但是在进行垃圾收集过程中可能会产生较长的停顿(Stop - The - World 状态)。虽然在收集垃圾过程中需要暂停所有其他的工作线程,但是它简单高效,对于单个CPU 环境来说,没有线程交互的情况下 , 它的单线程垃圾收集效率是最高的 , 因此 Serial 垃圾收集器依然是 java 虚拟机运行在 Client 模式下默认的新生代垃圾收集器。

串行收集器使用在客户端模式下的 , 而我们使用的 Java 都是作为 Server 端的 , 如果强行使用串行收集 直接会把程序拖死

JVM 参数是:-XX:+UseSerialGC

-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseSerialGC

并行 ( Parallel ) / ( Parallel Scavenge ) / ParNew

Parallel Scavenge 收集器类似 ParNew 也是一个新生代垃圾收集器,使用复制算法,也是一个并行的多线程的垃圾收集器,俗称吞吐量优先收集器

就是说 : 串行收集器在新生代和老年代的并行化,Parallel 就是 Serial 收集器的升级并行版多个收集线程

JVM 参数: -XX:+UseParallelGC 或 -XX:+UseParallelOldGC ( 可互相激活 ) 

-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParallelGC

-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParallelOldGC

CMS 并发标记清除

1. 初始标记 ( CMS initial mark ) 会发生 STW

只是标记一下 GC Roots 能直接关联的对象 ( 可以认为此阶段仅仅遍历了一层 , 而在并发标记阶段才进行整个对象的遍历 ) , 因为直接关联对象比较小 , 所以此阶段的速度很快 , 但是仍然需要暂停所有的工作线程

2. 并发标记 ( CMS concurrent mark ) GC 线程和用户线程一起

从 GC Roots 的直接关联对象开始遍历整个对象的过程 , 此阶段耗时较长 , 但是不需要暂停用户线程 , 可以和垃圾线程一起并发运行

3. 重新标记 ( CMS remark ) 会发生 STW

为了修正在并发标记期间 , 因为用户程序继续运行导致标记产生变动的那一部分对象的标记记录,仍然需要暂停所有的工作线程。修正后再做清除 , 此阶段比初始标记阶段耗时要长 , 但是比并发标记阶段耗时要短

4. 并发清除 ( CMS concurrent sweep ) GC 线程和用户线程一起

清除 GC Roots 不可达对象,和用户线程一起工作,不需要暂停工作线程。基于标记结果,直接清理对象由于耗时最长的并发标记和并发清除过程中,垃圾收集线程可以和用户线程一起并发工作,所以总体上来看CMS 收集器的内存回收和用户线程是一起并发地执行。

无法避免 STW 但是可以减少 STW 的时间 ( 并发收集低停顿 )

总结

  1. CMS 收集器 (Concurrent Mark Sweep:并发标记清除)是一种以获取最短回收停顿时间为目标的收集器。
  2. 适合应用在互联网站或者 B / S 系统的服务器上,这类应用尤其重视服务器的响应速度 ,希望系统停顿时间最短。( 打个比方 : 当你使用程序的时候 , 经常会出现长时间的程序停顿 , 心态肯定是崩的 )
  3. CMS非常适合堆内存大、CPU核数多的服务器端应用 ,也是G1出现之前大型应用的首选收集器。
  4. Concurrent Mark Sweep 并发标记清除,并发收集低停顿,并发指的是与用户线程一起执行

JVM参数  : -XX:+UseConcMarkSweepGC 

-Xms10m -Xmx10m -XX:+PrintGCDetails  -XX:+PrintCommandLineFlags -XX:+UseConcMarkSweepGC

CMS 回收器的优缺点

优点 : 

并发收集的低停顿

缺点 :

1. 因为使用了标记清除 , 所以会产生内存碎片 , 在并发清除后 , 用户线程的可用空间不足 , 又无法分配大对象的情况时 , 触发 Full GC , 拉低程序性能

2. CMS 的并发执行对CPU资源压力大 , 虽然不会导致太长的用户停顿 , 但是会因为占用了一部分的线程导致应用程序变慢 ( 上面的图我们可以看出它占用了用户的线程 ) 总吞吐量降低

问题来了 ? ? ?

并发标记清除 Mark  Sweep 会造成内存碎片 , 那么为什么不把算法换成Mark Compact 呢 ?

因为并发清除的时候 , 用Compact 整理内存的话 , 会将对象重新做了一个调整 , 其中的对象的地址全部在内存中发生了改变 , 我们不要忘了 并发标记清除阶段的用户线程是一直在执行的 , 对象的地址都发生了改变 , 就会导致原本正常运行的用户线程出错

Mark Compact 更适合 " STW " 这种静态的场景 

G1 

Java9 以后默认的垃圾收集器 , G1(Garbage-First)收集器,是一款面向服务端应用的收集器

为什么要出现 G1? CMS 不好吗 ?

  1. CMS 垃圾收集器虽然减少了暂停应用程序的运行时间 , 但是它还是存在着内存碎片问题。于是,为了解决内存碎片问题,同时又保留 CMS 垃圾收集器暂停时间短的优点,JAVA7 预发布了一个新的垃圾收集器 G1 垃圾收集器。
  2. 在 jdk9 中将 G1 变成默认的垃圾收集器以替代 CMS。它是一款面向服务端应用的收集器,主要应用在多CPU和大内存服务器环境下,极大的减少垃圾收集的停顿时间,全面提升服务器的性能,逐步替换 java8 以前的 CMS 收集器。

G1 收集器的设计目标是取代 CMS 收集器,它同 CMS 相比,在以下方面表现的更出色:

  1. G1 是一个有整理内存过程的垃圾收集器,不会产生很多内存碎片。
  2. G1 的 Stop The World ( STW ) 更加可控,G1收集器是把整个堆(新生代、老生代)划分成多个固定大小的区域 , G1 在停顿时间上添加了预测机制,用户可以精确控制停顿时间 , 每次根据允许停顿的时间去收集垃圾最多的区域 。

主要改变是 Eden,Survivor 和 Tenured 等内存区域不再是连续的了,而是变成了一个个大小一样的 region ,每个 region 从1M 到 32M 不等。一个 region 有可能属于 Eden,Survivor 或者 Tenured内存区域。

 

可以发现:  G1 类似于 CMS 

G1 的四个阶段

初始标记 : 仅仅只是标记一下GC Roots能直接关联到的对象 , 这个阶段需要停顿线程,但耗时很短

并发标记 : 进行 GC Roots Tracing 的过程 , 对堆中对象进行可达性分析,扫描整个堆中的对象 , 找出要回收的对象,这阶段耗时较长,但可与用户程序并发执行。

最终标记 : 修正并发标记期间 , 因程序运行导致标记发生变化的那一部分对象 , 用户线程有一个短暂的暂停

筛选回收 : 根据时间来进行价值最大化的回收 , 必须暂停用户线程,

G1 的优势

  1. G1 充分利用多 CPU , 多核环境硬件优势 , 尽量缩短 STW
  2. G1整体上采用标记 / 整理算法 , 布局是通过复制算法 , 因此不会产生内存碎片
  3. 宏观上看G1之中不再区分年轻代和老年代 , 把内存划分为多个独立的子区域 ( Region ) , 像一个棋盘一样
  4. G1 收集器里面将整个的内存区都混合在一起了,但其本身依然在小范围内要进行年轻代和老年代的区分,保留了新生代和老年代,但它们不再是物理隔离的,而是一部分 Region 的集合且不需要 Region 是连续的,也就是说依然会采用不同的 GC 方式来处理不同的区域
  5. G1 虽然也是分代收集器,但整个内存分区不存在物理上的年轻代与老年代的区别,也不需要完全独立的 survivor 做复制准备。
  6. G1 只有逻辑上的分代概念,或者说每个分区都可能随 G1 的运行在不同代之间前后切换;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值