引用类型(reference type: Integer)在 32 位系统上每一个占用 4bytes(即32bit, 才干管理 2^32=4G 的内存), 在 64 位系统上每一个占用 8bytes(开启压缩为 4 bytes)。

四. 对齐填充

HotSpot 的对齐方式为 8 字节对齐。不足的须要 Padding 填充对齐, 公式:(对象头 + 实例数据 + padding)% 8 == 0 (0<= padding <8)

五. 计算 Java 对象占用空间大小

借助 Instrument 接口的 getObjectSize 方法计算对象占用空间

SizeOfAgent: 计算对象大小类

package com.wenniuwuren.objectsizeof;

import java.lang.instrument.Instrumentation;

import java.lang.reflect.Array;

import java.lang.reflect.Field;

import java.lang.reflect.Modifier;

import java.util.IdentityHashMap;

import java.util.Map;

import java.util.Stack;

/**

* 借助 Instrumentation 接口的 getObjectSize 方法计算对象占用空间

* 原来的 sizeOf 仅仅能计算本对象占用空间, 无法计算继承下来的占用空间,

* 只是能够用反射的方法把全部占用空间计算出来

*

* Created by zhuyb on 16/3/20.

*/

public class SizeOfAgent {

static Instrumentation instrumentation;

// 第一个參数由 –javaagent。 第二个參数由 JVM 传入

public static void premain(String agentArgs, Instrumentation instP) {

instrumentation = instP;

}

// 返回没有子类对象大小的大小

public static long sizeOf(Object o) {

if (instrumentation == null) {

throw new IllegalStateException("Can not access instrumentation environment.\n" +

"Please check if jar file containing SizeOfAgent class is \n" +

"specified in the java's \"-javaagent\" command line argument.");

}

return instrumentation.getObjectSize(o);

}

/**

*

* 计算复合对象

* @param obj object to calculate size of

* @return object size

*/

public static long fullSizeOf(Object obj) {

Map visited = new IdentityHashMap();

Stack stack = new Stack();

long result = internalSizeOf(obj, stack, visited);

while (!stack.isEmpty()) {

result += internalSizeOf(stack.pop(), stack, visited);

}

visited.clear();

return result;

}

// 这个算法使每一个对象仅被计算一次。 避免循环引用,即死循环计算

private static boolean skipObject(Object obj, Map visited) {

if (obj instanceof String) {

// String 池里已有的不再计算

if (obj == ((String) obj).intern()) {

return true;

}

}

return (obj == null) // 已有对象不再计算

|| visited.containsKey(obj);

}

private static long internalSizeOf(Object obj, Stack stack, Map visited) {

if (skipObject(obj, visited)){

return 0;

}

visited.put(obj, null);

long result = 0;

// get size of object + primitive variables + member pointers

result += SizeOfAgent.sizeOf(obj);

// 处理全部数组内容

Class clazz = obj.getClass();

if (clazz.isArray()) {

// [I , [F 基本类型名字长度是2

if(clazz.getName().length() != 2) {// skip primitive type array

int length = Array.getLength(obj);

for (int i = 0; i < length; i++) {

stack.add(Array.get(obj, i));

}

}

return result;

}

// 处理对象的全部字段

while (clazz != null) {

Field[] fields = clazz.getDeclaredFields();

for (int i = 0; i < fields.length; i++) {

// 不反复计算静态类型字段

if (!Modifier.isStatic(fields[i].getModifiers())) {

// 不反复计算原始类型字段

if (fields[i].getType().isPrimitive()) {

continue;

} else {

// 使 private 属性可訪问

fields[i].setAccessible(true);

try {

// objects to be estimated are put to stack

Object objectToAdd = fields[i].get(obj);

if (objectToAdd != null) {

stack.add(objectToAdd);

}

} catch (IllegalAccessException ex) {

assert false;

}

}

}

}

clazz = clazz.getSuperclass();

}

return result;

}

}

使用上述代码必须将上述代码打成 jar 包, 而且 MANIFEST.MF 文件设置參数(

Premain-Class:sizeof.agent.SizeOfAgent

Boot-Class-Path:

Can-Redefine-Classes:false

)

假设使用 Maven 打包的话, 能够直接在 pom.xml 里面设置 MANIFEST.MF 的參数 :

maven-jar-plugin

2.4

SizeOfAgent

com.wenniuwuren.objectsizeof.SizeOfAgent

false

false

測试类: SizeOfAgentTest

package com.wenniuwuren.objectsizeof;

import static com.wenniuwuren.objectsizeof.SizeOfAgent.*;

/**

* 下面结果在 64-bit JVM 下測试

* 启动參数1(不压缩指针长度):-javaagent:target/SizeOfAgent.jar -XX:-UseCompressedOops

*

* Created by zhuyb on 16/3/20.

*/

public class SizeOfAgentTest {

public static void main(String[] args) {

System.out.println("------------------空对象----------------------------");

// 16 bytes + 0 + 0 = 16 空对象, 仅仅有对象头

System.out.println("sizeOf(new Object()) = " + sizeOf(new Object()));

System.out.println("fullSizeOf(new Object()) = " + fullSizeOf(new Object()));

System.out.println("----------------非空对象含有原始类型、引用类型------------------------------");

// 16 bytes + 8 + 4 + padding = 32

System.out.println("sizeOf(new A()) = " + sizeOf(new A()));

System.out.println("fullSizeOf(new A()) = " + fullSizeOf(new A()));

// 16 + 4 + padding =24 数据是一个 int

System.out.println("sizeOf(new Integer(1)) = " + sizeOf(new Integer(1)));

// (16 + int hash:4 + int hash32:4 + refer char value[]:8 + padding) = 32

// 静态属性(static)不计算空间。由于全部对象都是共享一块空间的

// 不同版本号JDK可能 String 内部 Field 可能不同,本次測试使用JDK1.7

System.out.println("sizeOf(new String()) = " + sizeOf(new String()));

// (16 + 4 + 4 + 8 + padding) + (24 + 0 + padding) = 56

System.out.println("fullSizeOf(new String()) = " + fullSizeOf(new String()));

// (16 + 4 + 4 + 8 + padding) = 32

System.out.println("sizeOf(new String('a')) = " + sizeOf(new String("a")));

// (16 + 4 + 4 + 8 +padding) + (24 + 2 + padding) = 64

System.out.println("fullSizeOf(new String('a')) = " + fullSizeOf(new String("a")));

System.out.println("-------------------原始类型数组对象---------------------------");

// 24 bytes + 0*1 + 0 = 24 数组长度为 0,所以仅仅有对象头的长度

System.out.println("sizeOf(new byte[0]) = " + sizeOf(new byte[0]));

System.out.println("fullSizeOf(new byte[0]) = " + fullSizeOf(new byte[0]));

// 24 + 1*1 + padding = 32

System.out.println("sizeOf(new byte[1]) = " + sizeOf(new byte[1]));

System.out.println("fullSizeOf(new byte[1]) = " + fullSizeOf(new byte[1]));

// 24 + 1*2 + padding = 32

System.out.println("sizeOf(new char[1]) = " + sizeOf(new char[1]));

System.out.println("fullSizeOf(new char[1]) = " + fullSizeOf(new char[1]));

// 24 + 9*1 + padding = 40

System.out.println("sizeOf(new byte[9]) = " + sizeOf(new byte[9]));

System.out.println("fullSizeOf(new byte[9]) = " + fullSizeOf(new byte[9]));

System.out.println("--------------------引用类型数组对象--------------------------");

// 24 bytes + 0*8 + 0 = 24 数组长度为 0

System.out.println("sizeOf(new Integer[0]) = " + sizeOf(new Integer[0]));

System.out.println("fullSizeOf(new Integer[0]) = " + fullSizeOf(new Integer[0]));

// 24 bytes + 1*8 + 0 = 32 引用对象 64-bit JVM 占用 8 bytes

System.out.println("sizeOf(new Integer[1]) = " + sizeOf(new Integer[1]));

System.out.println("fullSizeOf(new Integer[1]) = " + fullSizeOf(new Integer[1]));

// 24 bytes + 2*8 + padding = 40

System.out.println("sizeOf(new Integer[1]) = " + sizeOf(new Integer[1]));

System.out.println("fullSizeOf(new Integer[1]) = " + fullSizeOf(new Integer[1]));

// 24 + 3*8 + padding = 48

System.out.println("sizeOf(new Integer[3]) = " + sizeOf(new Integer[3]));

System.out.println("fullSizeOf(new Integer[3]) = " + fullSizeOf(new Integer[3]));

System.out.println("-------------------自己定义数组对象---------------------------");

// 16 + (4+8) + padding = 32

System.out.println("sizeOf(new B()) = " + sizeOf(new B()));

System.out.println("fullSizeOf(new B()) = " + fullSizeOf(new B()));

// 24 + 0*8 + padding = 24 引用对象 64-bit JVM 占用 8 bytes,

// 由于没创建真实的 new B()所以 B类内部数据还未占用空间

System.out.println("sizeOf(new B[0]) = " + sizeOf(new B[0]));

System.out.println("fullSizeOf(new B[0]) = " + fullSizeOf(new B[0]));

// 24 + 1*8 + padding = 32

System.out.println("sizeOf(new B[1]) = " + sizeOf(new B[1]));

System.out.println("fullSizeOf(new B[1]) = " + fullSizeOf(new B[1]));

// 24 + 2*8 + padding = 40

System.out.println("sizeOf(new B[2]) = " + sizeOf(new B[2]));

System.out.println("fullSizeOf(new B[2]) = " + fullSizeOf(new B[2]));

// 24 + 3*8 + padding = 48

System.out.println("sizeOf(new B[3]) = " + sizeOf(new B[3]));

System.out.println("fullSizeOf(new B[3]) = " + fullSizeOf(new B[3]));

System.out.println("-------------------复合对象---------------------------");

// 16 + (4+8) + padding = 32 sizeOf 仅仅计算单层次占用空间大小

System.out.println("sizeOf(new C()) = " + sizeOf(new C()));

// (16 + (4+8) + padding1) + (24 + 2*8 + padding2) + 2*(16 + (4+8) + padding3) = 136

// 递归计算当前对象占用空间总大小,包含当前类和超类的实例字段大小以及实例字段引用对象大小

System.out.println("fullSizeOf(new C()) = " + fullSizeOf(new C()));

System.out.println("-------------------继承关系---------------------------");

// 涉及继承关系的时候有一个最主要的规则:首先存放父类中的成员,接着才是子类中的成员, 父类也要依照 8 byte 规定

// 16 + 1 + padding = 24

System.out.println("sizeOf(new D()) = " + sizeOf(new D()));

System.out.println("fullSizeOf(new D()) = " + fullSizeOf(new D()));

// 16 + 父类(1 + padding1) + 1 + padding2 = 32

System.out.println("sizeOf(new E()) = " + sizeOf(new E()));

System.out.println("fullSizeOf(new E()) = " + fullSizeOf(new E()));

}

public static class A {

int a;

Integer b;

}

public static class B {

int a;

Integer b;

}

public static class C{

int c;

B[] b = new B[2];

// 初始化

C() {

for (int i = 0; i < b.length; i++) {

b[i] = new B();

}

}

}

public static class D {

byte d1;

}

public static class E extends D {

byte e1;

}

}

