JVM 内存模型与内存分配方式

JVM 内存模型概述

JVM 内存模型,也叫 JVM 运行时数据区。下面是 JVM 内存模型的图解:
在这里插入图片描述

可以看出,JVM 内存模型主要分为以下的几块:

  • 堆:线程共享,用于存放对象实例,是由垃圾收集器管理的区域,不同的垃圾收集器对于堆会有不同的布局实现
  • 方法区:线程共享,用于存储已被加载的所有类的类型信息静态变量字段信息方法信息字面量符号引用等数据
  • 程序计数器:线程私有,是当前线程所执行的字节码的行号指示器,是唯一一块不会发生 OOM 的区域
  • Native 栈:线程私有,也叫本地方法栈,当 JVM 执行 Native 方法时,存储一些必要的数据
  • JVM 栈:线程私有,也叫虚拟机栈,每个方法被执行时,JVM 都会创建一个栈帧用于存放方法的局部变量表操作数栈方法返回地址动态链接等数据

基于分代收集理论设计的垃圾收集器所管理的堆结构

堆是由垃圾收集器管理的内存区域,不同的垃圾收集器对于堆会有不同的布局实现,这里主要介绍基于分代收集理论设计的垃圾收集器所管理的堆结构。
基于分代收集理论,堆内存结构如下所示:
在这里插入图片描述

可以看出,堆内存结构主要分为:

  • Yound Gen:新生代,约占整个堆大小的 1/3
    • Eden 区:Eden 区,大约占新生代的 8/10
    • Survivor 0 区:S0 区,大约占新生代的 1/10
    • Survivor 1 区:S1 区,大约占新生代的 1/10
  • Old Gen:老年代,约占整个堆大小的 2/3

方法区的演变

方法区,在 JDK8 之前,是用永久代来实现的,在 JDK8 之后,是用元空间来实现的

内存分配

划分内存的方法

JVM 划分内存的方法有两种:

  • 指针碰撞:Bump the Pointer,当堆中的内存比较整齐,即用过的内存和空闲内存有一条清晰的分界线(分界线处有个指针作为分界点指示器)时,可以使用这种方法
    • 指针碰撞方法在分配内存时,仅仅需要将分界点指示器向空闲内存方向挪动一段距离,距离取决于所需内存大小
  • 空闲列表:Free List,当队中的内存不整齐,即用过的内存和空闲内存相互交错、没有清晰的分界线时,就不能使用指针碰撞的方式来分配内存了。JVM 会维护一个列表,记录队中每块内存的使用情况,在分配的时候从可用的内存中分配一块足够大的内存出去,并把这块内存标记为已使用

划分内存时如何解决并发问题

在实际的 JVM 运行过程中,很可能会出现多个线程同时申请内存的情况,此时如果不对划分内存操作进行并发控制操作,大概率会出现并发安全问题(多个内存分配请求被分配到了同一块内存空闲)。
针对上述情况,JVM 采取的并发控制手段有:

  • CAS:在划分内存时,采用 CAS + 自旋操作来保证同一块内存不会同时被分配给多个分配请求
  • TLABThread Local Allocation Buffer,即本地线程分配缓冲。核心思想就是把内存分配的动作按照线程划分在不同的空间中进行,即每个线程在堆中预先分配一小块内存,这个线程后续的内存分配请求都会先在这块区域上进行,这一小块内存就称为本地线程分配缓冲。可以通过设置参数来开启或关闭此功能。

对象栈上分配

为了减少生命周期较短的对象在堆内分配的次数,JVM 会通过逃逸分析结合标量替换两个功能,把一些引用不会逃逸出方法之外的,可以使用若干个标量来替代本身的对象,将其内存分配在栈上进行。即:

  • 应用不会逃逸出方法之外,即这个对象是当前方法栈帧中创建出来的一个局部变量,其生命周期随着方法的结束而结束
  • 可以使用若干个标量来替代本身,即这个对象可以被分解成若干个不可再分的标量(如基本数据类型引用类型等)来替代

满足上述两个条件的对象,将可能会不被创建,而是直接由分解成若干个分配在栈上的标量替代,如:

class User {
    private int age;
    private String name;
}

public void methodA() {
    User user = new User();
    user.age = 10;
    user.name = "Test";
    //将 user 数据插入到数据库中
    //由于 user 对象的引用没有传递到外部,且 user 对象本身可以被一个 int 类型和一个 reference 类型的标量替换掉
    //那么在开启了逃逸分析+标量替换时,将会用栈上分配的两个标量来替换掉,而不会在堆中创建这个对象
    ......
}

