JVM详解:运行时数据区-堆

12/31/2023 Java

目录


参考:

  • 链接1:[https://blog.csdn.net/qq_48435252/article/details/123697193

# JVM详解:运行时数据区-堆

  • 一个jvm实例只存在一个堆内存,堆也是java内存管理的核心区域

  • Java堆区在JVM启动的时候即被创建,其空间大小也就确定了。是JVM管理的最大一块内存空间(堆内存的大小是可以调节的)

  • 《Java虚拟机规范》规定,堆可以处于物理上不连续的内存空间中,但在逻辑上它应该被视为连续的

  • 所有的线程共享java堆,在这里还可以划分线程私有的缓冲区(TLAB:Thread Local Allocation Buffer).(面试问题:堆空间一定是所有线程共享的么?不是,TLAB线程在堆中独有的)

  • 《Java虚拟机规范》中对java堆的描述是:所有的对象实例以及数组都应当在运行时分配在堆上。

  • 从实际使用的角度看,“几乎”所有的对象的实例都在这里分配内存 (‘几乎’是因为可能存储在栈上)

  • 数组或对象永远不会存储在栈上,因为栈帧中保存引用,这个引用指向对象或者数组在堆中的位置

  • 在方法结束后,堆中的对象不会马上被移除,仅仅在垃圾收集的时候才会被移除

  • 堆,是GC(Garbage Collection,垃圾收集器)执行垃圾回收的重点区域

// -Xms100m : 设置初始内存100MB
// -Xmx100m : 设置JVM最大内存100MB
// -XX:+PrintGCDetails : 打印GC回收信息
System.out.println("最大可用内存:"+Runtime.getRuntime().maxMemory()/1024/1024+"MB");

System.out.println("当前JVM空闲内存:"+Runtime.getRuntime().freeMemory()/1024/1024+"MB");

System.out.println("当前JVM占用的内存总数:"+Runtime.getRuntime().totalMemory()/1024/1024+"MB");

1
2
3
4
5
6
7
8
9

堆的细分内存结构

JDK7及以前

  • 逻辑:新生区(伊甸园区+幸存者1区+幸存者2区)+养老区+永久区(方法区在1.7的实现)

82b14388bb9647d5b40834ef9439c97c

JDK8及以后

  • 逻辑:新生区(伊甸园区+幸存者1区+幸存者2区)+养老区+元空间(直接内存)(方法区在1.8的实现)

57ccbcf869f643e18e0feef0b6508695

新生代和老年代

  • 存储在JVM中的java对象可以被划分为两类:

    • ​ 一类是生命周期较短的瞬时对象,这类对象的创建和消亡都非常迅速(存入新生代)
    • ​ 另外一类对象时生命周期非常长,在某些情况下还能与JVM的生命周期保持一致 (存入老年代)
  • Java堆区进一步细分可以分为新生代(YoungGen)和老年代(OldGen)

    • ​ 其中新生代可以分为伊甸园区(Eden)、新生区1(from)和新生区2(to)

c569904d4d264356bcce0f932e1af27d

图解对象分配过程

(1)new的对象先放伊甸园区。此区有大小限制。

(2)当伊甸园的空间填满时,程序又需要创建对象,JVM的垃圾回收器将对伊甸园区进行垃圾回收(Minor GC),将伊甸园区中的不再被其他对象所引用的对象进行销毁。将伊甸园中的剩余对象移动到幸存者0区。

(3)然后加载新的对象放到伊甸园区

a23b101c09944b76a98e21c43eeedd69

(4)如果再次触发垃圾回收,此时上次幸存下来的放到幸存者0区的,如果没有回收,就会放到幸存者1区。

55fe9efd351c444e8f2841f369032264

(5)如果再次经历垃圾回收,此时会重新放回幸存者0区,接着再去幸存者1区。

(6)啥时候能去养老区呢?可以设置次数。默认是15次。·可以设置参数:-XX:MaxTenuringThreshold=进行设置。

3205d59c2c854fea829a6f403603fa27

(7)在老年区,相对悠闲。当老年区内存不足时,再次触发GC:Major GC,进行养老区的内存清理。

(8)若老年区执行了Major GC之后发现依然无法进行对象的保存,就会产生OOM异常

总结:

针对幸存者s0,s1区:复制之后有交换,谁空谁是to 关于垃圾回收:频繁在新生区收集,很少在养老区收集,几乎不再永久区/元空间收

为对象分配内存:TLAB

8.1为什么要有TLAB?

堆区是线程共享区域,任何线程都可以访问到堆区中的共享数据 由于对象实例的创建在JVM中非常频繁,因此在并发环境下从堆区中划分内存空间是线程不安全的 为避免多个线程操作同一地址,需要使用加锁等机制,进而影响分配速度

8.2什么是TLAB

从内存模型而不是垃圾收集的角度,对Eden区域继续进行划分,JVM为每个线程分配了一个私有缓存区域,它包含在Eden空间内

9380391d29774ee3950b98c6f9f052fe

  • 多线程同时分配内存时,使用TLAB可以避免一系列的非线程安全问题,同时还能够提升内存分配的吞吐量,因此我们可以将这种内存分配方式称之为快速分配策略
  • 所有OpenJDK衍生出来的JVM都提供了TLAB的设计

TLAB对象分配过程

e64c5a368a764f2a8c7abd76c184bbed

上次更新时间: 9/25/2024, 9:17:45 AM