在 Java 中,确定对象大小的最佳方法是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/52353/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
In Java, what is the best way to determine the size of an object?
提问by Jay R.
I have an application that reads a CSV file with piles of data rows. I give the user a summary of the number of rows based on types of data, but I want to make sure that I don't read in too many rows of data and cause OutOfMemoryError
s. Each row translates into an object. Is there an easy way to find out the size of that object programmatically? Is there a reference that defines how large primitive types and object references are for a VM
?
我有一个应用程序可以读取包含成堆数据行的 CSV 文件。我根据数据类型给用户总结了行数,但我想确保我不会读入太多行数据而导致OutOfMemoryError
s。每行转换为一个对象。有没有一种简单的方法可以以编程方式找出该对象的大小?是否有一个引用定义了 a 的基本类型和对象引用有多大VM
?
Right now, I have code that says read up to 32,000 rows, but I'd also like to have code that says read as many rows as possible until I've used 32MBof memory. Maybe that is a different question, but I'd still like to know.
现在,我有代码说 read up to 32,000 rows,但我也希望有代码说在我使用32MB内存之前读取尽可能多的行。也许这是一个不同的问题,但我仍然想知道。
采纳答案by Stefan Karlsson
You can use the java.lang.instrument package
Compile and put this class in a JAR:
编译并把这个类放在一个 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);
}
}
Add the following to your MANIFEST.MF
:
将以下内容添加到您的MANIFEST.MF
:
Premain-Class: ObjectSizeFetcher
Use getObjectSize:
使用 getObjectSize:
public class C {
private int x;
private int y;
public static void main(String [] args) {
System.out.println(ObjectSizeFetcher.getObjectSize(new C()));
}
}
Invoke with:
调用:
java -javaagent:ObjectSizeFetcherAgent.jar C
回答by jodonnell
I doubt you want to do it programmatically unless you just want to do it once and store it for future use. It's a costly thing to do. There's no sizeof() operator in Java, and even if there was, it would only count the cost of the references to other objects and the size of the primitives.
我怀疑您是否想以编程方式执行此操作,除非您只想执行一次并将其存储以备将来使用。这是一件代价高昂的事情。Java 中没有 sizeof() 运算符,即使有,它也只会计算对其他对象的引用成本和原语的大小。
One way you could do it is to serialize the thing to a File and look at the size of the file, like this:
您可以这样做的一种方法是将事物序列化为文件并查看文件的大小,如下所示:
Serializable myObject;
ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("obj.ser"));
oos.write (myObject);
oos.close ();
Of course, this assumes that each object is distinct and doesn't contain non-transient references to anything else.
当然,这假设每个对象都是不同的,并且不包含对其他任何对象的非瞬态引用。
Another strategy would be to take each object and examine its members by reflection and add up the sizes (boolean & byte = 1 byte, short & char = 2 bytes, etc.), working your way down the membership hierarchy. But that's tedious and expensive and ends up doing the same thing the serialization strategy would do.
另一种策略是获取每个对象并通过反射检查其成员并将大小相加(boolean & byte = 1 byte,short & char = 2 bytes 等),按照成员层次结构向下工作。但这既乏味又昂贵,并且最终会做与序列化策略相同的事情。
回答by sblundy
There isn't a method call, if that's what you're asking for. With a little research, I suppose you could write your own. A particular instance has a fixed sized derived from the number of references and primitive values plus instance bookkeeping data. You would simply walk the object graph. The less varied the row types, the easier.
如果这就是您所要求的,则没有方法调用。通过一些研究,我想你可以自己写。特定实例具有从引用和原始值的数量以及实例簿记数据导出的固定大小。您只需遍历对象图即可。行类型的变化越少,越容易。
If that's too slow or just more trouble than it's worth, there's always good old-fashioned row counting rule-of-thumbs.
如果这太慢或者只是比它的价值更麻烦,那么总是有很好的老式行计数经验法则。
回答by matt b
If you would just like to know how much memory is being used in your JVM, and how much is free, you could try something like this:
如果您只想知道您的 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: I thought this might be helpful as the question author also stated he would like to have logic that handles "read as many rows as possible until I've used 32MB of memory."
编辑:我认为这可能会有所帮助,因为问题作者还表示他希望拥有处理“在我使用 32MB 内存之前读取尽可能多的行”的逻辑。
回答by Jason Cohen
You have to walk the objects using reflection. Be careful as you do:
您必须使用反射来行走对象。这样做时要小心:
- Just allocating an object has some overhead in the JVM. The amount varies by JVM so you might make this value a parameter. At least make it a constant (8 bytes?) and apply to anything allocated.
- Just because
byte
is theoretically 1 byte doesn't mean it takes just one in memory. - There will be loops in object references, so you'll need to keep a
HashMap
or somesuch using object-equals as the comparatorto eliminate infinite loops.
- 仅仅分配一个对象在 JVM 中会有一些开销。数量因 JVM 而异,因此您可以将此值作为参数。至少使它成为一个常量(8 个字节?)并应用于分配的任何内容。
- 仅仅因为
byte
理论上是 1 个字节并不意味着它在内存中只需要一个字节。 - 对象引用中会有循环,因此您需要使用 object-equals 作为比较器来保留 a
HashMap
或 somesuch以消除无限循环。
@jodonnell: I like the simplicity of your solution, but many objects aren't Serializable (so this would throw an exception), fields can be transient, and objects can override the standard methods.
@jodonnell:我喜欢您的解决方案的简单性,但许多对象不是可序列化的(因此这会引发异常),字段可以是瞬态的,并且对象可以覆盖标准方法。
回答by erickson
You have to measure it with a tool, or estimate it by hand, and it depends on the JVM you are using.
你必须用工具测量它,或者手工估计它,这取决于你使用的JVM。
There is some fixed overhead per object. It's JVM-specific, but I usually estimate 40 bytes. Then you have to look at the members of the class. Object references are 4 (8) bytes in a 32-bit (64-bit) JVM. Primitive types are:
每个对象有一些固定的开销。它是特定于 JVM 的,但我通常估计 40 个字节。然后你必须看看班级的成员。在 32 位(64 位)JVM 中,对象引用是 4(8)个字节。原始类型是:
- boolean and byte: 1 byte
- char and short: 2 bytes
- int and float: 4 bytes
- long and double: 8 bytes
- 布尔值和字节:1 字节
- char 和 short:2 个字节
- int 和 float:4 字节
- long 和 double:8 字节
Arrays follow the same rules; that is, it's an object reference so that takes 4 (or 8) bytes in your object, and then its length multiplied by the size of its element.
数组遵循相同的规则;也就是说,它是一个对象引用,因此在您的对象中占用 4(或 8)个字节,然后它的长度乘以其元素的大小。
Trying to do it programmatically with calls to Runtime.freeMemory()
just doesn't give you much accuracy, because of asynchronous calls to the garbage collector, etc. Profiling the heap with -Xrunhprof or other tools will give you the most accurate results.
Runtime.freeMemory()
由于对垃圾收集器的异步调用等原因,尝试以编程方式调用just 不会给您带来太多准确性。使用 -Xrunhprof 或其他工具分析堆将为您提供最准确的结果。
回答by Nick Fortescue
Firstly "the size of an object" isn't a well-defined concept in Java. You could mean the object itself, with just its members, the Object and all objects it refers to (the reference graph). You could mean the size in memory or the size on disk. And the JVM is allowed to optimise things like Strings.
首先,“对象的大小”在 Java 中并不是一个明确定义的概念。您可以指对象本身,仅包含其成员、对象及其引用的所有对象(参考图)。您可能指的是内存大小或磁盘大小。JVM 可以优化字符串之类的东西。
So the only correct way is to ask the JVM, with a good profiler (I use YourKit), which probably isn't what you want.
所以唯一正确的方法是使用一个好的分析器(我使用YourKit)询问 JVM ,这可能不是你想要的。
However, from the description above it sounds like each row will be self-contained, and not have a big dependency tree, so the serialization method will probably be a good approximation on most JVMs. The easiest way to do this is as follows:
然而,从上面的描述来看,似乎每一行都是独立的,并且没有很大的依赖树,因此序列化方法可能是大多数 JVM 上的一个很好的近似值。最简单的方法如下:
Serializable ser;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(ser);
oos.close();
return baos.size();
Remember that if you have objects with common references this will notgive the correct result, and size of serialization will not always match size in memory, but it is a good approximation. The code will be a bit more efficient if you initialise the ByteArrayOutputStream size to a sensible value.
请记住,如果您有具有公共引用的对象,这将不会给出正确的结果,并且序列化的大小并不总是与内存中的大小匹配,但它是一个很好的近似值。如果您将 ByteArrayOutputStream 大小初始化为一个合理的值,代码会更有效率。
回答by Boris Terzic
Some years back Javaworld had an article on determining the size of composite and potentially nested Java objects, they basically walk through creating a sizeof() implementation in Java. The approach basically builds on other work where people experimentally identified the size of primitives and typical Java objects and then apply that knowledge to a method that recursively walks an object graph to tally the total size.
几年前 Javaworld 有一篇关于确定复合和潜在嵌套 Java 对象的大小的文章,他们基本上介绍了在 Java 中创建 sizeof() 实现的过程。该方法基本上建立在其他工作之上,人们通过实验确定基元和典型 Java 对象的大小,然后将这些知识应用于递归遍历对象图以计算总大小的方法。
It is always going to be somewhat less accurate than a native C implementation simply because of the things going on behind the scenes of a class but it should be a good indicator.
它总是会比本地 C 实现稍微不准确,因为在类的幕后发生的事情,但它应该是一个很好的指标。
Alternatively a SourceForge project appropriately called sizeofthat offers a Java5 library with a sizeof() implementation.
或者,一个名为sizeof的 SourceForge 项目提供了一个带有 sizeof() 实现的 Java5 库。
P.S. Do not use the serialization approach, there is no correlation between the size of a serialized object and the amount of memory it consumes when live.
PS 不要使用序列化的方法,序列化对象的大小与其存活时消耗的内存量之间没有相关性。
回答by Miguel Gamboa
The java.lang.instrument.Instrumentation
class provides a nice way to get the size of a Java Object, but it requires you to define a premain
and run your program with a java agent. This is very boring when you do not need any agent and then you have to provide a dummy Jar agent to your application.
本java.lang.instrument.Instrumentation
类提供了一个很好的方式来获得一个Java对象的大小,但它需要你定义一个premain
与一个Java代理运行程序。当您不需要任何代理,然后您必须为您的应用程序提供一个虚拟 Jar 代理时,这是非常无聊的。
So I got an alternative solution using the Unsafe
class from the sun.misc
. So, considering the objects heap alignment according to the processor architecture and calculating the maximum field offset, you can measure the size of a Java Object. In the example below I use an auxiliary class UtilUnsafe
to get a reference to the sun.misc.Unsafe
object.
所以我使用的替代解决方案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() { }
}
回答by JZeeb
You could generate a heap dump (with jmap, for example) and then analyze the output to find object sizes. This is an offline solution, but you can examine shallow and deep sizes, etc.
您可以生成堆转储(例如使用 jmap),然后分析输出以查找对象大小。这是一个离线解决方案,但您可以检查浅层和深层尺寸等。