大家好,我是庆哥Java,一个专注于干货分享的Java自学者!

写在前面

如果你已经知道什么是Mark Word,那我也希望你都好好阅读下本篇文章,因为你有可能发现不一样的切入点来帮助你更加深入的了解Mark Word,这对你来说是个很好的巩固所学知识的机会,同时也是一场技术交流,一个有逼格的程序员应该不会错过这样的机会吧!

​

如果你还不知道什么是Mark Word,那你更要好好阅读本篇文章了,因为Mark Word不仅是一个可以让你用来装X的词汇,实际上它是一个非常重要的概念,非常重要的知识点,对你学习Java中的各种锁是非常有必要的,也可以说是必须的,而为什么要阅读本篇文章嘞?

​

因为,庆哥的文章,接地气啊,通俗易懂,那咱就一起搞起吧!


先来看下Mark Word

Mark Word是啥?翻译过来就是对象标记,先通过代码让你直观看下Mark Word到底是个什么东东,来看下面代码:

class OneClass{

}

这是啥?一个非常非常简单的类,啥也没有,就是声明了一个类对吧,好,看接下来的操作:

public static void main(String[] args) {
System.out.println(ClassLayout.parseInstance(new OneClass()).toPrintable());
}

这是啥?先来看看打印出来的是些个啥?



看得懂吗?红框中的就是对象标记Mark Word了,那今天这篇文章的目的就是让你读懂上面这张图到底是个啥?以及有哪些重要的信息!

​

事先声明,今天的文章,干货比较多,看起来有点费劲,请先做好准备!

​

开干!

​


从对象实例开始讲起

什么是对象实例呢?说的简单点,我们new出来的东西就是一个对象实例,也就是平常说的什么实例化,就是你创建出来的那个在堆里面的实例对象,ok,这个概念相信大家都懂,不赘述,接下来你就有可能不知道了,你说对于一个对象实例,它有哪几部分组成呢?

​

  1. 对象头
  2. 实例数据
  3. 对齐填充

这些都是啥?这里我们可以类比下我们的人,一个对象实例就好比是一个完整的人,拿你自己来类比就行,这里我们拿你自己来类比,这个对象头就是你的大脑啦,然后实例数据就好比的身体,而对齐填充就好比你的脚,画个图大概就是这样的:



这里可能这个对齐填充大家不是很好理解,什么意思呢?就比如说你要参加一个面试,但是人家硬性要求一米八,但是你就是一米七八,咋办,鞋垫子拯救你啊,懂我意思吧。 那这个对齐填充也是这么回事,对齐填充要求一个对象实例的大小必须是8个字节的倍数,那你不够了的话,对齐填充起作用给你整到8字节的倍数,懂了吧!

现在我们知道了,一个对象实例包括对象头,实例数据和对齐填充,其实更详细的还有如下划分,看图:



什么意思呢?就是你得知道这么一回事,对于对象头来说,它是分为两部分的,一是对象标记,也就是今天要注重说的Mark Word,还有一个就是Class Pointer类型指针了,咱们的重点是Mark Word!

​

实例数据和对齐填充

这里先给大家简单说说什么是实例数据,帮助大家有个直观的认识!

​

什么叫做实例数据呢?写一个简单的代码来举例说明:



​

我们这里定义了一个超级简单的Person类,里面啥也没有,这个时候我们去实例化一个Person对象,也就是这样:

这个时候你知道我们这个对象有多大吗?实际上目前它就占16个字节,也就是对象头中的Mark Word和类型指针,说白了就是,你刚开始创建一个空荡荡的Java对象,起初里面是只有对象头的,也就是组成对象头的对象标记Mark Word占8个字节,类型指针占8个字节(存在指针压缩的话就只有4个字节,会对齐填充4个字节),一共16个字节,如果这个时候我们再添加一点代码,比如这样:

我们给Person类增加了一个属性,我们知道int占四个字节,加上对象头的16个字节,此时一共20个字节,但是一个对象的大小要是8字节的倍数,那目前20字节,所以需要到24字节才符合条件,此时就需要对齐填充4个字节,最终凑成24个字节,这个Person类中的一些东西(比如这里定义的int)就是实例数据了,另外想必这下你也更清楚什么是对齐填充了吧!

