C# 能否獲取一個對象所占內存的大小?


    今日,在項目重構的時候忽然想到一個問題,一個類哪些成員的增加,會影響一個類所占內存的大小?C#有沒有辦法知道一個對象占多少內存呢?

     第一個問題:很快想到是類的非靜態的字段、屬性。

     第二個問題:首先想到的是sizeof()。

下面開始驗證,首先來驗證值類型,驗證代碼如下:

int size = sizeof (int); //4個字節

注意點:sizeof 運算符僅適用於值類型,而不適用於引用類型。sizeof 運算符只能在不安全代碼塊中使用。如下面的代碼將無法編譯通過:

public struct TestStuct
    {

    }

int size = sizeof(new TestStuct());

編譯后,提示:

錯誤 1 “ConsoleApplication3.TestStuct”沒有預定義的大小,因此 sizeof 只能在不安全的上下文中使用(請考慮使用 System.Runtime.InteropServices.Marshal.SizeOf) 

修改為Marshal.SizeOf方法,改方法返回對象的非托管大小(以字節為單位)。參數可以是引用類型或裝箱的值類型。布局必須是連續的或顯式的。

int size = Marshal.SizeOf(new TestStuct()); //1個字節

接下來來驗證引用類型:

由於不能作為非托管結構進行封送處理;無法計算有意義的大小或偏移量。所有下面的代碼在運行的時候,會拋出異常。

 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.




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM