c#浅拷贝/深拷贝和内存分配

来源:互联网 发布:owncloud支持windows 编辑:程序博客网 时间:2024/06/06 18:57
  1. 值类型和引用类型
    概念:值类型直接存储其值,而引用类型存储对其值的引用。
    引用类型:基类为Objcet
    值类型:均隐式派生自System.ValueType
    • 值类型变量声明后,不管是否已经赋值,编译器为其分配内存。
    • 引用类型当声明一个类时,只在栈中分配一小片内存用于容纳一个地址,而此时并没有为其分配堆上的内存空间。当使用 new 创建一个类的实例时,分配堆上的空间,并把堆上空间的地址保存到栈上分配的小片空间中。
    • 值类型的实例通常是在线程栈上分配的(静态分配),但是在某些情形下可以存储在堆中。
    • 引用类型的对象总是在进程堆中分配(动态分配)。
  2. 内存分配
    CLR内存分配区域:线程堆栈(栈)、GC堆、大对象堆LOH

一、线程堆栈(栈)
用于分配值类型实例。栈由操作系统进行管理,不受GC管理,当值类型不在其作用域(主要是指其所在函数内)时,其所占栈空间自动释放。栈的执行效率是非常高的。

二、GC堆(堆)
用于分配小对象实例。所谓小对象就是大小小于85000字节的实例对象。GC堆分三代垃圾进行管理,当进行GC操作(垃圾回收)时,垃圾收集器会对GC堆进行压缩回收。具体的GC操作(略)。

三、大对象堆(LOH)
用于分配大对象实例。大对象就是大小小于85000字节的实例对象。大对象分配在LOH上,不受GC控制,不会被压缩,只有在完全GC回收时才会被回收。

注意

a.栈的分配是向低地址扩展,而堆的分配是向高地址扩展。
b.值参数与引用参数的本质,值参数是对栈中数据的拷贝,引用参数则是对栈地址的引用。当值参数为某引用对象时,可以改变该引用对象某些值,但不能将值改变成新对象的地址。
c.堆中的对象都有同步块索引(占4个字节)、类型句柄(占4个字节)
d.静态字段、静态方法不和方法所分配的方式与区域—–方法表
e.值类型变量的值存放在变量内部,而引用类型变量的值存放在堆上,变量本身存放一个指向堆中的值的引用。

    public class RefrenceAndValueType    {        public static void Run()        {            Struct_ValueType sValueType1 = new Struct_ValueType();            sValueType1.IntValue = 1;            sValueType1.StringObj = "1";            sValueType1.class_T = new Class_T();            Struct_ValueType sValueType2 = sValueType1;            sValueType2.IntValue = 2;            sValueType2.StringObj = "2";            sValueType2.class_T.INT_Value = 2;            sValueType1.class_T.String_Name = "李四";            Console.WriteLine(string.Format("sValueType1.IntValue: {0} ; sValueType2.IntValue: {1} ", sValueType1.IntValue, sValueType2.IntValue));            Console.WriteLine(string.Format("sValueType1.StringObj: {0} ; sValueType2.StringObj: {1} ", sValueType1.StringObj, sValueType2.StringObj));            Console.WriteLine(string.Format("sValueType1.class_T.INT_Value: {0} ; sValueType2.class_T.INT_Value: {1} ", sValueType1.class_T.INT_Value, sValueType2.class_T.INT_Value));            Console.WriteLine(string.Format("sValueType1.class_T.String_Name: {0} ; sValueType2.class_T.String_Name: {1} ", sValueType1.class_T.String_Name, sValueType2.class_T.String_Name));            Class_Refrence cRefrence1 = new Class_Refrence();            cRefrence1.IntValue = 1;            cRefrence1.StringObj = "1";            cRefrence1.StructValue = new Struct_ValueType();            cRefrence1.StructValue.IntValue = 1;            cRefrence1.StructValue.StringObj = "1";            Class_Refrence cRefrence2 = cRefrence1;            cRefrence2.IntValue = 2;            cRefrence2.StringObj = "2";            cRefrence2.StructValue.IntValue = 2;            cRefrence2.StructValue.StringObj = "2";            Console.WriteLine(string.Format("cRefrence1.IntValue: {0} ; cRefrence1.IntValue: {1} ", cRefrence1.IntValue, cRefrence2.IntValue));            Console.WriteLine(string.Format("cRefrence1.StringObj: {0} ; cRefrence1.StringObj: {1} ", cRefrence1.StringObj, cRefrence2.StringObj));            Console.WriteLine(string.Format("cRefrence1.StructValue.IntValue: {0} ;  cRefrence2.StructValue.IntValue: {1} ", cRefrence1.StructValue.IntValue, cRefrence2.StructValue.IntValue));            Console.WriteLine(string.Format("cRefrence1.StructValue.StringObj: {0} ; cRefrence2.StructValue.StringObj: {1} ", cRefrence1.StructValue.StringObj, cRefrence2.StructValue.StringObj));        }    }    public class Class_T    {        public int INT_Value = 1;        public string String_Name = "张三";    }    public struct Struct_ValueType    {        public int IntValue;        public string StringObj;        public Class_T class_T;    }    public class Class_Refrence    {        public int IntValue;        public string StringObj;        public Struct_ValueType StructValue;    }

这里写图片描述
值类型是完全拷贝,引用类型是对地址的拷贝。


*
3. 字符串的内存分配与驻留池(string.Intern)
string在初始化后不可变,每次操作都会创建新的对象重新分配内存。在赋值为常量字符串时,相同常量字符串值的string地址一致。可通过string.Intern(string)检查是否有常量字符串值,有则返回常量字符串值。
4. 深拷贝和浅拷贝
深拷贝:指的是拷贝一个对象时,不仅仅把对象的引用进行复制,还把该对象引用的值也一起拷贝。这样进行深拷贝后的拷贝对象就和源对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。
浅拷贝:指的是拷贝一个对象时,仅仅拷贝对象的引用进行拷贝,但是拷贝对象和源对象还是引用同一份实体。此时,其中一个对象的改变都会影响到另一个对象。
浅拷贝实现: object.MemberwiseClone()
.NET中值类型默认是深拷贝的,而对于引用类型,默认实现的是浅拷贝。
深拷贝实现:反射(当有互相引用时可能会有问题)、序列化/反序列化(xml/二进制/DataContractSerializer)、表达式树
参考:
C# 浅析值类型与引用类型的内存分配
C#中字符串的内存分配与驻留池
C#内存分配学习
深入解析深拷贝和浅拷贝
C#的两种类据类型:值类型和引用类型

原创粉丝点击