背景
一般遇到关于创建对象时内存不足的时候会爆出堆内存不足的情况,为了研究怎么合理避免这种情况的发生有必要了解下关于堆空间里年轻代和老年代的垃圾回收触发机制以及存放机制。那么下面就简单介绍下了。类似沟~
关于垃圾回收机制
说到垃圾回收,在内存不足的时候,JVM首先肯定不是直接把堆内存的eden区域直接清空或者其他内存不足的地方清空,这种操作没有道理。如果真的这样想那还有什么安全性可言。那么内心就这样了:
- 引用计数法
- 可达性算法
总而言之,会先标记好那些对象已经没有强引用之类的,然后根据情况进行不同的垃圾回收新生代一般会触发的是MinorGC基于复制算法的回收算法,例如下面的情况:
新生代的第一次MinorGC处理流程:
- 新生成对象的时候发现内存不够了
- 分析算法: 引用计数法和可达性算法进行分析寻找进行标记动作
- 迁移幸存区: 把存活的对象复制到幸存区,并且寿命加一。
- Minor GC后幸存区From和To互换
当再次内存满了之后,第二次Minor GC
- 先清空未引用的对象,包括幸存区的,如果有引用存入到幸存区,原来在幸存区仍然存活的对象再加一。
- 当寿命超过默认值的话就放到了老年代
注意:当新生代内存肯定不够的时候,会直接晋升存入老年代
老年代内存不足如何处理呢?
流程:
- 先进行Minor GC释放内存
- 如果还是不行触发FullGC,STW的时间更长
注意:关于STW是处理垃圾回收的一种机制,既是当触发GC操作时,先把用户线程挂起,优先处理垃圾回收线程,当垃圾回收线程结束,开始继续执行用户线程。由于复制算法会先复制一倍的原空间,并复制出存活的引用对象,虽然可以解决掉碎片空间的问题,但本身需要的双倍空间以及复制操作时间更长。在新生代,由于对象存活周期短,复制算法弊端比较少,如果加上老年代触发STW的时间就肯定会增长。
看你反应不大呢
那么不服就干,生死看淡
实际操作:
- -Xms20M 初始化堆内存
- -Xmx20M 最大堆内存
- -Xmn10M 设置年轻代大小
- -XX:+UseSerialGC 启用UseSerialGC
- -XX:+PrintGCDetails 每次GC时打印详细信息。
- -verbose:gc 在控制台输出GC情况
idea设置虚拟机参数:
package test;
import java.util.ArrayList;
import java.util.List;
public class TestGC {
private static final int _512KB=512*1024;
private static final int _1MB=1024*1024;
private static final int _6MB=6*1024*1024;
private static final int _7MB=7*1024*1024;
private static final int _8MB=8*1024*1024;
// 设置虚拟机参数:
//-Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc
public static void main(String[] arg0)
{
}
}
初始运行看下:这里新生代默认只消耗了30%(再简单的java程序也要加载一些内容所以就默认消耗了这么多),老年代为空from和to 的幸存区也是没有内容。可以看出新生代(new generation)的总大小是9M,8M给了eden,然后1M给了From,1M给了TO.老年代( tenured generation )给了10M
Heap
def new generation total 9216K, used 2497K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
eden space 8192K, 30% used [0x00000000fec00000, 0x00000000fee70428, 0x00000000ff400000)
from space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
to space 1024K, 0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
tenured generation total 10240K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
the space 10240K, 0% used [0x00000000ff600000, 0x00000000ff600000, 0x00000000ff600200, 0x0000000100000000)
Metaspace used 3448K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 382K, capacity 388K, committed 512K, reserved 1048576K
开始向年轻代中添加7MB数据:
好了,那么新生代已经证明了自己,但是上述的情况是两次回收之后发现自己无力然后才交给老年代的,下面就演示一种新生代自己玩不了直接就给老年代且不会触发垃圾回收的情况。
去掉之前的7M,换成新生代的最大值8M
那么就引出新的问题,如果老年代也扛不住呢? 实际操作修改8M为16M,