一个Java对象到底占多大内存

最近在读《深入理解Java虚拟机》,对Java对象的内存布局有了进一步的认识,于是脑子里自然而然就有一个很普通的问题,就是一个Java对象到底占用多大内存?

在网上搜到了一篇博客讲的非常好:http://yueyemaitian.iteye.com/blog/2033046,里面提供的这个类也非常实用:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99


import java.lang.instrument.Instrumentation; 

import java.lang.reflect.Array; 

import java.lang.reflect.Field; 

import java.lang.reflect.Modifier; 

import java.util.ArrayDeque; 

import java.util.Deque; 

import java.util.HashSet; 

import java.util.Set; 

  

/**

 * 对象占用字节大小工具类

 *

 * @author tianmai.fh

 * @date 2014-03-18 11:29

 */

public class SizeOfObject { 

    static Instrumentation inst; 

  

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

        inst = instP; 

    

  

    /**

     * 直接计算当前对象占用空间大小,包括当前类及超类的基本类型实例字段大小、<br></br>

     * 引用类型实例字段引用大小、实例基本类型数组总占用空间、实例引用类型数组引用本身占用空间大小;<br></br>

     * 但是不包括超类继承下来的和当前类声明的实例引用字段的对象本身的大小、实例引用数组引用的对象本身的大小 <br></br>

     *

     * @param obj

     * @return

     */

