✅JVM如何保证给对象分配内存过程的线程安全?

典型回答

首先,我们先来梳理下,JVM是如何给对象分配内存的:

  1. 如果JIT的逃逸分析后该对象没有逃逸,那么可能优化到栈上分配。
  2. 否则对象主要分配到新生代上,如果启动了TLAB,则分配到TLAB中。
  3. 如果被判断为大对象,则直接分配到直接进入老年代,譬如很长的字符串和数组,避免为大对象分配内存时由于分配担保机制带来的复制而降低效率 。可以设置-XX:PretenureSizeThreshold,令大于该尺寸的对象直接进入老年代

简而言之,如下图所示:

1671866089292-002857d1-4248-46bc-8710-2f17e0c5877e.png

所以,我们到这里就很清楚了,当给对象分配内存的时候,有可能在栈上分配,这自然不存在线程安全问题。除此之外,如果在堆上分配,则可能会启动TLAB机制,使得堆内存给线程单独划分空间,避免了线程安全的问题。

同时,当不启动TLAB机制的时候,如果一个空间被多个线程同时分配对象,JVM会采用CAS+失败重试的方式来避免线程问题。(具体的CAS机制和其利弊可以移步到JAVA并发专栏)

简而言之,是采用乐观锁的方式,只有假定该堆没有被其他线程操作的时候,当前线程才会在堆上分配对象,如果被其他线程操作,就获取当前堆中的最新标识,然后重试

知识扩展

什么是TLAB

TLAB是虚拟机在堆内存的eden划分出来的一块专用空间,是线程专属的。在虚拟机的TLAB功能启动的情况下,在线程初始化时,虚拟机会为每个线程分配一块TLAB空间,只给当前线程使用,这样每个线程都单独拥有一个空间,如果需要分配内存,就在自己的空间上分配,这样就不存在竞争的情况,可以大大提升分配效率。

注意到上面的描述中”线程专属”、”只给当前线程使用”、”每个线程单独拥有”的描述了吗?

所以说,因为有了TLAB技术,堆内存并不是完完全全的线程共享,其eden区域中还是有一部分空间是分配给线程独享的。

这里值得注意的是,我们说TLAB是线程独享的,但是只是在“分配”这个动作上是线程独占的,至于在读取、垃圾回收等动作上都是线程共享的。而且在使用上也没有什么区别。

也就是说,虽然每个线程在初始化时都会去堆内存中申请一块TLAB,并不是说这个TLAB区域的内存其他线程就完全无法访问了,其他线程的读取还是可以的,只不过无法在这个区域中分配内存而已。

并且,在TLAB分配之后,并不影响对象的移动和回收,也就是说,虽然对象刚开始可能通过TLAB分配内存,存放在Eden区,但是还是会被垃圾回收或者被移到Survivor Space、Old Gen等

TLAB的缺点

虽然在一定程度上,TLAB大大的提升了对象的分配速度,但是TLAB并不是就没有任何问题的。

前面我们说过,因为TLAB内存区域并不是很大,所以,有可能会经常出现不够的情况。在《实战Java虚拟机》中有这样一个例子:

比如一个线程的TLAB空间有100KB,其中已经使用了80KB,当需要再分配一个30KB的对象时,就无法直接在TLAB中分配,遇到这种情况时,有两种处理方案:

  1. 直接在堆内存中对该对象进行内存分配。
  2. 废弃当前TLAB,重新申请TLAB空间再次进行内存分配。

以上两个方案各有利弊,如果采用方案1,那么就可能存在着一种极端情况,就是TLAB只剩下1KB,就会导致后续需要分配的大多数对象都需要在堆内存直接分配。

如果采用方案2,也有可能存在频繁废弃TLAB,频繁申请TLAB的情况,而我们知道,虽然在TLAB上分配内存是线程独享的,但是TLAB内存自己从堆中划分出来的过程确实可能存在冲突的,所以,TLAB的分配过程其实也是需要并发控制的。而频繁的TLAB分配就失去了使用TLAB的意义。

为了解决这两个方案存在的问题,虚拟机定义了一个refill_waste的值,这个值可以翻译为“最大浪费空间”。

当请求分配的内存大于refillwaste的时候,会选择在堆内存中分配。若小于refillwaste值,则会废弃当前TLAB,重新创建TLAB进行对象内存分配。

前面的例子中,TLAB总空间100KB,使用了80KB,剩余20KB,如果设置的refill_waste的值为25KB,那么如果新对象的内存大于25KB,则直接堆内存分配,如果小于25KB,则会废弃掉之前的那个TLAB,重新分配一个TLAB空间,给新对象分配内存。

当一个TLAB被填满或者废弃时,原有TLAB中的对象不会被移动或复制到新的TLAB中。在JVM中,一旦对象被分配在堆上,它们通常会保持在原地直到被垃圾回收。所以,当一个TLAB用完时,线程会简单地分配一个新的TLAB,并在新的TLAB上继续对象分配。原有TLAB中的对象将保留在其当前位置,直到它们不再被引用并由垃圾收集器回收。

原文: https://www.yuque.com/hollis666/xkm7k3/gea4my