JVM之对象的内存布局

简介: JVM之对象的内存布局

JVM之对象内存布局

先来看一道大厂的面试题

一、对象的创建过程

二、对象在内存中的存储布局

2.1、对象头

对象头用于存储对象的元数据信息

对象头又可以分为两块内容:第一部分用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,这部分数据的长度在32位和64位的虚拟机中分别位32bit和64bit,官方称它为 Mark Word。对象头的另一部分是类型指针,指向它的类元数据的指针,用于判断对象属于哪个类的实例,另外,如果对像是一个数组,那在对象头中还必须有一块用于记录数组长度的数据,因为虚拟机可以通过普通Java对象的元数据信息确定Java对象的大小,但是从数组的元数据中却无法确定数组的大小。

2.2、实例数据

实例数据部分是对象真正存储的有效信息,也是在程序代码中所定义各种类型的字段内容。无论是从父类继承下来的,还是在子类中定义的,都需要记录下来。父类定义的变量会出现在子类定义的变量的前面。各字段的分配策略为longs/doubles、ints、shorts/chars、bytes/boolean、oops(ordinary object pointers),相同宽度的字段总是被分配到一起,便于之后取数据。

2.3、对齐填充

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

最后再给个图帮助理解记忆

三、使用JavaAgent测试Object的大小

3.1 对象大小(64位机)

3.2 观察虚拟机配置

java -XX:+PrintCommandLineFlags -version

3.3 普通对象

  1. 对象头:markword 8
  2. ClassPointer指针:-XX:+UseCompressedClassPointers 为4字节 不开启为8字节
  3. 实例数据
  1. 引用类型:-XX:+UseCompressedOops 为4字节 不开启为8字节 Oops Ordinary Object Pointers
  1. Padding对齐,8的倍数

3.4 数组对象

  1. 对象头:markword 8
  2. ClassPointer指针同上
  3. 数组长度:4字节
  4. 数组数据
  5. 对齐 8的倍数

3.5 实验

  1. 新建项目ObjectSize (1.8)
  2. 创建文件ObjectSizeAgent
package com.zhou.jvm.objectsize;
import java.lang.instrument.Instrumentation;
/**
 * @author zhouyanxiang
 * @create 2020-08-2020/8/5-17:53
 */
public class ObjectSizeAgent {
    private static Instrumentation instrumentation;
    public static void premain(String agentArgs, Instrumentation inst){
        instrumentation = inst;
    }
    public static long sizeOf(Object o){
        return instrumentation.getObjectSize(o);
    }
}
  1. src目录下创建META-INF/MANIFEST.MF
Manifest-Version: 1.0
Created-By: mashibing.com
Premain-Class: com.mashibing.jvm.agent.ObjectSizeAgent
  1. 注意Premain-Class这行必须是新的一行(回车 + 换行),确认idea不能有任何错误提示
  2. 打包jar文件
    选择Project Structure,然后点击Artifacts,点击+号

  3. 在需要使用该Agent Jar的项目中引入该Jar包 project structure - project settings - library 添加该jar包
  4. 运行时需要该Agent Jar的类,加入参数:
    1
-javaagent:C:\work\ijprojects\ObjectSize\out\artifacts\ObjectSize_jar\ObjectSize.jar
  1. 如何使用该类:
package com.mashibing.jvm.c3_jmm;
import com.mashibing.jvm.agent.ObjectSizeAgent;
public class T03_SizeOfAnObject {
    public static void main(String[] args) {
        System.out.println(ObjectSizeAgent.sizeOf(new Object()));
        System.out.println(ObjectSizeAgent.sizeOf(new int[] {}));
        System.out.println(ObjectSizeAgent.sizeOf(new P()));
    }
    //一个Object占多少个字节
    // -XX:+UseCompressedClassPointers -XX:+UseCompressedOops
    // Oops = ordinary object pointers
    private static class P {
                        //8 _markword
                        //4 _class pointer
        int id;         //4
        String name;    //4
        int age;        //4
        byte b1;        //1
        byte b2;        //1
        Object o;       //4
        byte b3;        //1
    }
}

四、Markword

32位的markword如下图所示

64位的和32位的markword区别如下图所示

Hotspot开启内存压缩的规则(64位机)

  1. 4G以下,直接砍掉高32位
  2. 4G - 32G,默认开启内存压缩 ClassPointers Oops
  3. 32G,压缩无效,使用64位 内存并不是越大越好(-

IdentityHashCode的问题

当一个对象计算过identityHashCode之后,不能进入偏向锁状态

https://cloud.tencent.com/developer/article/1480590https://cloud.tencent.com/developer/article/1484167

https://cloud.tencent.com/developer/article/1485795

https://cloud.tencent.com/developer/article/1482500

对象定位

https://blog.csdn.net/clover_lily/article/details/80095580

  1. 句柄池
  2. 直接指针

对象总结


相关文章
|
3天前
|
存储 缓存 算法
深入浅出JVM(二)之运行时数据区和内存溢出异常
深入浅出JVM(二)之运行时数据区和内存溢出异常
|
3天前
|
存储 安全 Java
Python中的引用和赋值机制允许变量引用内存中的对象,并通过引用计数来管理对象的生命周期
【5月更文挑战第14天】Python中的变量是对象引用,不存储数据,而是在内存中创建对象。赋值操作创建新变量并使其指向已有对象。引用计数用于管理对象生命周期,引用数为0时对象被回收。理解这些机制对编写高效Python代码很重要。
18 6
|
3天前
|
Java Linux Arthas
linux上如何排查JVM内存过高?
linux上如何排查JVM内存过高?
245 0
|
3天前
|
存储 缓存 算法
深入浅出JVM(十四)之内存溢出、泄漏与引用
深入浅出JVM(十四)之内存溢出、泄漏与引用
|
3天前
|
存储 算法 NoSQL
深入浅出JVM(十一)之如何判断对象“已死”
深入浅出JVM(十一)之如何判断对象“已死”
|
3天前
|
存储 缓存 算法
深入浅出JVM(一)之Hotspot虚拟机中的对象
深入浅出JVM(一)之Hotspot虚拟机中的对象
|
3天前
|
存储 缓存 Java
JVM 运行时内存篇
JVM 运行时内存篇
9 0
|
3天前
|
Arthas 监控 Java
JVM工作原理与实战(三十一):诊断内存泄漏的原因
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了诊断内存溢出的原因、MAT内存泄漏检测的原理等内容。
17 0
|
3天前
|
存储 Arthas 监控
JVM工作原理与实战(三十):堆内存状况的对比分析
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了堆内存状况的对比分析、产生内存溢出的原因等内容。
15 0
|
3天前
|
Arthas Prometheus 监控
JVM工作原理与实战(二十九):监控内存泄漏的工具
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了解决内存溢出的步骤、Top命令、VisualVM、Arthas、Prometheus + Grafana等内容。
21 0