jvm-为什么要分代?

381 阅读4分钟

为什么要分代?

内存分新生代和老年代。

分代,本质其实是分区,就是把内存分成两个区,一个新生区,一个老年区。示意图:

为什么要分代?因为对象的生命周期不一样,98%的对象都是朝生夕死,少量对象的生命周期比较长。如果不是这个原因,就根本不会有分代的概念。

为什么生命周期不一样,就要分区?因为不同生命周期的对象,要使用不同的垃圾回收算法。

垃圾回收算法

刚才说了,不同生命周期的对象,使用不同的算法。

为什么要使用不同的算法?因为不同的算法适用于不同的应用场景(其实就是适用于不同的生命周期)。

先来看下有哪几种算法。

最基础的算法

标记-删除,是最基础的算法。其他的算法,都是基于这个改进。

步骤如下:

1、  先标记死对象

2、  然后删除死对象


示意图如下:注意不同的颜色,区分不同的对象

缺点:速度慢。为什么慢?因为一边标记,一边删除,所以慢。还有一个问题,就是有内存碎片,仔细看示意图,删除死对象之后,死对象的空间和活对象的空间是混在一起的,所以导致死对象的空间都是碎片空间。

怎么解决?复制算法。

复制

复制算法,为什么能解决速度慢?因为复制算法是一次性删除死对象。看示意图:

核心是,内存分成两半,一半使用(左边是使用),一半空闲(右边是空闲,即保留区域)。


步骤:

1、  先标记死对象
标记了死对象,同时也就知道哪些是活对象。

2、  后复制
怎么复制?把活对象一次性从左边复制到右边。
怎么删除?把左边一次性删除,既删除死对象,也删除活对象,相当于把左边清空了。

所以,速度快的原因,就是一次性操作,痛快!

除了解决了快的问题,还解决了内存碎片的问题。因为现在是分成两半,每次垃圾回收之后,剩下的都是连续的内存。


改进

缺点?缺点显而易见,一半浪费了。那怎么解决?优化比例,即优化使用和空闲的比例,之前是一半一半,现在不,现在是8:1:1,每次使用一个1,即那个1才空闲,其他都使用。

这个8:1:1,其实就是所谓的eden区:survive区1:survive区2。survive就是幸存区的的意思,每次回收的时候,即把幸存的活对象复制到其中一个幸存区。

不用管这些拗口的名字,其实本质就是只有10%空闲,其他都被利用。也就是说,优化比例之后,只浪费了10%。

问题来了,10%不会太小吗?不会,因为98%的新生对象都朝生夕死。所以,一般情况下,10%够用了。

到目前为止,我们可以看到,复制算法的8:1:1,其实就是用于新生代,因为新生代的对象大部分朝生夕死。


缺点

那缺点呢?刚才说了那么多,就是解决新生代问题,没解决老年代问题啊,那老年代怎么解决呢?标记-整理。

为什么复制的8:1:1不能用于老年代?因为老年代的对象生命周期长,10%的内存不够放啊,太小了。如果50%空闲,又太浪费空间了。

标记-整理

先看示意图


步骤:

1、  先标记死对象

2、  然后,再把活对象向一端移动
其实也是复制,即复制活对象到一端。
复制完了之后,再把端的边界的另外一边全部删除,包括死对象和活对象。

所以,这里其实也是复制,只不过这里没有一半的空闲区域被浪费。具体怎么实现的呢?就是通过不同的算法(即移动/复制活对象到一端去)。

参考

www.cnblogs.com/java-spring…