今日,在項目重構的時候突然想到一個問題,一個類哪些成員的增長,會影響一個類所佔內存的大小?C#有沒有辦法知道一個對象佔多少內存呢?html
第一個問題:很快想到是類的非靜態的字段、屬性。web
第二個問題:首先想到的是sizeof()。安全
下面開始驗證,首先來驗證值類型,驗證代碼以下:app
int size = sizeof (int); //4個字節
注意點:sizeof 運算符僅適用於值類型,而不適用於引用類型。sizeof 運算符只能在不安全代碼塊中使用。以下面的代碼將沒法編譯經過:dom
public struct TestStuct { } int size = sizeof(new TestStuct());
編譯後,提示:ide
錯誤 1 「ConsoleApplication3.TestStuct」沒有預約義的大小,所以 sizeof 只能在不安全的上下文中使用(請考慮使用 System.Runtime.InteropServices.Marshal.SizeOf) 佈局
修改成Marshal.SizeOf方法,改方法返回對象的非託管大小(以字節爲單位)。參數能夠是引用類型或裝箱的值類型。佈局必須是連續的或顯式的。學習
int size = Marshal.SizeOf(new TestStuct()); //1個字節
接下來來驗證引用類型:this
因爲不能做爲非託管結構進行封送處理;沒法計算有意義的大小或偏移量。全部下面的代碼在運行的時候,會拋出異常。url
public class Student { } int size = Marshal.SizeOf(new Student());
須要給Student類,加上一個StructLayoutAttribute,來控制Student類的數據字段的物理佈局。修改代碼爲:
[StructLayout(LayoutKind.Sequential)] public class Student { } int size = Marshal.SizeOf(new Student()); //1個字節
LayoutKind 默認值爲Auto.
結論:
1:對於託管對象是沒有辦法直接獲取到一個對象所佔的內存大小。
2:非託管對象,能夠使用Marshal.SizeOf
3:對內置類型,如int,long,byte等使用sizeof
擴展:
有人提出使用二進制序列化,將一個對象序列化成一個MemoryStream,而後返回MemoryStream.Length,通過驗證是不能夠的。
驗證代碼以下:
[Serializable] public class Student { } private static long GetObjectSize(object o) { using (var stream = new MemoryStream()) { var formatter = new BinaryFormatter(); formatter.Serialize(stream, o); using (var fileStream = new FileStream(@"D:\Student.txt", FileMode.OpenOrCreate, FileAccess.Write)) { var buffer = stream.ToArray(); fileStream.Write(buffer, 0, buffer.Length); fileStream.Flush(); } return stream.Length; } } var student = new Student(); long size = GetObjectSize(student); //139個字節
Student.txt保存的文本信息以下所示,經過文本信息,能夠得知多出來的100多個字節,估計是就是這一串字符串吧。
JConsoleApplication3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null ConsoleApplication3.Student
延伸閱讀:
http://blogs.msdn.com/b/cbrumme/archive/2003/04/15/51326.aspx
原文以下:
We don't expose the managed size of objects because we want to reserve the ability to change the way we lay these things out. For example, on some systems we might align and pack differently. For this to happen, you need to specify tdAutoLayout for the layout mask of your ValueType or Class. If you specify tdExplicitLayout or tdSequentialLayout, the CLR’s freedom to optimize your layout is constrained.
If you are curious to know how big an object happens to be, there are a variety of ways to discover this. You can look in the debugger. For example, Strike or SOS (son-of-strike) shows you how objects are laid out. Or you could allocate two objects and then use unverifiable operations to subtract the addresses. 99.9% of the time, the two objects will be adjacent. You can also use a managed profiler to get a sense of how much memory is consumed by instances of a particular type.
But we don't want to provide an API, because then you could form a dependency over this implementation detail.
Some people have confused the System.Runtime.InteropServices.Marshal.SizeOf() service with this API. However, Marshal.SizeOf reveals the size of an object after it has been marshaled. In other words, it yields the size of the object when converted to an unmanaged representation. These sizes will certainly differ if the CLR’s loader has re-ordered small fields so they can be packed together on a tdAutoLayout type.
出處:http://www.cnblogs.com/supperwu/archive/2013/05/16/3082061.html