Java和.NET堆空间开销

3
我了解堆和垃圾回收器的工作原理:垃圾回收在几代中进行,内存分配按顺序进行,在垃圾回收期间,通过移动数据和形成连续块来压缩未使用的空间等。
已分配内存块是否有标题并且它们有多大(我听说.NET CLR为8-16字节),以及是否存在字节、字或四字对齐? 我对x86和x64处理器架构的JIT(Java)和CLR(.NET Framework或Mono)实现的任何信息都感兴趣。
3个回答

5
我相信标题大小是两个单词 - 一个用于类型引用,另一个用于同步块和其他标志。 填充物(我相信)仅足以将总大小舍入为整个字数。
例如,在x86上,只有“int”的引用类型需要占用12个字节,如此处所示:
using System;

public class Foo
{
    int x;

    public Foo(int x)
    {
        this.x = x;
    }
}

public class Test
{
    static void Main(string[] args)
    {
        int length = int.Parse(args[0]);

        Foo x = new Foo(0);
        Foo[] array = new Foo[length];
        // Make sure that JITting the string constructor doesn't
        // change things
        long start = GC.GetTotalMemory(true);
        for (int i=0; i < length; i++)
        {
            array[i] = new Foo(i);
        }
        long end = GC.GetTotalMemory(true);

        GC.KeepAlive(array);
        GC.KeepAlive(x);

        decimal totalDecimal = end-start;
        Console.WriteLine(totalDecimal / length);
    }
}

有一个有趣的点——某种原因,System.Object实例占用12个字节(在x86上),而不是我预测的8个字节。好像最小尺寸是12个字节,但你可以免费获取前四个字节的真实数据 :)

我不知道为什么报告的大小不是整数,顺便说一句——我猜测这与托管堆中每个页面需要一点额外内存有关。有时结果略大于12,有时略小于12——这似乎取决于给定的长度。(此答案的先前版本存在错误,它将解析第一个命令行参数,但然后忽略它。我已经修复了这个问题。)无论如何,我不认为这种轻微的不准确性与内存中单个对象的大小有任何关系。


“大概后台正在进行某些操作,但我不知道是什么…” - LicenseQ
@LicenseQ:就我看来,这与实际大小无关。我会更新答案进行解释。 - Jon Skeet
CLR via C#,Jeff Richter著。我会看看是否有类似信息的在线文章。 - Jon Skeet
我猜“同步块”是关键词,可以找到http://www.oreilly.com.tw/bookcode/JavaTwo_GC.pdf。谢谢。 - LicenseQ
FYI,至少在使用“经典”GC时,最小对象大小的原因我认为是,一旦GC重新定位了对象,它需要能够在旧位置存储指向其新位置的指针。 它还需要跟踪如何遍历对象树,并且无法在堆栈上使用(因为对树深度没有合理限制)。 一旦对象已复制,存储在旧位置的数据和类描述符指针将变得多余,因此它们可以用来保存新位置和堆遍历信息(如果数据足够大)。 - supercat
显示剩余5条评论

1
回答这个问题需要一个完整的答案,因为与对象分配相关的开销不仅取决于特定虚拟机的实现细节,还取决于对象所在的代(换句话说,与特定对象相关的开销可能会在对象的生命周期内发生变化)。
有一些简单的工具可以用来估计特定对象的开销,但没有稳健的工具(例如,请查看 http://java.sun.com/docs/books/performance/1st_edition/html/JPRAMFootprint.fm.html)。
在Java中,还有一个接口可以给出包括开销在内的对象大小,请参见http://download-llnw.oracle.com/javase/6/docs/platform/jvmti/jvmti.html#GetObjectSize

-2

我不了解Java,但对于CLR而言,每个分配的引用类型都有一个1个本地字节的开销。在32位系统上,它将是4个字节,在64位系统上,它将是8个字节。


它分配的内存。我正在寻找物理堆内存的外观。通常是[头部] [对象数据] [填充] [头部] [对象数据] [填充]....我正在寻找头部和填充的大小。 - LicenseQ
“我刚刚告诉你标题的大小,你还问标题的大小?” - JaredPar
为什么你会在头文件中使用引用? - LicenseQ
完全错误:每个引用类型有2个本地字开销,不包括对对象的每个引用的额外字。 - Qwertie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接