![](/img/trans.png)
[英]determine size of object: best way to use instrumentation in scala/sbt
[英]In Java, what is the best way to determine the size of an object?
我有一个应用程序可以读取包含大量数据行的 CSV 文件。 我根据数据类型向用户提供了行数的摘要,但我想确保我不会读入过多的数据行并导致OutOfMemoryError
s。 每行转换为 object。 有没有一种简单的方法可以以编程方式找出 object 的大小? 是否有参考定义了VM
的原始类型和 object 参考有多大?
现在,我的代码说 read up to 32,000 rows ,但我也希望代码说 read as many rows as possible 直到我使用了32MB的 memory。 也许这是一个不同的问题,但我仍然想知道。
您可以使用java.lang.instrument
包。
编译并把这个类放在一个 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
您应该使用jol ,这是作为 OpenJDK 项目的一部分开发的工具。
JOL(Java Object Layout)是用于分析 JVM 中的对象布局方案的小工具箱。 这些工具大量使用 Unsafe、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
)获取对象实例的浅层大小。 这只是该类的单个实例消耗的空间; 它不包括该类引用的任何其他对象。 它确实包括对象标头、字段对齐和填充的 VM 开销。 对于java.util.regex.Pattern
:
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()
, GraphLayout.parseInstance(obj).toPrintable()
会告诉您每个引用对象的字段取消引用的地址、大小、类型、值和路径,尽管这通常是太多细节GraphLayout.parseInstance(obj).toPrintable()
用。 对于正在进行的模式示例,您可能会得到以下信息。 (地址可能会在两次运行之间发生变化。)
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)
“(something else)”条目描述堆中不属于此对象图的其他对象。
最好的 jol 文档是 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
几年前 Javaworld 有 一篇关于确定复合和潜在嵌套 Java 对象的大小的文章,他们基本上介绍了在 Java 中创建 sizeof() 实现的过程。 该方法基本上建立在其他工作之上,人们通过实验确定基元和典型 Java 对象的大小,然后将这些知识应用于递归遍历对象图以计算总大小的方法。
它总是会比本地 C 实现稍微不准确,因为在类的幕后发生的事情,但它应该是一个很好的指标。
或者,一个名为sizeof的 SourceForge 项目提供了一个带有 sizeof() 实现的 Java5 库。
PS 不要使用序列化的方法,序列化对象的大小与其存活时消耗的内存量之间没有相关性。
首先,“对象的大小”在 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 大小初始化为一个合理的值,代码会更有效率。
如果您只想知道您的 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();
编辑:我认为这可能会有所帮助,因为问题作者还表示他希望拥有处理“在我使用 32MB 内存之前读取尽可能多的行”的逻辑。
当我在 Twitter 工作时,我编写了一个用于计算深度对象大小的实用程序。 它考虑了不同的内存模型(32 位、压缩 oops、64 位)、填充、子类填充,在循环数据结构和数组上正常工作。 你可以编译这个 .java 文件; 它没有外部依赖:
许多其他答案提供了较浅的大小 - 例如,没有任何键或值的 HashMap 的大小,这可能不是您想要的。
jamm 项目使用上面的 java.lang.instrumentation 包,但会遍历树,因此可以为您提供深层内存使用。
new MemoryMeter().measureDeep(myHashMap);
https://github.com/jbellis/jamm
要使用 MemoryMeter,请使用“-javaagent:/jamm.jar”启动 JVM
您必须使用反射来行走对象。 这样做时要小心:
byte
理论上是 1 个字节并不意味着它在内存中只需要一个字节。HashMap
或类似的对象,使用 object-equals 作为比较器来消除无限循环。@jodonnell:我喜欢您的解决方案的简单性,但许多对象不是可序列化的(因此这会引发异常),字段可以是瞬态的,并且对象可以覆盖标准方法。
你必须用工具测量它,或者手工估计它,这取决于你使用的JVM。
每个对象有一些固定的开销。 它是特定于 JVM 的,但我通常估计 40 个字节。 然后你必须看看班级的成员。 在 32 位(64 位)JVM 中,对象引用是 4(8)个字节。 原始类型是:
数组遵循相同的规则; 也就是说,它是一个对象引用,因此它在您的对象中占用 4(或 8)个字节,然后它的长度乘以其元素的大小。
尝试通过调用Runtime.freeMemory()
以编程方式执行此操作并不会为您提供太多准确性,因为对垃圾收集器的异步调用等。使用 -Xrunhprof 或其他工具分析堆将为您提供最准确的结果。
还有Memory Measurer工具(以前在Google Code ,现在在GitHub 上),它很简单,并在商业友好的Apache 2.0 许可下发布,如在类似问题中所讨论的。
如果您想测量内存字节消耗,它也需要 java 解释器的命令行参数,但其他方面似乎工作得很好,至少在我使用它的场景中。
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() { }
}
不必搞乱仪器等,如果您不需要知道对象的字节精确大小,您可以采用以下方法:
System.gc();
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
do your job here
System.gc();
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
通过这种方式,您可以在前后读取已用内存,并在获得已用内存之前调用 GC,您将“噪音”几乎降低到 0。
为了获得更可靠的结果,您可以将作业运行 n 次,然后将使用的内存除以 n,从而获得一次运行所需的内存。 更重要的是,您可以将整个过程运行更多次并取平均值。
我正在寻找满足以下要求的对象大小的运行时计算:
以下内容基于原始 Java 专家文章 ( https://www.javaspecialists.eu/archive/Issue078.html ) 的核心代码以及此问题的另一个答案中 Unsafe 版本的一些内容。
我希望有人觉得它有用。
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;
}
}
这是我使用一些链接示例制作的实用程序,用于处理带有压缩 OOP 的 32 位、64 位和 64 位。 它使用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 );
}
}
我曾经写了一个快速测试来即时估计:
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
考虑到对齐行为和可能的堆块头开销,这是我们所期望的。
此处接受的答案中详述的仪器方法最准确。 我描述的方法是准确的,但仅在没有其他线程创建/丢弃对象的受控条件下。
只需使用java可视化VM。
它拥有您分析和调试内存问题所需的一切。
它还有一个 OQL(对象查询语言)控制台,它允许您做许多有用的事情,其中之一是sizeof(o)
如果这就是您所要求的,则没有方法调用。 通过一些研究,我想你可以自己写。 特定实例具有从引用和原始值的数量以及实例簿记数据导出的固定大小。 您只需遍历对象图即可。 行类型的变化越少,越容易。
如果这太慢或只是比它的价值更麻烦,那么总是有很好的老式行计数经验法则。
一个可能的 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)
long heapSizeBefore = Runtime.getRuntime().totalMemory();
// Code for object construction
...
long heapSizeAfter = Runtime.getRuntime().totalMemory();
long size = heapSizeAfter - heapSizeBefore;
size 使您由于创建对象而增加了 jvm 的内存使用量,这通常是对象的大小。
我的回答是基于 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 和一些简单的类测试了这个解决方案。 也可能有不承保的情况。
更新:修改示例以支持数组对象的内存占用计算。
这个答案与对象大小无关,但是当您使用数组来容纳对象时; 它将为对象分配多少内存大小。
因此,数组、列表或映射所有这些集合不会真正存储对象(仅在原始对象时,才需要真正的对象内存大小),它只会存储这些对象的引用。
现在Used heap memory = sizeOfObj + sizeOfRef (* 4 bytes) in collection
原语
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 字节。
ReferenceMemoryTest ref1 = new ReferenceMemoryTest(); ( 4(ref1) + 12 = 16 字节) ReferenceMemoryTest ref2 = ref1; ( 4(ref2) + 16 = 20 字节)
您可以生成堆转储(例如使用 jmap),然后分析输出以查找对象大小。 这是一个离线解决方案,但您可以检查浅层和深层尺寸等。
假设我声明了一个名为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
If your application has the Apache commons lang library as a dependency or is using the Spring framework then you can also use the SerializationUtils
class to quickly find out the approximate byte size of any given object.
byte[] data = SerializationUtils.serialize(user);
System.out.println("Approximate object size in bytes " + data.length);
对于 JSONObject,以下代码可以帮助您。
`JSONObject.toString().getBytes("UTF-8").length`
以字节为单位返回大小
我通过将它写入文件来使用我的 JSONArray 对象检查它。 它给出了对象大小。
我怀疑您是否想以编程方式执行此操作,除非您只想执行一次并将其存储以备将来使用。 这是一件代价高昂的事情。 Java 中没有 sizeof() 运算符,即使有,它也只会计算对其他对象的引用成本和原语的大小。
您可以这样做的一种方法是将事物序列化为文件并查看文件的大小,如下所示:
Serializable myObject;
ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("obj.ser"));
oos.write (myObject);
oos.close ();
当然,这假设每个对象都是不同的,并且不包含对其他任何对象的非瞬态引用。
另一种策略是获取每个对象并通过反射检查其成员并将大小相加(boolean & byte = 1 byte,short & char = 2 bytes 等),按照成员层次结构向下工作。 但这既乏味又昂贵,并且最终会做与序列化策略相同的事情。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.