Java内存与垃圾回收篇(对象内存与垃圾回收机制)下篇

Posted 狗哥狗弟齐头并进

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java内存与垃圾回收篇(对象内存与垃圾回收机制)下篇相关的知识,希望对你有一定的参考价值。

1 垃圾回收相关概念

1.1 System.gc()的理解

  • 通过System.gc()会触发Full GC同时对老年代和新生代进行回收。
  • 附带免责声明,我调用你了,但是不确定是要立即执行。
  • Java虚拟机一般都是自动进行垃圾回收,无需手动触发,当遇到特殊情况,比如我们正在编写一个性能基础,在程序之前GC。

1.2 内存溢出与内存泄漏

1.2.1 内存溢出

  • 内存溢出相对于内存泄漏来说,更容易被理解,但是同样的,内存溢出也是引发程序崩溃的罪魁祸首之谊。
  • OOM很少出现其实。
  • 大多数情况下,GC会进行哥红年龄段的垃圾回收,实在不行了就放大招,来一次独占是的Full GC操作,这时候会回收大量的内存,供应用程序继续使用。
  • javadoc中对OOM的解释是:没有空闲内存,并且垃圾收集器也无法提供更多的内存。

没有空闲内存,垃圾回收以后哦也没有更多的内存就会出现oom

  • Java虚拟机的堆内存设置不够,可以同过-XMS,-XMX来设置。
  • 代码中狂减了大量大对象,并且长时间不能被垃圾收集器收集。

1.2.2 内存泄漏

严格来说,只有对象不会再被程序用到了,但是GC又不能回收他们的情况,才叫内存泄漏。

但是当内存泄漏越来越多就会导致OOM。

尽管内存泄漏并不会立刻引起程序崩溃,但是一旦发生内存泄漏,恒旭中的可用内存就会被逐步蚕食,直至耗尽所有内存,最终出现OOM

加粗样式在这里插入图片描述
举例:

在单例模式中,如果一个单例对象关联着外部的对象,那么外部对象的生命周期的也很长,就叫做内存泄漏。

数据库链接,io连接和网络连接提供的资源 close未关闭可能会导致内存泄漏。

1.3 Stop The World

简称STW,指的是GC事件发生过程中,会产生应用程序的停顿。停顿产生时整个应用程序线程都会被暂停,没有任何相应,有点像卡死的感觉,这个停顿称为STW。

哪里会出现STW?
垃圾回收的时候在进行可达性分析的时候会导致Java执行线程停顿,一位内分析工作必须在一个能确保一致性的快照中进行。如果出现分析过程中镀对象引用关系还在不断变化,则分析结果的准确性无法保证。

被STW中断的应用程序线程会在完成GC之后恢复,频繁中断会让用户感觉像是网速不快造成带你应卡带一样。所以要减少STW的发生。

所有的垃圾回收器都存在STW事件,

1.4 安全点与安全区域

Safepoint机制保证了程序执行时,在不太长的时间内就会遇到可进入GC 的Safepoint。但是,程序“不执行”的时候呢?例如线程处于Sleep状态或Blocked状态,这时线程无法响应JVM的中断请求,“走”到安全点去挂起,JVM也不太可能等待线程被唤醒。对于这种情况,就需要安全区域来解决。
安全区域是指在一段代码片段中,对象的引用关系不会发生变化,在这个区域中的任何位置开始GC都是安全的。

2 垃圾回收器

2.1 GC分类与性能指标

2.1.1 垃圾回收概述

  • 垃圾收集器没有在规范中进行过多的规定,可以由不同的厂商,不同的JVM来实现。
  • 从不同角度分析垃圾收集器,可以将GC分为不容的类型。

2.2.2 垃圾回收器分类

  • 按线程数分:可以分为串行和并行
  • 按照工作模式分:可以分为并发式垃圾回收器和独占式垃圾回收器。
  • 按碎片处理方式分:可分为压缩式垃圾回收器和非压缩式垃圾回收器。
  • 按工作得额内存区间分:可分为年轻代垃圾回收期和老年代垃圾回收器。
    串行回收指的是在同一时间段内只允许一个CPU用于执行垃圾回收操作,此时工作线程被暂停,直至垃圾收集工作结束。
    串行回收默认是被应用在客户端Client模式下的JVM中。 windows中默认的模式是Server 所以都是并行回收。 并发回收器产生的STW事件要短与串行回收器。

2.2.3 评估GC的性能指标

吞吐量:运行用户代码的时间占总运行时间的比例 总运行时间=程序的运行时间+内存回收的时间。

