资讯专栏INFORMATION COLUMN

java 对象内存大小

xiaokai / 2022人阅读

摘要:一个对象内存有多大为什么想知道这个,自以为很重要,其实。参考链接对象解析不得不了解的对象头从开始在位系统上会默认开启压缩指针研究位引用指针压缩技术

java 一个对象内存有多大
为什么想知道这个,自以为很重要,其实 just for fun =。=
测试相关说明

jdk: java 官网上下载的,HotSpot 虚拟机

java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)

使用的方法: Instrumentation 参考的是 一个对象占用多少字节?

确认编译时是 32 位还是 64 位,对测试结果有影响。x86 为 32 位, amd64 为 64 位,其余我不知道

System.out.println(System.getProperty("os.arch"));

准备工作

SizeOfObject.java 直接从上面链接上拷贝

编译这个类,得到 .class 文件

在包名路径下执行一下命令进行打包(注意修改相应的包名):

jar cvfm SizeOfObject.jar manifest.mf org/seal_de/SizeOfObject.class

其中 manifest.mf 清单信息为: PreMain-Class: org.seal_de.SizeOfObject

PS: 如果在打包好的 jar 包中,META-INF/MANIFEST.MF 没有 PreMain-Class 属性,添加上去即可

在运行测试程序的时候,添加 vm 参数

-javaagent:{jar包路径}SizeOfObject.jar

测试用例

测试 int, Object, 引用的大小。其余类型测试都类似

