分享一个关于java中查看一个对象内存布局和占用空间大小的工具

JVM快速找出耗内存大对象 1.找到进程号 ,使用top命令查看应用的系统的整体负载 load average: 1.单核CPU, 数字在0.00-1.00之间正常 0.00-1.00 之间的数字表示此时路况非常良好,没有拥堵,车辆可以毫无阻碍地通过。 1.00 表示道路还算正常,但有可能会恶化并造成拥堵。此时系统已经没有多余的资源了,管理员需要进行优化。 1.00以上 表示路况不太好了,这种情况... 阅读详情

概述

在现实的业务场景中,我们往往会把数据放在内存中进行缓存或其他处理,这就要求我们有必要知道这些数据占用的空间大小,进而去合理的规划机器配置、加载数据量的大小等。

通常,运行时数据区的内存布局不属于 JVM 规范的一部分,而是由实现者自行决定。因此,每个 JVM 实现在内存中布局对象和数组时可能会采用不同的策略。这反过来又会影响运行时的实例大小。

本次我们要分享的这个工具,在特定的JVM条件下:64bit HotSpot JVM(即:64位 HotSpot虚拟机)。

JVM中对象的内存布局

要想计算一个对象在JVM中的大小,我们首先得知道它在内存中的布局情况。

关于对象的布局,大体由以下三部分组成:

  • 对象头:Object Header,JVM记录特定信息,如:GC状态、同步状态、identity hash code、数组长度、以及class元信息的指针,组成为:8字节的MarkWord+4字节(开启指针压缩时为4字节,关闭时为8字节,jdk8及以后默认为开启状态)的klass pointer,如果是数组对象,则会仅跟着一个4字节的数组长度信息;
  • 实例数据:如字面意思,很好理解;
  • 内存填充/字节对齐:JVM为了效率,会采用固定长度(8的整数倍)统一存储,如果对象头+实例数据大小不足8的整数倍字节大小时,会进行补齐。

说明:在64位的HotSpot虚拟机中,类型指针、引用类型需要占用8个字节,这显然会增加对内存的消耗和使用,在jdk1.6开始,加入了UseCompressedOops参数,可对OOP(Ordinary Object Pointer,即:普通对象指针)进行压缩,从而使其仅占用4个字节。

关于指针压缩开启和关闭的相关参数如下:

-XX:+UseCompressedOops开启指针压缩;

-XX:-UseCompressedOops关闭指针压缩;

工具介绍与使用

对内存布局有个大概的了解之后,就可以进入主题了,也就是这次分享的工具:JOL。

什么是JOL?

JOL:Java Object Layout,由openJDK提供的一个工具,用于分析、了解一个java对象在内存中的具体分布情况。

官网:https://openjdk.org/projects/code-tools/jol/

 JOL Source Repository:GitHub - openjdk/jol: https://openjdk.org/projects/code-tools/jol

官网对它的介绍:

JOL (Java Object Layout) is the tiny toolbox to analyze object layout schemes in JVMs. 
These tools are using Unsafe, JVMTI, and Serviceability Agent (SA) heavily to decoder the actual object layout, footprint, and references. 
This makes JOL much more accurate than other tools relying on heap dumps, specification assumptions, etc.

如何使用JOL

JOL中常用的几个方法:

ClassLayout.parseInstance(obj).toPrintable():查看对象内部的内存布局;

ClassLayout.parseInstance(obj).instanceSize():计算对象的大小,单位为字节;

GraphLayout.parseInstance(obj).toPrintable():查看对象外部信息,包括引用的对象;

GraphLayout.parseInstance(obj).totalSize():查看对象占用空间总大小;

注:除了parseInstance(Object obj)外,还有parseClass(Class<?> class),用法基本相同。 

实际使用示例:

万能第一步:引入maven依赖

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

注:较新的版本中简化了打印的信息,所以建议使用0.10,或0.9的版本。

