对象头

class oopDesc {
 ...
private:
  volatile markOop _mark;
  union _metadata {
    Klass*      _klass;
    narrowKlass _compressed_klass;
  } _metadata;
  ...
}

在hotspot中对象指针称为oop(ordinary object pointer),而oopDesc则是对象头的结构.。除了Klass(之所以叫klass是因为class是C++关键字)指针外,,还由一个_mark字段,,是因为除了对象的class信息以外,还有一些对象信息需要保留, 比如GC年龄, 锁状态等。

markOop用于存储运行时自身的数据,包括哈希码、GC分代年龄、偏向锁标记、线程持有的锁、偏向线程ID、偏向时间戳等。从markOop.hpp中的注释中,我们可以详细的了解到Markword各个bit位所代表的内容和含义。以32位的系统为例,能总结出如下表:

其中轻量级锁和偏向锁是Java6 对 synchronized 锁进行优化后新增加的,稍后我们会简要分析。这里我们主要分析一下重量级锁也就是通常说synchronized的对象锁,锁标识位为10,其中指针指向的是monitor对象(也称为管程或监视器锁)的起始地址。每个对象都存在着一个 monitor 与之关联,对象与其 monitor 之间的关系有存在多种实现方式,如monitor可以与对象一起创建销毁或当线程试图获取对象锁时自动生成,但当一个 monitor 被某个线程持有后,它便处于锁定状态。在Java虚拟机(HotSpot)中,monitor是由ObjectMonitor实现的,其主要数据结构如下(位于HotSpot虚拟机源码ObjectMonitor.hpp文件,C++实现的)

ObjectMonitor() {
    _header       = NULL;
    _count        = 0; //记录个数
    _waiters      = 0,
    _recursions   = 0;
    _object       = NULL;
    _owner        = NULL;
    _WaitSet      = NULL; //处于wait状态的线程,会被加入到_WaitSet
    _WaitSetLock  = 0 ;
    _Responsible  = NULL ;
    _succ         = NULL ;
    _cxq          = NULL ;
    FreeNext      = NULL ;
    _EntryList    = NULL ; //处于等待锁block状态的线程,会被加入到该列表
    _SpinFreq     = 0 ;
    _SpinClock    = 0 ;
    OwnerIsThread = 0 ;
}

ObjectMonitor中有两个队列,_WaitSet 和 _EntryList,用来保存ObjectWaiter对象列表( 每个等待锁的线程都会被封装成ObjectWaiter对象),_owner指向持有ObjectMonitor对象的线程,当多个线程同时访问一段同步代码时,首先会进入 _EntryList 集合,当线程获取到对象的monitor 后进入 _Owner 区域并把monitor中的owner变量设置为当前线程同时monitor中的计数器count加1,若线程调用 wait() 方法,将释放当前持有的monitor,owner变量恢复为null,count自减1,同时该线程进入 WaitSet集合中等待被唤醒。若当前线程执行完毕也将释放monitor(锁)并复位变量的值,以便其他线程进入获取monitor(锁)。如下图所示  

由此看来,monitor对象存在于每个Java对象的对象头中(存储的指针的指向),synchronized锁便是通过这种方式获取锁的,也是为什么Java中任意对象可以作为锁的原因,同时也是notify/notifyAll/wait等方法存在于顶级对象Object中的原因(关于这点稍后还会进行分析)。

_klass是存在于union类型的_metadata中的,union类型的分配是按成员最大的那个进行分配的, 然后对这块内存的解释取决于代码中使用的是哪个字段。

typedef juint  narrowKlass; // typedef uint32_t juint;

typedef class markOopDesc* markOop;

对象头默认情况占16字节,在开启压缩对象指针时(通过-XX:+UseCompressedClassPointers),占12字节,默认状态是开启的. 

对象成员

  • long / double - 8 bytes
  • int / float - 4 bytes
  • short / char - 2 bytes
  • byte/boolean - 1 bytes
  • reference type - 4 or 8 bytes

heap小于32G时, 指针压缩默认开启.。JVM相应的控制参数为: -XX:+/-UseCompressedOops。

对象局部

与对象内存布局相关的命令如下:

(1)-XX:+UseCompressedClassPointers。

(2)-XX:+/-UseCompressedOops。

(3)-XX:PrintFieldLayout可查看对象的内存布局。

(4)-XX:FieldsAllocationStyle=mode, 默认mode是1。

(5)-XX:+/-CompactFields 由于填充会形成gap空洞, 比如使用压缩kclass指针时, 头占12字节, 后面如果是long的话, long的对齐要求是8字节, 中间会有4个字节的空洞, 为了高效利用, 可以把int/short/byte等比较小的对象塞进去, 与此同时JVM提供了开关控制该特性-XX:+/-CompactFields, 默认开启。

参考:

(1)https://blog.csdn.net/lqp276/article/details/52190503

(2)https://www.cnblogs.com/duanxz/tag/JVM%E8%99%9A%E6%8B%9F%E6%9C%BA/

