OutOfMemoryError
。每行转换为一个对象。有没有一种简便的方法以编程方式找出该对象的大小?是否有一个引用定义了VM
的原始类型和对象引用有多大?现在,我的代码可以读取多达32,000行,但我也想拥有表示要读取尽可能多的行,直到使用32MB内存为止。也许这是一个不同的问题,但我仍然想知道。
#1 楼
您可以使用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
评论
@Stefan很好的提示!您能否告诉您,使用您描述的方法,byte [0],byte [1],byte [5],int [0],int [1],int [2]的大小是多少?如果结果包括数组长度和内存对齐的开销,那就太好了。
– dma_k
10 Mar 8 '10在11:28
我尝试过此操作,但得到了奇怪而无益的结果。无论大小如何,字符串始终为32。我以为这可能是指针的大小,但是对于我创建的另一个不可变的类,我得到了24。它对于基元非常有效,但是您实际上并不需要一个程序来告诉您char有多大。
–伯尔
2011年9月16日下午12:39
@Brel此解决方案只是文档中指定的“指定对象消耗的存储量的近似值”。我还假设作者由于Java的String池而决定将String的大小设置为32个字节(仅指针?),这使得很难说是否共享(存储在池中)String实例还是将String实例共享。本地和独特的。
–安德烈一世
13年7月17日在8:33
如果不导出jar,如何使用ObjectSizeFetcher?我在Eclipse中测试了Java项目。
–尤拉·辛卡列夫(Yura Shinkarev)
13年8月5日在18:40
@brel不管实际长度如何,String都只有32个字节的原因是因为字符串的可变长度部分存储在char []中,而char []是它自己的对象。要获得对象的真实大小,您需要添加其自身的大小及其引用的每个对象的大小。
–tombrown52
18年5月31日在16:05
#2 楼
您应该使用jol,它是OpenJDK项目的一部分开发的工具。JOL(Java对象布局)是在JVM中分析对象布局方案的小工具箱。这些工具使用不安全,JVMTI和可维护性代理(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
13 24 312 java.util.regex.Pattern
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 .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs (object)
d5e64d48 24 java.util.regex.Pattern .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs.val$rhs (object)
d5e64d60 24 java.util.regex.Pattern .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs (object)
d5e64d78 24 java.util.regex.Pattern .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$rhs (object)
d5e64d90 24 java.util.regex.Pattern .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs (object)
d5e64da8 24 java.util.regex.Pattern .root.next.atom.val$lhs.val$lhs.val$lhs (object)
d5e64dc0 24 java.util.regex.Pattern .root.next.atom.val$lhs.val$lhs (object)
d5e64dd8 24 java.util.regex.Pattern .root.next.atom.val$lhs (object)
d5e64df0 24 java.util.regex.Pattern .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 .root.next.next.next.atom.val$lhs.val$lhs.val$lhs (object)
d5e64f80 24 java.util.regex.Pattern .root.next.next.next.atom.val$lhs.val$lhs.val$rhs (object)
d5e64f98 24 java.util.regex.Pattern .root.next.next.next.atom.val$lhs.val$lhs (object)
d5e64fb0 24 java.util.regex.Pattern .root.next.next.next.atom.val$lhs.val$rhs (object)
d5e64fc8 24 java.util.regex.Pattern .root.next.next.next.atom.val$lhs (object)
d5e64fe0 24 java.util.regex.Pattern .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 .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs.val$lhs (object)
d5e65170 24 java.util.regex.Pattern .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs.val$rhs (object)
d5e65188 24 java.util.regex.Pattern .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs (object)
d5e651a0 24 java.util.regex.Pattern .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$rhs (object)
d5e651b8 24 java.util.regex.Pattern .root.next.next.next.next.next.atom.val$lhs.val$lhs (object)
d5e651d0 24 java.util.regex.Pattern .root.next.next.next.next.next.atom.val$lhs (object)
d5e651e8 24 java.util.regex.Pattern .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)
“(其他)”条目描述了堆中不属于该对象图的其他对象。 br />
最好的jol文档是jol存储库中的jol示例。这些示例演示了常见的jol操作,并展示了如何使用jol分析VM和垃圾收集器内部。
评论
这个答案应该有更多的支持。绝对是一个非常好的选择。编辑:检查是否在今年'08中提出了问题?可能是OP目前要求的最好和最简单的选择。
–租金
2015年8月30日在19:22
工具作者写了一篇有关Jol的博客文章。
–迈克
2015年9月6日下午0:37
要确定对象“ obj”的大小,请使用:org.openjdk.jol.info.GraphLayout.parseInstance(obj).totalSize();
– vigor
17年1月19日在9:26
请注意,vmDetails现在是VM.current()。details()。
– Miha_x64
18年5月13日在7:35
查看GraphLayout.parseInstance(instance).toFootprint()我发现了解对象大小更有用
– Mugen
4月2日8:37
#3 楼
我不小心在jdk中找到了一个java类“ jdk.nashorn.internal.ir.debug.ObjectSizeCalculator”,
它易于使用,对于确定对象的大小似乎很有用。 br />
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
评论
同样在这里,我正在尝试上面提出的其他解决方案,并遇到了ObjectSizeCalculator。我相信,自从它最近作为项目Nashorn的一部分在JDK 8上引入以来,没有人提到过。但是,我没有在网上找到有关此类的任何官方文档。
–亨里克·贡蒂霍(Henrique Gontijo)
16-10-16在18:26
它似乎没有考虑字符串长度。它只是堆栈上的大小吗?
– jontejj
17年5月7日在8:50
我有一个哈希图,其中com.carrotsearch.RamUsageEstimator返回大约一半的ObjectSizeCalculator。哪一个是对的? -哪一个更舒适?
– Badera
17年5月18日在8:36
请注意,仅HotSpot VM支持ObjectSizeCalculator
– kellanburket
18-2-2在21:04
而且JDK 11上不再存在jdk.nashorn.internal.ir.debug.ObjectSizeCalculator
–马丁·维斯尼(Martin Vysny)
10月23日8:55
#4 楼
几年前,Javaworld发表了一篇有关确定复合对象和可能嵌套的Java对象大小的文章,它们基本上遍历了在Java中创建sizeof()实现的过程。该方法基本上建立在其他工作的基础上,人们通过实验确定了基元和典型Java对象的大小,然后将该知识应用于递归地遍历对象图以计算总大小的方法。它将总是有点仅仅由于类的幕后发生的事情而导致的准确性不如本地C实现,但是它应该是一个很好的指标。
或者,一个适当地称为sizeof的SourceForge项目提供了带有sizeof()实现的Java5库。
PS不要使用序列化方法,序列化对象的大小与其在活动时消耗的内存量之间没有关联。
评论
sizeof实用程序可能是最快的方法。基本上就是Stefan所说的,但已经装在一个罐子里准备使用。
–亚历山大·泰勒(Alexandre L Telles)
09年9月8日在22:42
答案中的链接已断开
– akram
7月10日23:31
#5 楼
首先,“对象的大小”不是Java中定义明确的概念。您可以指对象本身,仅包含其成员,对象及其引用的所有对象(参考图)。您可以指内存中的大小或磁盘上的大小。并且允许JVM优化诸如Strings之类的东西。因此,唯一正确的方法是使用一个良好的探查器(我使用YourKit)询问JVM,这可能不是您想要的。 br />
但是,从上面的描述来看,听起来每一行都是独立的,并且没有很大的依赖关系树,因此序列化方法可能是大多数JVM的良好近似。做到这一点最简单的方法如下:
Serializable ser;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(ser);
oos.close();
return baos.size();
请记住,如果您有具有公共引用的对象,这将无法提供正确的结果,并且序列化的大小也不会总是匹配内存中的大小,但这是一个很好的近似值。如果将ByteArrayOutputStream的大小初始化为一个合理的值,则代码将更有效率。
评论
我喜欢这种方法。就对象大小而言,您有多远?
–柏林·布朗
09年7月24日在17:56
非常简单有效。其他方法太混乱了(特别是在Eclipse RCP内部)。谢谢。
– marcolopes
2012年4月24日在16:18
序列化不会跟踪瞬态变量,默认的序列化方法以UTF-8格式写入字符串,因此任何ANSI字符仅占用一个字节。如果您有很多字符串,那么您的大小将相去甚远,以至于毫无用处。
– TMN
2012年7月9日在18:45
虽然这可能无法提供确切的大小,但对于我的需求,我只需要比较2个对象和SizeOf就不会从Web应用程序初始化。谢谢!
–艾萨克
2012年10月30日在22:57
很好的推荐YourKit。其他替代方案是VirtualVM和jvmmonitor
–天使
13年2月24日在10:46
#6 楼
如果您只想知道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内存之前,读取尽可能多的行。”
评论
这不是一个好的解决方案,因为您永远不知道何时会进行垃圾回收,或者一次将多少额外的内存分配给堆。
–尼克·福特斯库(Nick Fortescue)
08年9月9日在17:24
是的,我不打算用这个来解决本文的主要问题,但是当他接近达到最大堆大小时,这可能有助于他以编程方式知道。
–马特b
08年9月9日在17:25
此解决方案的另一个问题是在多线程环境中(例如在Web服务器中)。其他线程可能正在执行并占用内存。通过这种近似,您可以计算所有虚拟机中的已用内存。
–天使
13年2月24日在10:34
另一个缺点是freeMemory返回一个近似值。尝试创建一个javax.crypto.Cipher对象。两次调用freeMemory(以估计密码的大小)之间的差异不是恒定的!
– Eugen
2013年5月9日15:08
我相信您可以强制进行垃圾回收,因此您可以使用这种方法做一些事情。
– Matanster
15年2月12日在15:30
#7 楼
回到Twitter时,我写了一个用于计算深物体尺寸的实用程序。它考虑了不同的内存模型(32位,压缩的oops,64位),填充,子类填充,可在循环数据结构和数组上正常工作。您可以只编译一个.java文件。它没有外部依赖项:https://github.com/twitter/commons/blob/master/src/java/com/twitter/common/objectsize/ObjectSizeCalculator.java
评论
西亚!我也想大声疾呼您的演示文稿:幻灯片15-20非常适合帮助您直观理解各种数据结构决策的成本。感谢您发布!
–卢克·厄舍伍德
2015年10月1日在1:58
“它没有外部依赖关系”-因为番石榴什么时候不是外部依赖关系?
–l4mpi
16年1月11日在14:21
看起来非常类似于github.com/JetBrains/jdk8u_nashorn/blob/master/src/jdk/nashorn / ...? :O
– Francesco
17-10-24在14:02
番石榴是一种外部依赖性。
–Mert Serimer
17年12月14日在10:33
#8 楼
许多其他答案提供的尺寸较浅-例如没有任何键或值的HashMap的大小,这不太可能是您想要的。jamm项目使用上面的java.lang.instrumentation程序包,但可以遍历树,因此可以给出您可以使用深内存。
new MemoryMeter().measureDeep(myHashMap);
https://github.com/jbellis/jamm
要使用MemoryMeter,使用“ -javaagent:/jamm.jar”启动JVM。
#9 楼
您必须使用反射来走动对象。操作时请小心:在JVM中分配对象只会产生一些开销。该数量因JVM而异,因此您可以将此值作为参数。至少使它成为一个常量(8个字节?)并适用于分配的任何内容。
仅因为
byte
理论上为1个字节并不意味着它仅占用一个内存。对象引用中将存在循环,因此您需要保留
HashMap
或类似的东西,使用对象等于作为比较器以消除无限循环。@jodonnell:我喜欢您的解决方案的简单性,但是许多对象不是可序列化的(这样会引发异常),字段可以是瞬态的,并且对象可以覆盖标准方法。
评论
Java规范中没有定义各种原语的大小吗? (第2.4.1节)
– erickson
08年9月9日在17:25
问题不在于“它占用了多少内存”。仅在它们如何工作的意义上。例如,字节,字符和短裤在Java堆栈中占据了整个单词,即使它们进行舍入运算也是如此。
–詹森·科恩(Jason Cohen)
08年9月9日在17:27
这听起来与测量大小类似,如Heinz在其第78号通讯中所示:javaspecialists.eu/archive/Issue078.html。我用了他的方法行得通。
– Peter Kofler
10 Mar 21 '10在9:26
#10 楼
您必须使用工具对其进行测量,或者手动进行估算,这取决于您使用的JVM。每个对象有一些固定的开销。它是特定于JVM的,但是我通常估计40个字节。然后,您必须查看类的成员。在32位(64位)JVM中,对象引用是4(8)字节。基本类型为:
布尔值和字节:1个字节
char和short:2个字节
int和float:4个字节
long和double: 8个字节
数组遵循相同的规则;也就是说,它是一个对象引用,因此在对象中占用4(或8)个字节,然后将其长度乘以其元素的大小。
通过调用
Runtime.freeMemory()
来尝试以编程方式进行操作由于对垃圾回收器的异步调用等原因,它不会给您带来太大的准确性。使用-Xrunhprof或其他工具对堆进行概要分析将为您提供最准确的结果。评论
@erickson我不确定在查看该线程(stackoverflow.com/questions/1907318/…)的sizeof(boolean)== 1。你能对此发表评论吗?
– dma_k
10 Mar 8 '10在11:22
@ dma_k,Java实际上没有真正的布尔值。布尔值的大小在数组外部为4个字节,在boolean []内部为1个字节。实际上,所有非double / long类型的图元都是4个字节。后者为8(答案也错误地将其设为4)
–bestsss
2012-12-25 14:43
@bestsss:确切地说,最小的内存分配取决于JVM的平台和实现。另外,堆上的对象也对齐,因此在将所有大小加总后,需要将其四舍五入。
– dma_k
2012年12月29日在9:52
#11 楼
还有一个Memory Measurer工具(以前在Google Code上,现在在GitHub上),该工具很简单,并在商业友好的Apache 2.0许可下发布,正如在类似问题中所讨论的那样。它也是如此。 ,如果要测量内存字节消耗,则需要java解释器的命令行参数,但至少在我使用过的情况下,否则似乎工作得很好。
#12 楼
java.lang.instrument.Instrumentation
类提供了一种获取Java对象大小的好方法,但是它要求您定义premain
并使用Java代理运行程序。当您不需要任何代理程序,然后必须为应用程序提供虚拟的Jar代理程序时,这非常无聊。因此,我从
Unsafe
使用sun.misc
类获得了替代解决方案。因此,考虑到根据处理器体系结构进行对象堆对齐并计算最大字段偏移量,您可以测量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() { }
}
评论
有趣的方法,但这不是假设对象及其字段存储没有碎片吗?
–nicoulaj
2012年6月11日13:25
是的,我不知道会导致这种碎片的任何JVM实现。
–米格尔·甘博亚(Miguel Gamboa)
2012年6月18日在16:44
我不明白碎片不是一种选择:)让我们以存储为对象A和B的字段的对象C为例。不是将整个对象移到A或B中吗?
–nicoulaj
2012年6月19日下午13:46
抱歉,但您的观点我都不明白。根据我的解释,在Java对象中不能将其存储在其他对象内部,例如C结构或.Net中的值类型。因此,当您说:“存储为对象A和B的字段的对象C”时,意味着对象A和B的字段存储了对对象C的引用(指针)。那么A和B的大小等于该字段的偏移量加上对对象C的引用(指针)的大小。引用的大小是一个单词的大小。
–米格尔·甘博亚(Miguel Gamboa)
2012年6月19日14:24
哦,好的,我们在谈论浅尺寸。我的错。
–nicoulaj
2012年6月19日14:48在
#13 楼
使用JetBrains IntelliJ时,首先在“文件” |“启用”中启用“附加存储代理”。设置|构建,执行,部署|调试器。调试时,右键单击所需变量,然后选择“计算保留大小”:
评论
我在Intellij中看不到-使用2019.2。您使用什么版本?
–user2738206
7月15日0:48
#14 楼
无需弄乱仪器等等,并且如果您不需要了解对象的字节精确大小,则可以采用以下方法:System.gc();
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
do your job here
System.gc();
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
这样,您可以前后读取已使用的内存,并在获取已使用的内存之前调用GC,将“噪音”几乎降低到0。
要获得更可靠的结果,可以运行您的工作n次,然后将使用的内存除以n,获得一次运行需要多少内存。甚至可以将整个过程运行更多次并取平均值。
评论
System.gc()不会只是通知您要GC吗?不能保证完全调用GC。
–Raildex
17年1月11日在13:42
@非常好。这是不安全的,因为您可能永远不会执行或影响行之间的内存。因此,“在”两个freeMemory方法之间,GC可以释放更多您不考虑的空间,因此对象看起来会更小
–Mert Serimer
17年12月14日在10:37
@MertSerimer对我来说“不安全”是完全不同的:正如我也说的那样,这最多不过是准确的。另外,您不能驱动GC(如Raildex所述),但是对于这种情况,我也建议将其插入循环中。如前所述,这只是一个快速,肮脏且近似的系统,如果结果不需要非常可靠,则可以使用。
–reallynice
17年12月14日在14:34
这样做有很多问题,但是确实给您带来了很多好处。
– markthegrea
5月18日下午16:11
#15 楼
这是我使用一些链接的示例制作的实用程序,用于使用压缩的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 );
}
}
评论
您是否使用值对此类进行了测试?我尝试过,但对我来说,值不正确!
–德博拉
2014年8月17日14:26
它为我提供的一个简单对象的值大约是正确的,但对于包含1mio对象的列表,其值却减少了10倍。仍然,非常棒的工作!
– MichaelBöckling
14年8月21日在13:12
有趣。我已经在Windows 7 x64和Linux 2.6.16 / x86_64上使用JDK7u67使用32位/ 64位/ oop地址模式中的每一个对它进行了测试。我已将其与Eclipse Memory Analyzer 1.3.x中分析的内存转储进行了比较。您正在使用什么设置?你有我可以尝试的具体例子吗?
– dlaudams
2014年9月9日下午5:43
我能做的最好选择。我不能使用Instrumentation,因为我不启动tomcat,ObjectSizeCalculator,因为不确定VM类型(HotSpot)和JOL bacouse spring bean。我使用它并添加第二个参数来忽略单例,即AbstractRefreshableApplicationContext.getBeanFactory()。getSingletonMutex()并重构internalSizeOf代码以忽略Class和Enum
– Perlos
18/09/14在12:56
为了比较结果,请使用ObjectSizeCalculator(将整个服务器计算1GB至10s)。 JOL导致MemError(不会引爆6GB),我没有得到相同的结果,可能是因为枚举。
– Perlos
18/09/14在13:08
#16 楼
我一直在寻找满足以下要求的对象大小的运行时计算:在运行时可用,而无需包含检测。
可与Java 9+一起使用而无需访问不安全。
仅基于类。不是很深的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;
}
}
#17 楼
如果您要的是没有方法调用。经过一点研究,我想您可以自己编写。特定实例具有固定的大小,该大小由引用和原始值的数量加上实例簿记数据得出。您只需遍历对象图即可。行类型的变化越少,越容易。如果这太慢了或者只是麻烦而已,那么总会有一些老式的行计数经验法则。
#18 楼
我曾经编写了一个快速测试来快速估算: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
在给定对齐方式行为和可能的堆块头开销的情况下,我们期望的结果。
方法在此处被接受的答案中最准确地详述。我描述的方法是准确的,但仅在没有其他线程正在创建/丢弃对象的受控条件下进行。
#19 楼
只需使用Java visual VM。它具有解决和调试内存问题所需的一切。
它还具有OQL(对象查询语言)控制台,可让您执行许多有用的操作,其中之一就是
sizeof(o)
#20 楼
long heapSizeBefore = Runtime.getRuntime().totalMemory();
// Code for object construction
...
long heapSizeAfter = Runtime.getRuntime().totalMemory();
long size = heapSizeAfter - heapSizeBefore;
size使您可以通过创建对象来增加jvm的内存使用量,这通常是对象的大小。
评论
如果GC在//对象构造代码期间在中间运行怎么办?现在可能一直都能得到正确的结果。
– rajugaadu
2014年6月17日下午2:35
#21 楼
我的答案基于尼克提供的代码。该代码测量被序列化对象占用的字节总数。因此,这实际上是衡量序列化内容+普通对象内存占用量的原因(仅序列化例如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和一些琐碎的类测试了该解决方案。
更新:修改示例以支持数组对象的内存占用量计算。
#22 楼
您可以生成堆转储(例如,使用jmap),然后分析输出以查找对象大小。这是一个脱机解决方案,但是您可以检查较浅和较深的尺寸等。#23 楼
这个答案与对象的大小无关,而是在使用数组容纳对象时。它会为对象分配多少内存。所以数组,列表或映射所有这些集合将不会真正存储对象(仅在原始对象时才是实际对象的内存大小)是必需的),它将仅存储这些对象的引用。
现在
Used heap memory = sizeOfObj + sizeOfRef (* 4 bytes) in collection
(4/8字节)取决于(32/64位)操作系统
PRIMITIVES
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个字节的内存。它可能是String引用或Double对象引用,但是取决于对象的创建,所需的内存将有所不同。
例如)如果我为以下类
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字节)
评论
4字节整数和未知大小的对象引用如何适合4字节?
–user207421
2015年5月4日,1:14
@EJP我的意思是说所有对象REFERENCE只需要4个字节的内存。它可能是String引用或Double对象引用,但是取决于对象创建,所需的内存会有所不同。
–卡纳加韦卢·苏古玛(Kanagavelu Sugumar)
2015年5月4日4:50
#24 楼
假设我声明了一个名为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
#25 楼
对于JSONObject,以下代码可以为您提供帮助。`JSONObject.toString().getBytes("UTF-8").length`
返回以字节为单位的大小
我将其写入JSONArray对象并通过JSONArray对象进行了检查文件。它给出对象的大小。
评论
这仅适用于大多数为字符串的对象。
–exter Legaspi
18年5月7日在21:12
#26 楼
我怀疑您是否希望以编程方式执行此操作,除非您只想执行一次并将其存储以备将来使用。这是一件昂贵的事情。 Java中没有sizeof()运算符,即使有,它也只计算对其他对象的引用成本和原语的大小。一种方法是将事物序列化为文件并查看文件的大小,如下所示:
Serializable myObject;
ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("obj.ser"));
oos.write (myObject);
oos.close ();
当然,这假设每个对象都是独立的并且不包含
另一种策略是获取每个对象并通过反射检查其成员并累加大小(布尔值和字节= 1个字节,短字符和char = 2个字节,等等),按照成员资格层次结构逐步进行。但这既繁琐又昂贵,最终会执行序列化策略将要执行的相同操作。
评论
我将使用ByteArrayOutputStream将其序列化为byte []。这比将其写入文件要快得多。
–ScArcher2
08年9月9日在17:21
@KorayTugay确定对象的字节大小已经是一项昂贵的操作。将每个对象写入磁盘以确定大小,这只会使它爬行。
– HammerNL
17年12月19日在10:24
序列化的对象格式与堆内存中的对象格式完全不同。最值得注意的是,对象类(及其所有可序列化的超类)的描述符被写入流中。因此,编写一个简单的java.lang.Integer实例将产生大约80个字节,其中堆表示形式通常为32(与对象流表示形式不同,堆表示形式取决于指针大小和对象对齐方式)。相反,序列化的空引用需要一个字节,而不是堆内存中的四个或八个字节。
–霍尔格
18年5月9日,11:50
评论
我添加了具有mvn配置的代理,并在此处说明了如何:stackoverflow.com/a/36102269/711855