深入理解Java虚拟机之堆

深入理解Java虚拟机之堆,第1张

深入理解Java虚拟机之堆 深入理解Java虚拟机之堆

在上一篇 《深入理解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
如果错误,欢迎指正。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5707996.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存