执行:

假设在 IDE 执行时须要设置 JVM 參数: -javaagent:target/SizeOfAgent.jar -XX:-UseCompressedOops;

假设在命令行执行命令: java -javaagent:sizeofag.jar

-XX:-UseCompressedOops 主类名称。

測试结果:

------------------空对象----------------------------

sizeOf(new Object()) = 16

fullSizeOf(new Object()) = 16

----------------非空对象含有原始类型、引用类型------------------------------

sizeOf(new A()) = 32

fullSizeOf(new A()) = 32

sizeOf(new Integer(1)) = 24

sizeOf(new String()) = 32

fullSizeOf(new String()) = 56

sizeOf(new String('a')) = 32

fullSizeOf(new String('a')) = 64

-------------------原始类型数组对象---------------------------

sizeOf(new byte[0]) = 24

fullSizeOf(new byte[0]) = 24

sizeOf(new byte[1]) = 32

fullSizeOf(new byte[1]) = 32

sizeOf(new char[1]) = 32

fullSizeOf(new char[1]) = 32

sizeOf(new byte[9]) = 40

fullSizeOf(new byte[9]) = 40

--------------------引用类型数组对象--------------------------

sizeOf(new Integer[0]) = 24

fullSizeOf(new Integer[0]) = 24

sizeOf(new Integer[1]) = 32

fullSizeOf(new Integer[1]) = 32

sizeOf(new Integer[1]) = 32

fullSizeOf(new Integer[1]) = 32

sizeOf(new Integer[3]) = 48

fullSizeOf(new Integer[3]) = 48

-------------------自己定义数组对象---------------------------

sizeOf(new B()) = 32

fullSizeOf(new B()) = 32

sizeOf(new B[0]) = 24

fullSizeOf(new B[0]) = 24

sizeOf(new B[1]) = 32

fullSizeOf(new B[1]) = 32

sizeOf(new B[2]) = 40

fullSizeOf(new B[2]) = 40

sizeOf(new B[3]) = 48

fullSizeOf(new B[3]) = 48

-------------------复合对象---------------------------

sizeOf(new C()) = 48

fullSizeOf(new C()) = 152

-------------------继承关系---------------------------

sizeOf(new D()) = 24

fullSizeOf(new D()) = 24

sizeOf(new E()) = 32

fullSizeOf(new E()) = 32

測试类中复合对象计算可能较为麻烦, 能够參照下图较为清楚地看出 new C() 的占用空间计算:

六. 总结

总体的 Java 对象是依照一定规则进行的。 清楚了 JVM 对象的内存布局和分配规则。 计算 Java 对象的大小就比較简单了。

Java 不像 C++ 能够提供对象大小, 这是 Java 语言的设计初衷(自己主动内存管理), 可是随着对 Java 的深入了解。 又到了对 JVM (使用 C、C++ 实现) 底层实现的问题上。

