JVM内存管理——GC算法精解(五分钟教你终极算法—分代搜集算法)

引言

何为终极算法?

其实就是现在的JVM采用的算法,并非真正的终极。说不定若干年以后,还会有新的终极算法,而且几乎是一定会有,因为LZ相信高人们的能力。

那么分代搜集算法是怎么处理GC的呢?

对象分类

上一章已经说过,香港服务器租用,分代搜集算法是针对对象的不同特性,而使用适合的算法,这里面并没有实际上的新算法产生。与其说分代搜集算法是第四个算法,不如说它是对前三个算法的实际应用。

首先我们来探讨一下对象的不同特性,接下来LZ和各位来一起给这些对象选择GC算法。

内存中的对象按照生命周期的长短大致可以分为三种,以下命名均为LZ个人的命名。

1、夭折对象:朝生夕灭的对象,通俗点讲就是活不了多久就得死的对象。

例子:某一个方法的局域变量、循环内的临时变量等等。

2、老不死对象:这类对象一般活的比较久,岁数很大还不死,但归根结底,老不死对象也几乎早晚要死的,但也只是几乎而已。

例子:缓存对象、数据库连接对象、单例对象(单例模式)等等。

3、不灭对象:此类对象一般一旦出生就几乎不死了,它们几乎会一直永生不灭,记得,只是几乎不灭而已。

例子:String池中的对象(享元模式)、加载过的类信息等等。

对象对应的内存区域

还记得前面介绍内存管理时,JVM对内存的划分吗?

我们将上面三种对象对应到内存区域当中,就是夭折对象和老不死对象都在JAVA堆,而不灭对象在方法区。

之前的一章中我们就已经说过,对于JAVA堆,JVM规范要求必须实现GC,因而对于夭折对象和老不死对象来说,死几乎是必然的结局,但也只是几乎,还是难免会有一些对象会一直存活到应用结束。然而JVM规范对方法区的GC并不做要求,所以假设一个JVM实现没有对方法区实现GC,那么不灭对象就是真的不灭对象了。

由于不灭对象的生命周期过长,因此分代搜集算法就是针对的JAVA堆而设计的,也就是针对夭折对象和老不死对象。

JAVA堆的对象回收(夭折对象和老不死对象)

有了以上分析,我们来看看分代搜集算法如何处理JAVA堆的内存回收的,也就是夭折对象与老不死对象的回收。

夭折对象:这类对象朝生夕灭,存活时间短,还记得复制算法的使用要求吗?那就是对象存活率不能太高,因此夭折对象是最适合使用复制算法的。

小疑问:50%内存的浪费怎么办?

答疑:因为夭折对象一般存活率较低,因此可以不使用50%的内存作为空闲,一般的,使用两块10%的内存作为空闲和活动区间,而另外80%的内存,则是用来给新建对象分配内存的。一旦发生GC,将10%的活动区间与另外80%中存活的对象转移到10%的空闲区间,接下来,将之前90%的内存全部释放,以此类推。

为了让各位更加清楚的看出来这个GC流程,LZ给出下面图示。

图中标注了三个区域中在各个阶段,各自内存的情况。相信看着图,它的GC流程已经不难理解了。

不过有两点LZ需要提一下,第一点是使用这样的方式,我们只浪费了10%的内存,这个是可以接受的,因为我们换来了内存的整齐排列与GC速度。第二点是,这个策略的前提是,每次存活的对象占用的内存不能超过这10%的大小,一旦超过,多出的对象将无法复制。

为了解决上面的意外情况,也就是存活对象占用的内存太大时的情况,高手们将JAVA堆分成两部分来处理,上述三个区域则是第一部分,称为新生代或者年轻代。而余下的一部分,专门存放老不死对象的则称为年老代。

是不是很贴切的名字呢?下面我们看看老不死对象的处理方式。

老不死对象:这一类对象存活率非常高,因为它们大多是从新生代转过来的。就像人一样,活的年月久了,就变成老不死了。

通常情况下,以下两种情况发生的时候,对象会从新生代区域转到年老带区域。

1、在新生代里的每一个对象,都会有一个年龄,当这些对象的年龄到达一定程度时(年龄就是熬过的GC次数,每次GC如果对象存活下来,则年龄加1),则会被转到年老代,而这个转入年老代的年龄值,一般在JVM中是可以设置的。

2、在新生代存活对象占用的内存超过10%时,则多余的对象会放入年老代。这种时候,年老代就是新生代的“备用仓库”。

启程了,人的智慧才得以发挥。

JVM内存管理——GC算法精解(五分钟教你终极算法—分代搜集算法)

相关文章:

你感兴趣的文章:

标签云: