一、前言
感觉什么都不会,从哪开始呀!
这是最近我总能被问到的问题,也确实是。一个初入编程职场的新人,或是一个想重新努力学习的老司机,这也不会,那也不会,总会犯愁从哪开始。
讲道理,毕竟 Java 涉及的知识太多了,要学应该是学会学习的能力,而不是去背题、背答案,拾人牙慧是不会有太多收益的。
学习的过程要找对方法,遇到问题时最好能自己想想,你有哪些方式学会这些知识。是不感觉即使让你去百度搜,你都不知道应该拿哪个关键字搜!只能拿着问题直接找人问,这样缺少思考,缺少大脑撞南墙的过程,其实最后也很难学会。
所以,你要学会的是自我学习的能力,之后是从哪开始都可以,重要的是开始和坚持!
二、面试题
谢飞机,小记
,周末逛完奥特莱斯,回来就跑面试官家去了!
谢飞机:duang、duang、duang,我来了!
面试官:来的还挺准时,洗洗手吃饭吧!
谢飞机:嘿嘿...
面试官:你看我这块鱼豆腐,像不像 synchronized 锁!
谢飞机:啊!?
面试官:飞机,正好问你。synchronized、volatile,有什么区别呀?
谢飞机:嗯,volatile 保证可见性,synchronized 保证原子性!
面试官:那不用 volatile,只用 synchronized 修饰方式,能保证可见性吗?
谢飞机:这...,我没验证过!
面试官:吃吧,吃吧!一会给你个 synchronized 学习大纲,照着整理知识点!
三、synchronized 解毒
1. 对象结构
1.1 对象结构介绍
HotSpot 虚拟机 markOop.cpp 中的 C++ 代码注释片段,描述了 64bits 下 mark-word 的存储状态,也就是图 15-1 的结构示意。
这部分的源码注释如下:
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 && CMS free block)
源码地址:jdk8/hotspot/file/vm/oops/markOop.hpp
HotSpot 虚拟机中,对象在内存中存储的布局可以分为三块区域:对象头(Header)
、实例数据(Instance Data)
和对齐填充(Padding)
。
-
mark-word:对象标记字段占 4 个字节,用于存储一些列的标记位,比如:哈希值、轻量级锁的标记位,偏向锁标记位、分代年龄等。
-
Klass Pointer:Class 对象的类型指针,Jdk1.8 默认开启指针压缩后为 4 字节,关闭指针压缩(
-XX:-UseCompressedOops
)后,长度为 8 字节。其指向的位置是对象对应的 Class 对象(其对应的元数据对象)的内存地址。 -
对象实际数据:包括对象的所有成员变量,大小由各个成员变量决定,比如:byte 占 1 个字节 8 比特位、int 占 4 个字节 32 比特位。
-
对齐:最后这段空间补全并非必须,仅仅为了起到占位符的作用。由于 HotSpot 虚拟机的内存管理系统要求对象起始地址必须是 8 字节的整数倍,所以对象头正好是 8 字节的倍数。因此当对象实例数据部分没有对齐的话,就需要通过对齐填充来补全。
另外,在 mark-word 锁类型标记中,无锁,偏向锁,轻量锁,重量锁,以及 GC 标记,5 种类中没法用 2 比特标记(2 比特最终有 4 种组合00
、01
、10
、11
),所以无锁、偏向锁,前又占了一位偏向锁标记。最终:101 为无锁、001 为偏向锁。
1.2 验证对象结构
为了可以更加直观的看到对象结构,我们可以借助 openjdk
提供的 jol-core
进行打印分析。
引入 POM
<!-- https://mvnrepository.com/artifact/org.openjdk.jol/jol-cli -->