c - ArrayList中每个数据占用的内存大小
问题描述
c#菜鸟阅读《深入理解c#》,表示被虐的体无完肤啊,每天一点一点的啃!!图片中讲的是ArrayList中存储byte,计算每一个数据装箱后占用内存大小,图片在画红线的句子一直想不明白,它的意思好像是一个byte装箱后的object对象要占用8个字节,我自己写了下面一段测试代码,输出也是只要1个字节就行了,不知道哪里出错了。还请大家多多指教
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace NetPlusPage76_test
{
[StructLayout(LayoutKind.Sequential)]
class MyObject
{
byte myByte;
public MyObject(byte myByte)
{
this.myByte = myByte;
}
}
class Program
{
static void Main(string[] args)
{
MyObject myObject = new MyObject(2);
int size = Marshal.SizeOf(typeof(MyObject));
Console.WriteLine("myObject's size is " + size);//myObject's size is 1
Console.ReadKey();
}
}
}
不懂C#
, 但略懂Java
, 勉强回答一下.
对象开销
C#
和Java
相同, 会进行自动内存回收, 但这并不存在什么特别的魔法. 以最简单的引用计数算法, 需要在对象中存储引用数量, 数量归0时回收. 更广义地说, 实现自动内存回收, 是要在占用内存的对象中保存更多的信息, 而不仅仅是对象的值本身. 如, 有单属性int
的对象, 必然会占用超过4字节(32位系统中存储int值需要的内存数).
除了要存储引用计数, 对象都有所属的类, 如"hello"
做为字串, 是一定存储其类型String
的信息(不然如何调用方法), 通常实现为引用(或称指针). 引用在32位系统中占据4字节内存.
除了存储引用计数用于内存回收和存储类型引用用于实例方法调用外, 不同语言还会存储别的东西, 如Ruby
还会存储freezen
状态, 对象一旦freeze
将不可更改, 还有object_id
, 唯一标识对象(往往实现为对象在内存中的地址), 还有tainted
状态, 用于表示对象是否被污染.
这是很公平的交换, 存储更多信息, 实现更强的功能, 但同时也造成内存的消耗更大. (注意, 每个对象都会有开销.)
装箱和拆箱
为了内存占用考虑, C#
和Java
'不约而同'将int, double, byte, short
等基础类型实现为裸类型, 既没有引用计数, 也没有类型引用. 在需要将其转换为引用类型Int Double Byte
等时, 再重新申请内存, 存储值, 存储对象的相关信息, 俗称装箱.
ArrayList
存储的是引用, 因此它可以存储任意类型, 也因此, 存储byte
时进行装箱, 额外消耗8
字节对象开销. List<byte>
声明时已经指定类型, 已知类型, 则知类型大小, 继尔可以像数组那样连续存储值而非引用.
还要注意内存对齐, 在32位系统中, 内存为4的整数倍时, 存取最快. 虽然byte
只占用1字节, 每个Byte
对象除了对象开销, 却要占用4
字节, 其余3个用于填充. (64位系统, 为8的整数倍.)
至于你的程序, 虽然看不懂, 但猜测Marshal.SizeOf
应该是输出对象序列化后的大小, 可能会自动执行拆箱操作.
注: 32位系统下, 因为计数和引用都是4字节, 因此对象开销是8字节, 而在64位系统中, 对象开销是16字节.
后记
对于运行于虚拟机的语言, 通常是很难通过调用API
来计算对象占用的真实内存大小. 额外的对象开销, 属于底层实现细节, 虚拟机的细节, 回收算法的细节. 大部分情况下, 这些细节无益于编程, 除了分析算法的空间复杂度和探究编译原理. 即便是分析复杂度, 也只需要知道每个对象都有开销外, 具体的开销多少其实无所谓. 因为这可能在语言的发展中改变.
这篇关于c - ArrayList中每个数据占用的内存大小的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!