背景介绍:
这是在面试中碰到的问题,现在做一下总结:
面试题:对象进入堆内存中,怎么从Eden区转入Survivor区,过程是怎么样的?新生代的内存为什么这么分配?
首先讲这问题大部分资料讲的都不是很明确,这里也只是对大致过程做一些整理。
一、对象的回收过程
Java堆内存的空间分配如图:
堆内存分配
新生代的内存分配说明:
Eden:主要是存放新增的对象,由于Eden区的对象都有一个特点,就是生命周期比较短,所以在每一次的GC都会将保存的对象清理回收。Eden区经历一次gc会清理掉大量的对象,如果此时存活的对象很多,而且大于Survivor区能公接收的范围,这些对象就会进入老年代。反之进入To Survivor区,minorGC完成之后,To 与 From完成角色互换,此时的To Survivor区变成下一次的From Survivor区。
From/To : 存放经历gc存活的对象,在这两块内存区域的对象也不是会一直存活,VM为每个对象设定了一个年龄,进入Survivor区对象的年龄变为1,此后每经历一次GC,年龄就会增加1,到达设定的年龄阈值(-XX:MaxTenuringThreshold,默认15),对象就会进入老年代,如果相同年龄的对象占用内存超过Survivor区内存的一半也会直接进入老年代。
到此回答完了面试问题。下面做一些问题的扩展,对象回收的判定,回收算法,垃圾回收器等
二、对象的回收判断:
1、引用计数法:给每个对象添加一个引用计数器,每当有一个对象引用它,计数器增加1,引用失效时计数器减1。任何时刻计数器为0就判定位没有对象引用。实际的VM回收过程中,并没有按照引用计数法判断对象是否存活。
2、可达性分析算法:Java是根据该算法判定对象的回收。基本思路:每个对象的创建都是以GC Root作为对象的起点,每发生一次对象引用,就会往引用链中增加一个对象,根对象结束了生命周期,引用的对象就没有GC Root,当发生GC时,这些没用根对象的引用就会被回收。
三、垃圾回收算法:
标记清除:
分为两个阶段,标记,清除,被标记的对象会在标记结束有被清除回收。但由于回收的对象是分散的,所以就会导致内存碎片化严重。
复制算法:
将内存分为容量大小相等的两块,当发生GC之后,将from的对象,复制到to区,避免了内存碎片,但也造成空间浪费。
标记整理:
标记完存活的对象之后,将存活的对象集中存放。
分代收集:
按照对象的特性区分年轻代,老年代,根据对象的特性选择对应的回收算法。年轻代,存活时间短选择复制算法,老年代选择标记清除或标记整理。
四、垃圾回收器:
新生代回收器
老年代回收器
五、JVM的内存分配
JVM运行时的内存分配:
线程私有:
程序计数器(指示当前指令地址)、Java栈(运行出入口方法信息)、本地方法区(native方法)
线程共享:堆,方法区(包含运行常量池,保存VM加载的类的信息,常量,静态变量)
运行时内存
在对JVM进行参数调优,对垃圾回收器的调整比较少,一般是对内存分配进行跳转,下图是内存分配的参数示意图。
JVM参数分配