为了更好地了解更复杂对象的大小,我们首先应该知道每种简单数据类型所占用的空间。为此,我们可以用 JOL 打印虚拟机信息:

System.out.println(VM.current().details());

上述代码将打印简单数据类型的大小如下:

以下是 JVM 中每种简单数据类型所需的空间:

  • 对象引用占用 4 个字节
  • 布尔值和字节值消耗 1 个字节
  • short 和 char 值占用 2 个字节
  • int 和 float 值占用 4 个字节
  • long 和 double 值消耗 8 个字节

值得一提的是,所有数据类型在作为数组组件类型使用时消耗的内存量相同。

假如我们关闭指针压缩,也就是-XX:-UseCompressedOops,再看上面代码的输出:

现在,对象引用将占用 8 字节而不是 4 字节。其余数据类型的内存消耗量仍然相同。
此外,当堆大小超过 32 GB 时,HotSpot JVM 也无法使用压缩引用(除非我们更改对象对齐方式)。

好了,现在我们已经知道了基本数据类型的内存消耗量,那就来计算一下更复杂对象的内存消耗量吧。

查看一个不包含任何属性的类的内存布局:

import org.openjdk.jol.info.ClassLayout;

public class JOLTest {

    public static void main(String[] args) {
        Course course = new Course();
        //打印对象内存布局
        System.out.println(ClassLayout.parseInstance(course).toPrintable());
    }

    public static class Course {

    }

}

输出:

输出说明:(下同)

OFFSET:偏移地址,单位:字节;

SIZE:大小,单位:字节;

TYPE DESCRIPTION:类型描述

        object header:对象头,8字节MarkWord,4字节klass pointer;

        loss due to the next object alignment:由于下一个对象对齐而导致的丢失,内存补齐;

VALUE:对应内存中的值;

Instance size:实例字节数大小;(这里可以看出,不包含任何属性的实例大小为16bytes)

再来看一个包含属性的例子:

    public static void main(String[] args) {
        //打印对象内存布局
        System.out.println(ClassLayout.parseClass(Course.class).toPrintable());
    }

    @Data
    public static class Course {
        private String name;
    }

Course类,输出:

12字节的object header, 4字节的name对象引用。

再看一个属性多一点的:

    public static void main(String[] args) {
        //打印对象内存布局
        System.out.println(ClassLayout.parseClass(Professor.class).toPrintable());
    }

    @Data
    public static class Course {
        private String name;
    }
    @Data
    public static class Professor {
        private String name;
        private int level;
        private double score;
        private List<Course> courses = new ArrayList<>();
    }

Professor,输出:

12字节object header,4字节int属性,8字节double属性,4字节name对象引用,4字节courses对象引用。

这里,代码中明明是name,level,score的顺序,为什么内存中确是level,score,name呢?

原因:对于对象的大小,JVM在分配内存的时候会有个【重排序】的处理,进而达到节省空间的目的。比如:如果先分配byte或boolean后分配int,那byte之后可能需要3bytes的补充进行padding,【重排序】就是通过减少这个补充的可能性而减少空间的使用。

一般情况下,遵循如下的优先级顺序:

double>long>int>float>chat>short>byte>boolean>Object Reference。

不过,如上面Professor的例子,12字节的object headere之后紧接着是int而不是double,是因为4字节的int在此处更有可能不需要padding,所以int优先了。

sizeOf:计算浅层大小的一个简单方法。(所谓浅层大小,就是指只计算引用大小,如:1个引用按4bytes算,而不计算引用对象的大小,如果同时计算了引用对象的大小,那就叫Deep Size)

    public static void main(String[] args) {
        //打印对象内存布局
//        System.out.println(ClassLayout.parseClass(Professor.class).toPrintable());
        String ds = "Data Structures";
        Course course = new Course(ds);

        System.out.println("The shallow size is: " + VM.current().sizeOf(course));
    }

    @Data
    @AllArgsConstructor
    public static class Course {
        private String name;
    }

