-
循环CAS: 使用过多线程的同学都知道这种方式,就是把内存中的预期值拿出来进行更新,在更新之前再判断一下当内存中现有的值和预期值是否一致,不一致则重新获取预期值,一致的话直接进行更新。但是这种方式会出现因自旋太久带来的cpu开销问题,所以默认使用的是
TLAB
方式解决。 -
本地线程分配缓冲(Thread Local Allocation Buffer): 为每个线程都预先分配一块空间去划分内存,每个线程来了都从自己的空间里去分配内存。如果预留的内存不够划分则会回退到
CAS
的方式;可以通过-XX:TLABSize=xx
来设置预留的大小,避免回退到CAS。
补充:这里我们要注意一个细节:对象的半初始化问题。
对象的组成结构
我们java的对象不仅仅只有成员变量,这个层面的理解太浅了,实际上java的对象包含了3个部分:对象头
、实例数据
、对齐填充
。
-
对象头:对象头是Java对象中非常重要的一部分,他存储了对象的各种底层信息:比如MarkWord、KlassPointer、数组长度
-
实例数据:实例数据就是我们对象拥有的属性。
-
对其填充:因为是64bit操作系统,内存的宽度是64bit也就是8字节,8的整数倍寻址会更高效,对对象大小进行补位的,有中间对齐和尾部对齐。
对象头
下面我们深入对象头看看,到hotspot
源码中找到markOop.hpp
文件,看下注释怎么描述对象头的:
// Bit-format of an object header (most significant first, big endian layout below):
//
// 32 bits:
// --------
// hash:25 ------------>| age:4 biased_lock:1 lock:2 (normal object)
// JavaThread*:23 epoch:2 age:4 biased_lock:1 lock:2 (biased object)
// size:32 ------------------------------------------>| (CMS free block)
// PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object)
//
// 64 bits:
// --------
// unused:25 hash:31 -->| unused:1 age:4 biased_lock:1 lock:2 (normal object)
// JavaThread*:54 epoch:2 unused:1 age:4 biased_lock:1 lock:2 (biased object)
// PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object)
// size:64 ----------------------------------------------------->| (CMS free block)
//
// unused:25 hash:31 -->| cms_free:1 age:4 biased_lock:1 lock:2 (COOPs && normal object)
// JavaThread*:54 epoch:2 cms_free:1 age:4 biased_lock:1 lock:2 (COOPs && biased object)
// narrowOop:32 unused:24 cms_free:1 unused:4 promo_bits:3 ----->| (COOPs && CMS promoted object)
// unused:21 size:35 -->| cms_free:1 unused:7 ------------------>| (COOPs &&