聊聊JVM分代模型
本系列是为面试,更深的可以参看周志明那本国内最好的JVM书(要出第3版啦)
更好的阅读体验【长期有效】
本篇文章开始关注JVM内存划分上的一些细节,以及我们创建的那些对象在JVM中如何分配,如何流动。
作为开篇,先来看看JVM内存划分的一个分代模型:年轻代、老年代、永久代。
public class Test {
public static void main(String[] args) {
while (true) {
load();
Thread.sleep(1000);
}
}
private static void load() {
Manager manager = new Manager();
manager.load();
}
}
根据上篇的代码改版一下:main()
里,会周期性的执行load()
。执行load()
流程大家已经很清楚了。
而根据上篇文章垃圾回收,当load()
执行完毕,没有变量引用Manager
,此时就会被JVM垃圾回收线程回收。然后在while
这个循环里面,下一次执行load()
又会重复这个过程。实则我们看到这个Manager
实例对象的存活周期极为短暂。创建,执行,可能接着1ms以后又被销毁回收。
所以明显看出来,大部分我们的代码中创建的对象,其实都是存活周期很短的。这种对象占到绝大部分的比例。
不过来看看另外一种写法:
public class Test {
private static Manager manager = new Manager();
public static void main(String[] args) {
while (true) {
load();
Thread.sleep(1000);
}
}
private static void load() {
manager.load();
}
}
与上面唯一的区别就是把在load()
里面创建的对象移到类中成为静态变量。而Test
类是在方法区里的,也就是让方法区里的变量引用堆内存的实例对象。
接着在main()
还是周期性运行load()
,但是这个Manager
对象,一直被Test
这个类长期引用(一直存在方法区),所以一直驻留在堆内存中,是不会被垃圾回收掉。
分代模型
接下来就是本篇文章的核心:年轻代和老年代。
其实根据代码方式的不同,会采用不同的方式来创建和使用对象 ,而且对象的生存周期也是不一样的。所以JVM将Java堆内存划分为两个区域,一个是年轻代,一个是老年代。
其中年轻代,顾名思义,就是第一张代码示例那种,创建和使用完之后就要立马回收的对象;
老年代,就是第二种代码示例,创建之后需要一直长期存在的对象。
那紧接着就会有个疑问,为什么要分成年轻代和老年代?这和垃圾回收有关,对于年轻代的对象,他们的特点创建之后很快就会被回收,所以需要一种垃圾回收算法;对于老年代的对象,特点是需要长期存在,所以需要另一种垃圾回收算法。所以需要分成两个区域来对待。
而其实Manager
这个对象在最开始会在年轻代停留一下,但是最终会进入老年代,这就产生了疑问?不是说Manager
这个是长期存在的对象吗,为什么也会在年轻代?那又是怎么进入老年代的?什么时候进入的?
还有疑问就是,年轻代和老年代分别是怎么进行垃圾回收的?
别急,这些问题都会在后面的文章里面写到。本篇文章主要关注JVM内存划分,明白对象是如何在不同的内存区域分配就行了。
那什么是永久代呢?
很简单,其实就是我们之前说过的方法区。上面我们存放Manager
类的地方其实就是所谓的永久代,你可以认为永久代就是存放一些类信息。这个话题现在也不需要过多考虑,后续也会提到。