如何计算Java数组的内存大小?含32位JVM场景及示例代码需求
Great question! Let's break this down clearly, covering both 32-bit HotSpot JVM array memory calculation and practical code examples for measuring Java object sizes (including arrays).
32-bit HotSpot JVM: Calculating Array Memory Size
In a 32-bit HotSpot JVM, an array's header consists of three fixed components, and the total size follows specific alignment rules:
Array Header Components (Fixed 12 Bytes)
- Mark Word: 4 bytes (stores hash code, lock state, GC age, and other metadata)
- Class Pointer: 4 bytes (points to the array's class metadata; no pointer compression needed in 32-bit mode)
- Array Length: 4 bytes (stores the number of elements in the array)
Total Size Calculation Steps
- Calculate element total size:
size of element type × array length - Sum header + element size:
12 bytes + element total size - Align to 8-byte boundary: HotSpot requires all object sizes to be multiples of 8 bytes in 32-bit mode. If the sum isn't a multiple of 8, padding bytes are added to reach the nearest valid size.
Examples
int[] arr = new int[1]:12 + (4×1) = 16 bytes(already an 8-byte multiple, no padding)int[] arr = new int[2]:12 + (4×2) = 20 bytes→ padded to 24 bytes (adds 4 padding bytes)byte[] arr = new byte[3]:12 + 3 = 15 bytes→ padded to 16 bytes (adds 3 padding bytes)
Practical Code to Measure Java Object Memory Size
There are two reliable ways to calculate an object's shallow memory size (size of the object itself, excluding referenced objects): using the official Instrumentation API, or the low-level sun.misc.Unsafe (not recommended for production).
Method 1: Using Instrumentation API (Recommended)
This approach uses Java's official agent mechanism for accurate, JVM-compliant measurements.
Step 1: Create the Agent Class
import java.lang.instrument.Instrumentation; public class ObjectSizeAgent { private static Instrumentation instrumentation; // Called automatically when the JVM starts public static void premain(String args, Instrumentation inst) { instrumentation = inst; } // Returns the shallow size of the object public static long getObjectSize(Object obj) { if (instrumentation == null) { throw new IllegalStateException("Agent not initialized. Run with -javaagent flag."); } return instrumentation.getObjectSize(obj); } }
Step 2: Create MANIFEST.MF
Create a file named MANIFEST.MF with this content:
Premain-Class: ObjectSizeAgent
Step 3: Package into a JAR
Package the agent class and manifest into a JAR (e.g., object-size-agent.jar).
Step 4: Test Class
public class ObjectSizeTest { public static void main(String[] args) { // Test a regular object class User { private int id; private String name; private boolean active; } User user = new User(); System.out.println("User object shallow size: " + ObjectSizeAgent.getObjectSize(user) + " bytes"); // Test arrays int[] intArray = new int[5]; System.out.println("int[5] size: " + ObjectSizeAgent.getObjectSize(intArray) + " bytes"); byte[] byteArray = new byte[7]; System.out.println("byte[7] size: " + ObjectSizeAgent.getObjectSize(byteArray) + " bytes"); } }
Step 5: Run with the Agent
Execute the test class with the agent flag:
java -javaagent:object-size-agent.jar ObjectSizeTest
Method 2: Using sun.misc.Unsafe (Low-Level, Not Recommended)
Unsafe provides direct memory access, but it's an internal API and may break across JVM versions or be restricted in newer Java releases.
import sun.misc.Unsafe; import java.lang.reflect.Field; public class UnsafeSizeCalculator { private static final Unsafe unsafe; static { try { Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); unsafeField.setAccessible(true); unsafe = (Unsafe) unsafeField.get(null); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException("Failed to access Unsafe", e); } } public static long getObjectSize(Object obj) { if (obj == null) return 0; Class<?> clazz = obj.getClass(); // Handle arrays if (clazz.isArray()) { int length = java.lang.reflect.Array.getLength(obj); long headerSize = unsafe.arrayBaseOffset(clazz); long elementSize = unsafe.arrayIndexScale(clazz); long total = headerSize + (elementSize * length); return alignTo8Bytes(total); } // Handle regular objects long lastFieldOffset = 0; for (Field field : clazz.getDeclaredFields()) { if (!field.isSynthetic()) { long offset = unsafe.objectFieldOffset(field); lastFieldOffset = Math.max(lastFieldOffset, offset); } } long fieldSize = getFieldSize(clazz.getDeclaredFields()[clazz.getDeclaredFields().length - 1]); return alignTo8Bytes(lastFieldOffset + fieldSize); } private static long getFieldSize(Field field) { Class<?> type = field.getType(); if (type == boolean.class || type == byte.class) return 1; if (type == char.class || type == short.class) return 2; if (type == int.class || type == float.class) return 4; if (type == long.class || type == double.class) return 8; return 4; // Reference size in 32-bit JVM } private static long alignTo8Bytes(long size) { final long ALIGNMENT = 8; return (size + ALIGNMENT - 1) & ~(ALIGNMENT - 1); } public static void main(String[] args) { User user = new User(); System.out.println("User object shallow size: " + getObjectSize(user) + " bytes"); int[] intArray = new int[5]; System.out.println("int[5] size: " + getObjectSize(intArray) + " bytes"); } static class User { private int id; private String name; private boolean active; } }
内容的提问来源于stack exchange,提问作者shengbang he

