线程私有的区域——虚拟机栈、本地方法栈和程序计数器会随着线程结束而消亡,垃圾收集则主要关注方法区和堆两个共享区域。看一下下面这张图,方法区即被称为永久代,而堆中存放的是对象实例,为了回收的时候对不同的对象采用不同的方法,又将堆分为新生代和老年代,默认情况下新生代占堆的1/3,老年代占堆的2/3。
- 新生代(Young Generation):也有叫做年轻代的,这里使用《深入理解JAVA虚拟机》中的叫法,下同。
其实看名称就能看出一些,一般情况下,新创建的对象都会存放到新生代中(大对象除外)。
新生代中对象的特点是:很快就会被GC回收掉的或者不是特别大的对象。
为了方便垃圾收集,新生代又分出了一个Eden区,两个 Survivor区。
JVM 每次只会使用 Eden区 和其中的一块 Survivor 区域来为对象服务,另一块Survivor区域是空的,用于垃圾回收。
举个例子,第一次回收的时候,虚拟机会将 Eden区+Survivor(from)区域的存活对象复制到Survivor(to)上(存活对象小于Survivor(to)的空间),清空Survivor(from),虚拟机使用Eden区+Survivor(to);
第二次回收的时候,虚拟机再将Eden区+Survivor(to)存活的对象复制到Survivor(from)。
这三个区域默认情况下是按照8:1:1分配,也可以手动配置。
- 老年代(Old Generation):在新生代每进行一次垃圾收集后,就会给存活的对象“加1岁”,当年龄达到一定数量的时候就会进入老年代(默认是15,可以通过-XX:MaxTenuringThreshold来设置)。
另外,比较大的对象也会进入老年代,可以-XX:PretenureSizeThreshold进行设置。
如-XX:PretenureSizeThreshold3M,那么大于3M的对象就会直接就进入老年代。
因此,老年代中存放的都是一些生命周期较长的对象或者特别大的对象。
- 永久代(Permanent Generation ):即JVM的方法区。在这里存放着一些被虚拟机加载的类信息(别忘了还有动态生成的类)的静态文件,这就导致了这个区中的东西比老年代和新生代更不容易回收。
永久代大小通过-XX:MaxPermSize=进行设置。
- 元空间(Metaspace):从JDK 8开始,Java开始使用元空间取代永久代,元空间并不在虚拟机中,而是直接使用本地内存。
那么,默认情况下,元空间的大小仅受本地内存限制。当然,也可以对元空间的大小手动的配置。