在Java中,确定对象大小的最佳方法是什么?

我有一个应用程序,读取一个CSV文件与成堆的数据行。我根据数据类型向用户提供了行数的摘要,但我想确保不会读入太多行数据,从而导致OutOfMemoryErrors。每一行转换成一个对象。有没有一种简单的方法以编程方式找出该对象的大小?是否有一个引用定义了VM的基本类型和对象引用有多大?

现在,我有代码说读到32000行,但我也希望有代码说读尽可能多的行,直到我使用32 mb的内存。也许这是另一个问题,但我还是想知道。

347394 次浏览

我怀疑您是否希望以编程方式完成它,除非您只是想执行一次并将其存储起来以供将来使用。这是一件代价高昂的事情。在Java中没有sizeof()操作符,即使有,它也只会计算引用其他对象的代价和原语的大小。

你可以这样做的一种方法是将它序列化到File中,然后查看文件的大小,就像这样:

Serializable myObject;
ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("obj.ser"));
oos.write (myObject);
oos.close ();

当然,这假设每个对象都是不同的,并且不包含对其他任何对象的非瞬时引用。

另一种策略是获取每个对象并通过反射检查其成员,并将大小相加(boolean &字节= 1字节,短&Char = 2字节,等等),沿着成员层次结构向下工作。但这既乏味又昂贵,而且最终与序列化策略所做的事情相同。

没有方法调用,如果这是你想要的。只要稍加研究,我想你就可以自己写了。一个特定的实例具有一个固定的大小,该大小来自引用和原语值的数量以及实例簿记数据。您可以简单地遍历对象图。行类型变化越少,越容易。

如果这太慢或者麻烦太多,总有好的老式的行计数经验法则。

如果你只是想知道在你的JVM中有多少内存被使用,有多少是空闲的,你可以尝试这样做:

// Get current size of heap in bytes
long heapSize = Runtime.getRuntime().totalMemory();


// Get maximum size of heap in bytes. The heap cannot grow beyond this size.
// Any attempt will result in an OutOfMemoryException.
long heapMaxSize = Runtime.getRuntime().maxMemory();


// Get amount of free memory within the heap in bytes. This size will increase
// after garbage collection and decrease as new objects are created.
long heapFreeSize = Runtime.getRuntime().freeMemory();

edit:我认为这可能会有帮助,因为问题作者还表示,他希望有处理“读取尽可能多的行,直到我使用了32MB内存”的逻辑。

你必须利用反射在物体上行走。做的时候要小心:

  • 仅仅分配一个对象在JVM中就有一些开销。这个量因JVM而异,所以可以将此值作为参数。至少让它成为一个常量(8字节?),并应用于分配的任何东西。
  • 仅仅因为byte理论上是1个字节,并不意味着它只占用内存中的1个字节。
  • 在对象引用中会有循环,所以你需要保留一个HashMap使用object-equals作为比较器来消除无限循环。

@jodonnell:我喜欢你的解决方案的简单性,但许多对象是不可序列化的(所以这将抛出一个异常),字段可以是暂时的,对象可以覆盖标准方法。

您必须使用工具来测量它,或者手工估计它,这取决于您正在使用的JVM。

每个对象都有一些固定的开销。它是jvm特有的,但我通常估计有40个字节。然后你要看看这个班级的成员。对象引用在32位(64位)JVM中是4(8)个字节。基本类型是:

  • 布尔值和字节:1字节
  • Char和short: 2字节
  • Int和float: 4字节
  • Long和double: 8字节

数组也遵循同样的规则;也就是说,它是一个对象引用,因此在对象中占用4(或8)个字节,然后它的长度乘以其元素的大小。

尝试以编程方式调用Runtime.freeMemory()并不能给您提供很高的准确性,因为对垃圾收集器的异步调用等等。使用-Xrunhprof或其他工具对堆进行分析将为您提供最准确的结果。

首先,“对象的大小”在Java中并不是一个定义明确的概念。你可以指对象本身,包括它的成员、对象和它引用的所有对象(引用图)。您可以指内存中的大小或磁盘上的大小。JVM可以优化字符串之类的东西。

因此,唯一正确的方法是使用一个好的分析器(我使用YourKit)询问JVM,这可能不是您想要的。

