在上一篇 《深入理解Java虚拟机之内存模型》中已经大致的介绍了一下Java堆,在这里让我们来更深入的来了解一下
简介
Java堆(Java Heap)是虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,此内存区域的唯一目的就是存放对象实例。
堆内存分布
根据上图可以看到 Java 堆 分为 新生代和老年代、元空间,但是元空间不在堆内存中,存在与非堆上。
新生代:
新生代是类的诞生、成长、消亡的一个区域、一个类重这里诞生之后被应用,然后被垃圾回收器回收,结束生命。新生代被划分为:伊甸区(Eden)和幸存区(Survivor),幸存区又分为:幸存者0(Survivor 0)和幸存者1(Survivor 1)Eden:Survivor 0:Survivor 1 = 8:1:1
新生代:老年代= 1:2
老年代:
堆内存中会把长期存活的对象或者大对象 放到老年代中。
代码演示:
public static void testAllocation() { byte[] allocation1, allocation2, allocation3, allocation4; allocation1 = new byte[2 * _1MB]; allocation2 = new byte[2 * _1MB]; allocation3 = new byte[2 * _1MB]; allocation4 = new byte[4 * _1MB]; // 出现一次Minor GC }
运行结果: [GC (Allocation Failure) [PSYoungGen: 4285K->480K(6144K)] 4285K->2812K(19968K), 0.0023123 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] Heap PSYoungGen total 6144K, used 4742K [0x00000000ff980000, 0x0000000100000000, 0x0000000100000000) eden space 5632K, 75% used [0x00000000ff980000,0x00000000ffda9930,0x00000000fff00000) from space 512K, 93% used [0x00000000fff00000,0x00000000fff78020,0x00000000fff80000) to space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000) ParOldGen total 13824K, used 6428K [0x00000000fec00000, 0x00000000ff980000, 0x00000000ff980000) object space 13824K, 46% used [0x00000000fec00000,0x00000000ff247188,0x00000000ff980000) metaspace used 3083K, capacity 4620K, committed 4864K, reserved 1056768K class space used 327K, capacity 392K, committed 512K, reserved 1048576K Disconnected from the target VM, address: '127.0.0.1:62185', transport: 'socket'
这里我是基于JDK1.8运行的。从上面的运行情况中来去印证上图所画。PSYoungGen 新生代、ParOldGen 老年代、metaspace 元空间,虽然这里打印了元空间的信息,但实际上元空间并不在堆内,从内存分配中就可以看出,这里我设置了堆内存空间最大为 20M(-Xmx20M) ,从上面打印日志中可以看出,新生代占用了6144 老年代占用了 13824 两则相加 约等于 20M。由此可以推断 元空间时间上并不包含在对空间内。
Minor GC 过程如下图:
上诉在新生代时讲过,当对象在新生代存活15次之后会进入老年代,这是常规流程,但是还有一些特殊的情况,比如大对象直接进入老年代《深入理解JVM虚拟机》里面的内容来讲解。
大对象直接进入老年代
大对象就是指需要大量连续内存空间的Java对象,最典型的大对象便是那种很长的字符串或者元素数量很庞大的数组。HotSpot虚拟机提供了-XX:PretenureSizeThreshold参数,指定大于该设置值的对象直接在老年代分配,这样做的目的就是避免在Eden区及两个Survivor区 之间来回复制,产生大量的内存复制 *** 作。
代码演示:
public static void testPretenureSizeThreshold() { byte[] allocation; allocation = new byte[6 * _1MB]; //直接分配在老年代中 }
Connected to the target VM, address: '127.0.0.1:61204', transport: 'socket' Heap PSYoungGen total 9216K, used 2370K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000) eden space 8192K, 28% used [0x00000000ff600000,0x00000000ff850b88,0x00000000ffe00000) from space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000) to space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000) ParOldGen total 10240K, used 6144K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000) object space 10240K, 60% used [0x00000000fec00000,0x00000000ff200010,0x00000000ff600000) metaspace used 3083K, capacity 4620K, committed 4864K, reserved 1056768K class space used 327K, capacity 392K, committed 512K, reserved 1048576K Disconnected from the target VM, address: '127.0.0.1:61204', transport: 'socket'
从上述代码中可以看出当我们设置的对象较大时Eden 放不下时回直接放入到老年代中,但是这里还有一种情况,假如说老年代的空间也不够,此时会进行一次FullGC ,如果在GC之后还是不够,这个时候就会报OOM异常(前提是没有设置内存自动扩充)。
如下图展示:
堆空间常用参数:
-Xms:用来设置堆空间的初始大小
-Xmx:用来设置堆空间最大的内存大小
-Xmn:设置新生代大小
-XX:+PrintGCDetails 设置打印GC详细信息
-XX:SurvivorRatio=8 设置新生代Eden区和幸存区的比例为8:1
如果错误,欢迎指正。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)