How much memory does a Java object occupy?

Recently, I read in-depth understanding of Java virtual machine and had a further understanding of the memory layout of Java objects, so I naturally had a very common question in my mind, that is, how much memory does a Java object occupy?

I found a blog on the Internet, which said very well: http://yueyemaitian.iteye.com/blog/2033046 , the class provided in it is also very practical:

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);  
    }  
}

You can use this code to verify while watching. Note that to run this program, you need to inject instrumentation through javaagent. See the original blog for details. Today, I mainly summarize the basic rules for manually calculating the number of bytes occupied by Java objects. As a basic skill, you must get √. I hope it can help Java rookies like me.

Before the introduction, briefly review the memory layout of Java objects: object header, instance data and padding. For details, please refer to my reading notes. In addition, the results may be different in different environments. My environment is hotspot virtual machine and 64 bit windows.

Now enter the text:

Object header

Object headers occupy 8 bytes on 32-bit systems and 16 bytes on 64 bit systems.

Instance data

The memory occupation of primitive type is as follows:

The reference type occupies 4 bytes each on a 32-bit system and 8 bytes each on a 64 bit system.

Align fill

The alignment of hotspot is 8 bytes:

Pointer compression

The amount of memory occupied by the object is affected by the VM parameter usecompressedoops.

1) impact on object header

When (- XX: + usecompressedoops) is enabled, the object header size is 12bytes (64 bit machine).

static class A {
        int a;
    }

Memory occupied by object A:

Turn off pointer compression: 16 + 4 = 20 is not a multiple of 8, so + padding / 4 = 24

Enable pointer compression: 12 + 4 = 16 is already a multiple of 8, and there is no need for padding.

2) impact on reference type

On 64 bit machines, the reference type takes 8 bytes, and 4 bytes after pointer compression is turned on.

static class B2 {
        int b2a;
        Integer b2b;
}

B2 memory occupied by objects:

Turn off pointer compression: 16 + 4 + 8 = 28 is not a multiple of 8, so + padding / 4 = 32

Turn on pointer compression: 12 + 4 + 4 = 20 is not a multiple of 8, so + padding / 4 = 24

Array object

On 64 bit machines, the object header of array objects takes 24 bytes, and 16 bytes after compression is enabled. The reason why it takes more memory than ordinary objects is that it requires additional space to store the length of the array.

First consider the memory size occupied by new integer [0]. The length is 0, which is the size of the object header:

Compression not enabled: 24bytes

After compression is enabled: 16bytes

Next, it is easy to calculate new integer [1], new integer [2], new integer [3] and new integer [4]:

Compression not turned on:

Turn on compression:

Take new integer [3] as a specific explanation:

Compression not enabled: 24 (object header) + 8 * 3 = 48, no padding is required;

Enable compression: 16 (object header) + 3 * 4 = 28, + padding / 4 = 32, and so on.

The array of custom classes is the same, for example:

static class B3 {
        int a;
        Integer b;
    }

Memory size occupied by new B3 [3]:

Compression not turned on: 48

After compression on: 32

Compound object

Calculating the memory occupied by composite objects is actually using the above rules, which is just a little troublesome.

1) size of the object itself

Directly calculate the space occupied by the current object, including the basic type instance field size of the current class and superclass, the reference type instance field reference size, the total space occupied by the instance basic type array, and the space occupied by the instance reference type array reference itself; However, it does not include the size of the object itself inherited from the superclass and the instance reference field declared by the current class, and the size of the object itself referenced by the instance reference array.

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();
            }
        }
    }

Compression not enabled: 16 (object header) + 4 (BA) + 8 (as reference size) + padding / 4 = 32

Open compression: 12 + 4 + 4 + padding / 4 = 24

2) total space occupied by the current object

Recursively calculate the total space occupied by the current object, including the instance field size of the current class and superclass and the reference object size of the instance field.

When recursively calculating the memory occupied by composite objects, it should be noted that the alignment filling is carried out in units of each object. It is easy to understand by looking at the following figure.

Now let's manually calculate the total memory occupied by the C object, which is mainly composed of three parts: the size of the C object itself + the size of the array object + the size of the B object.

Compression not turned on:

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

Turn on compression:

(12 + 4 + 4 + 4 (padding)) + (16 + 4 * 3 + 4 (array object padding)) + (12 + 8 + 4 (b object padding)) * 3 = 128bytes

If you are interested, you can try.

In actual work, there should be few scenarios where you really need to manually calculate the object size, but I think as a basic knowledge, every java developer should understand. In addition, you should have an intuitive understanding of how much memory your code occupies and how it is arranged in memory.

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>