资讯专栏INFORMATION COLUMN

Java对象结构及大小计算

winterdawn / 2072人阅读

摘要:由于的自动内存管理系统要求对象起始地址必须是字节的整数倍,换句话说,就是对象的大小必须是字节的整数倍。对象大小计算要点在位系统下,存放指针的空间大小是字节,是字节,对象头为字节。静态属性不算在对象大小内。

jvm系列

垃圾回收基础

JVM的编译策略

GC的三大基础算法

GC的三大高级算法

GC策略的评价指标

JVM信息查看

GC通用日志解读

jvm的card table数据结构

Java类初始化顺序

Java对象结构及大小计算

Java的类加载机制

Java对象分配简要流程

年老代过大有什么影响

Survivor空间溢出实例

关于Object=null

Java线程与Xss

本文主要简述Java对象的内存布局以及其大小的计算。

Java对象内存布局

在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。

对象头

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

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

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

实例数据

实例数据部分是对象真正存储的有效信息,也是在程序代码中所定义的各种类型的字段内容。无论是从父类继承下来的,还是在子类中定义的,都需要记录起来。

对齐填充

第三部分对齐填充并不是必然存在的,也没有特别的含义,它仅仅起着占位符的作用。由于HotSpot VM的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说,就是对象的大小必须是8字节的整数倍。而对象头部分正好是8字节的倍数(1倍或者2倍),因此,当对象实例数据部分没有对齐时,就需要通过对齐填充来补全。

对象大小计算 要点

在32位系统下,存放Class指针的空间大小是4字节,MarkWord是4字节,对象头为8字节。

在64位系统下,存放Class指针的空间大小是8字节,MarkWord是8字节,对象头为16字节。

64位开启指针压缩的情况下,存放Class指针的空间大小是4字节,MarkWord是8字节,对象头为12字节。

数组长度4字节+数组对象头8字节(对象引用4字节(未开启指针压缩的64位为8字节)+数组markword为4字节(64位未开启指针压缩的为8字节))+对齐4=16字节。

静态属性不算在对象大小内。

实例
import java.util.HashMap;
/**
 * 64位开启指针压缩的话,markword变成8字节,压缩了class指针为4字节,故对象头12字节
 * 64位没有开启指针压缩的话,markword8字节,class指针8字节,对象头16字节
 * 32位markword为4字节,class指针为4字节,对象头8字节
 *
 * 另外,静态属性所占用的空间通常不算在对象本身,因为它的引用是在方法区。
 *
 * @author xixicat
 * @created 2014-10-03
 */
public class ObjectSize {
    public static void main(String[] args){
        System.out.println(SizeOfTool.getObjectSize(new A(),SizeEnum.B));
        System.out.println(SizeOfTool.getObjectSize(new B(),SizeEnum.B));
        System.out.println(SizeOfTool.getObjectSize(new C(),SizeEnum.B));
        System.out.println(SizeOfTool.getObjectSize(new D(),SizeEnum.B));
        System.out.println(SizeOfTool.getObjectSize(new E(),SizeEnum.B));
        System.out.println(SizeOfTool.getObjectSize(new Q(),SizeEnum.B));
        /**
         * 64位压缩指针下,对象头12字节,数组长度描述4字节,数据4*100 =16+400 = 416
         */
        System.out.println(SizeOfTool.getObjectSize(new int[100],SizeEnum.B));
        /**
         * 属性4位对齐
         * 64位压缩指针下,对象头12字节,数组长度描述4字节,数据1*100,对齐后104 = 16+104 = 120
         */
        System.out.println(SizeOfTool.getObjectSize(new byte[100],SizeEnum.B));
        /**
         * 二维数组
         * 64位指针压缩下
         * 第1维数组,对象头12字节,数组长度描述4字节,2个数组引用共8字节,共24字节
         * 第2维数组,对象头12字节,数组长度描述4字节,100个数组引用共400字节,对齐后共416字节
         *         第1维的2个引用所指对象大小 = 2*416 = 832 字节
         *         共24+832 = 856字节
         */
        System.out.println(SizeOfTool.getObjectSize(new int[2][100],SizeEnum.B));
        /**
         * 二维数组
         * 64位指针压缩下
         * 第1维数组,对象头12字节,数组长度描述4字节,100个数组引用共400字节,共416字节
         * 第2维数组,对象头12字节,数组长度描述4字节,2个数组引用共8字节,共24字节
         *         第1维的100个引用所指对象大小 = 100*24 = 2400 字节
         *         共416+2400 = 2816字节
         */
        System.out.println(SizeOfTool.getObjectSize(new int[100][2],SizeEnum.B));
        System.out.println(SizeOfTool.getObjectSize(new Object(),SizeEnum.B));
        /**
         * 不算static属性
         * private final char value[];
         * private int hash; // Default to 0
         * private transient int hash32 = 0;
         *
         * 32位下,String对象头8字节,2个int类型8字节,char数组引用占4字节,共占24字节
         *        另外,还要算上value[]数组的占用,数组对象头部8字节,数组长度4字节,对齐后共占16字节
         *    =》String对象对象大小24+16 = 40字节
         * 64位开启指针压缩下(压缩指针),String对象头12字节,2个int类型8字节,char数组引用占4字节,共占24字节
         *        另外,还要算上value[]数组的占用,数组对象头部12字节,数组长度4字节,对齐后共占16字节
         *    =》String对象大小24+16=40字节
         */
        System.out.println(SizeOfTool.getObjectSize(new String(),SizeEnum.B));
        /**
         *  transient Entry[] table = (Entry[]) EMPTY_TABLE;
         *  transient int size;
         *  int threshold;
         *  final float loadFactor;
         *  transient int modCount;
         *
         * 64位开启指针压缩下,对象头部12字节,数组引用4字节,3个int12字节,float4字节,共32字节
         *                 另外,算上Entry[] = 对象头12 +属性16字节+数组长度4字节 = 32字节
         *
         *                 final K key;
         *                 V value;
         *                 Entry next;
         *                 int hash;
         *
         *                 对象头12字节,3个引用共12字节,1个int4字节  =》 一个entry至少占用28字节
         *
         *             =》32+32=64字节
         */
        System.out.println(SizeOfTool.getObjectSize(new HashMap(),SizeEnum.B));
    }
}
//32位下对象头8字节,byte占1字节,对其填充后,总占16字节
//64位开启指针压缩下对象头12字节,byte1字节,对齐后占16字节
class A{
    byte b1;
}
//32位下对象头8字节,8个byte8字节,总16字节
//64位开启指针压缩下对象头12字节,8个byte8字节,对齐后占24字节
class B{
    byte b1,b2,b3,b4,b5,b6,b7,b8;
}
//32位下对象头8字节,9个byte9字节,对其填充后,总24字节
//64位开启指针压缩下对象头12字节,9个byte9字节,对齐后占24字节
class C{
    byte b1,b2,b3,b4,b5,b6,b7,b8,b9;
}
//32位下对象头8字节,int占4字节,引用占4字节,共16字节
//64位开启指针压缩下对象头12字节,int占4字节,引用占4字节,对齐后占24字节
class D{
    int i;
    String str;
}
//32位下对象头8字节,int4字节,byte占1字节,引用占4字节,对其后,共24字节
//64位开启指针压缩下对象头12字节,int占4字节,引用占4字节,byte占1字节,对齐后占24字节
class E{
    int i;
    byte b;
    String str;
}
/**
 * 对齐有两种
 * 1、整个对象8字节对齐
 * 2、属性4字节对齐   ****
 *
 * 对象集成属性的排布
 * markword     4          8
 * class指针     4          4
 * 父类的父类属性  1         1
 * 属性对齐       3         3
 * 父类的属性     1          1
 * 属性对齐       3          3
 * 当前类的属性    1         1
 * 属性对齐填充    3          3
 * 整个对象对齐   8+12 =》 24    12+12=》24
 */