输出:

这里输出course的浅层大小为16bytes.

看过了浅层大小,我们再来看看Deep Size,Deep Size如上面的解释,除了引用大小,还计算引用对象的大小,对应到代码中就是String ds的大小。

System.out.println(ClassLayout.parseInstance(ds).toPrintable());

看看ds的输出:

 可以看到该 String 实例的浅层大小为 24 字节,其中包括 4 字节的哈希码、4 字节的 char[] 引用和其他典型对象开销。

而,如果要看char[]数组的大小,可以通过如下方式:

System.out.println(ClassLayout.parseInstance(ds.toCharArray()).toPrintable());

输出:

这里,多了4字节的object header,表示数组的大小,这里值为15.

所以,Deep Size = 16bytes的Course实例+24bytes的String实例+48字节的char[]数组数据=88字节。

那么,用GraphLayout的totalSize API看下实际占用大小输出,与我们预期的是否一致:

    public static void main(String[] args) {
        //打印对象内存布局
//        System.out.println(ClassLayout.parseClass(Professor.class).toPrintable());
        String ds = "Data Structures";
        Course course = new Course(ds);
        //打印对象内存占用
        System.out.println(GraphLayout.parseInstance(course).totalSize());
    }

输出:

ok,完全符合预期!

最后,来一个完整版的示例输出展示:

    public static void main(String[] args) {
        String ds = "Data Structures";
        Course course = new Course(ds);
        System.out.println("course内存布局:");
        System.out.println(ClassLayout.parseInstance(course).toPrintable());
        System.out.println("ds内存布局:");
        System.out.println(ClassLayout.parseInstance(ds).toPrintable());
        System.out.println("ds char[]内存布局:");
        System.out.println("ds char[]内存布局:" + ClassLayout.parseInstance(ds.toCharArray()).toPrintable());
        //打印对象内存占用
        System.out.println("course instance size:" + ClassLayout.parseInstance(course).instanceSize());
        System.out.println("ds instance size:" + ClassLayout.parseInstance(ds).instanceSize());
        System.out.println("ds char[] instance size:" + ClassLayout.parseInstance(ds.toCharArray()).instanceSize());
        System.out.println("course total size:" + GraphLayout.parseInstance(course).totalSize());
    }

输出:

好了,到这一步,相信工具怎么使用应该就比较清楚了。

接下来,多练、多用,多踩坑,不断学习~~~ 

Win上通过Jconsole查看Java程序资源占用情况(教程总结,一篇就够了) JConsole 是一个内置 Java 性能分析器,Java 5开始 引入了 JConsole。可以从命令行或在 GUI shell 中运行。您可以轻松地使用 JConsole来监控 Java 应用程序性能和跟踪 Java 中的代码。Jconsole工具查看程序的资源占用请求。安装jdk时bin目录有jconsole.exe工具,或者通过 Win + R,输入jconsole.exe启动jconsole( 可以从命令行直接输入jconsole。 阅读详情

相关推荐

如何查看Java对象的内存占用大小

本文介绍了两种方法来查看Java对象的内存占用大小,分别使用了Instrumentation类和Java提供的工具类。接着,我们通过计算对象所占用的内存前后的堆内存使用量之差,即可得到对象的内存占用大小。需要注意的是,由于Java的垃圾回收机制的存在,对象的内存占用大小可能会受到垃圾回收的影响。然后,在您的代码中调用ObjectSizeFetcher.getObjectSize方法,传入要检测内存大小的对象,即可获取其内存占用大小。本文将介绍几种方法,可用于查看Java对象的内存占用大小。

TechChamp的博客 2183

java 内存分析工具

YourKit Java Profiler:这是另一款商业级的Java分析工具,用于性能和内存分析。它提供了实时的CPU和内存使用情况监视、线程分析、内存泄漏检测等功能。它能够解析和分析Java堆转储文件(例如使用jmap或jconsole工具生成的.hprof文件),帮助你识别内存泄漏和内存消耗过高的原因。JProfiler:这是一款商业级的Java分析工具,提供实时的内存分析和性能分析功能。它提供了一种可视化的界面,显示垃圾收集器的运行情况、堆的使用情况以及对象的分配和回收等信息。

Primary_wind的专栏 4393

对象在内存中的内存布局是什么样的?

一个Java对象的存储结构。在Hotspot虚拟机中,对象在内存中的存储布局分为 3 块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding) Java 对象实例 Hotspt 采用了 OOP-Klass 模型。 它是描述 java 对象实例的模型,可分为两部分: OOP (Ordinary Object Pointer)指的是普通对象指针,它包含 MarkWord 和Klass 指针。MarkWord 用于存储当前对象运行时的一些状态数据;Klass 指针则