注意,栈上分配必须依赖逃逸分析标量替换两个功能才能生效。

基于分代收集理论的垃圾收集器管理下的内存分配规则

基于分代收集理论的垃圾收集器,将堆根据存放对象的年龄不同划分成了不同的区域。在不同的区域内,会有不同的分配规则。

对象优先分配在 Eden 区

在大多数情况下,对象将会优先分配在 Eden 区。当 Eden 区没有足够的空间进行分配时,将会尝试进行一次 Young GC

大对象直接进入老年代

当对象的大小超过一定的阈值时,对象将会直接被分配到老年代

长期存活的对象将逐步进入老年代

Eden + S0 + S1 共同组成 Young Generation 的设计,称为 Appel 式垃圾收集机制。其主要的特点就是长期存活的对象将逐步进入老年代

  • 每个对象的对象头 Mark Word 中都会记录一个当前对象年龄的计数器
  • 在每一次经历 Young GC 后,如果对象依然存活,那么计数器将 +1
  • 在经历若干次(默认为 15 次)Young GC 还依然存活的对象,也即对象年龄大于晋升老年代年龄阈值(默认为 15)的对象,将会被移动到老年代中

对象动态年龄判断机制

在某一次 Young GC 时,如果此时 Survivor 空间中小于等于某年龄的所有对象大小的总和大于等于 Survivor 空间大小的一半,那么大于或等于该年龄的所有存活对象将直接进入到老年代中(尽管此时对象年龄可能尚未达到晋升老年代年龄阈值

老年代空间分配担保机制

Appel 式垃圾收集机制,核心思想使用的是复制算法。但是这个复制算法的备用空间(空闲的 Survivor 区)远小于主用空间(Eden 区 + 使用中的 Survivor 区),那么如果在某次 Young GC 时,存活下来的对象比备用空间大怎么办?
Appel 式垃圾收集机制给出的解决方案就是将这些所有存活下来的对象都移动到老年代中。这就是老年代空间分配担保机制的来源以及核心思想。
更细节的:

  • 在发生 Young GC 之前,JVM 必须检查当前老年代最大可用的连续空间是否大于新生代当前所有对象的总空间
    • 如果成立,那么可以安全地执行本次 Young GC
    • 如果不成立,那么 JVM 会查看当前是否开启了老年代空间分配担保机制(JDK 8 之后默认开启)
      • 如果未开启,那么将会执行一次 Full GC
      • 如果开启了,那么将会继续检查老年代最大可用的连续空间是否大于历次 Young GC 晋升到老年代的对象的平均总大小
        • 如果小于,那么将会执行一次 Full GC
        • 如果大于,那么将会有风险地执行本次 Young GC

注意,当有风险地执行 Young GC 时,如果本次 Young GC 存活下来的对象总大小大于老年代最大可用的连续空间(即出现了担保失败的情况),那么将会执行一次 Full GC

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
jvm内存模型和垃圾回收(GC)是Java程序中重要的概念。 Java虚拟机(JVM内存模型定义了Java程序中对象的分配和回收。它包括了堆、栈、方法区和程序计数器。Java内存模型(JMM)则用于规定多线程环境下的内存访问和操作顺序。 GC是垃圾收集器自动完成的过程,用于回收不再使用的对象,释放内存空间。JVM会根据系统环境和内存需求来决定何时进行GC。我们也可以通过调用System.gc()方法手动触发一次垃圾回收,但是具体的回收时机由JVM决定。需要注意的是,手动调用System.gc()方法并不推荐,因为它会消耗较多的资源。 垃圾收集器是实现垃圾回收的具体实现。它们采用不同的算法和策略来收集和回收垃圾对象。Java中有多种垃圾收集器可供选择,如Serial、Parallel、CMS(Concurrent Mark Sweep)和G1(Garbage-First)等。每个垃圾收集器都有不同的特点和适用场景,可以根据应用程序的需求进行选择和配置。 因此,JVM内存模型定义了对象的分配和回收方式,而垃圾回收器则是具体实现了垃圾回收的过程,根据不同的算法和策略来回收不再使用的对象。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [JVM内存模型与垃圾回收](https://blog.csdn.net/weixin_40980639/article/details/125934179)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值