详解Java中的GC机制

 

GC(Garbage Collections),就是垃圾回收机制。在C或C++中,开发人员需要手动的对内存进行分配和回收,需要时刻关注以防止出现内存泄漏。但在Java中,有了GC机制,程序员就不再需要对对象进行内存的回收,也不会轻易的出现内存的泄漏现象了,那么我们来研究下JVM中是如何实现GC机制的。

什么样的对象可以被认为垃圾?

首先,JVM需要判断出什么样的对象可以被判断为垃圾,这样才能对垃圾进行回收。JVM利用提供了两种算法来实现。

1、引用计数法

当对象被生成时,同时伴随有一个计数器。当这个对象被引用是,计数器就+1。当引用失效时,计数器则-1。当计数器等于0时,则认为该对象已经“死亡”。但这种算法存在一个致命的问题,就是无法解决“循环引用”的现象,即对象之间相互引用。所以这种算法没有很少被使用。

2、可达性分析算法

JVM会将所有的引用关系转化为图结构,以GC Roots对象为起始点,并向下搜索标记,标记到的即为存活,没标记到的即为死亡。这也是目前主流的垃圾判断算法。

那么如何选定GC Roots对象,一般JVM会在以下几种对象中选取:

  1. 虚拟机栈中引用的对象; 
  2. 方法区中类静态属性引用的对象; 
  3. 方法区中的常量引用的对象; 
  4. 本地方法栈中JNI(即一般说的Native方法)的引用的对象;

 

 垃圾回收算法

当可以判断出内存中的垃圾时,我们可以回收垃圾了,JVM中一般有以下几种回收算法:

1、标记—清除算法。

通过遍历内存,标记出所有需要清理的垃圾,然后等标记完成后统一回收。

标记清除算法存在的问题是,当回收垃圾后,内存会出现大量不连续的内存段,如果此时要存储占有较大内存的对象,则无法满足,此时JVM会再次触发垃圾回收机制。 

2、复制算法。

首先将内存平分为AB两部分,每次只是用其中一部分,当A部分内存存满后,将A内存中所有存活的对象移到B内存中(有序),然后清空A全部内存。

复制算法孙然解决了内存的不连续性,但却需要牺牲一半的内存容量,代价较高。 

3、标记—整理算法。

此算法是标记清除算法的改进,在标记过后,将存活内存移到一端按序存放,然后清除最后存活内存以后的所有内存。

4、分代收集算法

这个算法是目前大部分JVM所采用的算法,它将内存按生命周期分为不同的区域,然后对不同区域对使用不同的收集算法。接下来将详细描述区域的划分以及不同区域的回收机制。

 

分代收集算法

在分代收集算法中,JVM会将内存按对象生命周期划分为不同的区域。在JDK1.8以前,内存堆分为新生代、老年代和永久代。在JDK1.8及以后,内存堆只被划分为了新生代和老年代(划分比例1:2)。

新生代:大部分新创建的对象都会存放在新生代中,新生代是以生命周期较短划分出来的,所以里面的大多数对象都是朝夕生死的,只有少部分生命周期较长的会被转移到老年代中。新生代中分为三个部分,一个eden区和两个survive区(form和to),其分配比例为8:1:1,其大致结构如下:

那么对象是如何存储的呢,首先每一个新对象会被存放在eden区,由于新生代生命周期较短,所以80%的对象已经死亡。此时JVM将还存活的对象记录年龄为0(出生了)。经过一段时间,当eden区被填满时,会触发Minor GC机制处理垃圾。通过上面讲到的复制算法将eden区存活的对象存放到from区,并将年龄增加一岁(活过了一次Minor GC)。其流程如下图:

 当eden区又被填满时,会再次触发Minor GC机制。此时eden和from区都会有大量的对象死亡,同样利用复制算法,将eden和from中存活的对象复制到to区,并将其年龄+1。此时to区变为from区,from区变为to区。其流程如下:

如此反复循环,其关键是永远保持to区为空,用于存放 Minor GC后存活下来的对象。

经过不断的Minor GC后,存活下来的对象年龄不断增长,直到年龄到达15岁(JVM默认)。JVM就会认为你这个对象生命周期过长,不适合在生命周期较短的新生代中生活。所以将这些对象转移到 了老年代。

老年代:和新生代相反,这里存放着生命周期较长的对象。但老年代中也同样会出现死亡的现象,只是没有新生代这么频繁。当老年代中被填满时,就会触发Full GC来回收整的堆内存上的垃圾(新生代+老年代)。此时,老年代中不像新生代中使用复制算法(因为老年代存活率高,复制算法效率低),而是使用标记清除或标记整理算法来实现垃圾的回收。

在实际情况中会出现一些特殊情况。

1、当新建的对象太大超过JVM默认的一个值时,会讲这个对象直接存放在老年代中。

2、当eden中存活下来的对象,想要放到survive区却没有足够的空间时,则会直接存放到老年代中。

3、如果survive中存活对象进入老年代,但老年代没有足够的内存空间时,就会触发一次Full GC。

以上就是GC机制中的所有理论原理,在实际中JVM会通过垃圾收集器来完成垃圾收集的工作。

 

垃圾收集器

其中新生代中主流的垃圾收集器有Serial、ParNew和Parallel Scavenge收集器。老年代中主流的收集器有CMS、Serial Old和Parallel Old。G1收集器可以同时使用在老年代和新生代。图中的连线表示收集器之间可以组合配合使用。这些收集器有各自的优缺点,在不同JVM中会使用不同的组合来完成GC。 

好了,JVM中的GC机制的大致流程就是这样。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值