码农架构 709

4种方法教你如何查看java对象所占内存大小

本文讲述4种查看java对象所占内存大小的方法

华为云官方博客 1万+

【安全加固】应对Sweet32攻击:TLS/SSL协议3DES漏洞深度解析与修复指南

本文深度解析了CVE-2016-2183(Sweet32攻击)漏洞,该漏洞利用TLS/SSL协议中3DES加密算法的64位块大小缺陷,通过大量数据流量实施“生日攻击”导致信息泄露。文章提供了从原理分析、系统排查到跨平台修复的完整指南,重点指导如何在Windows、Linux服务器及网络设备中禁用不安全的3DES密码套件,并采用AES-GCM等更安全的加密方式,以彻底修复此TLS/SSL漏洞。

weixin_29230649的博客 586

Matlab实战:手把手教你用Zernike多项式模拟光学像差(附完整代码)

本文详细介绍了如何在Matlab中使用Zernike多项式模拟光学像差,包括基础理论、不同排序系统的实现、完整代码示例以及高级应用技巧。通过Zernike多项式,可以有效地描述和分析光学系统中的波前畸变,适用于望远镜校正、眼科光学等多个领域。

weixin_42523720的博客 161

Java对象的大小如何计算?

Java对象的大小如何计算?

java1234的博客 880

Qt多线程实战:如何优雅地处理Modbus设备通信中的线程安全问题

本文深入探讨了在Qt框架下实现Modbus设备通信时的多线程安全问题,提供了优雅的解决方案。通过生产者-消费者模型、任务队列与锁机制等设计模式,有效解决了界面卡顿、数据包错乱等问题。文章还详细介绍了对象生命周期管理、信号槽连接类型选择等关键实现细节,帮助开发者构建稳定可靠的Modbus通信模块。

weixin_30897233的博客 484

每天分享一个小知识————JVM对象之对象的内存布局

比如对象总大小是 18 字节,HotSpot 会偷偷加 6 字节的“空气砖”,凑成 24 字节(因为 24 是 8 的倍数)。如果是数组,对象头还要额外存数组长度(因为普通对象的大小可以通过类元数据推断,但数组长度不确定)。(默认开),子类的窄字段可能插到父类字段的“缝隙”里省空间(类似行李箱塞袜子)。(3字节):让总大小 12 + 9 + 3 = 24 字节(8 的倍数)。指向对象的“户口”(类元数据),告诉虚拟机这个对象是哪个类的实例。对齐填充(4字节):让对象头凑满 12 字节(假设需要对齐)

weixin_44343315的博客 680

聚焦112Gb/s SerDes芯片的AN/LT端口自协商和链路学习,评估验证高速链路的信号质量并分析调优(400/800G高速以太网互联接口,AI加速卡网络RDMA性能测试,交换背板接口性能评估)