(3)JVM之压缩指针(CompressedOops)https://juejin.im/post/5c4c8ad9f265da6179752b03

Hotspot对象的内存布局的更多相关文章

  1. HotSpot源码分析之C++对象的内存布局

    HotSpot采用了OOP-Klass模型来描述Java类和对象.OOP(Ordinary Object Pointer)指的是普通对象指针,而Klass用来描述对象的具体类型.为了更好理解这个模型, ...

  2. JVM——深入分析对象的内存布局

    概述 一个对象本身的内在结构需要一种描述方式,这个描述信息是以字节码的方法存储在方法区中的.Class本身就是一个对象,都以KB为单位,如果new Integer()为了表示一个数据就占用KB级别的内 ...

  3. Java对象的内存布局

    对象的内存布局 平时用java编写程序,你了解java对象的内存布局么? 在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域: 对象头 实例数据 对齐填充 对象头 对象头包括两部分信息: ...

  4. jvm学习记录-对象的创建、对象的内存布局、对象的访问定位

    简述 今天继续写<深入理解java虚拟机>的对象创建的理解.这次和上次隔的时间有些长,是因为有些东西确实不好理解,就查阅各种资料,然后弄明白了才来做记录. (此文中所阐述的内容都是以Hot ...

  5. Java对象的内存布局以及对象所需内存大小计算详解

    1. 内存布局 在HotSpot虚拟机中,对象的内存布局可以分为三部分:对象头(Header). 实例数据(Instance Data)和对齐填充(Padding). 1) 对象头(Header): ...

  6. JVM总结-java对象的内存布局

    在 Java 程序中,我们拥有多种新建对象的方式.除了最为常见的 new 语句之外,我们还可以通过反射机制.Object.clone 方法.反序列化以及 Unsafe.allocateInstance ...

  7. Java对象创建的过程及对象的内存布局与访问定位

    这里以HotSpot为例,且所说的对象指普通的Java对象,不包括数组和Class对象等. 1.对象创建的过程 1.类加载.解析.初始化:虚拟机遇到new时先检查此指令的参数是否能在常量池中找到类的符 ...

  8. JVM中对象的内存布局与访问定位

      一.对象的内存布局 已主流的HotSpot虚拟机来说,   在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header).实例数据(Instance Data)和对齐填 ...

  9. Java内存区域与内存溢出异常---对象的内存布局和对象的访问定位

    对象的内存布局   在HotSpot虚拟机中,对象在内存中的存储布局可以划分为三个区域:对象头,实例数据,对齐填充.   对象头包括两部分信息:第一部分用于存储对象自身的运行时数据,如哈希码,GC分代 ...

随机推荐

  1. python基础08--迭代器,生成器

    1.1 迭代器 1.可迭代对象:str,list,tuple,set,dict, 迭代器:f(文件),range() 可迭代对象和迭代器都可以for循环 可迭代对象不会计数, 迭代器会计数,上面操作到 ...

  2. android shap画圆(空心圆、实心圆)

    实心圆: <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android=" ...

  3. React Children 使用

    React 有一个特殊的属性children, 主要用于组件需要渲染内容,但它并不知道具体要渲染什么内容,怎么会有这种使用场景?确实比较少,但并不是没有,比如弹出框.当你写一个弹出框组件的时候,你知道 ...

  4. Crontab常用命令总结

    一.启动服务 /sbin/service crond start 二.关闭服务 /sbin/service crond stop 三.重启服务 /sbin/service crond restart ...

  5. JavaScript 流程控制(一)顺序结构与分支结构

    语句:语句可以理解为一个行为,循环语句和判断语句就是典型的语句.一个程序有很多个语句组成,一般情况下分号;分割一个一个的语句:如果省略分号,则由解析器确定语句的结尾(不推荐使用) 一.流程控制 流程控 ...

  6. Unable to establish SSL connection

    当wget出现如下错误: Can't connect to HTTPS URL because the SSL module is not available 需要安装: # sudo apt-get ...

  7. Win10下免安装版MySQL5.7的安装和配置

    1.MySQL5.7解压 2.新建配置文件my.ini放在D:\Free\mysql-5.7.26-winx64目录下 [mysql] # 设置mysql客户端默认字符集 default-charac ...

  8. JAVA自定义查询策略

    此文章为个人笔记,考虑之后工作用到,博客方便于查找,如果可以给他人提供参考价值,那再好不过 1.定义查询接口参数 package com.qy.code.generator.query; import ...

  9. 【异常】org.eclipse.jgit.api.errors.TransportException: git@xxx.xxx.xxx/base-config-center.git: channel is not opened.

    一.异常原因 连不上git仓库,可能原因有: 1.)git仓库不存在 2)连接git仓库超时 二.对应的解决办法 1) 创建对应仓库 2) 2.1 换个服务性能更好的部署gitlab 2.2 可以研究 ...

  10. Ansible--Ansible之Roles

    Ansible之Roles Roles介绍 ansible自1.2版本引入的新特性,用于层次性.结构化地组织playbook.roles能够根据层次型结构自动装载变量文件.tasks以及handler ...