class O{
    byte b;
}
class P extends O{
    byte b;
}
class Q extends P{
    byte b;
}
参考

深入理解Java虚拟机(第2版)

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/65601.html

相关文章

  • Java对象内存占用分析

    摘要:对于不同的实现,对象占用的内存空间大小可能不尽相同,本文主要分析中的情况,实验环境为位系统,使用进行结论验证。内存占用这里分析一个只有一组键值对的结构如下首先分析本身的大小。 本文深入分析并验证了不同Java对象占用内存空间大小的情况。对于不同的jvm实现,Java对象占用的内存空间大小可能不尽相同,本文主要分析HotSpot jvm中的情况,实验环境为64位window10系统、JD...

    JouyPub 评论0 收藏0
  • 想进大厂?50个多线程面试题,你会多少?(一)

    摘要:下面是线程相关的热门面试题,你可以用它来好好准备面试。线程安全问题都是由全局变量及静态变量引起的。持有自旋锁的线程在之前应该释放自旋锁以便其它线程可以获得自旋锁。 最近看到网上流传着,各种面试经验及面试题,往往都是一大堆技术题目贴上去,而没有答案。 不管你是新程序员还是老手,你一定在面试中遇到过有关线程的问题。Java语言一个重要的特点就是内置了对并发的支持,让Java大受企业和程序员...

    wow_worktile 评论0 收藏0
  • Java 内存结构备忘录

    摘要:本文详细描述了堆内存模型,垃圾回收算法以及处理内存泄露的最佳方案,并辅之以图表,希望能对理解内存结构有所帮助。该区域也称为内存模型的本地区。在中,内存泄露是指对象已不再使用,但垃圾回收未能将他们视做不使用对象予以回收。 本文详细描述了 Java 堆内存模型,垃圾回收算法以及处理内存泄露的最佳方案,并辅之以图表,希望能对理解 Java 内存结构有所帮助。原文作者 Sumith Puri,...

    wow_worktile 评论0 收藏0
  • Java对象分配简要流程

    摘要:在一般应用中,不会逃逸的局部对象所占的比例很大,如果能使用栈上分配,那大量的对象就会随着方法的结束而自动销毁了,垃圾收集系统的压力将会小很多。相关参数设置大对象直接进入年老代的阈值,当对象大小超过这个值时,将直接在年老代分配。 jvm系列 垃圾回收基础 JVM的编译策略 GC的三大基础算法 GC的三大高级算法 GC策略的评价指标 JVM信息查看 GC通用日志解读 jvm的card t...

    zorro 评论0 收藏0
  • 类的认识使用

    摘要:注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类。如果一个类没有对象,那么它的对象需要给进行占位,标识对象存在,这不存储有效数据。 类 类的使...

    alexnevsky 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<