前言
本章讲解JVM的堆分代策略
方法
1.概念
通过上一章的说明我们可以知道,几乎所有的Java对象实例都放在Java堆中。
这也就意味着,堆中对象分配和回收将是JVM的处理重点!为了更好的处理这些对象,JVM便将Java堆分成几块区域。由于根据对象存活的周期不同,所以称之为新生代、老年代和永久代(JDK1.8起被移除)
顾名思义,新生代存储的是年轻对象,老年代是稍微老一点的对象,永久代是老的不能再老的对象,哈哈哈
在JDK7以及其前期的JDK版本中,Java堆通常被分为三块区域年轻代(young
generation)、老年代(old generation)、永久代(permanent generation),其中,年轻代中又划分了Eden区、Survivor0、Survivor1区。显示如下图:
这里需要澄清一个问题:方法区和永久代区别和联系
在JDK1.7以前,方法区位于永久代,我们可以说永久代是方法区的一种实现方式,也就是说我们平时称之为永久代的东西实际上就是方法区,永久代和堆相互隔离,但是是堆的逻辑组成部分,永久代的大小在启动JVM时可以设置一个固定值,不可变
在JDK1.7,存储在永久代的部分数据就已经转移到Java 堆或者直接内存。但永久代仍存在于JDK 1.7中,并没有完全移除,譬如符号引用(Symbols)转移到了直接内存;字符串常量池(interned strings)转移到了Java 堆;类的静态变量(class statics variables )转移到了Java 堆
在JDK1.8,仍然保留方法区的概念,只不过实现方式不同。取消永久代,方法存放于元空间(Metaspace),元空间仍然与堆不相连,但存在于直接内存中,逻辑上可认为在堆中。
一个对象在Java堆中的生活一般为下面步骤:
- 1)所有新生成的对象首先都是放在年轻代的。年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。
- 2)新生代内存按照8:1:1的比例分为一个eden区和两个survivor(survivor0,survivor1)区。大部分对象在Eden区中生成。回收时先将eden区存活对象复制到一个survivor0区,然后清空eden区,当这个survivor0区也存放满了时,则将eden区和survivor0区存活对象复制到另一个survivor1区,然后清空eden和这个survivor0区,此时survivor0区是空的,然后将survivor0区和survivor1区交换,即保持survivor1区为空, 如此往复。
- 3)当survivor1区不足以存放 eden和survivor0的存活对象时,就将存活对象直接存放到老年代。若是老年代也满了就会触发一次Full GC,也就是新生代、老年代都进行回收。
- 4)新生代发生的GC也叫做Minor GC,MinorGC发生频率比较高(不一定等Eden区满了才触发)
在JDK1.8,由于对永久代的调优过程非常困难,永久代的大小很难确定,所以JDK1.8取消了永久代的概念,转而叫元空间,因为其使用堆外内存,也就是直接内存,元空间的最大可分配空间就是系统可用内存空间,可以避免永久代的内存溢出问题