二、对象分配
G1提供了两种对象的分配策略:基于线程本地分配缓冲区(TLAB,Thread Local Allocation Buffer)的快速分配和慢速分配。
无论是快速分配还是慢速分配,都应该在STW之外调用,都应该避免使用全局锁。因此优先进行无锁分配,再进行加锁,从而尽可能地满足并行化分配。
简单的实现是将释放的对象内存加入 FreeList,下次分配对象的时候,优先从 FreeList 中寻找合适的内存大小进行分配,之后再在主内存中撞针分配。
目前大多数应用内存分配是多线程的,都从主内存中分配,CAS 更新重试过于频繁导致效率低下。目前的应用,一般根据不同业务区分了不同的线程池,在这种情况下,一般每个线程分配内存的特性是比较稳定的。这里的比较稳定指的是,每次分配对象的大小,每轮 GC 分配区间内的分配对象的个数以及总大小。所以,我们可以考虑每个线程分配内存后,就将这块内存保留起来,用于下次分配,这样就不用每次从主内存中分配了。
在进行TLAB分配之前,会优先进行栈上分配,如果没有分配成功才会尝试进行TLAB
1.快速分配
TLAB的目的就是为了进行内存的快速分配。一般来说,从堆空间分配对象时,必须锁定整个堆,以便不会被其他线程中断和影响。因此TLAB试图通过为每一个线程分配一个缓冲区来避免和减少使用锁。
分配线程对象的时候,从JVM堆中分配一个固定大小的内存区域并将其作为线程的私有缓冲区,这个缓冲区就被称为TLAB
有了TLAB之后,只有为每个线程分配TLAB缓冲区的时候才需要锁定整个JVM堆。不同的线程不共享TLAB,我们尝试分配一个对象的时候优先从当前线程的TLAB中分配对象,不需要锁,所以达到了快速分配的目的
具体操作是,TLAB区是Eden区域中的一块内存,不同线程的TLAB都位于Eden区,所有的TLAB内存堆所有的线程都是可见的。每个线程都有一个TLAB的数据结构,但是仅仅只是保存待内存区间的起始地址(start)和结束地址(end)。在分配的时候也只在这个区间内做分配,从而达到无锁分配。top是当前的分配指针
G1中使用CAS来分配TLAB空间,一个TLAB空间分区中可能存在多个TLAB块,但是一个TLAB是不可能跨分区的