通过上一篇文章的学习,我们对JVM堆区有了初步的认识,接下来,我们继续展开讲解堆区。
对象生命周期和GC的关系。
我们已经知道了,堆区的新生区分成了三个部分:伊甸园区、幸存者0区、幸存者1区。
其中0区也叫from,1区也叫to区。但是这个from和to是固定不变的吗?NO,NO,NO不是的。当新生区执行一次GC(YGC)后,from和to区会进行交换。交换后,也就是在GC之后,谁空谁是to区。所以,from区和to区的位置和名分不是一成不变的。会发生变化的。
Java堆从GC的角度可以分为:新生区和老年代。这个是从物理分的。
新生代和老年代,占用堆区的比例是:1比2
在新生代中的伊甸园区和幸存者0区、幸存者1区的占比是:8比1比1
从逻辑来分的话,又有永久代(元空间)。如下图:
新生区GC (MinorGC) 的过程:复制->清空->互换
1:复制
eden、Survivor From区 复制到Survivor To区,年龄+1
首先,当Eden区满的时候会触发第一次GC,把还存活的对象拷贝到survivorFrom区,当Eden区再次触发GC的时候,会扫描Eden区和From区域,对这两个区域进行垃圾回收,经过这次垃圾回收后还存活的对象,则直接复制到To区(如果有对象的年龄已经达到了去往老年代的标准,则这些达到标准的对象将会被复制到老年代区),同时把这些对象的年龄+1
2:清空
清空Eden、survivorFrom区
然后,清空Eden和SuriviorFrom中的对象,也即是复制之后有交换。谁空谁做to区。
3:SurvivorTo和SurvivorFrom互换
最后,Survivor To和Survivor From互换,原来Survivor To成为下一次GC的Survivor From区,部分对象会在From和To区中复制来复制去的,如此交换15次(由JVM参赛MaxTenuringThreshold决定的,这个参赛默认值就是15),最终如果还是存活的对象,就把这些存活的对象存入到老年代区域。
这个过程可以有个生活中的case.士兵到将军的过程。士兵上战场,幸存下来,成班长,下次战争来临的时候,班长带着新的士兵继续冲杀,幸存下来,班长升级成排长,以此类推。经历大大小小战役十五次之后,升级成了将军,进入到年老代。
Java7的永久代:
JVM内存管理的模型图如下:
Why?
真相:经过研究,不同对象的生命周期是不同的(废话嘛,不同对象,操作链路长短不一样,当然生命周期不一样了),98%的对象是临时对象(临时对象,就是用完就被回收了)。
实际而言,方法区(Method Area)和堆区一样,是各个线程共享的内存区域,它用于存储虚拟机加载的:类信息+普通常量+静态常量+编译器编译后的代码等等。虽然JVM规范将方法区描述为堆的一个逻辑部分,但是它却还有一个别名:N0n-Heap(非堆),目的就是要和堆区分开的(这句话怎么理解?我们从小教育都知道,台湾是中国的一部分,但是台湾还有个别名:中华台北。其目的就是台湾想在国际上和中国区分开而已)。
对于HotSpot虚拟机,很多开发者习惯将方法区称之为”永久代(Parmanent Gen)”,但严格本质上说两者不同。或者说使用永久代来实现方法区而已,永久代是方法区(相当于是一个接口Interface)的一个实现。JDK1.7的版本中,已经将原本存放永久代的字符串常量池移走了。
三者之间在堆内存中的关系,我们在来看一遍,如下图:
永久代:java7之前
永久存储区是一个常驻内存区域,用于存放JDK自身所携带的Class,Interface的元数据,也就是说它存储的是运行环境必须的类信息,被装载进此区域的数据是不会被垃圾回收器回收掉的,关闭JVM才会释放此区域所占用的内存。
至此,我们已经把JVM堆区讲完了。那么接下来凯哥(凯哥Java:kaigejava)将接着讲解JVM调优命令了。欢迎大家一起继续学习。