Marvel-Site Marvel-Site
首页
  • Java

    • Java基础
    • Java进阶
    • Java容器
    • Java并发编程
    • Java虚拟机
  • 计算机基础

    • 数据结构与算法
    • 计算机网络
    • 操作系统
    • Linux
  • 框架|中间件

    • Spring
    • MySQL
    • Redis
    • MQ
    • Zookeeper
    • Git
  • 架构

    • 分布式
    • 高并发
    • 高可用
    • 架构
  • 框架

    • React
    • 其他
  • 实用工具
  • 安装配置

    • Linux
    • Windows
    • Mac
  • 开发工具

    • IDEA
    • VsCode
  • 关于
  • 收藏
  • 草稿
  • 索引

    • 分类
    • 标签
    • 归档
GitHub (opens new window)

Marvel

吾必当乘此羽葆盖车
首页
  • Java

    • Java基础
    • Java进阶
    • Java容器
    • Java并发编程
    • Java虚拟机
  • 计算机基础

    • 数据结构与算法
    • 计算机网络
    • 操作系统
    • Linux
  • 框架|中间件

    • Spring
    • MySQL
    • Redis
    • MQ
    • Zookeeper
    • Git
  • 架构

    • 分布式
    • 高并发
    • 高可用
    • 架构
  • 框架

    • React
    • 其他
  • 实用工具
  • 安装配置

    • Linux
    • Windows
    • Mac
  • 开发工具

    • IDEA
    • VsCode
  • 关于
  • 收藏
  • 草稿
  • 索引

    • 分类
    • 标签
    • 归档
GitHub (opens new window)
  • Java

    • Java基础

    • Java进阶

    • Java容器

    • Java并发编程

      • 并发编程基本概念
      • 多线程
      • 创建多线程的方法
      • 比较与交换CAS
      • Java对象头与Monitor监视器
        • Java对象头
          • Mark Word
          • Klass Word
          • array length
        • Monitor
      • Java主流锁
      • synchronized关键字
      • volatile关键字
      • 线程池
      • 原子类Atomic
      • LockSupport工具
      • 抽象队列同步器AQS
      • ThreadLocal深度理解
      • 多线程循环打印代码
      • 线程等待和唤醒的三种方式
      • ReentrantLock非公平锁的源码分析
    • Java虚拟机

    • 常见面试题

  • 计算机基础

  • 框架|中间件

  • 架构

  • 后端
  • Java
  • Java并发编程
Marvel
2022-07-15
目录

Java对象头与Monitor监视器

# Java对象头与Monitor监视器

# Java对象头

普通对象的对象头

32 位虚拟机 Mark Word 占 32 位,也就是 4 个字节;64 位虚拟机 Mark Word 占 64 位,也就是 8 个字节;

|--------------------------------------------------------------|
|                     Object Header (64 bits)                  |
|------------------------------------|-------------------------|
|        Mark Word (32 bits)         |   Klass Word (32 bits)  |
|------------------------------------|-------------------------|
1
2
3
4
5

数组对象的对象头

|---------------------------------------------------------------------------------|
|                                 Object Header (96 bits)                         |
|--------------------------------|-----------------------|------------------------|
|        Mark Word(32bits)       |   Klass Word(32bits)  |  array length(32bits)  |
|--------------------------------|-----------------------|------------------------|
1
2
3
4
5

# Mark Word

这部分用来存储对象自身的运行时数据,如 hashcode、gc 分代年龄。

Mark word的位长度为 JVM 的一个 Word 大小,也就是说 32 位 JVM 的 Mark word 为32位,64 位 JVM 的 Mark word 为 64 位。

32 位对象头

|-------------------------------------------------------|--------------------|
|                  Mark Word (32 bits)                  |       State        |
|-------------------------------------------------------|--------------------|
| identity_hashcode:25 | age:4 | biased_lock:1 | lock:2 |       Normal       |
|-------------------------------------------------------|--------------------|
|  thread:23 | epoch:2 | age:4 | biased_lock:1 | lock:2 |       Biased       |
|-------------------------------------------------------|--------------------|
|               ptr_to_lock_record:30          | lock:2 | Lightweight Locked |
|-------------------------------------------------------|--------------------|
|               ptr_to_heavyweight_monitor:30  | lock:2 | Heavyweight Locked |
|-------------------------------------------------------|--------------------|
|                                              | lock:2 |    Marked for GC   |
|-------------------------------------------------------|--------------------|
1
2
3
4
5
6
7
8
9
10
11
12
13

64 位对象头

|---------------------------------------------------------------------------------------|--------------------|
|                                  Mark Word (64 bits)                                  |       State        |
|---------------------------------------------------------------------------------------|--------------------|
| unused:25 | identity_hashcode:31 | unused:1 | age:4 | biased_lock:1 (0) | lock:2 (01) |       Normal       |
|---------------------------------------------------------------------------------------|--------------------|
| thread:54 |       epoch:2        | unused:1 | age:4 | biased_lock:1 (1) | lock:2 (01) |       Biased       |
|---------------------------------------------------------------------------------------|--------------------|
|                            ptr_to_lock_record:62                        | lock:2 (00) | Lightweight Locked |
|---------------------------------------------------------------------------------------|--------------------|
|                         ptr_to_heavyweight_monitor:62                   | lock:2 (10) | Heavyweight Locked |
|---------------------------------------------------------------------------------------|--------------------|
|                                                                         | lock:2 (11) |    Marked for GC   |
|---------------------------------------------------------------------------------------|--------------------|
1
2
3
4
5
6
7
8
9
10
11
12
13