ok,到了这里,什么是实例数据,什么是对齐填充,是不是比较清晰了?当然如果你目前理解的还不是很透彻也没关系,只有一个大致知道是怎么回事就ok啦!

​

类型指针是什么

这是个啥意思呢?我们来看,比如我们创建一个Person对象,我们一般怎么写,是不是这样:

Person p = new Person()

别看这一行简单的代码,你得分清楚几个概念了,首先就是new person(),这个大家最熟悉,就是创建一个新的对象,在哪,是在堆中创建吧,然后就是这个p了,它是啥,这个是对象引用,存储在哪,知道吧,这个是在栈内存,那么这个Person呢?是不是很多人就不太清楚了呢?

记住了,这个Person可以理解成一个模板,就好比人类,然后后面new Person()生产出具体的某个人,而这个Person就是个生产出这个人的模具,最重要的要知道,这个Person存在哪里,它是在方法区的,结合上面说的对象标记,来看一张图:

​



一定要注意这个Person的位置在哪里!它是存在方法区的,这个是java虚拟机的知识了,先作简单了解即可!

​

而这里的类型指针,就是指向这个Person类的指针了!那么关于什么是类型指针,你是否也有一个简单的认识了?不再是一个陌生的词汇了吧!

​

这就ok啦,咱们继续,重难点还在后面嘞!

​


详解Mark Word

对于一个Java对象来说,对象头是极其重要的,对象头主要有对象标记和类型指针两部分组成,接下来需要重点看下对象标记,也就是Mark Word,下面是针对64位JVM(那也就是说还有32位的,考虑到现在基本都是64的了,32的直接pass掉)的Mark Word来说的,它的组成是这样的:



第一次看到这张图会觉得比较懵,没事,很正常,我刚开始学习也是这样,别着急,经过下面的一步步的分析,你就会清晰很多!首先记住,上图表示的就是一个Mark Word的内部是啥样的!我们之前不是说了嘛,对象标记Mark Word是占8字节的,那也就是64bit,那这64bit都是存储的啥,上面这种图就给出了答案,你看这里:

​



​

一个Mark Word就是8字节64位,里面存储了啥,就得看上面这张图,有内味儿了吧,上面这张图是不是稍微熟悉了一点点,那现在你应该知道这么些东西:

​

一个Java对象由对象头,实例数据和对齐填充组成,其中对象头是极其重要的,对象头是由对象标记Mark Word和类型指针组成,其中又以Mark Word最重要,对于Mark Word它占8个字节也就是64位!

ok,以上内容都清楚吧!那咱就继续!

​

接下来咱就以锁这个切入点去详细的看看这个Mark Word!

​

我们都知道在并发情况下也就是要解决线程安全的话要加锁,其实这个加锁就是对对象加锁,那如何去判断或者说知道这个对象加锁了没有或者加的什么锁,这些个信息其实就保存在对象头中的Mark Word中,上面这张图就是关于一个Mark Word的具体结构,可以看出,一个Java对象其实在不同的状态下,它是不一样的,主要是有不同的锁,比如没有锁,轻量级锁,还有什么偏向锁,重量级锁等等,不同的锁,在Mark Word中就会有不同的状态标记。

​

JOL的引入

只看这些,其实是比较晦涩难懂的,接下来需要结合代码来看下:

​



​

这就是一个空类,啥也没有,要怎么看这个对象标记Mark Word呢?这里我们需要加入一个依赖,也就是你的Java项目是个maven项目,可以引入pom依赖,然后添加以下依赖:

<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.10</version>
</dependency>

这个依赖是干嘛的呢?这是一个代码工具叫做JOL,也就是Java Object Layout,主要就是用来分析java虚拟机中的对象布局的,也就是在java对象在虚拟机中的大小和分布,ok,接下来加入上述这个依赖之后我们就可以这样操作,看代码:

​

这样我们就可以看出使用虚拟机的一些情况,看打印输出:



接着我们就来看下我们创建的那个空类是怎样的一个情况,可以这样操作:

MyClass myClass = new MyClass();
//打印出相关的对象头信息
System.out.println(ClassLayout.parseInstance(myClass).toPrintable());

得出以下内容:

OK了,相关信息打印出来了,记住这是myclass的相关内部信息,那这些都是啥呢?看上面的图,是不是有个“Object header”啥意思?不就是对象头嘛,然后我们来看这里:

​

这是啥?主要就是来看我们的一个空对象占多大空间,之前也说了,对象头包括对象标记8字节和类型指针8字节,我们先来看对象标记,是不是这个:

​



​

也就是起始位置是0,然后前进4个字节,此时的起始位置就变成了4,接着再前进4个字节,那此时起始位置就成了8,此时对象标记就占8个字节,这没啥问题,接着我们看,起始位置是8,然后继续前进4字节,到达12的时候,其实此时类型指针就结束了,也就是8到12,类型指针就占了4字节,看这句话:

也就是此时这个对象就占据12个字节,可是不是说占据16个字节才对吗?我们知道java对象所占大小需要是8字节的整数倍,那为了满足这块,就又对齐填充了4字节,最终达到16字节 。

​

为什么会这样?其实是因为这里发生了指针压缩,在说这个指针压缩之前,需要明白这样的一东西:

我们通过代码来说下:

我们在空类中加入一个char类型,再看下结果:

​

接着我们再看一个:



​

我就问此时你说会进行对齐填充几个字节?思考一下:

​

为什么是6,一定要想明白了,记住8的倍数哦!对了,一个对象有多大,记得可以看这里:

​



​

指针压缩

接着我们再说之前指针压缩的问题,也就是类型指针是占8个字节的,可是实际上只占了4个字节,这就是指针压缩的问题,这里我们需要这么一个jvm参数,就是它:

java -XX:+PrintCommandLineFlags -version

我们执行看下:



​

也就是默认开启了指针压缩,所以我们的类型指针才变成了4字节,接着我们可以把这个指针压缩给关闭掉,可以这样操作:

再看:

此时就正确啦,就是对象标记占8个字节,类型指针也占8个字节,也就是对象头16个字节。

​

我们之前说过,一个java对象包括对象头,实例数据和对齐填充,经过我们上面的讲解,然后在看下面一张图,我相信你可以秒懂:



​

有没有一种豁然开朗的感觉?到了这里就需要分析最后一波,就是value值了,也就是这部分值(注意我们重点在Mark Word的value值上):

​

value值分析

那这里需要再看看一下这张非常重要的图了:



然后再来看我们这里创建的类:



​

这就是一个普通的类,没有加锁,也就是最普通的无锁,我们再看它的对象结构是啥:

​

我们主要来看value这部分,这里我们仍然需要对照Mark Word的结构来看,这里是无锁结构,所以我们需要看的是这里:

​



注意重点等下我们需要看下这个锁标志位,然后下面看一个对照图:

一张图理解Mark Word



​

要仔细看这张图了,懂了这个,基本上你就掌握了Mark Word!为了让你更加的清晰,再给你来一张图:



​

总结

对于一个Java对象而言,Mark Word是相当重要的,而且它是你后续学习并发编程,研究Java中的各种锁需要必会的知识,刚开始可能觉得是个新奇陌生的词汇,但是等你慢慢的去接触它。了解它,你会发现它真的能帮你解决很多看起来很难的知识,会让你觉得那些看起来很高大上的东西,原来是这么回事啊,比如偏向锁,轻量级锁什么的,总之,作为一个Java程序员,Mark Word是你需要要掌握的!

​

当然,由于个人水平有限,本篇描述可能存在有误的地方,烦请指正,大家共同学习,一起进步!

​

