【问题标题】:Calculate size of Object in Java [duplicate]在Java中计算对象的大小[重复]
【发布时间】:2012-03-11 05:51:44
【问题描述】:

我想记录一个对象为一个项目占用了多少内存(希望以字节为单位)(我正在比较数据结构的大小),似乎在 Java 中没有方法可以做到这一点。据说,C/C++ 有 sizeOf() 方法,但这在 Java 中是不存在的。我尝试在创建对象之前和之后使用Runtime.getRuntime().freeMemory() 记录JVM 中的空闲内存,然后记录差异,但它只会给出0 或131304,而两者之间没有任何内容,无论结构中的元素数量如何。请帮忙!

【问题讨论】:

  • 您可能想尝试 totalMemory() 然后 freeMemory() 并减去它们。
  • 我建议您看看 Jbellis JAMM。 (github.com/jbellis/jamm) 它依赖于上面提到的检测代理,但封装了一些实用工具来深入测量整个对象图的足迹。对我来说看起来很简单而且很酷。

标签: java memory memory-management data-structures


【解决方案1】:

您可以使用java.lang.instrumentation 包。

它有一个方法可以用来获取对象大小的实现特定的近似值,以及与对象相关的开销。

谢尔盖链接的答案有一个很好的例子,我将在这里重新发布,但你应该已经从他的评论中看到了:

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);
    }
}

使用getObjectSize:

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

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

Source

【讨论】:

  • 说“访问 Instrumentation 接口实例的唯一方法是以指示代理类的方式启动 JVM - 请参阅包规范。Instrumentation 实例被传递给 premain代理类的方法。一旦代理获取了 Instrumentation 实例,代理就可以随时调用实例上的方法。"
  • 我不需要指定JVM是如何启动的吗?我试过了,它一直给我 NullPointerException
  • 用您尝试过的内容编辑您的帖子。
  • 不知道这个答案是如何得到这么多支持的。它完全忽略了必须编译代理类以作为参数传递给 JVM 的事实。不这样做会导致检测为 null,并触发 NullPointerException。这是正确的答案:stackoverflow.com/questions/52353/…
  • 在尝试查找我的 Web 会话大小时,这种方法一定有一些不太正确的地方:它报告 40,而 ObjectGraphMeasurer (github.com/DimitrisAndreou/memory-measurer) 报告“足迹{Objects=34, References=52,基元=[float, int x 19, char x 257, long x 2, byte x 68557]}”。 ObjectGraphMeasurer 显然是赢家。
【解决方案2】:

查看https://github.com/DimitrisAndreou/memory-measurer
Guava 在内部使用它,ObjectGraphMeasurer 开箱即用特别简单,无需任何特殊的命令行参数。

import objectexplorer.ObjectGraphMeasurer;

public class Measurer {

  public static void main(String[] args) {
    Set<Integer> hashset = new HashSet<Integer>();
    Random random = new Random();
    int n = 10000;
    for (int i = 1; i <= n; i++) {
      hashset.add(random.nextInt());
    }
    System.out.println(ObjectGraphMeasurer.measure(hashset));
  }
}

【讨论】:

  • 这很有帮助。即使是另一个也很容易让它工作。你以前用过这个吗?
  • 我以前用过memory-measurer,是的。 (我为 Guava 做出了贡献,我确实提到我们在内部使用它。;))
  • 我怀疑最频繁的 SO 用户来这里是为了拖延——当然,我们一直都在这里, 我们会立即回复 cmets。跨度>
  • 这取决于您的 JVM。如果你想测量字节,那么你应该使用来自同一个项目的MemoryMeasurer;按照那里的说明进行操作。
  • @rodi 结果为线性,但由于反射具有相当高的常数因子。
【解决方案3】:

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() { }
}

【讨论】:

  • 我不确定:((int)maxOffset/WORD) + 1)*WORD,但我认为它确实有效,因为我很确定 64 位变量永远不会超过 8 64 位 VM 中的字节边界。否则代码很好,尽管您当然可以摆脱整个地图的制作工作。我制作了自己的版本,它也在这里处理对象数组:tinybrain.de/1011517(仍然需要添加原始数组)。
猜你喜欢
  • 2011-04-28
  • 2011-05-06
  • 1970-01-01
  • 2012-08-29
  • 2020-11-20
  • 2013-06-03
  • 2015-02-26
  • 2017-07-19
  • 2022-11-12
相关资源
最近更新 更多