jvm内存管理分为内存回收跟内存分配两个大部分,内存分配时本篇的主题。
分配规则
- 对象优先在eden区域申请空间
- 大对象直接进入老年代
- 长期存活的对象进入老年代
- 内存分配担保(老年代对survivor区域的担保)
前三个很好理解,我们分析下第四个,堆分为老年代跟新生代,新生代有一块eden跟两块survivor,eden跟survivor占新生代空间大小是可以通过jvm运行参数配置的,一般8:1;
假设我们配置了新生代跟老年代分别10M,eden跟survivor8:1;我们依次创建2M,2M,2M,4M的对象,那么情况如下:
- 前三个对象进入eden区域
- 第四个对象发现空间不够了(eden区域剩下2M,survivorA 1M),触发GC
- GC时前三个对象都是不可回收对象,需要放入survivorB,但是survivorB空间不够,此时就会触发分配担保,这样这三个对象就直接放入了老年代。此时老年代使用了6M,eden跟两个survivor区域都是空,
- 将4M的对象放入eden区域
所有的对象都会在堆上申请空间吗?
大部分是的,但也会有在栈上申请空间的对象。
这里涉及内存逃逸跟栈内存分配。
我们先回顾下虚拟机栈的内容,它可以用来描述方法执行过程,每个方法对应一个栈帧,栈帧中有局部变量表等。
如果在一个方法中有一个变量的作用域只是该方法,那么就会在栈上给它申请空间(包含在方法栈帧中),此时该变量对方法来说就是没有逃逸的,也就是被方法栈帧“捉”住了。当方法执行完毕之后随着栈帧的释放会释放该变量的内存。