然而,从上面的描述来看,似乎每一行都是自包含的,没有很大的依赖树,因此序列化方法在大多数jvm上可能是一个很好的近似方法。最简单的方法如下:

 Serializable ser;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(ser);
oos.close();
return baos.size();

请记住,如果您有具有公共引用的对象,这个不会会给出正确的结果,序列化的大小并不总是与内存中的大小匹配,但这是一个很好的近似值。如果您将ByteArrayOutputStream大小初始化为一个合理的值,代码将会更有效。

几年前Javaworld有一篇关于确定组合和可能嵌套的Java对象大小的文章,他们基本上是在Java中创建sizeof()实现。这种方法基本上建立在其他工作的基础上,在这些工作中,人们通过实验确定了原语和典型Java对象的大小,然后将该知识应用于递归地遍历对象图以计算总大小的方法。

它总是比原生C实现更不准确,这仅仅是因为类背后发生的事情,但它应该是一个很好的指示器。

或者是一个名为运算符的SourceForge项目,它提供了一个带有sizeof()实现的Java5库。

附注:不要使用序列化方法,序列化对象的大小和它在运行时所消耗的内存量之间没有相关性。

你可以使用# EYZ0包

编译并将这个类放入JAR:

import java.lang.instrument.Instrumentation;


public class ObjectSizeFetcher {
private static Instrumentation instrumentation;


public static void premain(String args, Instrumentation inst) {
instrumentation = inst;
}


public static long getObjectSize(Object o) {
return instrumentation.getObjectSize(o);
}
}

添加以下到您的MANIFEST.MF:

Premain-Class: ObjectSizeFetcher

使用getObjectSize()方法:

public class C {
private int x;
private int y;


public static void main(String [] args) {
System.out.println(ObjectSizeFetcher.getObjectSize(new C()));
}
}

调用:

java -javaagent:ObjectSizeFetcherAgent.jar C

java.lang.instrument.Instrumentation类提供了一种获取Java对象大小的好方法,但它要求您定义premain并使用Java代理运行程序。当您不需要任何代理,而又必须为应用程序提供一个虚拟Jar代理时,这是非常无聊的。

所以我从sun.misc中使用Unsafe类得到了一个替代解决方案。因此,根据处理器架构考虑对象堆对齐并计算最大字段偏移量,就可以测量Java对象的大小。在下面的例子中,我使用了一个辅助类UtilUnsafe来获取对sun.misc.Unsafe对象的引用。

private static final int NR_BITS = Integer.valueOf(System.getProperty("sun.arch.data.model"));
private static final int BYTE = 8;
private static final int WORD = NR_BITS/BYTE;
private static final int MIN_SIZE = 16;


public static int sizeOf(Class src){
//
// Get the instance fields of src class
//
List<Field> instanceFields = new LinkedList<Field>();
do{
if(src == Object.class) return MIN_SIZE;
for (Field f : src.getDeclaredFields()) {
if((f.getModifiers() & Modifier.STATIC) == 0){
instanceFields.add(f);
}
}
src = src.getSuperclass();
}while(instanceFields.isEmpty());
//
// Get the field with the maximum offset
//
long maxOffset = 0;
for (Field f : instanceFields) {
long offset = UtilUnsafe.UNSAFE.objectFieldOffset(f);
if(offset > maxOffset) maxOffset = offset;
}
return  (((int)maxOffset/WORD) + 1)*WORD;
}
class UtilUnsafe {
public static final sun.misc.Unsafe UNSAFE;


static {
Object theUnsafe = null;
Exception exception = null;
try {
Class<?> uc = Class.forName("sun.misc.Unsafe");
Field f = uc.getDeclaredField("theUnsafe");
f.setAccessible(true);
theUnsafe = f.get(uc);
} catch (Exception e) { exception = e; }
UNSAFE = (sun.misc.Unsafe) theUnsafe;
if (UNSAFE == null) throw new Error("Could not obtain access to sun.misc.Unsafe", exception);
}
private UtilUnsafe() { }
}

您可以生成一个堆转储(例如,使用jmap),然后分析输出以查找对象大小。这是一种离线解决方案,但是您可以检查浅尺寸和深尺寸等。

还有记忆测量器工具(以前在谷歌代码,现在在GitHub),它很简单,在商业友好的Apache 2.0许可证下发布,正如在类似的问题中讨论的那样。

如果您想测量内存字节消耗,它也需要一个java解释器的命令行参数,但在其他方面似乎工作得很好,至少在我使用它的场景中是这样。

我曾经写过一个快速测试来进行评估:

public class Test1 {


// non-static nested
class Nested { }


// static nested
static class StaticNested { }


static long getFreeMemory () {
// waits for free memory measurement to stabilize
long init = Runtime.getRuntime().freeMemory(), init2;
int count = 0;
do {
System.out.println("waiting..." + init);
System.gc();
try { Thread.sleep(250); } catch (Exception x) { }
init2 = init;
init = Runtime.getRuntime().freeMemory();
if (init == init2) ++ count; else count = 0;
} while (count < 5);
System.out.println("ok..." + init);
return init;
}


Test1 () throws InterruptedException {


Object[] s = new Object[10000];
Object[] n = new Object[10000];
Object[] t = new Object[10000];


long init = getFreeMemory();


//for (int j = 0; j < 10000; ++ j)
//    s[j] = new Separate();


long afters = getFreeMemory();


for (int j = 0; j < 10000; ++ j)
n[j] = new Nested();


long aftersn = getFreeMemory();


for (int j = 0; j < 10000; ++ j)
t[j] = new StaticNested();


long aftersnt = getFreeMemory();


System.out.println("separate:      " + -(afters - init) + " each=" + -(afters - init) / 10000);
System.out.println("nested:        " + -(aftersn - afters) + " each=" + -(aftersn - afters) / 10000);
System.out.println("static nested: " + -(aftersnt - aftersn) + " each=" + -(aftersnt - aftersn) / 10000);


}


public static void main (String[] args) throws InterruptedException {
new Test1();
}


}

一般概念是分配对象并测量空闲堆空间的变化。键是getFreeMemory(),即请求GC运行并等待报告的空闲堆大小稳定下来。上面的输出是:

nested:        160000 each=16
static nested: 160000 each=16

考虑到对齐行为和可能的堆块报头开销,这正是我们所期望的。

仪器仪表方法详细在这里接受的答案是最准确的。我描述的方法是准确的,但只有在受控条件下,即没有其他线程创建/丢弃对象。

long heapSizeBefore = Runtime.getRuntime().totalMemory();


// Code for object construction
...
long heapSizeAfter = Runtime.getRuntime().totalMemory();
long size = heapSizeAfter - heapSizeBefore;

大小提供了由于创建对象而增加的JVM内存使用,通常是对象的大小。

下面是我使用一些链接示例制作的实用程序,用于处理32位、64位和64位压缩OOP。它使用sun.misc.Unsafe

它使用Unsafe.addressSize()获取本机指针的大小,使用Unsafe.arrayIndexScale( Object[].class )获取Java引用的大小。

它使用已知类的字段偏移量来计算对象的基大小。

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.IdentityHashMap;
import java.util.Stack;
import sun.misc.Unsafe;


