精华内容
下载资源
问答
  • 在对齐对象的时候
    千次阅读
    2020-08-13 23:16:41

    对象在堆内存中的存储布局可以划分为三个部分:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。

    对象头(Header)

    对象头分为两类信息:一类是用于存储对象自身的运行时数据,一类是类型指针。

    1. 第一部分是用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。 这部分数据的长度在32位和64位的虚拟机(未开启压缩指针)中分别为32个比特和64个比特,官方称它为"Mark Word"

    2. 第二部分是类型指针,即对象指向它的类型元数据的指针,Java虚拟机通过这个指针来确定该对象是哪个类的实例。 并不是所有的虚拟机实现都必须在对象数据上保留类型指针,换句话说,查找对象的元数据信息并不一定要经过对象本身。此外,如果对象是一个Java数组,那在对象头中还必须有一块用于记录数组长度的数据。因为虚拟机可以通过普通Java对象的元数据信息确定Java对象的大小,但是如果数组的长度是不确定的,将无法通过元数据中的信息u梯段出数组的大小。

    实例数据(Instance Data)

    实例数据:示例数据部分是对象真正存储的有效信息,即我们在程序代码里面所定义的各种类型的字段内容,无论是从父类继承下来的,还是在子类中定义的字段都必须记录起来。 这部分的存储顺序会受到虚拟机分配策略参数(-XX:FieldsAllocationStyle 参数)和字段在Java源码中定义顺序的影响。

    HotSpot虚拟机默认的分配顺序为longs/doubles、ints、shorts/chars、bytes/booleans、oops(Ordinary Object Pointers, OOPs),从以上默认的分配策略中可以看到,相同宽度的字段总是被分配到一起存放,在满足这个前提条件的情况下,在父类中定义的变量会出现在子类之前。 如果虚拟机的+XX:CompactFields参数值为true(默认为true),那子类之中较窄的变量也允许插入父类变量的空隙中,以节省出一点点空间。

    对齐填充(Padding)

    对齐填充:对齐填充并不是必然存在的,也没有特殊的含义,它仅仅起着占位符的作用。 由于HotSpot虚拟机的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说就是任何对象的大小都必须是8字节的整数倍。 对象头部分已经被精心设计成正好是8字节的倍数(1倍或2倍),因此,如果对象实例数据部分没有对齐的话,就需要通过对齐填充来补全。

    更多相关内容
  • 选中多个对象,再用工具栏中的对齐对象工具 和分布对象工具 来调整对象的对齐方式和分布方式。  例如,选中3个对象,单击对齐对象工具,选择“左边缘”使3个对象的左边缘对齐,如图1 所示;单击分布对象工具,...
  • 选中多个对象,再用工具栏中的对齐对象工具 和分布对象工具 来调整对象的对齐方式和分布方式。  例如,选中3个对象,单击对齐对象工具,选择“左边缘”使3个对象的左边缘对齐,如图1 所示;单击分布对象工具,...
  • AutoCad对象对齐,均布对象工具,包括左对齐、居中对齐,右对其,上对其、下对齐等6种对其方式,此外还有指定点对其,指定对象对齐,均布工具有垂直均布、水平均布等8种均布方式,还包括指定间距均布,制定范围均布...
  • 草图插件可按对象的质心对齐对象许多情况下,Magic Aligner可以自动进行光学调整。 查看我我的网站上写。 安装 并解压缩,然后打开.sketchplugin文件进行自动安装。 用法 通过边框将对象居中 选择一个对象 ...
  • word启用或关闭对齐网格和对齐对象选项.docx
  • 右对齐键 右对齐对象的关键点。安装用安装$ npm i right-align-keys --save用法示例 align ( { a : 'x' , bbb : 'x' , ccccc : 'x' , ddddddd : 'x'} ) ; 结果是: { ' a' : 'x' , ' bbb' : 'x' , ' ccccc' : 'x' , ...
  • Align库允许您将显示对象彼此对齐 计算子显示对象相对于父显示对象的x和y位置。 关系:中心,左,右,上或下 通过传入显示对象或传入数字来进行计算。 如果传入显示对象,则将设置孩子的x和/或y位置。 您可以选择...
  • PowerPoint2021中对齐所选对象.docx
  • 使用候选对象对齐对象检测.zip
  • office使图表、图片和对象与网格对齐,从而对齐这些对象.docx
  • 使用候选对象对齐对象检测(1).zip
  • 使用候选对象对齐对象检测(2).zip
  • cad 单一对象坐标对齐 lisp
  • 200使用候选对象对齐对象检测_new.pdf
  • 84使用候选对象对齐对象检测_new.pdf
  • 15使用候选对象对齐对象检测_new.pdf
  • 复选框后边一句文字,如何让它们两个并排对齐,相差几个像素的问题,但各个浏览器就是不一样,特别是ie6,经测试解决方法如下,有类似问题的朋友可以参考下
  • 对象均布与对齐工具

    2015-05-22 15:07:36
    对象均布与对齐工具,框选对象对齐到边,可预览。
  • 具有潜在特征对齐和过滤功能以进行对象检测的子类别聚类
  • 将 2D 线对象 LINE_OBJ 捕捉到以下约束之一 - 一种。 另一组线路。 湾定义的网格。 C。 鼠标光标(无约束)。
  • 下面小编就为大家带来一篇C++对象内存分布详解(包括字节对齐和虚函数表)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • PowerPoint2021对齐多个对象方法.docx
  • stateflow align 实现了三个基本函数来对齐和修改图形 stateflow 对象: 1.对齐所有选定的对象都与选择中的最后一个对象对齐。 因此必须至少选择两个对象。 对特定的对齐线进行对齐。 它们是矩形图形对象的左侧、...
  • JVM对象创建和对齐填充详解

    千次阅读 2021-03-04 10:58:42
    Java虚拟机遇到一条字节码new指令时,检查指令的参数能否常量池中定位一个符号引用。 检查类是否被加载 检查这个符号引用代表的类是否已被加载、解析和初始化过。 类加载过程 确定所加载类占用内存大小,...

    1.虚拟机对象创建

    ​ 语言层面上,创建对象通常(例外:复制、反序列化)仅仅是一个new关键字而已,本文所探讨的虚拟机对象创建不包含数组和Class对象等,就对于普通对象而言。

    1. 常量池中定位符号引用

      • Java虚拟机遇到一条字节码new指令时,检查指令的参数能否在常量池中定位一个符号引用。
    2. 检查类是否被加载

      • 检查这个符号引用代表的类是否已被加载、解析和初始化过。
    3. 类加载过程

      • 确定所加载类占用内存大小,并划分空间进行存储
      • 划分空间两种方式
        • 指针碰撞,堆内存规整
          • 所代表的垃圾收集器有Serial、ParNew,用算法标记-整理算法
          • 可能导致线程不安全问题,可看下方tip1:指针碰撞所带来的线程安全问题。
        • 空闲列表
          • 所代表的垃圾收集器CMS,采用标记清除算法
      • 对象设置
        • 虚拟机还要对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码对象的GC分代年龄等信息。。这些信息存放在对象的对象头(Tip2:对象都存储内容)之中。

      上面工作都完成之后,从虚拟机的视角来看,一个新的对象已经产生了。但是从Java程序的视角看来,对象创建才刚刚开始——构造函数,即Class文件中的()方法还没有执行,所有的字段都为默认的零值。梳理完虚拟机对象创建过程后,在来看看对象在堆内存的存储布局.

    2.对象的内存布局

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

    2.1对象头

    HotSpot虚拟机对象的对象头部分包括两类信息:

    1. 第一类是用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,官方称它为“Mark Word”。
    2. 第二类类型指针

    2.2实例数据

    ​ 实例数据部分是对象真正存储的有效信息,即我们在程序代码里面所定义的各种类型的字段内容,无论是从父类继承下来的,还是在子类中定义的字段都必须记录起来。这部分的存储顺序会受到虚拟机分配策略参数(-XX:FieldsAllocationStyle参数)和字段在Java源码中定义顺序的影响。

    ​ HotSpot虚拟机默认的分配顺序为longs/doublesintsshorts/charsbytes/booleans、oops。

    ​ 从以上默认的分配策略中可以看到,相同宽度的字段总是被分配到一起存放,在满足这个前提条件的情况下,在父类中定义的变量会出现在子类之前。

    ​ 对象的第三部分是对齐填充,这并不是必然存在的,也没有特别的含义,它仅仅起着占位符的作用。

    2.3对齐填充

    ​ 由于HotSpot虚拟机的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说就是任何对象的大小都必须是8字节的整数倍。对象头部分已经被精心设计成正好是8字节的倍数(1倍或者2倍),因此,如果对象实例数据部分没有对齐的话,就需要通过对齐填充来补全。

    ​ 总的一句话来说,“数据项仅仅能存储在地址是数据项大小的整数倍的内存位置上(分别为偶地址、被4整除的地址、被8整除的地址)”比如int类型占用4个字节,地址仅仅能在0,4,8等位置上。

    ​ 要讲好对齐填充,其实还是得去了解c++实现。

    例1:

    #include <stdio.h>
    struct xx{
        char b;
        int a;
        int c;
        char d;
    };   //总体结构用了16个字节
    
    struct xx{
            char b; 
            char d;
            int a;          
            int c;                  
    };	//总体结构占用12个字节
    

    只说结论,过程可以将对应得字段地址打印出来。

    会发现b之后填充3字节,d之后也会填充3字节,这儿就体现了对齐填充,平时代码,分类要做好,对齐填充也要注意。

    Tip1:指针碰撞所带来的线程安全问题。

    ​ 对象创建在虚拟机中是非常频繁的行为,即使仅仅修改一个指针所指向的位置,在并发情况下也并不是线程安全的,可能出现正在给对象A分配内存,指针还没来得及修改,对象B又同时使用了原来的指针来分配内存的情况。解决这个问题有两种可选方案:

    1. 一种是对分配内存空间的动作进行同步处理——实际上虚拟机是采用CAS配上失败重试的方式保证更新操作的原子性;
    2. 另外一种是把内存分配的动作按照线程划分在不同的空间之中进行,即每个线程在Java堆中预先分配一小块内存,称为本地线程分配缓冲(Thread Local AllocationBuffer,TLAB),哪个线程要分配内存,就在哪个线程的本地缓冲区中分配,只有本地缓冲区用完了,分配新的缓存区时才需要同步处理。虚拟机是否使用TLAB,可以通过-XX:+/-UseTLAB参数来设定。

    Tip2:对象头存储内容

    对象头又称"Mark Word"。定义了一个有着动态数据的数据结构例如在32位的HotSpot虚拟机中,如对象未被同步锁锁定的状态下,Mark Word的32个比特存储空间中的25个比特用于存储对象哈希码4个比特用于存储对象分代年龄2个比特用于存储锁标志位1个比特固定为0,在其他状态(轻量级锁定、重量级锁定、GC标记、可偏向)下对象的存储内容如表2-1所示。

    在这里插入图片描述

    对象头的另外一部分是类型指针,即对象指向它的类型元数据的指针,Java虚拟机通过这个指针来确定该对象是哪个类的实例。

    展开全文
  • ECharts多行文字两端对齐 - 饼图
  • 比如这个刀鞘坐标内部 而刀柄轴点原点 开启轴点调整 选择菜单中的"对齐",然后点击要对齐到的轴点对象

    比如这个刀鞘坐标在内部

    而刀柄轴点在原点

    开启轴点调整

    选择菜单中的"对齐",然后点击要对齐到的轴点对象

    展开全文
  • FIXUILABELS 遍历包含当前 MATLAB 图窗中的 java 对象,并为所有“文本”样式的 uicontrol 调整底层 java 对象,以便它们的文本更好地与相邻的“编辑”样式 uicontrol 同一高度对齐。 FIXUILABELS(FIG) 修复图...
  • java对象对齐规则

    千次阅读 2019-09-22 15:52:30
    零、注记 本文是一次讨论的流水账,旨在讲明原理就行了,...简单一句话:对象实例jvm堆内存中存放的结构。就是随便实例化一个对象new Object(),他堆内存里面是怎么放置的。 看下面这个jol工具给出的java.mat...

    零、注记

    本文是一次讨论的流水账,旨在讲明原理就行了,行文大家不要抱太大的希望。

    另外,特别重要的是,本文是基于hotspot来讨论的,不同的java虚拟机可能是有不同的,这一点,一定要注意。

     

    一、什么是对象的内存布局

    简单一句话:对象实例在jvm堆内存中存放的结构。就是随便实例化一个对象new Object(),他在堆内存里面是怎么放置的。

    看下面这个jol工具给出的java.math.BigInteger内存布局的例子:一个对象的内存布局包含了对象头object header、实例数据域和对齐填充alignment padding(可能有,可能没有,下面再细说)。

    ***** 64-bit VM, compressed references enabled: ***************************
    java.math.BigInteger object internals:
     OFFSET  SIZE  TYPE DESCRIPTION                    VALUE
          0    12       (object header)                N/A
         12     4   int BigInteger.signum              N/A
         16     4 int[] BigInteger.mag                 N/A
         20     4   int BigInteger.bitCount            N/A
         24     4   int BigInteger.bitLength           N/A
         28     4   int BigInteger.lowestSetBit        N/A
         32     4   int BigInteger.firstNonzeroIntNum  N/A
         36     4       (loss due to the next object alignment)
    Instance size: 40 bytes (estimated, the sample instance is not available)
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

     

    二、查看对象内存布局的工具

    1. openjdk jol

    openjdk官网给了一个查看对象内存布局的工具,jol(java object layout)http://openjdk.java.net/projects/code-tools/jol/

    怎么拿呢?openjdk给了maven的依赖:

    Use as Library Dependency
    OpenJDK Community semi-regularly pushes the releases to Maven Central. Therefore, you can use it right away by setting up the Maven dependency:
    <dependency>
        <groupId>org.openjdk.jol</groupId>
        <artifactId>jol-core</artifactId>
        <version>put-the-version-here</version>
    </dependency>
    It is a good idea to review JOL Samples and CLI tools source before using the tool at its full capacity as the library.

    怎么用呢?上面给的jol的链接页面,最下面官方给了jol samples的链接,使用极其简单,就是一个ClassLayout就没了。示例就懒得给了,看samples吧。

    jol sampleshttp://hg.openjdk.java.net/code-tools/jol/file/tip/jol-samples/src/main/java/org/openjdk/jol/samples/

    jol sourcecodehttp://central.maven.org/maven2/org/openjdk/jol/

    如果不想看samples呢?这篇参考文章给了用例和讲解《JDK之JVM中Java对象的头部占多少byte》https://my.oschina.net/u/2518341/blog/1838006

    那如果不想用jol工具怎么办呢?卧槽,我好难啊。。。

    2. sun.misc.Unsafe

    • sun.misc.Unsafe.objectFieldOffset方法获取第一个field的偏移地址(弊端:当对象头后面有padding的时候,你看不出来,什么时候有padding呢,下面会细说)
    • JDK8及之前,是用的sun.misc.Unsafe
    • JDK9有两个Unsafe,除了sun.misc.Unsafe还提供了jdk.internal.misc.Unsafe,但是jdk.internal.misc.Unsafe不像sun.misc.Unsafe是可以通过反射使用的,实际上目前在JDK9以后的版本中,sun.misc.Unsafe中组合了jdk.internal.misc.Unsafe的实例,实际上sun.misc.Unsafe是一个简单包装,你可以自己翻翻源码。

    至少有两种方式可以获取到sun.misc.Unsafe实例对象:

    • 通过反射sun.misc.Unsafe的构造函数获取其实例对象;
    • 通过反射sun.misc.Unsafe的实例属性theUnsafe获取其实例对象;
    package cn.wxy.unsafe;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import sun.misc.Unsafe;
    
    public class UnsafeUtils {
    	/**
    	 * 通过反射sun.misc.Unsafe的构造函数获取其实例对象
    	 * 
    	 * @return sun.misc.Unsafe
    	 */
    	public static Unsafe getUnsafeByConstructor() {
    		Constructor<Unsafe> constructor = null;
    		try {
    			constructor = Unsafe.class.getDeclaredConstructor();
    			constructor.setAccessible(true);
    			return constructor.newInstance();
    		} catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException
    				| IllegalArgumentException | InvocationTargetException e) {
    			e.printStackTrace();
    		}
    		return null;
    	}
    
    	/**
    	 * 通过反射sun.misc.Unsafe的属性获取其实例对象
    	 * 
    	 * @return sun.misc.Unsafe
    	 */
    	public static Unsafe getUnsafeByField() {
    		Field field = null;
    		try {
    			field = Unsafe.class.getDeclaredField("theUnsafe");
    			field.setAccessible(true);
    			return (Unsafe) field.get(null);
    		} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
    			e.printStackTrace();
    		}
    		return null;
    	}
    }

     

    三、#program pack(n)

    C、C++里面的对齐规则,默认32bit机器是4byte对齐,64比特机器是8byte对齐。那如果想修改默认对齐规则呢?在源码开头写上#program pack(n)声明就行了。

    #program pack(n),n必须是2的次方,这个声明的作用就是告诉编译器使用的对齐方式是n(不管对齐方式n是1byte、2byte、4byte、8byte、16byte还是多少,对齐规则不变,都如下所示),就不再使用默认的对齐方式。

    在C、C++里面的对齐规则如下(链接:https://baike.baidu.com/item/%E5%86%85%E5%AD%98%E5%AF%B9%E9%BD%90/9537460?fr=aladdin):

    规则:

    1、数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行

    2、结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行

    3、结合1、2可推断:当#pragma pack的n值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任何效果。

    众所周知,jvm是C、C++写的,那java默认的8byte对齐规则,和这个一样吗?

    结果是有相同的部分,也有不同的部分:

    • 对象内的对齐规则:对象头、field和padding和第一条规则一致;
    • 对象间的对齐规则:不一样,java默认就是对象间8byte对齐,不管对象头的size、fields中最大的field的size是否小于8byte,如果是16byte,那对象之间就是按照16byte对齐,一样的也不管对象头的size、fields中最大的field的size是否小于16byte。

    第五部分,通过jol会给出两条规则的示例,更多的下面部分再细说。

    补充

    为什么要对齐?1. 效率;2. 有些OS平台有要求。

    参考链接:《Data alignment: Straighten up and fly right》https://developer.ibm.com/articles/pa-dalign/

     

    四、java对象的内存布局

    java对象的内存布局,在周志明的《深入理解java虚拟机》第二章有讲解,三个结构:

    • 对象头:mark word和元数据指针,注意如果是数组对象,其对象头除了mark word和元数据指针之外,还有个4byte int类型的length,本文未讨论数组类型(如果是数组类型,那么对象头加4byte,其他规则不变)。
    • 实例数据域
    • 对齐填充padding

    1. 对象头

    如果你理解hotspot的oop-klass二分模型,那这里你一定了解过。jvm中对象的对象头分为两部分,mark work和元数据指针。

    在hotspot的oop.hpp文件中class oopDesc描述了对象头,链接和源码如下:

    src/share/vm/oops/oop.hpp:http://hg.openjdk.java.net/jdk8/jdk8/hotspot/file/87ee5ee27509/src/share/vm/oops/oop.hpp

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

    其中,markOop _mark官方文档叫做mark word,union _metadata中的Klass* _klass是元数据指针,指向持久代或者metaspace中每个类的元数据,也就是java.lang.Class类实例访问的jvm中该类的数据结构。

    mark word的内存结构及源码如下,其中在32bit机器上是占4byte,在64bit机器上是8byte,不管是否开启压缩指针-XUseCompressOops。是否开启压缩指针,影响的是元数据指针_klass的size。

    src/share/vm/oops/markOop.hpp:http://hg.openjdk.java.net/jdk8/jdk8/hotspot/file/87ee5ee27509/src/share/vm/oops/markOop.hpp

    // Bit-format of an object header (most significant first, big endian layout below):
    //
    //  32 bits:
    //  --------
    //             hash:25 ------------>| age:4    biased_lock:1 lock:2 (normal object)
    //             JavaThread*:23 epoch:2 age:4    biased_lock:1 lock:2 (biased object)
    //             size:32 ------------------------------------------>| (CMS free block)
    //             PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object)
    //
    //  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)
    //
    //  - hash contains the identity hash value: largest value is
    //    31 bits, see os::random().  Also, 64-bit vm's require
    //    a hash value no bigger than 32 bits because they will not
    //    properly generate a mask larger than that: see library_call.cpp
    //    and c1_CodePatterns_sparc.cpp.
    //
    //  - the biased lock pattern is used to bias a lock toward a given
    //    thread. When this pattern is set in the low three bits, the lock
    //    is either biased toward a given thread or "anonymously" biased,
    //    indicating that it is possible for it to be biased. When the
    //    lock is biased toward a given thread, locking and unlocking can
    //    be performed by that thread without using atomic operations.
    //    When a lock's bias is revoked, it reverts back to the normal
    //    locking scheme described below.
    //
    //    Note that we are overloading the meaning of the "unlocked" state
    //    of the header. Because we steal a bit from the age we can
    //    guarantee that the bias pattern will never be seen for a truly
    //    unlocked object.
    //
    //    Note also that the biased state contains the age bits normally
    //    contained in the object header. Large increases in scavenge
    //    times were seen when these bits were absent and an arbitrary age
    //    assigned to all biased objects, because they tended to consume a
    //    significant fraction of the eden semispaces and were not
    //    promoted promptly, causing an increase in the amount of copying
    //    performed. The runtime system aligns all JavaThread* pointers to
    //    a very large value (currently 128 bytes (32bVM) or 256 bytes (64bVM))
    //    to make room for the age bits & the epoch bits (used in support of
    //    biased locking), and for the CMS "freeness" bit in the 64bVM (+COOPs).
    //
    //    [JavaThread* | epoch | age | 1 | 01]       lock is biased toward given thread
    //    [0           | epoch | age | 1 | 01]       lock is anonymously biased
    //
    //  - the two lock bits are used to describe three states: locked/unlocked and monitor.
    //
    //    [ptr             | 00]  locked             ptr points to real header on stack
    //    [header      | 0 | 01]  unlocked           regular object header
    //    [ptr             | 10]  monitor            inflated lock (header is wapped out)
    //    [ptr             | 11]  marked             used by markSweep to mark an object
    //                                               not valid at any other time
    //
    //    We assume that stack/thread pointers have the lowest two bits cleared.

    在64bit机器上,元数据指针的大小是会受压缩类指针是否开启的影响的。32bit机器,元数据指针大小4byte,在64byte机器上,默认是开启压缩指针的(-XX:+UseCompressedClassPointers),开启之后,元数据指针也是4byte,关闭则占8byte。

    补充:

    在Scott oaks写的《java性能权威指南》第八章8.22节提到了当heap size堆内存大于32GB是用不了压缩指针的,对象引用会额外占用20%左右的堆空间,也就意味着要40GB左右的内存才相当于开启了指针压缩的32GB堆空间。

    这是为什么呢?看下面引用中的红字(来自openjdk wiki:https://wiki.openjdk.java.net/display/HotSpot/CompressedOops)。开启压缩指针需要连续的地址空间(进程的虚拟地址空间),其实现原理了是64bit基址+32bit的偏移地址,32bit最大寻址空间是4GB,Java默认8byte对齐,所以开启了压缩指针之后呢,一个地址寻址不再是1byte,而是8byte,因为不管是32bit的机器还是64bit的机器,java对象都是8byte对齐的,而类是java中的基本单位,对应的堆内存中都是一个一个的对象。

    Compressed oops represent managed pointers (in many but not all places in the JVM) as 32-bit values which must be scaled by a factor of 8 and added to a 64-bit base address to find the object they refer to. This allows applications to address up to four billion objects (not bytes), or a heap size of up to about 32Gb. At the same time, data structure compactness is competitive with ILP32 mode.

    你还可以查看jvm中压缩指针参数文档,压缩指针有两个开关——-XX:+UseCompressedOops和-XX:+UseCompressedClassPoints,前者是压缩对象指针,后者是压缩类指针。

    -XX:-UseCompressedOops: 

    Disables the use of compressed pointers. By default, this option is enabled, and compressed pointers are used when Java heap sizes are less than 32 GB. When this option is enabled, object references are represented as 32-bit offsets instead of 64-bit pointers, which typically increases performance when running the application with Java heap sizes less than 32 GB. This option works only for 64-bit JVMs.

    It is also possible to use compressed pointers when Java heap sizes are greater than 32GB. See the -XX:ObjectAlignmentInBytes option.

    为什么坚持8byte对齐呢?Scott oaks在书上给了理由:

    2. 实例数据域

    实例数据域紧跟在对象头之后。一个类没有field,就不需要实例数据域,有那就按照第三部分#program pack对齐规则的第一条放在堆内存中。这部分在第五部分,给出jol的示例详细讨论其规则。

    3. padding

    对象内可以有padding也可以没有;对象间默认按照8byte对齐,对齐则不需要padding,否则需要padding补充8byte对齐,也在第五部分根据jol的示例详细讨论其规则。

     

    五、java的对齐规则

    1. 对象内的对齐规则:对象头、field和padding和#program pack第一条规则一致;
      1. 注意:对象头默认是没有padding的,是否需要padding,要看是32bit机器还是64bit机器,以及对象头后面跟的field size,这是#program pack的对齐规则导致的padding,而不是对象头导致padding,这一点看过很多人讨论错了。
    2. 对象间的对齐规则:不一样,java的对象间对齐如果是按照8byte,那就是8byte,不会再像#program pack中还需要和对象内最大size的属性比较;
      1. 解释:java默认就是对象间8byte对齐,不管对象头的size、fields中最大的field的size是否小于8byte;如果是16byte,那对象之间就是按照16byte对齐,一样的也不管对象头的size、fields中最大的field的size是否小于16byte。
      2. -XX:ObjectAlignmentInBytes=alignment:你可以通过这个JVM参数修改对象的对齐方式

     

    -XX:ObjectAlignmentInBytes=alignment

    Sets the memory alignment of Java objects (in bytes). By default, the value is set to 8 bytes. The specified value should be a power of two, and must be within the range of 8 and 256 (inclusive). This option makes it possible to use compressed pointers with large Java heap sizes.

    The heap size limit in bytes is calculated as:

    4GB * ObjectAlignmentInBytes

    Note: As the alignment value increases, the unused space between objects will also increase. As a result, you may not realize any benefits from using compressed pointers with large Java heap sizes.

    补充:前四个例子均来源于jol官网示例,我感觉足够说明了,就懒得动手了,第五个需要动手搞一下,但是我也没动手 ,直接用了和群友讨论的例子。感觉足够说明了,就偷懒取巧不动手了。

    例一、32bit机器的对象内存布局(默认对象间8byte对齐)

    ***** 32-bit VM: **********************************************************
    $ java -jar jol-cli/target/jol-cli.jar estimates java.math.BigInteger
    java.math.BigInteger object internals:
     OFFSET  SIZE  TYPE DESCRIPTION                    VALUE
          0     8       (object header)                N/A
          8     4   int BigInteger.signum              N/A
         12     4 int[] BigInteger.mag                 N/A
         16     4   int BigInteger.bitCount            N/A
         20     4   int BigInteger.bitLength           N/A
         24     4   int BigInteger.lowestSetBit        N/A
         28     4   int BigInteger.firstNonzeroIntNum  N/A
    Instance size: 32 bytes (estimated, the sample instance is not available)
    Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

    32bit机器上,对象头8byte,其中,mark word 4byte,元数据指针4byte。

    java.math.BigInteger有6个成员属性field,都是4byte(5个int基本数据类型每个4byte;一个int[]数组引用类型,这里其实保存的是指针,因为数组本身就是一个引用类型,所以在堆内存中还有自己对象结构,因此在本例BigInteger中的int[]其实是一个4byte指针),按照4byte对齐,对象头0~7byte,后面的每个field的起始地址都是4byte的整倍数,不需要额外的padding来对齐。

    对象大小:object header(mark word+metadata klass)+6*field = 8byte+4*6byte =32byte。

    因为32byte % 8byte= 0,所以对象间不需要额外的padding来帮助对齐。

     

    例二、64bit机器的对象内存布局(默认对象间8byte对齐,没有开启指针压缩)

    ***** 64-bit VM: **********************************************************
    java.math.BigInteger object internals:
     OFFSET  SIZE  TYPE DESCRIPTION                    VALUE
          0    16       (object header)                N/A
         16     8 int[] BigInteger.mag                 N/A
         24     4   int BigInteger.signum              N/A
         28     4   int BigInteger.bitCount            N/A
         32     4   int BigInteger.bitLength           N/A
         36     4   int BigInteger.lowestSetBit        N/A
         40     4   int BigInteger.firstNonzeroIntNum  N/A
         44     4       (loss due to the next object alignment)
    Instance size: 48 bytes (estimated, the sample instance is not available)
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

    64bit机器,未开启指针压缩,对象头16byte,其中mark word 8byte,元数据指针8byte。

    java.math.BigInteger有6个成员属性,其中int[] BigInteger.mag是引用类型,未开启指针压缩占8byte,剩下5个int各占4byte,对象头0~15byte,mag 8byte对齐,占16~23,剩下的5个4byte对齐,对象内不需要额外的padding对齐。

    对象大小:16byte+8byte+5*4byte = 44byte。

    因为44byte%8byte = 4byte,所以按照8byte对齐,对象间还需要额外的4byte来帮助对齐。

    所以,对象真正占用的内存是44byte + 4byte = 48byte。

     

    例三、64bit机器并开启压缩指针的对象内存布局(默认对象间8byte对齐)

    ***** 64-bit VM, compressed references enabled: ***************************
    java.math.BigInteger object internals:
     OFFSET  SIZE  TYPE DESCRIPTION                    VALUE
          0    12       (object header)                N/A
         12     4   int BigInteger.signum              N/A
         16     4 int[] BigInteger.mag                 N/A
         20     4   int BigInteger.bitCount            N/A
         24     4   int BigInteger.bitLength           N/A
         28     4   int BigInteger.lowestSetBit        N/A
         32     4   int BigInteger.firstNonzeroIntNum  N/A
         36     4       (loss due to the next object alignment)
    Instance size: 40 bytes (estimated, the sample instance is not available)
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

    64bit机器开启指针压缩,对象头12byte,其中mark word 8byte,元数据指针4byte。

    java.math.BigInteger 6个成员属性,因为开启了指针压缩,所以例二中的int[] BigInteger.mag不再占用8byte,而是4byte,其他5个成员属性int各占4byte。对象头12byte,每个成员属性按照4byte对齐,对象内不需要额外的padding来帮助对齐。

    对象大小:12byte + 6*4byte = 36byte。

    因为36byte % 8byte = 4byte,按照8byte对齐,所以对象间还需要额外的4byte来帮助对齐。

    所以对象真正占用的内存大小:36byte + 4byte = 40byte。

     

    例四、64bit机器并开启压缩指针、修改按照16byte对齐

    ***** 64-bit VM, compressed references enabled, 16-byte align: ************
    java.math.BigInteger object internals:
     OFFSET  SIZE  TYPE DESCRIPTION                    VALUE
          0    12       (object header)                N/A
         12     4   int BigInteger.signum              N/A
         16     4 int[] BigInteger.mag                 N/A
         20     4   int BigInteger.bitCount            N/A
         24     4   int BigInteger.bitLength           N/A
         28     4   int BigInteger.lowestSetBit        N/A
         32     4   int BigInteger.firstNonzeroIntNum  N/A
         36    12       (loss due to the next object alignment)
    Instance size: 48 bytes (estimated, the sample instance is not available)
    Space losses: 0 bytes internal + 12 bytes external = 12 bytes total

    对象按16byte对齐,也就意味着对象放到堆中的时候,其起始地址模16必须为0,即address % 16byte = 0。

    对象头和例三一样,12byte。

    开启指针压缩,对象内6个属性也都是4byte,对象内各个属性按照4byte对齐,不需要额外的padding。

    对象大小:12byte + 6*4byte = 36byte。

    因为36byte % 16byte = 4byte,为了模16byte为0,对象间还需要额外的12byte来帮助对齐(12byte + 4byte = 16byte)。

    所以对象真正占用的的内存大小:36byte+12byte(对象间的alignment)=48byte。

     

    例五、对象头是否需要padding

    这个对比例子中,java.lang.Integer对比java.lang.Long。Integer中只有一个private final int value的对象,Long中只有一个private final long value属性(就是JDK的源码)。

    环境:64bit机器开启压缩指针,默认按照8byte对齐。

    Integer的例子中,对象头12byte,属性int value 4byte并按照4byte对齐,所以最终对象大小16byte。对象内的属性int value起始地址12,所以对象内不需要额外的padding,对象大小16byte,是8byte的整倍数,所以对象间也不需要额外的padding来对齐。

    Long的例子中,对象头12byte,long value是8byte并按照8byte对齐,而对象头12byte从0byte~11byte,所以Long的实际存放地址是16~23byte,在long value和对象头之间需要4byte的padding(但这个padding不是对象头的,是后面的long value根据第一条对齐规则导致的,Integer例子中int value是4byte对齐,就不需要额外的padding)。填充之后,对象大小为24byte,是默认对齐8byte的整倍数,对象间不需要额外的padding。

     

    六、附注

    以上就是根据表象推断出来的java的对齐规则。可能有遗漏,甚至有错误,欢迎指正,欢迎留言讨论!

    展开全文
  • 实例数据部分是对象真正存储的有效信息,也既是我们程序代码里面所定义的各种类型的字段内容,无论是从父类继承下来的,还是子类中定义的都需要记录下来。 这部分的存储顺序会受到虚拟机分配策略参数Fiel

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 217,411
精华内容 86,964
关键字:

在对齐对象的时候