本文的參考资料为 2007 年的, 至今已有 9 年, 參考资料内容至今还是有效的,JVM 相关的东西变动确实小,挺有意思的

七. 參考资料

java统计空间占用_JVM —— Java 对象占用空间大小计算相关推荐

  1. JVM —— Java 对象占用空间大小计算

    零. 为什么要知道 Java 对象占用空间大小 缓存的实现: 在设计 JVM 内缓存时(不是借助 Memcached. Redis 等), 需要知道缓存的对象是否会超过 JVM 最大堆限制, 如果会超 ...

  2. Java对象内存大小计算

    背景 今天办公室两个人事妹子因为一道Java试题各持己见,誓死捍卫自己的答案(对,是HR没错 -- 程序猿快没活路了). 题:字符串 "7天学会JAVA" 占用的内存空间是 ( ) ...

  3. java统计空间占用_如何计算Java对象占用了多少空间?

    在Java中没有sizeof运算符,所以没办法知道一个对象到底占用了多大的空间,但是在分配对象的时候会有一些基本的规则,我们根据这些规则大致能判断出来对象大小. 对象头 对象的头部至少有两个WORD, ...

  4. java virtual 方法_JVM(Java Virtual Machine)

    Java语言的一个非常重要的特点就是与平台的无关性.而使用Java虚拟机是实现这一特点的关键.一般的高级语言如果要在不同的平台上运行,至少需要编译成不同的目标代码.而引入Java语言虚拟机后,Java ...

  5. java char占用多少字节_Java虚拟机:Java对象大小、对象内存布局及锁状态变化

    一个对象占多少字节? 关于对象的大小,对于C/C++来说,都是有sizeof函数可以直接获取的,但是Java似乎没有这样的方法.不过还好,在JDK1.5之后引入了Instrumentation类,这个 ...

  6. Java 中对象占用内存大小计算

    原文地址 mp.weixin.qq.com byte 与 bit bit:位,比特.信息的最小单位,二进制数中的一个位数 (二进制位),其值为"0" 或"1": ...

  7. java string 内存占用_JVM系列之:String,数组和集合类的内存占用大小

    简介 之前的文章中,我们使用JOL工具简单的分析过String,数组和集合类的内存占用情况,这里再做一次更详细的分析和介绍,希望大家后面再遇到OOM问题的时候不再抱头痛哭,而是可以有章可循,开始吧. ...

  8. Java对象内存空间大小计算

    一.查看基础类型的对象内存大小 八股文中很明确的告诉你了基础类型的大小 ,如下图: 类型 值大小(byte) 对象内存大小(byte) 备注 byte 1 16 char 2 16 int 4 16 ...

  9. JVM概念之Java对象的大小与引用类型

    2019独角兽企业重金招聘Python工程师标准>>> 本文来自和你在一起的博客,原文标题:<JVM调优总结(二)-一些概念>.本文总结了JVM概念中的Java对象的大小 ...

最新文章

  1. node.js学习5--------------------- 返回html内容给浏览器
  2. 【PAT (Advanced Level) Practice】1054 The Dominant Color (20 分)
  3. 常用的方法论-鱼骨图
  4. update empty content to text instance - where is B mode changed to D by frame
  5. plsql 简单介绍
  6. Java自学手记——多态
  7. Andorid 系统性能优化---(22)Android性能相关常用命令收集
  8. java http data chunk_HTTP协议之Chunked解析
  9. 编译器为C++ 空类自动生成的成员函数
  10. 十年磨一剑,奋进新征程!麒麟信安在上交所科创板成功上市
  11. OSChina 周一乱弹 —— 帅气的红薯为何迷倒了万千 OSCers ~~~
  12. linux系统下解压缩
  13. 曲奇的ndnSIM API教程翻译 命名数据网络 NDN ndn simulator
  14. php,ajax -->Uncaught SyntaxError: Unexpected end of JSON input at JSON.parse (<anonymous>)
  15. 第六章 Linux实际操作——实用指令
  16. 同为博客,不同风格 ——Hexo另类搭建
  17. 儿童编程Scratch入门课程都学习什么内容?
  18. Layui 数据表格合计
  19. 【ASML】EUV光刻技术PPT
  20. 亚利桑那大学公开课:【知识产权课程】笔记

热门文章

  1. 一步一步图文介绍SpriteKit使用TexturePacker导出的纹理集Altas
  2. Alictf 2015决赛题目设计和解题思路
  3. C#获取刚插入的数据的id
  4. 浅谈JavaScript中闭包
  5. java每日小算法(10)
  6. Asp.Net MVC开源CMS - Orchard
  7. 自定义按钮动态变化_新闻价值的变化定义
  8. 莫烦Pytorch神经网络第四章代码修改
  9. leetcode350. 两个数组的交集 II(hashmap)
  10. iref streams_如何利用Neo4j Streams并建立即时数据仓库