JVM如何确保新对象的内存分配的线程安全性 [英] How JVM ensures thread safety of memory allocation for a new object

查看:465
本文介绍了JVM如何确保新对象的内存分配的线程安全性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

让我们假设这将在一个真正的并行环境,一个VM同时发生:

  //线程1:
new Cat()

//线程2:
new Dog()

//线程3:
new JVM如何确保堆上内存分配的线程安全性?

$ b()


$ b

Heap是所有线程的一个,它有自己的内部数据。



为简单起见,假设一个简单的压缩垃圾收集器实现,-XX :+ UseSerialGC
-XX:+ UseParallelGC,用简单的增量指针标记一个开始的自由空间和一个连续的可用空间在Eden(堆)。



当为 Cat , Dog 和 Mouse 实例分配堆空间时,线程之间必须有某种同步,否则它们很容易覆盖其他。这是否意味着每个操作符隐藏了一些同步块?这样,许多无锁算法实际上完全没有锁定)。



我假设内存分配由应用程序线程自己同步,而不是另一个专用线程。



我知道 TLAB ,或者线程本地分配缓冲区。它们允许线程在Eden中有一个单独的内存区域用于分配,因此不需要同步。但我不知道如果TLAB默认设置,它是有点非常模糊的HotSpot功能。注意:不要混淆TLAB和 ThreadLocal 变量!



我也假设,对于更复杂的垃圾收集器, G1或非压缩垃圾收集器,必须维护更复杂的堆结构数据,例如CMS的空闲块列表,因此需要更多的同步。



UPDATE :请让我澄清一下。我接受对带有和不带有活动TLAB的HotSpot JVM实现和变体的回答。



UPDATE
根据我的快速测试 TLAB在我的64位JDK 7上默认设置为ON ,用于串行,并行和CMS垃圾收集器,但不是G1 GC。

解决方案

我已经简要描述了HotSpot JVM中的分配过程此回答

对象的分配方式取决于分配对象的堆区域。



1。 TLAB。最快和最常用的方式。



TLAB是Eden为线程本地分配保留的区域。每个线程可以创建许多TLAB:一旦填充,使用#2中描述的技术创建新的TLAB。也就是说创建一个新的TLAB类似于在Eden中直接分配一个大的元对象。



每个Java线程都有两个指针: tlab_top tlab_limit 。 TLAB中的分配只是一个指针增量。因为指针是线程局部的,所以不需要同步。

  if(tlab_top + object_size <= tlab_limit){
new_object_address = tlab_top;
tlab_top + = object_size;
}

-XX:+ UseTLAB 默认启用。如果关闭它,对象将按如下所述在Eden中分配。



2。分配在Eden(Young Generation)。



如果一个新对象的TLAB中没有足够的空间,则创建一个新的TLAB或者直接在Eden中分配对象(取决于TLAB浪费限制和其他人体工程学参数)。



Eden中的分配与TLAB中的分配类似。还有两个指针: eden_top eden_end ,它们对于整个JVM是全局的。分配也是一个指针增量,但是使用原子操作,因为Eden空间在所有线程之间共享。线程安全性通过使用特定于架构的原子指令实现: CAS (例如 LOCK CMPXCHG on x86)或 LL / SC (在ARM上)。



3。



这取决于GC算法,例如, CMS使用自由列表。 Old Generation中的分配通常仅由Garbage Collector本身执行,因此它知道如何同步其自己的线程(通常与分配缓冲区,无锁原子操作和互斥体的混合)。


Let's assume this is about to happen in a true parallel environment, one VM, at the same time:

// Thread 1:
  new Cat()

// Thread 2:
  new Dog()

// Thread 3:
  new Mouse()

How does JVM ensure thread safety of memory allocations on heap?

Heap is one for all threads and it has its own internal data.

For simplicity assume a simple compacting garbage collector implementation, -XX:+UseSerialGC -XX:+UseParallelGC, with simple incremental pointer to mark a start of free space and one continuous free space in Eden (heap).

There must be some kind of synchronization between threads when heap space is allocated for Cat, Dog and Mouse instances otherwise they can easily end up overwriting each other. Does that mean that every new operator hides inside some synchronized blocks? This way, many "lock free" algorithms are not in fact completely lock free ;)

I assume that memory allocations are made by the application thread themselves, synchronously, not by another dedicated thread(s).

I am aware of TLABs, or Thread Local Allocation Buffer. They allow threads to have a separate memory areas in Eden for allocations, so no synchronization is required. But I am not sure if TLAB is set by default, it is somewhat very obscure HotSpot feature. Note: do not confuse TLAB and ThreadLocal variables!

I also assume, that with more complex garbage collectors, like G1, or non-compacting garbage collectors, more complex heap structure data has to be maintained, like list of free blocks for CMS, so more synchronization is needed.

UPDATE: Please let me clarify this. I accept answer for HotSpot JVM implementation and variants with and without active TLAB.

UPDATE: According to my quick test, TLAB are set ON by default, on my 64-bit JDK 7, for Serial, Parallel and CMS garbage collectors, but not for G1 GC.

解决方案

I've briefly described the allocation procedure in HotSpot JVM in this answer.
The way how an object is allocated depends on the area of the heap where it is allocated.

1. TLAB. The fastest and the most frequent way.

TLABs are the areas in Eden reserved for thread-local allocations. Each thread may create many TLABs: as soon as one gets filled, a new TLAB is created using the technique described in #2. I.e. creating of a new TLAB is something like allocating a large metaobject directly in Eden.

Each Java thread has two pointers: tlab_top and tlab_limit. An allocation in TLAB is just a pointer increment. No synchronization is needed since the pointers are thread-local.

if (tlab_top + object_size <= tlab_limit) {
    new_object_address = tlab_top;
    tlab_top += object_size;
}

-XX:+UseTLAB is enabled by default. If turn it off, the objects will be allocated in Eden as described below.

2. Allocation in Eden (Young Generation).

If there is not enough space in TLAB for a new object, either a new TLAB is created or the object is allocated directly in Eden (depending on TLAB waste limit and other ergonomics parameters).

Allocation in Eden is similar to allocation in TLAB. There are also two pointers: eden_top and eden_end, they are global for the whole JVM. The allocation is also a pointer increment, but atomic operations are used since the Eden space is shared between all threads. Thread-safety is achieved by using architecture-specific atomic instructions: CAS (e.g. LOCK CMPXCHG on x86) or LL/SC (on ARM).

3. Allocation in Old Generation.

This depends on GC algorithm, e.g. CMS uses free lists. Allocation in Old Generation is typically performed only by Garbage Collector itself, so it knows how to synchronize its own threads (generally with the mix of allocation buffers, lock-free atomic operations and mutexes).

这篇关于JVM如何确保新对象的内存分配的线程安全性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