public class MemoryTest {
    /**
     * -javaagent:{jar包路径}SizeOfObject.jar -XX:+UseCompressedOops
     * 使用指针压缩,在一定情况下64位HotSpot jvm默认指针压缩
     *
     *Output:
     *amd64
     *Object:                              16
     *    
     *include one int:                     16
     *include two int:                     24
     *include three int:                   24
     *
     *include one object:                  16
     *include one object:                  24
     */
    static void test1() {
        System.out.println(System.getProperty("os.arch"));
        System.out.printf("%-30s%9d
", "Object:", SizeOfObject.sizeOf(new Object()));
        System.out.println();
        System.out.printf("%-30s%9d
", "include one int:", SizeOfObject.sizeOf(new IntegerTestOne()));
        System.out.printf("%-30s%9d
", "include two int:", SizeOfObject.sizeOf(new IntegerTestTwo()));
        System.out.printf("%-30s%9d
", "include three int:", SizeOfObject.sizeOf(new IntegerTestThree()));
        System.out.println();
        System.out.printf("%-30s%9d
", "include one object:", SizeOfObject.sizeOf(new ReferenceTestOne()));
        System.out.printf("%-30s%9d
", "include one object:", SizeOfObject.sizeOf(new ReferenceTestTwo()));
    }

    /**
     * -javaagent:{jar包路径}SizeOfObject.jar -XX:-UseCompressedOops
     * 不使用指针压缩
     *
     *Output:
     *amd64
     *Object:                              16
     *
     *include one int:                     24
     *include two int:                     24
     *include three int:                   32
     *
     *include one object:                  24
     *include one object:                  32
     */
    static void test2() {
        System.out.println(System.getProperty("os.arch"));
        System.out.printf("%-30s%9d
", "Object:", SizeOfObject.sizeOf(new Object()));
        System.out.println();
        System.out.printf("%-30s%9d
", "include one int:", SizeOfObject.sizeOf(new IntegerTestOne()));
        System.out.printf("%-30s%9d
", "include two int:", SizeOfObject.sizeOf(new IntegerTestTwo()));
        System.out.printf("%-30s%9d
", "include three int:", SizeOfObject.sizeOf(new IntegerTestThree()));
        System.out.println();
        System.out.printf("%-30s%9d
", "include one object:", SizeOfObject.sizeOf(new ReferenceTestOne()));
        System.out.printf("%-30s%9d
", "include one object:", SizeOfObject.sizeOf(new ReferenceTestTwo()));
    }

    public static void main(String[] args) {
        test2();
    }

    static class IntegerTestOne {
        private int i1 = 1;
    }

    static class IntegerTestTwo {
        private int i1 = 1;
        private int i2 = 1;
    }

    static class IntegerTestThree {
        private int i1 = 1;
        private int i2 = 1;
        private int i3 = 1;
    }

    static class ReferenceTestOne {
        private Object o1 = new Object();
    }

    static class ReferenceTestTwo {
        private Object o1 = new Object();
        private Object o2 = new Object();
    }
}
一些概念

对象内存 = 对象头 + 类型指针 + 对齐填充

对象头不参与指针压缩,并且 32 位时为 4 个字节,64 位时为 8 个字节

类型指针参与指针压缩,并且 32 位时为 4 个字节,64 位时为 8 个字节;指针压缩时 64 位为 4 个字节

对齐填充,由于 jvm 设计内存要为 8 字节的整数倍,所以不足的需要填充。如 对象头和类型指针一共 12 字节,填充后为 16 字节,填充了 4 个字节

测试结果验证上面的假设

其中 (8 + 8) 为对象头和类型指针的字节数

64 位 -XX:-UseCompressedOops -XX:+UseCompressedOops
Object 8 + 8 = 16 (对象头+类型指针) 8 + 4 + 4 = 16(对象头+压缩的类型指针+对齐填充)
包含一个int (8 + 8) + 4 + 4 = 24 (8 + 4) + 4 = 16
包含两个int (8 + 8) + 4*2 = 24 (8 + 4) + 4*2 + 4 = 24
包含三个int (8 + 8) + 4*3 + 4 = 32 (8 + 4) + 4*3 = 24
不压缩引用占 8 个字节,压缩占 4 个字节
包含一个引用 (8 + 8) + 8 = 24 (8 + 4) + 4 = 16
包含两个引用 (8 + 8) + 8*2 = 32 (8 + 4) + 4*2 + 4 = 24
静态数据不在对象内存里面
public class MemoryStaticTest {
    /**
     * -javaagent:{jar包路径}SizeOfObject.jar
     * 使用指针压缩,在一定情况下64位HotSpot jvm默认指针压缩
     *
     * Output:
     * amd64    +UseCompressedOops
     * StaticTest:                          16
     * Integer:                             16
     * StaticReferenceTest:                 16
     *
     * @param args
     */
    public static void main(String[] args) {
        System.out.println(System.getProperty("os.arch") + "    +UseCompressedOops");
        System.out.printf("%-30s%9d
", "StaticTest:", SizeOfObject.sizeOf(new StaticTest()));
        System.out.printf("%-30s%9d
", "Integer:", SizeOfObject.sizeOf(new Integer(1)));
        System.out.printf("%-30s%9d
", "StaticReferenceTest:", SizeOfObject.sizeOf(new StaticReferenceTest()));
    }

    static class StaticTest {
        private static int i1 = 1;
        private static int i2 = 1;
        private static int i3 = 1;
        private static int i4 = 1;
        private static int i5 = 1;
        private static int i6 = 1;
    }

    static class StaticReferenceTest {
        private static Object o1 = new Object();
        private static Object o2 = new Object();
        private static Object o3 = new Object();
        private static Object o4 = new Object();
        private static Object o5 = new Object();

    }
}

代码中还有一点其他测试: https://github.com/Deeeeeeeee/jcoding.git

为什么是这个样子呢

对象头在 64 位的时候,为什么是 8 个字节;32 位的时候,为什么是 4 个字节

一个引用在 64 位的时候,为什么是 8 个字节;32 位的时候,为什么是 4 个字节

这个跟 C++ 指针有关了,跟寄存器有关了

指针压缩又是怎么个回事

这一切都可以在 jvm 的实现中找到答案,也就是看 C++ 代码。只是我还没到这个阶段=。=

参考链接

【Java对象解析】不得不了解的对象头

JDK从6 update 23开始在64位系统上会默认开启压缩指针

【Java JVM】 Hotspot GC研究- 64位引用指针压缩技术

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

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

相关文章

  • Java对象内存占用分析

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

    JouyPub 评论0 收藏0
  • Java对象内存布局解析

    摘要:堆内存用于存放我们在程序中创建的对象,一旦没有足够的空间用于存放这些对象,即会抛出异常。当我们采用后一种方式时,我们需要了解一个对象是如何占据堆内存空间的,或者说是了解一个对象是由哪些部分组成的。 JVM将内存划分为程序计数器(Program Counter Register)、虚拟机栈(VM Stack)、本地方法栈(Native Method Stack)、堆(Heap)以及方法区...

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

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

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

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

    wow_worktile 评论0 收藏0
  • Java性能优化之性能调优常用参数设置

    摘要:作用默认,即,适当调整年轻代大小,可以一定层度上较少出现的概率其余性能调优常用参数设置指定的初始和最大堆内存大小,两值可以设置相同,以避免每次垃圾回收完成后重新分配内存。 Java性能优化之针对分代垃圾回收调整 [TOC] JVM内存的系统级的调优主要的目的是减少Minor GC的频率和Full GC的次数,过多的Minor GC和Full GC是会占用很多的系统资源,影响系统的吞吐...

    Jrain 评论0 收藏0
  • Java GC

    摘要:对字节码文件进行解释执行,把字节码翻译成相关平台上的机器指令。使用命令可对字节码文件以及配置文件进行打包可对一个由多个字节码文件和配置文件等资源文件构成的项目进行打包。和不存在永久代这种说法。 Java技术体系 从广义上讲,Clojure、JRuby、Groovy等运行于Java虚拟机上的语言及其相关的程序都属于Java技术体系中的一员。如果仅从传统意义上来看,Sun官方所定义的Jav...

    justCoding 评论0 收藏0

发表评论

0条评论

阅读需要支付1元查看
<