今日,在项目重构的时候忽然想到一个问题,一个类哪些成员的增加,会影响一个类所占内存的大小?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.

 
#1楼 2013-05-16 16:23 jintianzhang 
可以考虑GC.GetTotalMemory方法

  

#2楼[楼主] 2013-05-16 16:55 supperwu 

@jintianzhang
如何用?请指教。

  

#3楼[楼主] 2013-05-16 16:59 supperwu 

@jintianzhang
var student = new Student();
long size = GC.GetTotalMemory(true); //95028个字节

Student没有任何成员。


  

#4楼[楼主] 2013-05-16 17:11 supperwu 

@jintianzhang
GC.Collect(0);
var student = new Student();
long n = GC.GetTotalMemory(true);
student = null;
GC.Collect(0); 
long m = GC.GetTotalMemory(true);
Console.WriteLine(n - m); //12个字节

  

#5楼 2013-05-16 20:50 AlexanderYao 

[StructLayout(LayoutKind.Sequential)]
呵呵,学习了!

  

#6楼[楼主] 2013-05-16 22:44 supperwu 

@AlexanderYao
谢谢支持!多多赐教!

  

#7楼 2013-05-16 23:58 jintianzhang 

@supperwu
GC.Collect();
GC.WaitForFullGCComplete();
long start = GC.GetTotalMemory(true);
Student student = new Student();
GC.Collect();
GC.WaitForFullGCComplete();
long end = GC.GetTotalMemory(true);
Console.WriteLine(end-start);//12
这是比较接近的值

  

#8楼 2013-06-16 21:23 永远的阿哲 

学习了!

  

#9楼 2013-09-09 09:51 zwq_sj 

整个系统是单线程环境下时才能使用GC.GetTotalMemory

  

#10楼 2014-01-09 10:11 halon 

enum呢?

出处:http://www.cnblogs.com/supperwu/archive/2013/05/16/3082061.html

最新文章

  1. hadoop之快照
  2. ctrl+z暂停任务
  3. glsl计算sprite的亮度饱和度对比度
  4. pureftpd安装配置-pureftp参数详解(一)
  5. eclipse中java项目的build path详解(转载)
  6. kd树的构建以及搜索
  7. mysql 变量set
  8. LeetCode OJ 219.Contains Duplicate 2
  9. Android 应用程序崩溃日志捕捉
  10. MySQL列类型选择
  11. 使用WPF动态显示CPU使用率
  12. vue 安卓5.1 ios9 兼容性 白屏问题
  13. rest规范是什么?
  14. Gradle实现编译差异
  15. Locust环境搭建及应用-hc课堂笔记
  16. 【刷题】LOJ 6013 「网络流 24 题」负载平衡
  17. 安装 neo4j 在 .../bin 目录下使用 ./neo4j 没反应 和 从csv 导入数据到neo4j
  18. Android基础之使用Fragment控制切换多个页面[转]
  19. Cocos2d-三维拾取Ray-AABB碰撞检测算法【转】
  20. 对opencv MeanShift 融合矩形框的改进

热门文章

  1. Es+kafka搭建日志存储查询系统(设计)
  2. ng-深度学习-课程笔记-9: 机器学习策略1(Week1)
  3. Eclipse中如何查看使用的JDK版本
  4. python webdriver 登录163邮箱发邮件加附件, 外加数据和程序分离,配置文件的方式
  5. AVAudioFoundation(1):使用 AVAsset
  6. Centos75 firewalld防火墙
  7. duilib : 滑动显示的窗口实现以及 悬浮窗 (转载)
  8. IDEA类和方法注释模板设置
  9. linux kernel 提示VFS: Cannot open root device "mmcblk0p1" or unknown-block(179,1): error -19等信息后发生panic
  10. js的重载