/** Usage:
* MemoryUtil.sizeOf( object )
* MemoryUtil.deepSizeOf( object )
* MemoryUtil.ADDRESS_MODE
*/
public class MemoryUtil
{
private MemoryUtil()
{
}


public static enum AddressMode
{
/** Unknown address mode. Size calculations may be unreliable. */
UNKNOWN,
/** 32-bit address mode using 32-bit references. */
MEM_32BIT,
/** 64-bit address mode using 64-bit references. */
MEM_64BIT,
/** 64-bit address mode using 32-bit compressed references. */
MEM_64BIT_COMPRESSED_OOPS
}


/** The detected runtime address mode. */
public static final AddressMode ADDRESS_MODE;


private static final Unsafe UNSAFE;


private static final long ADDRESS_SIZE; // The size in bytes of a native pointer: 4 for 32 bit, 8 for 64 bit
private static final long REFERENCE_SIZE; // The size of a Java reference: 4 for 32 bit, 4 for 64 bit compressed oops, 8 for 64 bit
private static final long OBJECT_BASE_SIZE; // The minimum size of an Object: 8 for 32 bit, 12 for 64 bit compressed oops, 16 for 64 bit
private static final long OBJECT_ALIGNMENT = 8;


/** Use the offset of a known field to determine the minimum size of an object. */
private static final Object HELPER_OBJECT = new Object() { byte b; };




static
{
try
{
// Use reflection to get a reference to the 'Unsafe' object.
Field f = Unsafe.class.getDeclaredField( "theUnsafe" );
f.setAccessible( true );
UNSAFE = (Unsafe) f.get( null );


OBJECT_BASE_SIZE = UNSAFE.objectFieldOffset( HELPER_OBJECT.getClass().getDeclaredField( "b" ) );


ADDRESS_SIZE = UNSAFE.addressSize();
REFERENCE_SIZE = UNSAFE.arrayIndexScale( Object[].class );


if( ADDRESS_SIZE == 4 )
{
ADDRESS_MODE = AddressMode.MEM_32BIT;
}
else if( ADDRESS_SIZE == 8 && REFERENCE_SIZE == 8 )
{
ADDRESS_MODE = AddressMode.MEM_64BIT;
}
else if( ADDRESS_SIZE == 8 && REFERENCE_SIZE == 4 )
{
ADDRESS_MODE = AddressMode.MEM_64BIT_COMPRESSED_OOPS;
}
else
{
ADDRESS_MODE = AddressMode.UNKNOWN;
}
}
catch( Exception e )
{
throw new Error( e );
}
}




/** Return the size of the object excluding any referenced objects. */
public static long shallowSizeOf( final Object object )
{
Class<?> objectClass = object.getClass();
if( objectClass.isArray() )
{
// Array size is base offset + length * element size
long size = UNSAFE.arrayBaseOffset( objectClass )
+ UNSAFE.arrayIndexScale( objectClass ) * Array.getLength( object );
return padSize( size );
}
else
{
// Object size is the largest field offset padded out to 8 bytes
long size = OBJECT_BASE_SIZE;
do
{
for( Field field : objectClass.getDeclaredFields() )
{
if( (field.getModifiers() & Modifier.STATIC) == 0 )
{
long offset = UNSAFE.objectFieldOffset( field );
if( offset >= size )
{
size = offset + 1; // Field size is between 1 and PAD_SIZE bytes. Padding will round up to padding size.
}
}
}
objectClass = objectClass.getSuperclass();
}
while( objectClass != null );


return padSize( size );
}
}




private static final long padSize( final long size )
{
return (size + (OBJECT_ALIGNMENT - 1)) & ~(OBJECT_ALIGNMENT - 1);
}




/** Return the size of the object including any referenced objects. */
public static long deepSizeOf( final Object object )
{
IdentityHashMap<Object,Object> visited = new IdentityHashMap<Object,Object>();
Stack<Object> stack = new Stack<Object>();
if( object != null ) stack.push( object );


long size = 0;
while( !stack.isEmpty() )
{
size += internalSizeOf( stack.pop(), stack, visited );
}
return size;
}




private static long internalSizeOf( final Object object, final Stack<Object> stack, final IdentityHashMap<Object,Object> visited )
{
// Scan for object references and add to stack
Class<?> c = object.getClass();
if( c.isArray() && !c.getComponentType().isPrimitive() )
{
// Add unseen array elements to stack
for( int i = Array.getLength( object ) - 1; i >= 0; i-- )
{
Object val = Array.get( object, i );
if( val != null && visited.put( val, val ) == null )
{
stack.add( val );
}
}
}
else
{
// Add unseen object references to the stack
for( ; c != null; c = c.getSuperclass() )
{
for( Field field : c.getDeclaredFields() )
{
if( (field.getModifiers() & Modifier.STATIC) == 0
&& !field.getType().isPrimitive() )
{
field.setAccessible( true );
try
{
Object val = field.get( object );
if( val != null && visited.put( val, val ) == null )
{
stack.add( val );
}
}
catch( IllegalArgumentException e )
{
throw new RuntimeException( e );
}
catch( IllegalAccessException e )
{
throw new RuntimeException( e );
}
}
}
}
}


return shallowSizeOf( object );
}
}

我的答案是基于Nick提供的代码。该代码测量被序列化对象占用的字节总数。因此,这实际上衡量的是序列化的东西+普通对象的内存占用(例如序列化int,您将看到序列化的字节总数不是4)。所以如果你想要得到你的对象使用的原始字节数-你需要修改代码一点。像这样:

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;


