对于任意实例(不同对象、组合、单个对象等的集合)
如何确定它的字节大小?
(我目前有一个各种对象的集合,我正在尝试确定它的聚合大小)
编辑:有没有人为对象写了一个扩展方法来做这件事?这将是相当整洁的国际海事组织。
发布于 2009-07-14 23:36:35
首先,警告:接下来的内容严格属于丑陋的、未经记录的黑客领域。不要依赖这个工作-即使它现在对你有效,明天它可能会停止工作,有任何次要或主要的.NET更新。
你可以使用这篇文章中关于CLR内部的信息MSDN Magazine Issue 2005 May - Drill Into .NET Framework Internals to See How the CLR Creates Runtime Objects --据我所知,它仍然适用。这是如何实现的(它通过类型的TypeHandle
检索内部的“基本实例大小”字段)。
object obj = new List<int>(); // whatever you want to get the size of
RuntimeTypeHandle th = obj.GetType().TypeHandle;
int size = *(*(int**)&th + 1);
Console.WriteLine(size);
这在3.532位SP1上有效。我不确定字段大小在64位上是否相同-如果不是,您可能需要调整类型和/或偏移量。
这将适用于所有“普通”类型,所有实例都具有相同的、定义良好的类型。数组和字符串肯定不是这样的,我相信StringBuilder
也是如此。对于它们,您需要将所有包含的元素的大小与它们的基本实例大小相加。
发布于 2009-07-14 22:57:41
如果您使用的是可序列化对象,则可以通过假装使用二进制序列化程序对其进行序列化来近似大小(但将输出路由到忽略状态)。
class Program
{
static void Main(string[] args)
{
A parent;
parent = new A(1, "Mike");
parent.AddChild("Greg");
parent.AddChild("Peter");
parent.AddChild("Bobby");
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf =
new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
SerializationSizer ss = new SerializationSizer();
bf.Serialize(ss, parent);
Console.WriteLine("Size of serialized object is {0}", ss.Length);
}
}
[Serializable()]
class A
{
int id;
string name;
List<B> children;
public A(int id, string name)
{
this.id = id;
this.name = name;
children = new List<B>();
}
public B AddChild(string name)
{
B newItem = new B(this, name);
children.Add(newItem);
return newItem;
}
}
[Serializable()]
class B
{
A parent;
string name;
public B(A parent, string name)
{
this.parent = parent;
this.name = name;
}
}
class SerializationSizer : System.IO.Stream
{
private int totalSize;
public override void Write(byte[] buffer, int offset, int count)
{
this.totalSize += count;
}
public override bool CanRead
{
get { return false; }
}
public override bool CanSeek
{
get { return false; }
}
public override bool CanWrite
{
get { return true; }
}
public override void Flush()
{
// Nothing to do
}
public override long Length
{
get { return totalSize; }
}
public override long Position
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public override int Read(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
public override long Seek(long offset, System.IO.SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
}
发布于 2015-10-27 07:57:50
对于非托管类型,也就是值类型,结构:
Marshal.SizeOf(object);
对于托管对象,我得到的近似值是近似值。
long start_mem = GC.GetTotalMemory(true);
aclass[] array = new aclass[1000000];
for (int n = 0; n < 1000000; n++)
array[n] = new aclass();
double used_mem_median = (GC.GetTotalMemory(false) - start_mem)/1000000D;
不要使用序列化。二进制格式化程序会添加头,因此您可以更改类并将旧的序列化文件加载到修改后的类中。
此外,它不会告诉您内存中的实际大小,也不会考虑内存对齐。
通过在类的每个属性上递归地使用BiteConverter.GetBytes(prop-value)进行编辑,您将获得以字节为单位的内容,这不会计算类或引用的权重,但更接近实际。如果大小很重要,我建议使用字节数组和非托管代理类来使用指针强制转换访问值,请注意,这将是非对齐内存,因此在旧计算机上速度会很慢,但在现代RAM上的大型数据集将会快得多,因为最小化从RAM读取的大小将比未对齐的影响更大。
https://stackoverflow.com/questions/1128315
复制相似问题