暂停时间:执行垃圾收集时,程序的工作线程被暂停的时间。 凸显。
内存占用:JAVA 堆区所占的内存大小。

吞吐量优先:指的是在单位时间内,STW的时间最短。
在这里插入图片描述
STW优先:指的是尽可能让单词STW的时间最短,但是可能单位时间内的STW次数会增多。在这里插入图片描述

3 不同的垃圾回收器概述

以下是各种GC的时间戳
在这里插入图片描述
目前,CMS垃圾回收器已被删除,G1是现在的默认垃圾回收器,parnew是第一个GC,ZGC是目前最强的GC。

4 七款经典的垃圾收集器

串行:Serial ,Serial Old
并行:ParNew,Parallel Scavenge ,Parallel Old
并发:CMS,G1
垃圾收集器与垃圾分代之间的关系如下图:
在这里插入图片描述

4.1 Serial垃圾回收器:串行回收

  • Serial收集器是最基本、历史最悠久的垃圾收集器。
  • Serial收集器作为HotSpot中Client模式下的默认新生代垃圾收集器
  • Serial 收集器采用复制算法,STW机制,与串行回收方式执行内存回收。
  • 除了年轻代之外,Serial收集器还提供用于执行老年代垃圾收集的Serial Old收集器。Serial Old收集器同样也采用了串行回收和STW机制,但是内存回收算法采用的是整理压缩算法。

优势:简单而高效,一般使用在Client模式下。

4.2 ParNew 并行回收

  • ParNew是Serial收集器的多线程版本 ,new指的是处理心神后代。
  • ParNew收集器除了采用并行回收的防止执行内存回收外,两款垃圾回收收集器之间几乎没有任何区别。ParNew收集器在年轻代中同样也是采用复制算法,STW机制。
  • ParNew是很多JVM运行在Server模式下新生代的默认垃圾回收器
  • 除了Serial只有ParNew能与CMS搭配使用。

4.3 Parallel Scanvenge回收器:吞吐量优先

  • Parallel Scanvenge收集器同样采用了复制算法,STW,并行回收。
  • 和ParNew收集器不同的是,Parallel Scanvenge收集器的目标则是达到一个可控制的吞吐量,它是吞吐量优先的垃圾收集器
  • 高吞吐量可以高效率低利用CPU时间,尽快挖成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。因此,常见在服务器环境中使用。
  • Parallel Old 用于老年代垃圾回收。
  • Parallel Old收集器采用了标记——压缩算法,单勇杨也是基于并行回收和STW机制的。

是JAVA8 中的默认垃圾回收器

4.4 CMS垃圾回收器:低延时,低暂停时间

CMS是JDK1.5时代HotSpot推出的一款具有划时代意义的垃圾收集器,这款收集器是HotSpot虚拟机真正意义上的并发收集器,它第一次实现了让垃圾收集线程和用户线程同时进行。

CMS收集器的关注点是尽可能缩短垃圾收集时,用户线程的停顿时间。停顿时间越短就越适合于用户交互的程序、良好的响应速度能提升用户体验。
目前很大一部分的Java应用集中在互联网站或者B/S系统的服务端上,这类应用尤其重视服务的响应速度,希望系统停顿时间最短,以给用户带来脚好的体验。

CMS采用 标记清除算法。也会STW。

不幸的是,CMS作为老年代的收集器,却无法于JDK1.3中已经村现在的新生代收集器Parallel Scanvenge配合工作,新生代只能选择ParNew和 Serial收集器的一个。

后来G1的出现,替代了CMS。

4.4.1 CMS的工作原理

在这里插入图片描述
CMS整个过程分为4个主要阶段:
初始标记阶段: 在这个阶段中,程序中所有的工作线程都会出现STW,这个阶段的主要任务仅仅是标记出GC Roots能直接关小到的对象。一旦标记完成之后就会恢复之前被暂停的所有应用线程,由于直接关联的对象比较小,这里的速度非常块。
并发标记阶段: 从GC Roots 的直接关联对象开始遍历整个 对象图的过程,这个过程耗时较长但是不需要停顿用户线程,可以和垃圾收集线程一起并发运行。
重新标记阶段:为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录
并发清理阶段


  • 初始标记和重新标记需要STW,但是时间较短。
  • 目前所有的额垃圾收集器都做不到完全不需要STW,只是尽可能低缩短暂停时间。
  • 由于最耗费时间的并发标记和并发清理并不需要暂停工作,所以整体的回收是低停顿的。
  • CMS不是在堆内存满的时候才进行垃圾回收,当达到某阈值的时候,就开始CMS。如果CMS失败就会使用SerialOld对老年代进行清理。

