JVM: 垃圾收集器与内存分配策略

Posted hankuikui

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM: 垃圾收集器与内存分配策略相关的知识,希望对你有一定的参考价值。

GC需要完成的三个件:

哪些内存需要回收 ?什么时候回收? 如何回收?

java内存运行时区域中程序计数器,虚拟机栈,本地方法栈3个区域随线程而生,随线程而灭,栈中的栈帧随着方法的进入和退出 而有条不紊的执行着出栈和入栈操作。每一个桢帧中分配多少内存基本上是类结构确定下来就已知的,因此这个区域的内存分配和回收都具备确定性。而java堆和方法区则不一样,一个接口中的多个实现类的内存可能不一样,一个方法中的多个分支需要的内存也可能不一样,我们只在程序运行期间才能知道会创建哪些对象,这部分内存的分配和回收都是动态的,GC主要关注的就是这一部分。

 

哪些对象可以进行回收:

引用计数算法:引用时加1,失效时减1,实现简单,但如果有循环引用时就有问题,所以Java虚拟机没有选用这个方法。

可达性分析算法:主流的程序语言的主流实现中都采用可达性分析来断定对象是否是存活的。基本思路是通过一系列的称为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索走过的路径称为引用链当一个对象到GC Roots没有任何引用链相连时,则此对象不可用。如下图:

技术图片

 

 

在java中可作用GC Root的对象包括:

1. 虚拟机栈中引用对象

2. 方法区中的类静态属性引用的对象。

3. 方法区常量引用的对象

4. 本地方法栈中NATIVE方法引用的对象。

 

 

回收方法区:

在堆中,尤其在新生代中,常规应用进行一次垃圾收集一般可以回收70-95%

垃圾收集算法:

1. 标记-清除算法:分为标记和清除两个阶段,首先标记出所有需要回收的对象,然后回收已标记的对象。

主要有两个不足:一是效率问题,二是清除后会产生大量不连接的内存碎片。

2.复制算法:将可用内存容量划分为大小相待的两块,每次只使用其中的一块,当这一块内存用完了,将存活的对象复制到另外一块上面,然后再将使用过的内存一次清理掉,就不用考虑碎片等复杂情况。简单高效,牺牲了一半的内存。

现在虚拟机都采用这种收集算法来回收新生代。新生代的对象98%是朝生夕死的,所以并不需要按照1:1的比例来划分内存空间。而是将内存分为一块较大的eden空间和现场较小的survivor空间,每次使用eden和其中一块survivor。hotspot黑夜eden:survivor为8:1

3. 标记-整理算法:

复制整理算法在对象存活率较高时就要进行较多的复制操作,效率将会降低,更关键的是如果不想浪费空间,就要有额外的空间进行分配担保,以应对被使用的内存中所有对象都存活的极端情况,所以在老年代中一般不能直接选用这种算法。

所在在老年代使用标记-整理算法:与清除算法一样,担后续步骤不是直接对可回收对象进行清理,而是让所有存活对象都向一端移动,然后直接清理掉端边界以外的内存。

4.分代收集算法:

当前商业虚拟机的垃圾收集都采用分代收集:根据对象存活周期的不同将内存划分为几块:就是新生代和老年代:这样就可以根据种个年代的特点采用最适当的收集算法。

新生代:因为大批对象会死去,少量存活,采用复制算法:老年代:因为对象存活率高,采用标记清理或标记整理算法。

 

Hotspot算法实现:

枚举根节点GC Root:

可达性分析对执行时间的第三还体现在GC停顿上,因为这项工作必须在一个能确保一致性的快照中进行,不可以出现分析的过程中对象引用有关系还在不断变化的情况,该点不满足的话分析结果准确性就无法得到保证。这点导致GC进行时必须停顿所有线程

安全点:只在在特定的位置时,才停止线程,这个位置叫安全点safepoint.

安全区域:安全区域是指在一段代码片段之中,引用关系不会发生变化,如线程sleep, blocked。

 

垃圾收集器:

技术图片

 

 

 

Serial串行收集器

针对新生代;

      采用复制算法;单线程收集;进行垃圾收集时,必须暂停所有工作线程,直到完成。

应用场景

      依然是HotSpot在Client模式下默认的新生代收集器;

      也有优于其他收集器的地方:

    简单高效(与其他收集器的单线程相比);

        对于限定单个CPU的环境来说,Serial收集器没有线程交互(切换)开销,可以获得最高的单线程收集效率;

        在用户的桌面应用场景中,可用内存一般不大(几十M至一两百M),可以在较短时间内完成垃圾收集(几十MS至一百多MS),只要不频繁发生,这是可以接受的

ParNew收集器:

ParNew垃圾收集器是Serial收集器的多线程版本。

特点

      除了多线程外,其余的行为、特点和Serial收集器一样;

  在单个CPU环境中,不会比Serail收集器有更好的效果,因为存在线程交互开销

Parallel Scavenge收集器

有一些特点与ParNew收集器相似

      新生代收集器;

      采用复制算法;

      多线程收集;

主要特点是:它的关注点与其他收集器不同

      CMS等收集器的关注点是尽可能地缩短垃圾收集时用户线程的停顿时间;

      而Parallel Scavenge收集器的目标则是达一个可控制的吞吐量(Throughput);

Serial Old收集器

针对老年代;

      采用"标记-整理"算法(还有压缩,Mark-Sweep-Compact);

      单线程收集;

Parallel Old收集器

Parallel Old垃圾收集器是Parallel Scavenge收集器的老年代版本;

CMS收集器

并发标记清理(Concurrent Mark Sweep,CMS)收集器也称为并发低停顿收集器(Concurrent Low Pause Collector)或低延迟(low-latency)垃圾收集器;

特点

      针对老年代;

      基于"标记-清除"算法(不进行压缩操作,产生内存碎片);            

      以获取最短回收停顿时间为目标;

      并发收集、低停顿;

G1收集器

 

(Garbage-First)是JDK7-u4才推出商用的收集器;

(A)、并行与并发

      能充分利用多CPU、多核环境下的硬件优势;

      可以并行来缩短"Stop The World"停顿时间;

      也可以并发让垃圾收集与用户程序同时进行;

(B)、分代收集,收集范围包括新生代和老年代    

C)、结合多种垃圾收集算法,空间整合,不产生碎片

      从整体看,是基于标记-整理算法;

      从局部(两个Region间)看,是基于复制算法;

      这是一种类似火车算法的实现; 

      都不会产生内存碎片,有利于长时间运行;

(D)、可预测的停顿:低停顿的同时实现高吞吐量

      G1除了追求低停顿处,还能建立可预测的停顿时间模型;

      可以明确指定M毫秒时间片内,垃圾收集消耗的时间不超过N毫秒;

      能独立管理整个GC堆(新生代和老年代),而不需要与其他收集器搭配;

      能够采用不同方式处理不同时期的对象;

 

参考:https://www.cnblogs.com/cxxjohnson/p/8625713.html

 

内存分配与回收策略:

大多数情况下,对象在新生代eden区中分配,当eden区没有足够空间进行分配时,虚拟机进行一次Minor GC.

Minor GC:指新生代的垃圾收集动作

Major GC/Full GC:指老年代发生的GC

大对象直接进入老年代。大对象是华需要大量连续内存空间的java对象,如很长的字符串及数组。

长期存活的对象将进入老年代:如果一个对象经过多次(默认15次)minorGC还存活,将进入老年代。

以上是关于JVM: 垃圾收集器与内存分配策略的主要内容,如果未能解决你的问题,请参考以下文章

jvm系列jvm垃圾收集器与内存分配策略

JVM垃圾收集器与内存分配策略

JVM 垃圾收集器与内存分配策略

垃圾收集器与内存分配策略 (深入理解JVM二)

JVM-垃圾收集器与内存分配策略

jvm笔记2--垃圾收集器与内存分配策略