JVM 的栈上分配、TLAB、PLAB 有啥区别?

JVM 的栈上分配、TLAB、PLAB 有啥区别?

栈上分配

稍微了解过 Java 虚拟机内存结构的同学都知道,在 Java 虚拟机中有两个关键的存储数据节点,那就是:堆与栈。

其中堆是所有线程共享的一块内存,几乎所有对象的分配都在这块内存中。而栈则是线程自己私有的,只存储线程自己的局部变量等信息。每个线程都有自己的栈,栈信息无法在线程之间共享。
一般情况下,每个线程如果有新建的对象,那么会跟 JVM 申请在堆上创建对应的对象,而线程的栈则存储了指向堆对象的指针。每当一个线程想创建一个对象时,首先会请求 JVM,之后 JVM 进行协调,创建完成之后再告诉线程,线程最后将引用放到栈中。

在对象创建的这个过程,堆和栈之间的关系就像是列车的中央调度室和火车的关系。每次线程需要分配内存空间,都需要去到堆去申请空间,会耗费不少时间和精力。

这个时候有人就发现,线程的有些对象其实别人也不会访问到,放在堆中貌似也没什么大作用。于是他提出:对于这些其他线程不会访问的对象,我们能不能让线程自己分配在它自己的栈空间上?这样不就可以节省不少交互时间了么!

这个方法确实不错,如果能实现应该可以提高对象创建的时间,提高虚拟机的运行效率。

但问题是:我怎么知道哪些对象可以分配在栈上,哪些不行呢?

其实聪明的软件工程师们早就解决了这个问题了,他们新造了一个名字:逃逸分析

那么什么是逃逸分析呢?

从字面意思上来讲,逃逸分析的目的是判断对象的作用域是否有可能逃出函数体。例如下面的代码就显示了一个逃逸的对象:

private static User user;
private static void hello(){
   u = new User();
   u.name = "java.top.select";
   u.website = "http://www.shuyi.me";
}

对象实例 user 是类的成员变量,可以被任何线程访问,因此它属于逃逸对象。但如果我们将代码稍微改动一下,该对象就可以线程非逃逸的了。

private static void hello(){
   User u = new User();
   u.name = "java.top.select";
   u.website = "http://www.shuyi.me";
}

可以看到 user 实例作用域只在 hello 函数中,不会被其他线程访问到,也不会访问。所以该 user 实例对象的作用域只在该函数中,因此它并未发生逃逸。对于这样的情况,虚拟机就有可能将其分配在栈上,而不在堆上。

看到这里,我相信许多人都应该明白了什么是栈上分配了。简单点说,就是将本来应该分配在堆中的对象,让其分配在线程私有的栈上。通过这种方式,减少垃圾回收的压力,提高虚拟机的运行效率。

TLAB

TLAB(Thread Local Allocation Buffer),即线程本地分配缓存。这是一块线程专用的内存分配区域,TLAB 占用的是 eden 区的空间。在 TLAB 启用的情况下(默认开启),JVM 会为每一个线程分配一块 TLAB 区域。

那么问什么需要 TLAB 呢?这是为了加速对象的分配!

由于对象一般分配在堆上,而堆事线程共用的,因此可能会有多个线程在堆上申请空间,而每一次的对象分配都必须线程同步,这样会降低内存分配的效率。

考虑到对象分配是非常常见的操作,于是 JVM 使用 TLAB 这样的线程转悠区域来避免多线程冲突,提高对象分配效率。

为了不至于导致 Eden 区被填充满,因此 TLAB 空间一般不会太大。因此大对象有可能无法在 TLAB 分配,只能直接分配到堆上。这其实是一种折中的设计哲学,因为大多数分配的对象都比较小,因此 TLAB 空间能满足大多数的需求。

PLAB

PLAB(Promotion Local Allocation Buffers),即晋升本地分配缓存。PLAB 的作用于 TLAB 类似,都是为了加速对象分配效率,避免多线程竞争而诞生的。 只不过 PLAB 是应用于对象晋升到 Survivor 区或老年代。与 TLAB 类似,每个线程都有独立的 PLAB 区。

在 Java 虚拟机 (JVM) 的语境中,PLAB 代表 Promotion Local Allocation Buffer(晋升本地分配缓冲区)。它是一个专门设计用于分配那些在下一个 GC(垃圾回收)周期中很可能会被晋升到老生代的对象的内存空间。

PLAB 的主要目的是优化 JVM 中的对象分配和垃圾回收性能。通过将那些具有高存活概率的对象分配到一个专用缓冲区中,JVM 可以减少与将这些对象晋升到老生代相关的开销。

PLAB 的工作机制

PLAB 通常作为 Young Generation 中的一个单独的内存池来实现。当创建新对象时,JVM 会决定将其分配到 Eden、Survivor 空间或 PLAB 中。被认为具有高晋升可能性的对象将被分配到 PLAB 中。

在 Young GC 周期期间,Eden 和 Survivor 空间之一中的对象会被复制到另一个 Survivor 空间中。然而,PLAB 中的对象不会被复制;相反,它们会被直接晋升到老生代。这种直接晋升消除了多次复制操作的需要,这可以提高 GC 效率。

PLAB 的优势

使用 PLAB 对 JVM 中的对象分配和垃圾回收具有以下几个优势:

降低 GC 开销 通过直接将对象从 PLAB 晋升到老生代,JVM 减少了 Young GC 周期期间复制对象相关的开销。

提高 GC 效率 PLAB 对长生存期对象的有效处理可以导致整体 GC 性能的提高。

减少内存碎片化 通过避免不必要地复制对象,PLAB 可以帮助减少 Young Generation 中的内存碎片化。

如下:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WeChat098

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值