public class ObjectSizeCalculator {
private Object getFirstObjectReference(Object o) {
String objectType = o.getClass().getTypeName();


if (objectType.substring(objectType.length()-2).equals("[]")) {
try {
if (objectType.equals("java.lang.Object[]"))
return ((Object[])o)[0];
else if (objectType.equals("int[]"))
return ((int[])o)[0];
else
throw new RuntimeException("Not Implemented !");
} catch (IndexOutOfBoundsException e) {
return null;
}
}


return o;
}


public int getObjectSizeInBytes(Object o) {
final String STRING_JAVA_TYPE_NAME = "java.lang.String";


if (o == null)
return 0;


String objectType = o.getClass().getTypeName();
boolean isArray = objectType.substring(objectType.length()-2).equals("[]");


Object objRef = getFirstObjectReference(o);
if (objRef != null && !(objRef instanceof Serializable))
throw new RuntimeException("Object must be serializable for measuring it's memory footprint using this method !");


try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(o);
oos.close();
byte[] bytes = baos.toByteArray();


for (int i = bytes.length - 1, j = 0; i != 0; i--, j++) {
if (objectType != STRING_JAVA_TYPE_NAME) {
if (bytes[i] == 112)
if (isArray)
return j - 4;
else
return j;
} else {
if (bytes[i] == 0)
return j - 1;
}
}
} catch (Exception e) {
return -1;
}


return -1;
}


}

我已经用基本类型String和一些普通类测试了这个解决方案。可能也有不包括在内的情况。


更新:修改示例,支持数组对象的内存占用计算。

这个答案与对象大小无关,而是当你使用数组来容纳对象时;它将为对象分配多少内存大小。

所以数组,列表,或map所有这些集合不会真正存储对象(只有在使用原语时,需要实际对象的内存大小),它只存储这些对象的引用。

现在是Used heap memory = sizeOfObj + sizeOfRef (* 4 bytes) in collection

  • (4/8字节)取决于(32/64位)操作系统

原语

int   [] intArray    = new int   [1]; will require 4 bytes.
long  [] longArray   = new long  [1]; will require 8 bytes.

对象

Object[] objectArray = new Object[1]; will require 4 bytes. The object can be any user defined Object.
Long  [] longArray   = new Long  [1]; will require 4 bytes.

我的意思是说,所有对象REFERENCE只需要4个字节的内存。它可能是字符串引用或双对象引用,但根据对象创建所需的内存会有所不同。

例)如果我为下面的类ReferenceMemoryTest创建对象,那么4 + 4 + 4 = 12字节的内存将被创建。当您尝试初始化引用时,内存可能会有所不同。

 class ReferenceMemoryTest {
public String refStr;
public Object refObj;
public Double refDoub;
}

因此,当创建对象/引用数组时,它的所有内容都将被NULL引用占用。我们知道每个引用需要4个字节。

最后,下面代码的内存分配为20字节。

reference ememorytest ref1 = new reference ememorytest ();(4(ref1) + 12 = 16字节) 参考内存测试ref2 = ref1;(4(ref2) + 16 = 20字节)

许多其他答案提供了浅的大小——例如,没有任何键或值的HashMap的大小,这可能不是您想要的。

jamm项目使用上面的java.lang.instrumentation包,但是遍历树,因此可以为您提供深层内存使用。

new MemoryMeter().measureDeep(myHashMap);

https://github.com/jbellis/jamm

要使用MemoryMeter,请使用“-javaagent:/jam .jar”启动JVM

不需要干扰插装等,如果你不需要知道一个对象的确切字节大小,你可以使用以下方法:

System.gc();
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();


do your job here


System.gc();
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

通过这种方式,您可以读取之前和之后使用的内存,并在获得使用的内存之前调用GC,将“噪声”降低到几乎为0。

为了得到更可靠的结果,您可以运行作业n次,然后将使用的内存除以n,得到一次运行占用的内存。甚至,你可以把整个过程运行更多次,得到一个平均值。

当我在Twitter工作时,我写了一个计算深度对象大小的实用程序。它考虑了不同的内存模型(32位,压缩oops, 64位),填充,子类填充,在循环数据结构和数组上正确工作。你可以编译这个。java文件;它没有外部依赖:

https://github.com/twitter/commons/blob/master/src/java/com/twitter/common/objectsize/ObjectSizeCalculator.java

您应该使用约尔,这是作为OpenJDK项目的一部分开发的工具。

JOL (Java对象布局)是一个用于分析jvm中的对象布局方案的小工具箱。这些工具大量使用不安全、JVMTI和可服务性代理(Serviceability Agent, SA)来解码实际的对象布局、占用空间和引用。这使得JOL比依赖堆转储、规格假设等的其他工具更加准确。

要获取原语、引用和数组元素的大小,使用VMSupport.vmDetails()。在运行在64位Windows上的Oracle JDK 1.8.0_40上(用于以下所有示例),此方法返回

Running 64-bit HotSpot VM.
Using compressed oop with 0-bit shift.
Using compressed klass with 3-bit shift.
Objects are 8 bytes aligned.
Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

您可以使用ClassLayout.parseClass(Foo.class).toPrintable()(可选地将一个实例传递给toPrintable)来获取对象实例的浅大小。这只是该类的单个实例所消耗的空间;它不包括该类引用的任何其他对象。它包括虚拟机开销的对象头,字段对齐和填充。# EYZ2:

java.util.regex.Pattern object internals:
OFFSET  SIZE        TYPE DESCRIPTION                    VALUE
0     4             (object header)                01 00 00 00 (0000 0001 0000 0000 0000 0000 0000 0000)
4     4             (object header)                00 00 00 00 (0000 0000 0000 0000 0000 0000 0000 0000)
8     4             (object header)                cb cf 00 20 (1100 1011 1100 1111 0000 0000 0010 0000)
12     4         int Pattern.flags                  0
16     4         int Pattern.capturingGroupCount    1
20     4         int Pattern.localCount             0
24     4         int Pattern.cursor                 48
28     4         int Pattern.patternLength          0
32     1     boolean Pattern.compiled               true
33     1     boolean Pattern.hasSupplementary       false
34     2             (alignment/padding gap)        N/A
36     4      String Pattern.pattern                (object)
40     4      String Pattern.normalizedPattern      (object)
44     4        Node Pattern.root                   (object)
48     4        Node Pattern.matchRoot              (object)
52     4       int[] Pattern.buffer                 null
56     4         Map Pattern.namedGroups            null
60     4 GroupHead[] Pattern.groupNodes             null
64     4       int[] Pattern.temp                   null
68     4             (loss due to the next object alignment)
Instance size: 72 bytes (reported by Instrumentation API)
Space losses: 2 bytes internal + 4 bytes external = 6 bytes total

您可以使用GraphLayout.parseInstance(obj).toFootprint()获得对象实例深度大小的摘要视图。当然,占用空间中的某些对象可能是共享的(也从其他对象引用),因此它是垃圾收集对象时可以回收的空间的过度近似值。对于Pattern.compile("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$")的结果(来自这个答案), jol报告总共占用了1840个字节,其中只有72个是Pattern实例本身。