4.4.2 CMS的优缺点

CMS的优点:
并发收集
低延迟,低暂停时间。
CMS的缺点:

  • 会产生内存碎片,分配大对象的情况下,没有连续的内存可以存,导致FullGC。
  • CMS收集器对CPU资源非常敏感。在并发阶段,它虽然不会导致用户停顿, 但是会因为占用了一部分线程而导致应用程序变慢,总吞吐量会降低。
  • CMS垃圾收集器无法处理浮动垃圾:可能出现“Concurrent Mode Failure”失败而导致另一次Full GC的产生。在并发标记阶段由于程序的工作线程和垃圾收集线程是同时运行或者交叉运行的,那么在并发标记阶段如果产生新的垃圾对象,CMS将无法对这些垃圾对象进行标记,最终会导致这些新产生的垃圾对象没有被及时回收,只能等到下一次GC的时候。

小结:
如果你想要最小化使用内存和并行开销:Serial GC
如果你想要最大化应用程序的吞吐量:Parallel Scanvenge GC
如果你想要最小化GC的STW: CMS GC

JDK9 之后废弃了CMS而是采用了G1

所以现在默认的垃圾回收器是G1。

4.5 G1回收器 区域化分布式

为什么还要发布G1 GC?
因为应用程序所应对的业务越来越庞大、复杂、用户越来越多,没有GC就不能保证应用程序正常进行,而造成进场卡顿的GC又跟不上实际的需求,所以不断地尝试对GC进行优化。G1垃圾回收器是在Java 7 Update4之后引入的一个新的垃圾回收器。与此同时,为了适应现在不断扩大的内存和不断增加的处理去数量,进一步降低暂停时间,同时兼顾良好的吞吐量。

为什么叫做Garbage First?

  • 因为G1是一个并行回收器,它把堆内存分割为很多不相关的区域(Region)(物理上是不连续的)。使用不同的Region来表示Eden、S0、S1、老年代等。
  • G1 GC 有计划地避免在整个Java堆中进行全区域的垃圾收集。G1跟踪各个Region里面垃圾堆的价值大小。(回收所收获的空间大小以及回收所需要时间的经验值),在后台为了一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region。
  • 又称为垃圾优先。
    G1回收器的特点
  • 并行与并发 :G1在回收期间,可以又多个GC线程同时工作,有效利用多核计算能力,此时用户线程STW。G1拥有与应用程序交替执行的能力,部分工作可以和应用程序交替执行。是并发性的。
  • 分代收集; 从分代上看,G1依然属于分带型垃圾回收器,它会区分为年轻代和老年代,年轻代依然会又Eden区和Surbibor区。但从对的结构上看,它不要求整个Eden区、老年代都是连续的。
  • 将堆空间分为若干个区域,这些区域中包含了逻辑上的年轻代和老年代。
  • 同时兼顾老年代和年轻代。

在这里插入图片描述

回收过程
当年轻代Eden区用尽的时候开始年轻代回收过程,G1的年轻代收集阶段是一个并行的独占式收集器。在年轻代回收期,G1 暂停所有应用程序线程,启动多线程执行年轻代回收。然后从年轻代区间移动存活对象到Suervivor区间或者老年区间,也有可能是两个区间都会涉及。

当堆内存使用达到一定值默认为45%时,老年代并发标记过程开始。

标记完成之后马上开始混合回收过程,对于混合回收期间,G1 GC从老年区间移动存活对象到空闲区间,这些空闲区间也就成为了老年代的一部分。和年轻代不同,老年代的G1回收器和其他GC不同,G1的老年代回收器不需要震哥哥老年代被回收。一次只需要回收一小部分老年代的Region就可以了。同时这个老年代Region是和年轻代一起被回收的。

5 七种经典垃圾回收器总结

在这里插入图片描述

Java’内存与垃圾回收整理完毕,后续会更新字节码文件与性能调优相关。



每天努力一点点,成长一点点。

以上是关于Java内存与垃圾回收篇(对象内存与垃圾回收机制)下篇的主要内容,如果未能解决你的问题,请参考以下文章

JVM垃圾回收机制与内存回收

垃圾回收与内存分配——总结篇

java 怎么对一个对象强制垃圾回收

垃圾回收与对象的引用

JS高程中的垃圾回收机制与常见内存泄露的解决方法

java垃圾回收