    public static long sizeOf(Object obj) { 

        return inst.getObjectSize(obj); 

    

  

    /**

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

     *

     * @param objP

     * @return

     * @throws IllegalAccessException

     */

    public static long fullSizeOf(Object objP) throws IllegalAccessException { 

        Set<Object> visited = new HashSet<Object>(); 

        Deque<Object> toBeQueue = new ArrayDeque<Object>(); 

        toBeQueue.add(objP); 

        long size = 0L; 

        while (toBeQueue.size() > 0) { 

            Object obj = toBeQueue.poll(); 

            //sizeOf的时候已经计基本类型和引用的长度,包括数组 

            size += skipObject(visited, obj) ? 0L : sizeOf(obj); 

            Class<?> tmpObjClass = obj.getClass(); 

            if (tmpObjClass.isArray()) { 

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

                if (tmpObjClass.getName().length() > 2) { 

                    for (int i = 0, len = Array.getLength(obj); i < len; i++) { 

                        Object tmp = Array.get(obj, i); 

                        if (tmp != null) { 

                            //非基本类型需要深度遍历其对象 

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

                        

                    

                

            } else

                while (tmpObjClass != null) { 

                    Field[] fields = tmpObjClass.getDeclaredFields(); 

                    for (Field field : fields) { 

                        if (Modifier.isStatic(field.getModifiers())   //静态不计 

                                || field.getType().isPrimitive()) {    //基本类型不重复计 

                            continue

                        

  

                        field.setAccessible(true); 

                        Object fieldValue = field.get(obj); 

                        if (fieldValue == null) { 

                            continue

                        

                        toBeQueue.add(fieldValue); 

                    

                    tmpObjClass = tmpObjClass.getSuperclass(); 

                

            

        

        return size; 

    

  

    /**

     * String.intern的对象不计;计算过的不计,也避免死循环

     *

     * @param visited

     * @param obj

     * @return

     */

    static boolean skipObject(Set<Object> visited, Object obj) { 

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

            return true

        

        return visited.contains(obj); 

    

}

大家可以用这个代码边看边验证,注意的是,运行这个程序需要通过javaagent注入Instrumentation,具体可以看原博客。我今天主要是总结下手动计算Java对象占用字节数的基本规则,做为基本的技能必须get√,希望能帮到和我一样的Java菜鸟。

在介绍之前,简单回顾下,Java对象的内存布局:对象头(Header),实例数据(Instance Data)和对齐填充(Padding),详细的可以看我的读书笔记。另外:不同的环境结果可能有差异,我所在的环境是HotSpot虚拟机,64位Windwos。

下面进入正文:

对象头

对象头在32位系统上占用8bytes,64位系统上占用16bytes。

实例数据

原生类型(primitive type)的内存占用如下:

Primitive Type Memory Required(bytes)
boolean 1
byte 1
short 2
char 2
int 4
float 4
long 8
double 8

reference类型在32位系统上每个占用4bytes, 在64位系统上每个占用8bytes。

对齐填充

HotSpot的对齐方式为8字节对齐:

(对象头 + 实例数据 + padding) % 8等于0且0 <= padding < 8

指针压缩

对象占用的内存大小收到VM参数UseCompressedOops的影响。

1)对对象头的影响

开启(-XX:+UseCompressedOops)对象头大小为12bytes(64位机器)。

?


1

2

3


static class A {

        int a;

    }

A对象占用内存情况:

关闭指针压缩: 16+4=20不是8的倍数,所以+padding/4=24

开启指针压缩: 12+4=16已经是8的倍数了,不需要再padding。

2) 对reference类型的影响

64位机器上reference类型占用8个字节,开启指针压缩后占用4个字节。

?


1

2

3

4


static class B2 {

        int b2a;

        Integer b2b;

}

B2对象占用内存情况:

关闭指针压缩: 16+4+8=28不是8的倍数,所以+padding/4=32

开启指针压缩: 12+4+4=20不是8的倍数,所以+padding/4=24

数组对象

64位机器上,数组对象的对象头占用24个字节,启用压缩之后占用16个字节。之所以比普通对象占用内存多是因为需要额外的空间存储数组的长度。

先考虑下new Integer[0]占用的内存大小,长度为0,即是对象头的大小:

未开启压缩:24bytes

开启压缩后:16bytes

接着计算new Integer[1],new Integer[2],new Integer[3]和new Integer[4]就很容易了:

未开启压缩:

开启压缩:

拿new Integer[3]来具体解释下:

未开启压缩:24(对象头)+8*3=48,不需要padding;

开启压缩:16(对象头)+3*4=28,+padding/4=32,其他依次类推。

自定义类的数组也是一样的,比如:

?


1

2

3

4


static class B3 {

        int a;

        Integer b;

    }

new B3[3]占用的内存大小:

未开启压缩:48

开启压缩后:32

复合对象

计算复合对象占用内存的大小其实就是运用上面几条规则,只是麻烦点。

1)对象本身的大小

直接计算当前对象占用空间大小,包括当前类及超类的基本类型实例字段大小、引用类型实例字段引用大小、实例基本类型数组总占用空间、实例引用类型数 组引用本身占用空间大小; 但是不包括超类继承下来的和当前类声明的实例引用字段的对象本身的大小、实例引用数组引用的对象本身的大小。

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14


static class B {

        int a;

        int b;

    }

static class C {

        int ba;

        B[] as = new B[3];

        C() {

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

                as[i] = new B();

            }

        }

    }

未开启压缩:16(对象头)+4(ba)+8(as引用的大小)+padding/4=32

开启压缩:12+4+4+padding/4=24

2)当前对象占用的空间总大小

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

递归计算复合对象占用的内存的时候需要注意的是:对齐填充是以每个对象为单位进行的,看下面这个图就很容易明白。

现在我们来手动计算下C对象占用的全部内存是多少,主要是三部分构成:C对象本身的大小+数组对象的大小+B对象的大小。

未开启压缩:

(16 + 4 + 8+4(padding)) + (24+ 8*3) +(16+8)*3 = 152bytes

开启压缩:

(12 + 4 + 4 +4(padding)) + (16 + 4*3 +4(数组对象padding)) + (12+8+4(B对象padding))*3= 128bytes

?

时间: 2024-11-05 02:25:01

一个Java对象到底占多大内存的相关文章

一个Java对象到底占多大内存?(转)

最近在读<深入理解Java虚拟机>,对Java对象的内存布局有了进一步的认识,于是脑子里自然而然就有一个很普通的问题,就是一个Java对象到底占用多大内存? 在网上搜到了一篇博客讲的非常好:http://yueyemaitian.iteye.com/blog/2033046,里面提供的这个类也非常实用: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35

一个Java对象到底有多大

经常遇到一个问题,需要在内存里缓存一批数据来提高效率(避免每次都读取DB).那问题来了,这些对象到底会占用多大内存呢,这直接决定了可以缓存多少条记录,以及上线之后是否会内存不够等问题. 来看几种解决方法. 测试 实践是检验真理的唯一标准!比如你要想cache10w条记录,那你就把10w条记录加载到内存,然后看看到底用了多少内存.至于怎么看内存花了多少,你可以 任务管理器 top Java Runtime类 blabla.... 我们来看看直接从Java程序里能获取到的Runtime. impor

一个PHP数组能占多大内存

最近用PHP读取一个大文件把相关数据存放到数组中,之后处理并输出, 读取过程中发现占用内存很大, 于是很好奇这个问题. 简单的写一个代码 <?php $m1 = memory_get_usage(); $arr = array( ); $m2 = memory_get_usage(); $arr2 = array( 'testa'=>'hello world1', 'testb'=>'hello world2', ); $m3 = memory_get_usage(); echo $m1

如何获取一个Java对象所占内存大小

新建一个maven工程 我们先在IDEA中新建一个名为ObjectSizeFetcherAgent的maven工程,如下图: 在maven项目中的pom.xml中新增一个打jar包的插件,如下: <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId&g

一个java对象占多少内存空间

最近在做flex,需要封装一个事件单pojo对象进行展现.想想数据库里6000多条记录,封装多了肯定会占用很大内存,并且flex对象完全是从java对象传递过来再次封装的,也就是两倍的内存量.所以有了标题的考虑,那么一个java pojo对象到底占多少内存呢.如下题所示: Object o=new Object(): 在java中空对象占八个字节,对象的引用占四个字节.所以上面那条语句所占的空间是4byte+8byte=12byte.java中的内存是以8的倍数来分配的,所以分配的内存是16by

JAVA内存使用--如何计算一个Java对象占用的字节数

本文中,我们讨论一个问题:如何计算(或者说,估算)一个Java对象占用的内存数量? 通常,我们谈论的堆内存使用的前提是以"一般情况"为背景的.不包括下面两种情形: 某些情况下,JVM根本就没有把Object放入堆中.例如:原则上讲,一个小的thread-local对象存在于栈中,而不是在堆中. 被Object占用内存的大小依赖于Object的当前状态.例如:Object的同步锁是否生效,或者,Object是否正在被回收. 我们先来看看在堆中单个的Object长什么样子 在堆中,每个对象

Win10到底需要多大内存才够用,你知道吗

Win10带来了语音助手.通知中心.Edge浏览器以及安全上的诸多改进,依然有人因为界面不够华丽,隐私保护有潜在威胁,体验中容易卡顿.蓝屏等诸多问题而选择不升级.如果你已经升级Win10,玩硬件的用户一般在这时候就要考虑一个问题:Win10操作系统对配置有什么要求呢?官方给出的基本配置需求和实际使用又有什么区别呢?今天我们就带着这些问题来分析一下Win10专业版到底需要多大内存才够用? ▼ ——官方给出最低支持配置 除开内存和处理器,诸如硬盘空间也只要16GB-20GB,和Win7.Win8大同

每天进步一点点-实例为导学-一个java对象序列化的例子

序列化和反序列化例子 如果我们想要序列化一个对象, (对象 转 序列)首先要创建某些OutputStream(如FileOutputStream.ByteArrayOutputStream等),然后将这些OutputStream封装在一个ObjectOutputStream中.这时候,只需要调用writeObject()方法就可以将对象序列化,并将其发送给OutputStream(记住:对象的序列化是基于字节(1字节8位)的,不能使用Reader和Writer等基于字符的层次结构).而反序列的过

Java千问:你知道Java语言布尔型数据到底占多大空间吗?

我们都知道,Java语言中有个boolean类型.每个boolean类型的变量中存储的是一个true或者是false的逻辑值.那么存储这个逻辑值,需要多大的空间呢?从理论上来讲,存储这个逻辑值只需要1个位(bit)就可以了,很多教科书上谈到这个问题的时候,也说boolean类型的数据在内存中只占1个位. 但是稍微有点计算机常识的人都知道:计算机完成寻址操作的时候,是以字节为最小单位进行的.也就是说每次要读取内存中数据的时候,最小只能精确到1个字节,不能单独读取某个位上的信息.如果boolean类