Java-基础知识-GC

本文印象笔记原文版本(带重点标注)java

 

jvm垃圾回收机制算法

 

JVM的内存空间,从大的层面上来分析包含:新生代空间(Young)和老年代空间(Old)。新生代空间(Young)又被分为2个部分(Eden区域、Survivous区域)和3个板块(1个Eden区域和2个Survivous区域)安全

 

1)Eden(伊甸园)区域:用来存放使用new或者newInstance等方式建立的对象,默认这些对象都是存放在Eden区,除非这个对象太大,或者超出了设定的阈值-XX:PretenureSizeThresold,这样的对象会被直接分配到Old区域。多线程

2)2个Survivous(幸存)区域:通常称为S0,S1,理论上他们同样大。并发

 

 

第一次GC:app

在不断建立对象的过程当中,当Eden区域被占满,此时会开始作Young GC也叫Minor GCjvm

  • 1)第一次GC时,Survivous中S0区和S1区都为空,将其中一个做为To Survivous(用来存储Eden区域执行GC后不能被回收的对象)。好比:将S0做为To Survivous,则S1为From Survivous。性能

  • 2)将Eden区域通过GC不能被回收的对象存储到To Survivous(S0)区域(此时Eden区域的内存会在垃圾回收的过程当中所有释放),但若是To Survivous(S0)被占满了,Eden中剩下不能被回收对象只能存放到Old区域。spa

  • 3)将Eden区域空间清空,此时From Survivous区域(S1)也是空的。.net

  • 4)S0与S1互相切换标签,S0为From Survivous,S1为To Survivous。

 

第二次GC:

当第二次Eden区域被占满时,此时开始作GC

  • 1)将Eden和From Survivous(S0)中通过GC未被回收的对象迁移到To Survivous(S1),若是To Survious(S1)区放不下,将剩下的不能回收对象放入Old区域;

  • 2)将Eden区域空间和From Survivous(S0)区域空间清空;

  • 3)S0与S1互相切换标签,S0为To Survivous,S1为From Survivous。

 

第三次,第四次一次类推,始终保证S0和S1有一个空的,用来存储临时对象,用于交换空间的目的。反反复复屡次没有被淘汰的对象,将会被放入Old区域中,默认15次(由参数--XX:MaxTenuringThreshold=15 决定)。

 

 

问题3:对象进入Old区域有什么坏处?

  • Old区域通常称为老年代,老年代与新生代不同。新生代,咱们能够认为存活下来的对象不多,而老年代则相反,存活下来的对象不少,因此JVM的堆内存,才是咱们一般关注的主战场,由于这里面活着的对象很是多,因此发生一次FULL GC,来找出来全部存活的对象是很是耗时的,所以,咱们应该避免FULL GC的发生。

 

问题6:为何发生FULL GC会带来很大的危害?

  • 在发生FULL GC的时候,意味着JVM会安全的暂停全部正在执行的线程(Stop The World),来回收内存空间,在这个时间内,全部除了回收垃圾的线程外,其余有关JAVA的程序,代码都会静止,反映到系统上,就会出现系统响应大幅度变慢,卡机等状态。

 

对于不一样的世代可使用不一样的垃圾回收算法。好比对因为年轻代存放的对象可能是朝生夕死,所以能够采用标记-复制,而对于老年代则能够采用标记-整理/清除.

 

Minor GC

发生在新生代的GC为Minor GC 。在Minor GC时会将新生代中还存活着的对象复制进一个Survivor中,而后对Eden和另外一个Survivor进行清理。因此,日常可用的新生代大小为Eden的大小+一个Survivor的大小。

 

Major GC

在老年代中的GC则为Major GC.

 

Full GC

一般是和Major GC等价的,针对整个新生代,老年代,元空间metaspace(java8以上版本取代perm gen)的全局范围的GC.

 


常见的垃圾回收算法有哪些?简述其原理.

 

垃圾回收从理论上很是容易理解,具体的方法有如下几种:

  • 标记-清除

  • 标记-复制

  • 标记-整理

  • 分代回收

 

1. 标记——清除算法(Mark-Sweep)

标记——清除算法是第一种使用和比较完善的垃圾回收算法,算法分为两个过程:

  • 一、标记正全部须要回收的对象

  • 二、标记完成后清除被标记的对象。其标记的过程就是判断对象有效性,执行可达性分析的过程。

 

其执行流程图大致如何下所示:

 

一样,咱们也借助现实的场景进行描述。图书管里有好多人在看书,图书管理员想要收集起没有被看的书的时候,他决定让全部正在看书的人站起来,而后询问每一个人:那一本书是不看的。询问完全部的人以后,同窗们作下继续看书。这时候,图书管理员开始寻找全部作过标记的书,把它们收集起来。

 

缺点:

  • 一、每次进行垃圾回收时,会暂停当前用户程序的运行(相似让全部的同窗站起来)

  • 二、垃圾回收器须要间隔性的检查,而且标记和清除的过程相对较慢。

  • 三、在标记清除以后可能会产生大量内存碎片,致使一旦须要为大对象分配空间时,因为找不到足够大的内存空间,而不得以引起另一次GC过程。

 

 

2. 标记——复制算法(Mark——Copy)

  • 标记——复制存储算法经过采用双区域交替使用这种方式解决了标记——清除算法中效率低下的问题。它将可可用内存划分为两个等量的区域(使用区和空闲区),每次只使用一块。当正在使用的区域须要进行垃圾回收时,存活的对象将被复制到另一块区域。原先被使用的区域被重置,转为空闲区。其执行流程大致以下所示:

图书管理员为了更好的发现不看的书,将图书室一分为二(A区和B区),同一时刻只有一块区域容许看书。开始时只容许在A区看书。当管理员想要回收A区不被看的书的时候,大喊一嗓子“正在看书的同窗拿着你书到B区”。等全部人都到了B区后,图书管理员只要把A区的书收集起来,就完成了任务。下一次收集的时候,则是要求同窗带着本身看的书从B区转移到A区。如此循环往复便可。

 

缺点:

  • 一、原有可用空间被缩小为1/2,空间利用率下降了。

  • 二、过程当中也会暂停当前应用的运行。

 

 

3. 标记——整理算法(Mark——Compat)

  • 标记-复制算法在对象存活率较高的状况下就要进行较多的复制操做,更重要的是该算法浪费通常的内存空间,为了解决该问题,出现了标记——整理算法:其标记的过程和“标记-清除”算法同样,而整理的过程则是让全部存活的对象都向另一端移动,而后直接清理掉端边界之外的内存。其执行流程大致以下所示:

缺点:

  • 一、暂停当前应用的运行,非实时性的回收。

 

4. 分代收集算法

分代收集算法理论来源于统计学。IBM公司的专门研究发现,对象的生存周期整体可分为三种:新生代、老年代和永久代。所以能够根据各个年代的特色采用适当的垃圾回收算法。

好比新生代的对象在每次垃圾时都会有大量的对象死去,只有不多一部分存活,那就能够选择标记-复制算法。另外,在新生代中每次死亡对象约占98%,那么在标记-复制算法中就不须要按照1:1的比例来划份内存区域,而是将新生代细分为了一块较大的Eden和两块较小的Survivor区域,HotSpot中默认这两块区域的大小比例为8:2。

每次新生代可用区域为Eden加上其中一块Survivor区域,共90%的内存空间,这样就只有10%的内存空间处在被闲置状态。

在进行垃圾回收时,存活的对象被转移到本来处在“空闲的”Eden区域。

若是某次垃圾回收后,存活对象所占空间远大于这10%的内存空间时,也就是Survivor空间不够用时,须要额外的空间来担保,一般是将这些对象转移到老年代。

对于老年代来讲,大部分对象都处在存活状态。同时,若是一个大对象要在该区域进行分配,而内存空间又不足,那么在没有外部内存空间担保的状况下,就必须选用标记-清除或者标记-整理算法来进行垃圾回收了。

 

总而言之,分代收集只是根据对象生存周期的不一样来选择不一样的算法,其自己并无任何新思想。

 

 

5. 增量收集算法

以上所述的算法,都存在一个缺点:在进行垃圾回首时须要暂停当前应用的执行,也就是这时候的垃圾回收线程不能和应用线程同时运行。

若是咱们想作到“在不打断同窗们看书的状况下,图书管理员就能够收集没有被看的书”,这也是增量收集算法的目标,即在不中断应用线程的状态下垃圾回收线程也能进行垃圾回收。

可是这里须要面对的问题是:垃圾回收线程在标记阶段标记好了,还没来的及清除时,当前应用线程进行内存操做,以致于清除阶段没法正确开展,相似的状况是:图书管理员刚标记了《JAVA核心技术》这本书已经没有人看了,等标记完后,却发现这本书已经有人在看了。

 


哪几个常见的垃圾回收器

原文地址

 