各部分含义

biased_lock lock 状态
0 01 无锁
1 01 偏向锁
0 00 轻量级锁
0 10 重量级锁
0 11 GC标准
  • biased_lock:对象是否启用偏向锁标记,只占 1 个二进制位。为 1 时表示对象启用偏向锁,为 0 时表示对象没有偏向锁
  • lock:2 位的锁状态标记位,由于希望用尽可能少的二进制位表示尽可能多的信息,所以设置了 lock 标记。该标记的值不同,整个mark word 表示的含义不同;
  • age:4 位的 Java 对象年龄。在垃圾回收过程中,对象在 Survivor 区复制一次,年龄增加 1。当对象达到设定的阈值时,将会晋升到老年代,年龄默认阈值为 15。由于 age 只有 4 位,所以最大值为 15,这就是-XX:MaxTenuringThreshold 选项最大值为 15 的原因
  • identity_hashcode:25 位的对象标识 Hash 码,采用延迟加载技术。调用方法 System.identityHashCode() 计算(本地方法),并会将结果写到该对象头中。当对象被锁定时,该值会移动到管程 Monitor 中
  • thread:持有偏向锁的线程 ID
  • epoch:偏向时间戳
  • ptr_to_lock_record:指向栈中锁记录的指针
  • ptr_to_heavyweight_monitor:指向管程 Monitor 的指针

# Klass Word

这一部分用于存储对象的类型指针,该指针指向它的类元数据,JVM 通过这个指针确定对象是哪个类的实例。该指针的位长度为 JVM 的一个字大小,即 32 位的 JVM 为 32 位,64 位的 JVM 为 64位。

如果应用的对象过多,使用 64 位的指针将浪费大量内存,统计而言,64 位的 JVM 将会比 32 位的 JVM 多耗费 50% 的内存。为了节约内存可以使用选项 +UseCompressedOops 开启指针压缩,其中,oop 即 ordinary object pointer 普通对象指针。开启该选项后,下列指针将压缩至 32 位:

  1. 每个Class的属性指针(即静态变量)

  2. 每个对象的属性指针(即对象变量)

  3. 普通对象数组的每个元素指针

当然,也不是所有的指针都会压缩,一些特殊类型的指针 JVM 不会优化,比如指向 PermGen 的 Class 对象指针(JDK8 中指向元空间的 Class 对象指针)、本地变量、堆栈元素、入参、返回值和 NULL 指针等。

# array length

如果对象是一个数组,那么对象头还需要有额外的空间用于存储数组的长度,这部分数据的长度也随着 JVM 架构的不同而不同:32 位的JVM上,长度为 32 位;64 位 JVM 则为 64 位。64 位 JVM 如果开启 +UseCompressedOops 选项,该区域长度也将由 64 位压缩至 32 位。

# Monitor

Monitor被翻译为监视器或管程,可以理解为一个同步工具或机制,每一个 Java 对象都有一把看不见的锁,称为内部锁或 Monitor 对象。Monitor 是线程私有的数据结构,每一个线程都有一个可用 monitor record 列表,同时还有一个全局的可用列表。

每一个被锁住(重量级锁)的对象都会和一个 monitor 关联,该对象头的Mark Word中就被设置指向Monitor对象的指针,同时 monitor 中有一个 Owner 字段存放拥有该锁的线程的唯一标识,表示该锁被这个线程占用。可以通过 synchronized 关键字来获取该对象的 monitor。当一个线程获取了一个对象的 monitor 后,其他线程就无法再获取该对象的 monitor,直到该线程释放该对象的 monitor。这样就可以保证同一时刻只有一个线程能够访问该对象的关键代码段,从而避免了多线程并发访问的问题。

Monitor结构如下

image-20220715202827649

obj 是 java 对象,monitor 是操作系统提供的。obj 的 MarkWord 部分有指针指向 Monitor。

  • 刚开始 Monitor 中 Owner 为 null
  • 当 Thread-2 执行 synchronized(obj) 就会将 Monitor 的所有者 Owner 置为 Thread-2,Monitor 中只能有一个 Owner。
  • 在 Thread-2 上锁的过程中,如果 Thread-3, Thread-4, Thread-5 也来执行 synchronized(obj),就会进入 EntryList,线程进入 BLOCKED 状态。
  • Thread-2 执行完同步代码块的内容,然后唤醒 EntryList 中等待的线程来竞争锁,竞争是非公平的。
  • 图中 WaitSet 中的 Thread-0, Thread-1 是之前获得过锁,但因条件不满足而进入 WAITING 状态的线程。

注意

  • synchronized 必须是进入同一个对象的 monitor 才有上述的效果
  • 不加 synchronized 的对象不会关联监视器,不遵从以上规则
编辑 (opens new window)
#Java#JUC
上次更新: 2024/04/19, 15:43:56
比较与交换CAS
Java主流锁

← 比较与交换CAS Java主流锁→

最近更新
01
位运算
05-21
02
二叉树
05-12
03
Spring三级缓存解决循环依赖
03-25
更多文章>
Theme by Vdoing | Copyright © 2022-2024 Marvel
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式