涨姿势啦!Java程序员装X必备词汇之对象标记Mark Word!的更多相关文章

  1. Java程序员25个必备的Eclipse插件

    原文:http://www.fromdev.com/2012/01/25-best-free-eclipse-plug-ins-for-java.html "工欲善其事, 必先利器" ...

  2. Java程序员必备的6款最佳开发工具

    工欲善其事,必先利其器.每一个Java程序员都有其惯用的工具组件.对于Java程序员,各种有用的软件和工具泛滥成灾.初级开发人员要么找不到合适的工具,要么在寻找过程中浪费了大量的时间.下面,我将为大家 ...

  3. Java程序员必备的 15框开发工具

    15款Java程序员必备的开发工具 如果你是一名Web开发人员,那么用膝盖想也知道你的职业生涯大部分将使用Java而度过.这是一款商业级的编程语言,我们没有办法不接触它. 对于Java,有两种截然不同 ...

  4. Java 程序员必备的 15 个框架,前 3 个地位无可动摇!

    Java 程序员方向太多,且不说移动开发.大数据.区块链.人工智能这些,大部分 Java 程序员都是 Java Web/后端开发.那作为一名 Java Web 开发程序员必须需要熟悉哪些框架呢? 今天 ...

  5. 干货收藏 | Java 程序员必备的一些流程图

    阅读本文大概需要 6 分钟. 转载自:https://juejin.im/post/5d214639e51d4550bf1ae8df 1.Spring 的生命周期 Spring 作为当前 Java 最 ...

  6. Java 程序员必备的一些流程图

    1.spring的生命周期 2.TCP三次握手,四次挥手 3.线程池执行流程图 4.JVM内存结构 5.Java内存模型 6.springMVC执行流程图 7.JDBC执行流程 8.spring cl ...

  7. Java程序员必备的一些流程图

    Java程序员必备的一些流程图 转自https://juejin.im/post/5d214639e51d4550bf1ae8df 前言: 整理了一些Java基础流程图/架构图,做一下笔记,大家一起学 ...

  8. Java程序员面试必备:Volatile全方位解析

    前言 volatile是Java程序员必备的基础,也是面试官非常喜欢问的一个话题,本文跟大家一起开启vlatile学习之旅,如果有不正确的地方,也麻烦大家指出哈,一起相互学习~ 1.volatile的 ...

  9. Android java程序员必备技能,集合与数组中遍历元素,增强for循环的使用详解及代码

    Android java程序员必备技能,集合与数组中遍历元素, 增强for循环的使用详解及代码 作者:程序员小冰,CSDN博客:http://blog.csdn.net/qq_21376985 For ...

随机推荐

  1. Redis在linux系统中的优化

    通常来看,Redis开发和运维人员更加关注的是Redis本身的一些配置优化,例如AOF和RDB的配置优化.数据结构的配置优化等,但是对于操作系统是否需要针对Redis做一些配置优化不甚了解或者不太关心 ...

  2. 12.QT项目中启用c++11语法支持

    greater Than(QT_MAJOR_VERSION, 4): CONFIG += c++11 less Than(QT_MAJOR_VERSION, 5): QMAKE_CXXFLAGS += ...

  3. 27、异常处理(except)

    27.1.什么是异常: 1.异常介绍: 异常就是程序运行时发生错误的信号,在程序出错的时候,则会产生一个异常,若程序没有处理它,则会抛出该异常, 程序的运行也会随之终止,在python中,错误触发的异 ...

  4. Java实验项目二——打印某年某月日历

    Program:打印万年历(输入年份,月份,输出该月的日历,已知1900年1月1日是星期一), 要 求: (1)编写一个方法判断闰年: (2)编写一个方法判断某年某月有多少天: (3)编写一个方法计算 ...

  5. XCTF_RE-Crc-300

    这题讲道理其实还算简单的,还以为是啥算法呢..吓我一跳..拖入ida之后,发现逻辑还是挺清晰的 这个是关键函数,第一个if就可以求出后10个字符是啥了.. 接下就是对一个列表的赋值,然后就是一个dow ...

  6. WPF特效:流光动画效果

    今天有人问我想实现一个流光效果.我简单的录了一下他想实现效果的GIF. 实际效果如下:因为今天有事情,只是大概实现了一个效果.主要使用PointAnimationUsingKeyFrames动画效果来 ...

  7. TWAIN.CPP

    include "twaincpp.h" #include <QtGlobal> #include <QDateTime> #include <QSt ...

  8. C语言:printf*("%x")

    #include <stdio.h> int main(){ int a = 100; char str[20] = "hello world!"; char *zza ...

  9. ASP.NET C# 打包再修改aspx文件报错解决方案

    aspx文件最开始: <%@ page language="C#" autoeventwireup="true" inherits="tiddk ...

  10. SpringBoot 构造器注入、Setter方法注入和Field注入对比

    0. 引入 今天在看项目代码的时候发现在依赖注入的时候使用了构造器注入,之前使用过 Field 注入和 Setter 方法注入,对构造器注入不是很了解.经过查阅资料看到,Spring 推荐使用构造器注 ...