class foo { }
writeln(foo.classinfo.init.length); // = 8 bytes
class foo { char d; }
writeln(foo.classinfo.init.length); // = 9 bytes
d实际存储的是8个字节中的任何内容,如果是,那么什么?这似乎是一个巨大的浪费,如果我只是包装一些值类型,那么该类显着膨胀程序,特别是如果我使用了很多。 char变大8倍而int变为3倍。
结构的最小大小为1个字节。
答案 0 :(得分:9)
在D中,对象有一个包含2个指针的标题(因此它可能是8字节或16个,具体取决于您的体系结构)。
第一个指针是虚方法表。这是一个由填充了函数指针的编译器生成的数组,因此可以进行虚拟调度。同一个类的所有实例共享相同的虚方法表。
第二个指针是监视器。它用于同步。它不确定这个字段永远留在这里,因为D强调本地存储和不变性,这使得许多对象上的同步变得毫无用处。由于此字段比这些功能旧,它仍然在这里并且可以使用。但是,它可能在将来消失。
对象上的这种标题非常常见,例如,您可以在Java或C#中找到相同的标题。您可以在此处查看更多信息:http://dlang.org/abi.html
答案 1 :(得分:9)
D在每个类实例中使用两个机器单词:
答案 2 :(得分:0)
我不知道D的细节,但是在Java和.net中,每个类对象都包含有关其类型的信息,并且还保存有关它是否是任何监视器锁的目标的信息,是否有资格进行最终清理和其他各种事情。具有所有对象存储此类信息的标准方法可以使语言和/或框架的用户和实现者更方便。顺便提一下,在.net的32位版本中,每个对象的开销是8个字节,除了最小对象大小为12个字节。这个最小值源于这样一个事实:当垃圾收集器移动对象时,它需要在旧位置临时存储对新对象的引用以及某种链接数据结构,这将允许它任意深度嵌套检查引用而不需要任意大的堆栈。
修改的 如果你想使用一个类,因为你需要能够持久引用数据项,那么空间是非常宝贵的,你的使用模式是这样的,你可以知道数据项何时仍然有用,何时它们已经过时,你也许能够定义一个结构数组,然后将索引传递给数组元素。如果程序的结构允许您确保每个被分配的项目只发布一次并且一旦发布它们就不会被使用,那么编写代码就可以非常有效地处理这一问题而且开销基本上是零。
如果您无法轻易确定对象的最后一次引用何时超出范围,则八个字节将是非常合理的开销。我希望大多数框架都会强制对象在32位边界上对齐(所以我很惊讶添加一个字节会将大小推到9而不是12)。如果系统的垃圾收集器比Commodore 64(*)更好,那么每个对象需要一个绝对最小的开销,以指示使用哪些东西,哪些不是。此外,除非想要为可以包含补充信息的对象和不能包含补充信息的对象分别使用堆,否则每个对象要么包含用于补充信息指针的空间,要么包括用于所有补充信息的空间(锁定,放弃)通知请求等)。虽然在某些情况下为这两类对象分别堆叠可能是有益的,但我怀疑这些好处通常会证明增加的复杂性。
(*)Commodore 64垃圾收集器通过从内存顶部向下分配字符串来工作,而变量(不是GC)则是自下而上分配的。当内存已满时,系统将扫描所有变量以查找对存储在最高地址的字符串的引用。然后该字符串将被移动到内存的最顶层,并且将更新对它的所有引用。然后,系统将扫描所有变量,以便在最刚移动的地址下方的最高地址处找到对该字符串的引用,并更新对该引用的所有引用。该过程将重复,直到它找不到任何更多的字符串移动。该算法不需要将任何额外的数据与字符串一起存储在内存中,但它当然是狗慢。 Commodore 128垃圾收集器与GC空间中的每个字符串一起存储一个指向变量的指针,该变量包含一个引用和一个长度字节,可用于查找GC空间中的下一个较低字符串;因此,它可以检查每个字符串,以确定它是否仍然使用,如果是这样,将其重新定位到内存顶部。速度要快得多,但每串的开销是三个字节。
答案 3 :(得分:-2)
您应该查看各种类型的存储要求。每个指令,存储分配(即:变量/对象等)都会占用特定的空间量。在c#中,Int32类型的整数对象应该将整数信息存储到4字节(32位)的曲调中。它也可能有其他信息,因为它是一个对象,但您的字符数据类型可能只需要1个字节的信息。如果你在课堂上有类似的或类似的结构,那些东西也会占用空间,因为每一件事都告诉你的班级要做点什么。该类本身需要在内存中创建许多指令,这将占8个初始字节。
参加汇编语言课程。您将学习所有您想知道的内容,然后了解一些有关为什么您的程序使用了大量内存或在编译时占用大量内存的原因。