说说 jvm锁的膨胀过程?锁内存怎么变化的?

1 个回答

  • 周维清
    能治愈生活的良方,是保持对生活的热爱。
    在 JVM 锁膨胀过程中,锁的状态从低到高逐步升级,包括无锁状态、偏向锁、轻量级锁、重量级锁。这种升级的过程主要是为了应对不同的线程竞争情况,逐步提高锁的获取效率。下面我将详细整理 JVM 锁的膨胀过程及其内存变化,帮助你理解面试中可能遇到的相关问题。 ### 1. **锁的四种状态** JDK 1.6 引入了偏向锁和轻量级锁,为了优化 `synchronized` 的性能,这使得锁的状态分为四种:无锁、偏向锁、轻量级锁、重量级锁。 - **无锁状态**:对象刚创建时,没有线程竞争锁,此时没有加锁操作。 - **偏向锁状态**:适用于没有多线程竞争的情况。若一个线程反复访问同步代码,JVM 会将锁偏向于该线程,从而避免每次加锁和解锁的开销。 - **轻量级锁状态**:当有多个线程竞争锁时,会从偏向锁升级为轻量级锁。轻量级锁使用自旋等待,线程不会立即阻塞。 - **重量级锁状态**:如果竞争进一步激烈,轻量级锁会升级为重量级锁,此时会挂起没有获得锁的线程,性能下降。 ### 2. **锁膨胀的具体过程** 锁的膨胀是一个从低级别到高级别的升级过程,锁一旦升级就不会降级。下面是锁的膨胀过程: #### 2.1 **无锁 → 偏向锁** 当一个对象刚被创建时,处于无锁状态。如果一个线程第一次获取该对象的锁,JVM 会为该线程赋予偏向锁。此时,Mark Word 会记录偏向锁的线程 ID 和时间戳。偏向锁极大地优化了单线程的加锁性能,因为此后该线程再次进入同步块时,不需要进行锁竞争。 #### 2.2 **偏向锁 → 轻量级锁** 当另一个线程尝试获取已经被偏向锁持有的锁时,偏向锁会升级为轻量级锁。此时,第一个持有锁的线程不会被立即阻塞,另一个线程会通过 CAS 操作进行锁的竞争。如果两个线程通过自旋成功获得锁,轻量级锁的性能仍然较高。 #### 2.3 **轻量级锁 → 重量级锁** 如果多个线程竞争同一个锁,且自旋操作未成功,轻量级锁将升级为重量级锁。此时,线程会被阻塞,等待锁的释放。重量级锁的内存结构会从指向线程栈中的锁记录(轻量级锁)变为指向一个互斥量(重量级锁的管理器)。 ### 3. **Mark Word 的内存变化** 锁的膨胀过程中,锁信息存储在对象头的 Mark Word 中。在 JVM 中,Mark Word 的结构会随着锁的状态发生变化: - **无锁状态**: - Mark Word 中存储对象的 `hashCode`、分代年龄等信息。 - 标志位:`01`(未加锁)。 - **偏向锁状态**: - Mark Word 中记录偏向线程的 ID、时间戳、分代年龄等信息。 - 偏向标志位:`1`,锁标志位:`01`。 - **轻量级锁状态**: - Mark Word 指向当前线程栈中的锁记录(Lock Record),用来存储 Mark Word 的拷贝。 - 标志位:`00`(轻量级锁状态)。 - **重量级锁状态**: - Mark Word 指向重量级锁的管理器(Monitor 对象)。 - 标志位:`10`(重量级锁状态)。 ### 4. **锁膨胀的适用场景** - **偏向锁**:适用于线程间没有竞争的场景,偏向锁极大优化了加锁和解锁的性能。 - **轻量级锁**:适用于竞争较少的场景,多个线程通过自旋进行锁竞争,提高了响应速度。 - **重量级锁**:适用于多线程频繁竞争的场景,在竞争激烈时会阻塞线程,保证安全但牺牲了性能。 ### 5. **锁的膨胀总结** 锁状态的膨胀遵循从低级别到高级别的过程,即无锁 → 偏向锁 → 轻量级锁 → 重量级锁。锁的升级是为了适应不同线程竞争的情况,但一旦锁升级,就不会降级。这种机制在提升性能的同时也确保了线程安全性。
    编辑于 2024-11-14 01:51 •IP属地 美国
    举报
    评论
    收藏