java.util.regex.Pattern instance footprint:
COUNT       AVG       SUM   DESCRIPTION
1       112       112   [C
3       272       816   [Z
1        24        24   java.lang.String
1        72        72   java.util.regex.Pattern
9        24       216   java.util.regex.Pattern$1
13        24       312   java.util.regex.Pattern$5
1        16        16   java.util.regex.Pattern$Begin
3        24        72   java.util.regex.Pattern$BitClass
3        32        96   java.util.regex.Pattern$Curly
1        24        24   java.util.regex.Pattern$Dollar
1        16        16   java.util.regex.Pattern$LastNode
1        16        16   java.util.regex.Pattern$Node
2        24        48   java.util.regex.Pattern$Single
40                1840   (total)

如果使用GraphLayout.parseInstance(obj).toPrintable(), jol将告诉您对每个引用对象的字段解引用的地址、大小、类型、值和路径,尽管这通常太详细了,没有用处。对于正在进行的模式示例,您可能会得到以下内容。(地址可能会在运行期间发生变化。)

java.util.regex.Pattern object externals:
ADDRESS       SIZE TYPE                             PATH                           VALUE
d5e5f290         16 java.util.regex.Pattern$Node     .root.next.atom.next           (object)
d5e5f2a0        120 (something else)                 (somewhere else)               (something else)
d5e5f318         16 java.util.regex.Pattern$LastNode .root.next.next.next.next.next.next.next (object)
d5e5f328      21664 (something else)                 (somewhere else)               (something else)
d5e647c8         24 java.lang.String                 .pattern                       (object)
d5e647e0        112 [C                               .pattern.value                 [^, [, a, -, z, A, -, Z, 0, -, 9, _, ., +, -, ], +, @, [, a, -, z, A, -, Z, 0, -, 9, -, ], +, \, ., [, a, -, z, A, -, Z, 0, -, 9, -, ., ], +, $]
d5e64850        448 (something else)                 (somewhere else)               (something else)
d5e64a10         72 java.util.regex.Pattern                                         (object)
d5e64a58        416 (something else)                 (somewhere else)               (something else)
d5e64bf8         16 java.util.regex.Pattern$Begin    .root                          (object)
d5e64c08         24 java.util.regex.Pattern$BitClass .root.next.atom.val$rhs        (object)
d5e64c20        272 [Z                               .root.next.atom.val$rhs.bits   [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
d5e64d30         24 java.util.regex.Pattern$1        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs (object)
d5e64d48         24 java.util.regex.Pattern$1        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs.val$rhs (object)
d5e64d60         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs (object)
d5e64d78         24 java.util.regex.Pattern$1        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$rhs (object)
d5e64d90         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs (object)
d5e64da8         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs.val$lhs (object)
d5e64dc0         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs.val$lhs (object)
d5e64dd8         24 java.util.regex.Pattern$5        .root.next.atom.val$lhs        (object)
d5e64df0         24 java.util.regex.Pattern$5        .root.next.atom                (object)
d5e64e08         32 java.util.regex.Pattern$Curly    .root.next                     (object)
d5e64e28         24 java.util.regex.Pattern$Single   .root.next.next                (object)
d5e64e40         24 java.util.regex.Pattern$BitClass .root.next.next.next.atom.val$rhs (object)
d5e64e58        272 [Z                               .root.next.next.next.atom.val$rhs.bits [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
d5e64f68         24 java.util.regex.Pattern$1        .root.next.next.next.atom.val$lhs.val$lhs.val$lhs (object)
d5e64f80         24 java.util.regex.Pattern$1        .root.next.next.next.atom.val$lhs.val$lhs.val$rhs (object)
d5e64f98         24 java.util.regex.Pattern$5        .root.next.next.next.atom.val$lhs.val$lhs (object)
d5e64fb0         24 java.util.regex.Pattern$1        .root.next.next.next.atom.val$lhs.val$rhs (object)
d5e64fc8         24 java.util.regex.Pattern$5        .root.next.next.next.atom.val$lhs (object)
d5e64fe0         24 java.util.regex.Pattern$5        .root.next.next.next.atom      (object)
d5e64ff8         32 java.util.regex.Pattern$Curly    .root.next.next.next           (object)
d5e65018         24 java.util.regex.Pattern$Single   .root.next.next.next.next      (object)
d5e65030         24 java.util.regex.Pattern$BitClass .root.next.next.next.next.next.atom.val$rhs (object)
d5e65048        272 [Z                               .root.next.next.next.next.next.atom.val$rhs.bits [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
d5e65158         24 java.util.regex.Pattern$1        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs.val$lhs (object)
d5e65170         24 java.util.regex.Pattern$1        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs.val$rhs (object)
d5e65188         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs (object)
d5e651a0         24 java.util.regex.Pattern$1        .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$rhs (object)
d5e651b8         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom.val$lhs.val$lhs (object)
d5e651d0         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom.val$lhs (object)
d5e651e8         24 java.util.regex.Pattern$5        .root.next.next.next.next.next.atom (object)
d5e65200         32 java.util.regex.Pattern$Curly    .root.next.next.next.next.next (object)
d5e65220        120 (something else)                 (somewhere else)               (something else)
d5e65298         24 java.util.regex.Pattern$Dollar   .root.next.next.next.next.next.next (object)

“(其他东西)”条目描述堆中不属于此对象图的其他对象

最好的jol文档是jol存储库中的约尔样品。这些示例演示了常见的jol操作,并展示了如何使用jol分析VM和垃圾收集器的内部结构。

我偶然发现了一个java类 "jdk.nashorn.internal.ir.debug. objectsizecalculator ",已经在jdk中,

?

System.out.println(ObjectSizeCalculator.getObjectSize(new gnu.trove.map.hash.TObjectIntHashMap<String>(12000, 0.6f, -1)));
System.out.println(ObjectSizeCalculator.getObjectSize(new HashMap<String, Integer>(100000)));
System.out.println(ObjectSizeCalculator.getObjectSize(3));
System.out.println(ObjectSizeCalculator.getObjectSize(new int[]{1, 2, 3, 4, 5, 6, 7 }));
System.out.println(ObjectSizeCalculator.getObjectSize(new int[100]));

结果:

164192
48
16
48
416

对于JSONObject,下面的代码可以帮助您。

`JSONObject.toString().getBytes("UTF-8").length`

返回以字节为单位的大小

我通过将JSONArray对象写入文件来检查它。它给出了对象的大小。

使用java visual VM即可。

它具有分析和调试内存问题所需的一切。

它还有一个OQL(对象查询语言)控制台,允许您做许多有用的事情,其中之一就是sizeof(o)

假设我声明了一个名为Complex的类:

public class Complex {


private final long real;
private final long imaginary;


// omitted
}


为了查看这个类的活动实例被分配了多少内存:

$ jmap -histo:live <pid> | grep Complex


num     #instances         #bytes  class name (module)
-------------------------------------------------------
327:             1             32  Complex

我正在寻找一个对象大小的运行时计算,满足以下要求:

  • 在运行时可用,不需要包括插装。
  • 使用Java 9+,无需访问Unsafe。
  • 仅基于类。不是考虑字符串长度,数组长度等的深度sizeOf。

以下内容基于java专家的原始文章(https://www.javaspecialists.eu/archive/Issue078.html)的核心代码,以及不安全版本中对这个问题的另一个回答中的一些内容。

我希望有人觉得它有用。

public class JavaSize {


private static final int NR_BITS = Integer.valueOf(System.getProperty("sun.arch.data.model"));
private static final int BYTE = 8;
private static final int WORD = NR_BITS / BYTE;
private static final int HEADER_SIZE = 8;


public static int sizeOf(Class<?> clazz) {
int result = 0;


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()) {
Class<?> primitiveClass = fields[i].getType();
if (primitiveClass == boolean.class || primitiveClass == byte.class) {
result += 1;
} else if (primitiveClass == short.class) {
result += 2;
} else if (primitiveClass == int.class || primitiveClass == float.class) {
result += 4;
} else if (primitiveClass == double.class || primitiveClass == long.class) {
result += 8;
}


} else {
// assume compressed references.
result += 4;
}
}
}


clazz = clazz.getSuperclass();


// round up to the nearest WORD length.
if ((result % WORD) != 0) {
result += WORD - (result % WORD);
}
}


result += HEADER_SIZE;


return result;
}
}

当使用JetBrains IntelliJ时,首先在|文件设置|构建,执行,部署|调试器中启用“附加内存代理”。

调试时,右键单击感兴趣的变量,选择“计算保留大小”:计算保留大小

如果您的应用程序有Apache commons lang库作为依赖项,或者正在使用Spring框架,那么您还可以使用SerializationUtils类来快速查找任何给定对象的大致字节大小。

byte[] data = SerializationUtils.serialize(user);
System.out.println("Approximate object size in bytes " + data.length);

一个可能的答案是2022年。

https://github.com/ehcache/sizeof

https://mvnrepository.com/artifact/org.ehcache/sizeof

https://mvnrepository.com/artifact/org.ehcache/sizeof/0.4.0

版本0.4.0只有一个(编译)依赖

https://mvnrepository.com/artifact/org.slf4j/slf4j-api

这是一件好事。

示例代码:

//import org.ehcache.sizeof.SizeOf;


SizeOf sizeOf = SizeOf.newInstance(); // (1)
long shallowSize = sizeOf.sizeOf(someObject); // (2)
long deepSize = sizeOf.deepSizeOf(someObject); // (3)