1. Serial 收集器(新生代)

  • Serial 即串行的意思,也就是说它以串行的方式执行,它是单线程的收集器,只会使用一个线程进行垃圾收集工做,GC 线程工做时,其它全部线程都将中止工做。

  • 使用复制算法收集新生代垃圾。

  • 它的优势是简单高效,在单个 CPU 环境下,因为没有线程交互的开销,所以拥有最高的单线程收集效率,因此,它是 Client 场景下的默认新生代收集器。

  • 显式的使用该垃圾收集器做为新生代垃圾收集器的方式:-XX:+UseSerialGC

 

2. ParNew 收集器(新生代)

  • 就是 Serial 收集器的多线程版本,但要注意一点,ParNew 在单核环境下是不如 Serial 的,在多核的条件下才有优点。

  • 使用复制算法收集新生代垃圾。

  • Server 场景下默认的新生代收集器,除了性能缘由外,主要是由于除了 Serial 收集器,只有它能与 CMS 收集器配合使用。

  • 显式的使用该垃圾收集器做为新生代垃圾收集器的方式:-XX:+UseParNewGC

3. Parallel Scavenge 收集器(新生代)

  • 一样是多线程的收集器,其它收集器目标是尽量缩短垃圾收集时用户线程的停顿时间,而它的目标是提升吞吐量(吞吐量 = 运行用户程序的时间 / (运行用户程序的时间 + 垃圾收集的时间))。

  • 停顿时间越短就越适合须要与用户交互的程序,良好的响应速度能提高用户体验。而高吞吐量则能够高效率地利用 CPU 时间,尽快完成程序的运算任务,适合在后台运算而不须要太多交互的任务。

  • 使用复制算法收集新生代垃圾。

  • 显式的使用该垃圾收集器做为新生代垃圾收集器的方式:-XX:+UseParallelGC

 

Scavenge[ˈskævɪndʒ]

4. Serial Old 收集器(老年代)

  • Serial 收集器的老年代版本,Client 场景下默认的老年代垃圾收集器。

  • 使用标记-整理算法收集老年代垃圾。

  • 显式的使用该垃圾收集器做为老年代垃圾收集器的方式:-XX:+UseSerialOldGC

5. Parallel Old 收集器(老年代)

  • Parallel Scavenge 收集器的老年代版本。

  • 在注重吞吐量的场景下,能够采用 Parallel Scavenge + Parallel Old 的组合。

  • 使用标记-整理算法收集老年代垃圾。

  • 显式的使用该垃圾收集器做为老年代垃圾收集器的方式:-XX:+UseParallelOldGC

6. CMS 收集器(老年代)

  • CMS(Concurrent Mark Sweep),收集器几乎占据着 JVM 老年代收集器的半壁江山,它划时代的意义就在于垃圾回收线程几乎能作到与用户线程同时工做。

  • 使用标记-清除算法收集老年代垃圾。

 

工做流程主要有以下 4 个步骤:

  • 初始标记: 仅仅只是标记一下 GC Roots 能直接关联到的对象,速度很快,须要停顿(Stop-the-world)

  • 并发标记: 进行 GC Roots Tracing 的过程,它在整个回收过程当中耗时最长,不须要停顿

  • 从新标记: 为了修正并发标记期间因用户程序继续运做而致使标记产生变更的那一部分对象的标记记录,须要停顿(Stop-the-world)

  • 并发清除: 清理垃圾,不须要停顿

在整个过程当中耗时最长的并发标记和并发清除过程当中,收集器线程均可以与用户线程一块儿工做,不须要进行停顿。

 

但 CMS 收集器也有以下缺点:

  • 吞吐量低

  • 没法处理浮动垃圾

  • 标记 - 清除算法带来的内存空间碎片问题

 

显式的使用该垃圾收集器做为老年代垃圾收集器的方式:-XX:+UseConcMarkSweepGC

7. G1 收集器(新生代 + 老年代)

  • G1(Garbage-First),它是一款面向服务端应用的垃圾收集器,在多 CPU 和大内存的场景下有很好的性能。HotSpot 开发团队赋予它的使命是将来能够替换掉 CMS 收集器。

  • 使用复制 + 标记 - 整理算法收集新生代和老年代垃圾。

  • G1 把堆划分红多个大小相等的独立区域(Region),新生代和老年代再也不物理隔离。

  • 显式的使用该垃圾收集器做为老年代垃圾收集器的方式:-XX:+UseG1GC

 

如何判断一个对象是否应该被回收?

这就是所谓的对象存活性判断,经常使用的方法有两种:

  • 引用计数法

  • 对象可达性分析

 

 

因为引用计数法存在互相引用致使没法进行GC的问题,因此目前JVM虚拟机多使用对象可达性分析算法.