在112G SerDes高速通道下的400G/800G高速以太网端口下,如何保障通信速率,验证SerDes的数据完整性和链路余量,自动协商(AN)和链路训练(LT)功能显得尤其重要,其在DAC、ACC、AEC铜缆连接或高速背板通信上,自动协商(AN)和链路训练(LT)的工作与否将决定高速链路传输质量的优劣。本文将系统的讨论介绍自动协商(AN)和链路训练(LT)的测试流程,其可以独立使用,也可以与teledynelecroy SierraNet M1288配合使用。

专注有线以太网性能测试--丹麦信雅纳网络(Xena Networks) 9404

YOLOv8n模型导出ONNX的避坑指南:解决版本差异,正确生成TensorRT/RKNN所需输出节点

本文详细介绍了YOLOv8n模型导出ONNX格式的实战避坑指南,涵盖版本差异处理、代码修改技巧及TensorRT/RKNN部署优化方案。针对不同推理框架需求,提供输出节点生成、动态轴处理和后处理划分等核心问题的解决方案,帮助开发者高效完成模型部署。

weixin_33247358的博客 357

java查看对象地址_如何查看一个 Java 对象的大小

(点击上方公众号,可快速关注)来源:scugxl,blog.csdn.net/scugxl/article/details/50740038最近一直着迷于缓存行,要想正确理解缓存行以及伪共享就一定要知道Java对象的大小,以及如何分布.在这里演示了使用JOL 工具来查看一个对象的大小和分布.JOL (Java Object Layout) is the tiny toolbox to analyz...

weixin_39979948的博客 571

STM32F407+LWIP实战:手把手教你移植mbedtls库实现Modbus TCP双向认证

本文详细介绍了如何在STM32F407微控制器上移植mbedtls库,结合LWIP协议栈实现Modbus TCP的双向认证功能。通过TLS 1.2加密通道,有效提升工业物联网通信的数据安全性,涵盖证书管理、内存优化及性能调优等关键步骤,为嵌入式系统开发者提供了一套完整的解决方案。

weixin_28721917的博客 516

java计算一个实例对象占用空间大小的方法分享

