由于Java的设计者不想让程序员管理和了解内存的使用,我们想要知道一个对象在内存中的大小变得比较困难了。本文提供了可以获取对象的大小的方法,但是由于各个虚拟机在内存使用上可能存在不同,因此该方法不能在各虚拟机上都适用,而是仅在hotspot 32位虚拟机上,或者其它内存管理方式与hotspot 32位虚拟机相同的虚拟机上 适用。
本方法使用了Unsafe类来访问对象的私有属性,因此有些特殊的设置和做法,要留意类定义前面的文字说明。
要想计算对象大小,我们必须熟悉hotspot32上不同类型所占的空间:
一,原始类型primitives:
boolean:1 byte,尽管Java语言规范里面boolean是一个bit;
byte:1 byte;
char:2 bytes;
short:2 bytes;
int:4 bytes;
float:4 bytes;
long:8 bytes;
double:8 bytes。
二,引用类型:
4 bytes,即使是null值也是如此。
三,空的普通对象(无任何属性,如new Object(),不是null对象):
8 bytes。存放对象头的各种信息。
四,空的数组(即长度为0的数组,而不是null数组):
12 bytes,其中比普通对象多出来的4 bytes是用来放数组长度的。
五,hotspot 32分配内存是以8 bytes的整数倍来计算的,因此不足8个字节的对象要补足剩余的字节数以对齐。
/** * 这个例子在eclipse里不能直接编译,要到项目的属性, * Java Compiler,Errors/Warnings中Deprecated and restricted API * 中Forbidden reference(access rules)中设置为warning。 * * 获取一个Java对象在内存所占的空间,不同的虚拟机内存管理方式可能不同, * 本例是针对32位的hotspot虚拟机的。 * * 由于虚拟机对字符串做了特殊处理,比如将其放入常量池,因此sizeof得到的字符串 * 包含了常量池里面占用的空间。基本类型的包装类也会重复利用对象。 * * 设计作者: teasp * 信息描述: */ @SuppressWarnings("restriction") public class HotspotSizeof { public static final int OBJ_BASIC_LEN = 8 * 8; public static final int ARRAY_BASIC_LEN = 12 * 8; public static final int OBJ_REF_LEN = 4 * 8; public static final int ALIGN = 8 * 8; private static Unsafe UNSAFE; static { try { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); UNSAFE = (Unsafe) theUnsafe.get(null); } catch (Exception e) { e.printStackTrace(); } } /** * 原始类型的种类,以及每个类型所占空间,单位为bit * @author Administrator * */ private enum PType { 布尔(8)/*Java语言规定是1个bit*/,字节(8),字符(16),短整(16), 整形(32),浮点(32),长整(64),双精(64); private int bits; private PType(int bits) { this.bits = bits; } public int getBits() { return bits; } } /** * 计算obj对象在虚拟机中所占的内存,单位为bit。 * 如果isPapa为true,则表明计算的是obj对象父类定义的属性。 * * @param obj * @param clazz * @param isPapa * @return */ private static int getObjBits(Object obj, Class<?> clazz, boolean isPapa) { int bits = 0; if (obj == null) { return bits; } bits += OBJ_BASIC_LEN; if (isPapa) { bits = 0; } Field[] fields = clazz.getDeclaredFields(); if (fields != null) { for (Field field : fields) { //静态属性不参与计算 if (Modifier.isStatic(field.getModifiers())) { // System.out.println("static " + field.getName()); continue; } Class<?> c = field.getType(); if (c == boolean.class) { bits += PType.布尔.getBits(); } else if (c == byte.class) { bits += PType.字节.getBits(); } else if (c == char.class) { bits += PType.字符.getBits(); } else if (c == short.class) { bits += PType.短整.getBits(); } else if (c == int.class) { bits += PType.整形.getBits(); System.out.println(field.getName() + "=" + UNSAFE.getInt(obj, UNSAFE.objectFieldOffset(field))); } else if (c == float.class) { bits += PType.浮点.getBits(); } else if (c == long.class) { bits += PType.长整.getBits(); } else if (c == double.class) { bits += PType.双精.getBits(); } else if (c == void.class) { //nothing } else if (c.isArray()) {//是数组 Object o = UNSAFE.getObject(obj, UNSAFE.objectFieldOffset(field)); bits += OBJ_REF_LEN; if (o != null) { try { bits += bitsofArray(o); } catch (Exception e) { throw new RuntimeException(e); } } } else {//普通对象 Object o = UNSAFE.getObject(obj, UNSAFE.objectFieldOffset(field)); bits += OBJ_REF_LEN; if (o != null) { try { bits += bitsof(o); } catch (Exception e) { throw new RuntimeException(e); } } } } } Class<?> papa = clazz.getSuperclass(); if (papa != null) { bits += getObjBits(obj, papa, true); } //补齐,当计算父类属性时,因为是对同一个对象在进行统计,所以不要补齐。 //一个对象只做一次补齐动作。 if (false == isPapa) { if (bits%ALIGN > 0) { bits += (ALIGN - bits%ALIGN); } } return bits; } /** * 计算arr数组在虚拟机中所占的内存,单位为bit * @param arr * @return */ private static int bitsofArray(Object arr) { int bits = 0; if (arr == null) { return bits; } bits += ARRAY_BASIC_LEN; Class<?> c = arr.getClass(); if (c.isArray() == false) { throw new RuntimeException("Must be array!"); } if (c == boolean[].class) { bits += PType.布尔.getBits() * ((boolean[])arr).length; } else if (c == byte[].class) { bits += PType.字节.getBits() * ((byte[])arr).length; } else if (c == char[].class) { bits += PType.字符.getBits() * ((char[])arr).length; } else if (c == short[].class) { bits += PType.短整.getBits() * ((short[])arr).length; } else if (c == int[].class) { bits += PType.整形.getBits() * ((int[])arr).length; } else if (c == float[].class) { bits += PType.浮点.getBits() * ((float[])arr).length; } else if (c == long[].class) { bits += PType.长整.getBits() * ((long[])arr).length; } else if (c == double[].class) { bits += PType.双精.getBits() * ((double[])arr).length; } else { Object[] os = (Object[])arr; for (Object o : os) { bits += OBJ_REF_LEN + bitsof(o); } } //补齐 if (bits%ALIGN > 0) { bits += (ALIGN - bits%ALIGN); } return bits; } /** * 计算obj对象在虚拟机中所占的内存,单位为bit * @param obj * @return */ private static int bitsof(Object obj) { if (obj == null) { return 0; } if (obj.getClass().isArray()) { return bitsofArray(obj); } return getObjBits(obj, obj.getClass(), false); } /** * 计算obj对象在虚拟机中所占的内存,单位为byte * @param obj * @return */ public static int sizeof(Object obj) { return bitsof(obj)/8; } private static void runGC() throws Exception { // It helps to call Runtime.gc() // using several method calls: for (int r=0; r<4; ++r) _runGC(); } private static void _runGC() throws Exception { long usedMem1 = usedMemory(), usedMem2 = Long.MAX_VALUE; for (int i=0; (usedMem1<usedMem2) && (i<500); ++i) { Runtime.getRuntime().runFinalization(); Runtime.getRuntime().gc(); Thread.yield(); usedMem2 = usedMem1; usedMem1 = usedMemory(); } } private static long usedMemory() { return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(); } /** * 本方法在计算String以及原始类型的包装类的时候可能不准。 * String s = "abc"; 这种方式产生的String对象会被放入常量池。 * Integer.valueOf(1)会返回缓存的对象而不是new一个。 * @param cls * @return * @throws Exception */ private static long determinObjSize(Class<?> cls) throws Exception { // Warm up all classes/methods we will use runGC(); usedMemory(); // Array to keep strong references to allocated objects final int count = 100000; Object[] objects = new Object[count]; long heap1 = 0; // Allocate count+1 objects, discard the first one for (int i = -1; i < count; ++i) { Object object = null; // Instantiate your data here and assign it to object // object = new Object(); //object = new Integer(i); //object = new Long(i); //object = new String(); //object = new byte[128][1] object = cls.newInstance(); if (i >= 0) objects [i] = object; else { object = null; // Discard the warm up object runGC(); heap1 = usedMemory(); // Take a before heap snapshot } } runGC(); long heap2 = usedMemory(); // Take an after heap snapshot: final int size = Math.round(((float)(heap2 - heap1))/count); System.out.println("'before' heap: " + heap1 + ", 'after' heap: " + heap2); System.out.println("heap delta: " + (heap2 - heap1) + ", {" + objects [0].getClass () + "} size = " + size + " bytes"); for (int i = 0; i < count; ++i) objects [i] = null; objects = null; return size; } public static void main(String[] args) { HotspotSizeof hs = new HotspotSizeof(); hs.test(); } @Test public void test() { try { Assert.assertEquals(determinObjSize(Obj4SizeofTest.class), sizeof(new Obj4SizeofTest())); } catch (Exception e) { throw new RuntimeException(e); } } }
测试用的两个类:
/** * 设计作者: teasp * 信息描述: */ public class Papa { @SuppressWarnings("unused") private int aint = 4; public static int bint; @SuppressWarnings("unused") // private String str = "123"; // private String str = new String("123"); // private String str = new String(new byte[]{49,50,51}); private String str = new String(new char[]{49,50,51}); @SuppressWarnings("unused") private int[] ints = {}; @SuppressWarnings("unused") private int[][] intss = {{}}; // private int[][] intss = {{1},{1,2}}; } /** * 设计作者: teasp * 信息描述: */ public class Obj4SizeofTest extends Papa { @SuppressWarnings("unused") private int aint = 3; public int bint = 4; @SuppressWarnings("unused") private boolean b1 = true; @SuppressWarnings("unused") private boolean b2 = true; @SuppressWarnings("unused") private boolean b3 = true; @SuppressWarnings("unused") private boolean b4 = true; @SuppressWarnings("unused") private boolean b5 = true; @SuppressWarnings("unused") private boolean b6 = true; @SuppressWarnings("unused") private boolean b7 = true; @SuppressWarnings("unused") private boolean b8 = true; @SuppressWarnings("unused") private String str1; @SuppressWarnings("unused") private Object obj = new Papa(); public static final byte[] bytes = {97}; @SuppressWarnings("unused") private String str2 = new String(bytes); @SuppressWarnings("unused") private Integer i = new Integer(1); @SuppressWarnings("unused") private int[] is = {1,2,3}; @SuppressWarnings("unused") private Object[][] objs = {{new Object(),new Object()},{new Object(),new Object()}}; }
相关推荐
如果你不想引入额外的依赖,可以自己编写一个简单的工具类来实现`sizeOf`功能。通常,这涉及到遍历对象的字段并估算每个字段的大小。例如,对于基本类型,我们可以知道它们在内存中的固定大小;对于引用类型,我们...
"java-sizeof-0.0.4"是一个用于分析Java对象内存占用的工具,它可以帮助开发者更好地理解对象在运行时的内存消耗。这个工具提供了查看对象实例大小的能力,这对于调试和性能优化非常有用。"java-sizeof-0.0.4"包含三...
在`clazzSize`这个文件名中,我们可以推测这可能是一个与类或者对象大小相关的资源,可能包含了不同数据类型实例化后的内存占用情况。通过分析这样的数据,我们可以更深入地理解Java内存模型,并优化我们的代码以...
首先,`SizeOf.java`可能是一个实现自定义内存大小计算的类。在Java中,由于垃圾回收机制的存在,直接获取对象的内存占用并不像C++等语言那样简单。通常,我们可以使用一些工具或API来帮助我们。例如,`sun.misc....
4. **对齐和填充**:Java对象在内存中是以字节对齐的,可能会有填充来保证内存效率。虽然这不是强制的,但是一些JVM实现可能进行填充。因此,实际大小可能比简单相加所有字段大小要大。 5. **注意事项**:使用反射...
SizeOf.jar是一个专门用于计算Java对象内存大小的工具。在Java编程中,了解对象占用的内存大小对于优化程序性能、防止内存泄漏以及理解程序运行时的内存消耗至关重要。这个工具通常由开发者使用,以帮助他们更好地...
例如,可能存在一个名为`SizeOf`的类,其中包含诸如`sizeof()`这样的静态方法,可以直接传入Java对象来获取其大小。 在Java中,对象的大小不仅包括实例变量的大小,还可能包含对齐填充、对象头信息以及可能的引用...
例如,可以通过创建一个包含数组或集合的对象,并使用SizeOf.jar提供的方法来获取其占用的内存大小。 ### 步骤5:查看测试结果 运行上述测试代码后,控制台将显示所测试对象的内存占用大小。通过这种方式,你可以...
面向对象编程是现代软件开发的核心概念,而Java作为一种流行的面向对象编程语言,其简单数据类型是编程的基础元素。简单数据类型是由编程语言本身定义的基本构建块,用于表示各种数值、字符和逻辑值。在Java中,简单...
在这个名为"common-22.7.0.zip"的压缩包中,包含了一个名为"sizeof-master"的子目录,这可能是一个关于`sizeof`引擎的源代码库,特别是针对`ehcacheconfigurable sizeof`引擎的实现。 `sizeof`引擎是`ehcache`的一...
- Java中没有`sizeof`操作符来获取变量或数据类型的大小,因为这些信息在编译时就已经确定,并不需要在运行时动态获取。 - 变量的声明和初始化需要遵循一定的规则,例如静态变量(`static`)在类加载时就会被初始...
**题目**:在Java中,一个类可同时定义许多同名的方法,这种面向对象程序的特性称为 **知识点**: - 这种特性称为**方法重载**(Overloading),允许在一个类中定义多个同名但参数列表不同的方法。 ### 19. 访问...
在Java编程语言中,直接获取一个对象所占用的内存大小并不像C++那样简单,因为Java没有提供类似于`sizeof`的运算符。然而,我们可以通过理解对象在内存中的布局和分配规则来估算对象的大小。这里我们将深入探讨计算...
JVM是一个在各个操作系统上运行的软件层,它负责解释和执行Java类文件,使得Java程序可以在任何支持JVM的平台上运行,而无需关心底层操作系统的细节。 实例方法是指与类的实例(对象)关联的方法,它们可以通过对象...
- `sizeof`不是Java中的保留关键字,因为Java自动管理内存,不需要显式地获取对象大小。 #### 十、Java控制结构与循环 - **知识点**: - 无限循环:`while (true)` 和 `while (!false)` 均会导致无限循环。 - ...
- sizeof运算符:这是一个在C/C++中用于获取变量或数据类型所占内存大小的运算符。 - 函数与参数:提到了函数(function)和参数(parameters),这是编程中用于实现代码复用和模块化的重要概念。 3. 计算机基础...
数组在Java中是固定大小的,如int[] x = new int[10](A)创建了一个长度为10的数组,x[9]默认初始化为0,x[10]是越界的。 浮点数比较时,即使数值相等,不同的对象(如new Float(0.1f))也不相等,因此s==t为false...
- C/C++ 中的 `sizeof` 关键字在 Java 中不存在,Java 使用 `DataSize` 或 `Array.length` 获取对象或数组大小。 13. **native 方法**: - 用 `native` 关键字标记的方法表示其实现是在非 Java 代码(如 C/C++)...
3. Java中没有`sizeof`关键字,它是C/C++中的运算符,用于获取变量的大小。在Java中,可以使用`System.out.println(Object.getClass().getSimpleName());`来获取对象的类名。 4. 导致无限循环的语句通常是因为循环...
- `sizeof`不是一个Java关键字,而是在C/C++中用于获取变量大小的关键字。Java中没有这个关键字,因为内存管理是自动的。 6. **注释符号**: - 单行注释使用`//`,多行注释使用`/* ... */`,文档注释使用`/** ......