下文笔者将讲述一个java对象占用空间大小的方法分享,如下所示: 对象头 一个对象头部至少有两个WORD 如果对象是数组,则需要三个WORD,如下: 对象的HashCode,锁信息 对象类型数据的指针 数组的长度(对象为数组) 计算规则 在Java代码中,任何对象都是8字节对齐 属性按照[long,double]、[int,float]、[char,short]、[byte,boolean]、reference的顺序存放 例: public class Test { byte a;

qq_25073223的博客 320

【C++】ROS:ROS1环境配置与基础示例

ROS1环境配置与基础示例

DevFrank的博客 2104

STM32入门教程:智能马桶盖控制

通过编写相应的代码,您可以根据需要实现其他功能,例如座圈温度调节和液晶显示屏显示等。初始化配置 首先,使用STM32CubeMX创建一个新的工程,并选择您所使用的STM32单片机型号。智能马桶盖是一种集成了多种功能的智能家居设备,通过控制芯片来实现自动冲水、座圈加热、座圈温度调节等功能。在本教程中,我们将使用STM32系列单片机来控制智能马桶盖的各个功能。以上代码演示了如何通过STM32的GPIO控制引脚来控制智能马桶盖的冲水和座圈加热功能。写代码 接下来,我们将通过编写代码来控制智能马桶盖的各个功能。

2401_85258012的博客 657

在java中一个空的Object对象占用多大内存呢?

Java空对象内存占用与JVM内存模型解析

专注后端开发 1365

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Java基础知识:如何计算Java对象占用内存大小?

在Java中没有sizeof运算符,所以没办法知道一个对象到底占用了多大的空间,但是在分配对象的时候会有一些基本的规则,我们根据这些规则大致能判断出来对象大小。今天就和大家分享一下如何计算Java对象占用内存大小。 一、对象头 对象的头部至少有两个WORD,如果是数组的话,那么三个WORD,内容如下: 对象的HashCode,锁信息等 到对象类型数据的指针 数组的长度(如果是数组的话) 二、分配对象时的基本规则 首先,任何对象都是8字节对齐,属性按照[long,double]、[int,float]、[c

java700001的博客 1671

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

Java对象的内存布局

今天来讲些抽象的东西 -- 对象头,因为我在学习的过程中发现很多地方都关联到了对象头的知识点,例如JDK中的 synchronized锁优化 和 JVM 中对象年龄升级等等。要深入理解这些知识的原理,了解对象头的概念很有必要,而且可以为后面分享 synchronized 原理和 JVM 知识的时候做准备。 对象内存构成 Java 中通过 new 关键字创建一个类的实例对象,对象存于内存的...

JaJian的博客 301

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

python如何查看内存占用_浅谈Python 对象内存占用 python如何查看内存占用空间

请教各位牛人,python中有没有计算某个对象占用内1 2 s = 'abc' print sys.getsizeof(s) 如果你要监测所有的变量,可以用python的smiley 模块监测所有的内存变量情况人生就是一次感恩之旅,开心是一种觉悟的境界,独步人生,小编们会遇到种种困难,甚至于举步维艰,甚至于悲观失望。分享助protobuf在python中创建一个对象为何要占用那小编用python ...

weixin_28747829的博客 2119

JAVA面试题分享八十四:一个空Objiect对象的占多大空间?

我们主要关注 normal object, 这种类型的 Object 的 Markword 一共是 8 个字节(64位),其中 25 位暂时没有使用,31 位存储对象的 hash 值(注意这里存储的 hash 值对根据对象地址算出来的 hash 值,不是重写 hashcode 方法里面的返回值),中间有 1 位没有使用,还有 4 位存储对象的 age(分代回收中对象的年龄,超过 15 晋升入老年代),最后三位表示偏向锁标识和锁标识,主要就是用来区分对象的锁状态(未锁定,偏向锁,轻量级锁,重量级锁)

之乎者也·的博客 1248

Java 虚拟机:Java对象的内存布局

文章收录地址:Java-Bang 专注于系统架构、高可用、高性能、高并发类技术分享 在 Java 程序中,我们拥有多种新建对象的方式。除了最为常见的 new 语句之外,我们还可以通过反射机制、Object.clone 方法、反序列化以及 Unsafe.allocateInstance 方法来新建对象。 其中,Object.clone 方法和反序列化通过直接复制已有的数据,来初始化新建对象的实例字段。Unsafe.allocateInstance 方法则没有初始化实例字段,而 new 语句和反射机制.

码农架构 618

来来来,看看Java对象的内存布局

今天来讲些抽象的东西 – 对象头,因为我在学习的过程中发现很多地方都关联到了对象头的知识点,例如JDK中的 synchronized锁优化 和 JVM 中对象年龄升级等等。要深入理解这些知识的原理,了解对象头的概念很有必要,而且可以为后面分享 synchronized 原理和 JVM 知识的时候做准备。 对象内存构成 Java 中通过 new 关键字创建一个类的实例对象,对象存于内存的堆中并给其分配一个内存地址,那么是否想过如下这些问题: 这个实例对象是以怎样的形态存在内存中的? 一个Object对象在内

285

推荐项目:深入了解Java对象布局 —— JOL

在Java开发的浩瀚世界中,对内存管理和对象布局的理解往往决定着程序性能的生死。今天,我们来深入探索一个强大的工具——**Java Object Layout (JOL)**,它帮助开发者揭开Java对象在JVM中的神秘面纱。 ## 项目介绍 JOL,作为一款轻量级工具箱,专注于剖析JVM内部的对象布局细节。通过利用`Unsafe`、JVMTI(Java Virtual Machine Too

gitblog_01071的博客 412
上一篇: Redis适合把它当作一个数据库来使用么?
下一篇: 聊一聊java中的volatile关键字是如何保障变量的可见性的
Jack_abu
博客等级 码龄10年 633粉丝 78原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jack_abu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值