.Net高级技术:内存相关的深入理解

一、C#与C++的内存处理区别==》类似一个食堂吃饭,一个自己放回盘子,一个是服务员帮忙收拾

1.基本概述:

 C#有GC(Garbage Collection),程序员不再关心内存的释放,只需要在使用内存的时候New就可以了。

C++代码在编写的时候需要手动的释放内存,内存释放不及时的时候会造成内存泄露(Memory Leap)

2.两种处理方式的优缺点:

优点:不再关心内存的释放

缺点:内存不能得到及时的释放

 虽然C#程序员不再关心内存的释放问题,但是还是会出现内存泄露的问题(原因在于GC回收是要看时机的,只有当一个对象不再指向任何引用的时候才可以被回收)

二、GC回收机制

当对象一定不再有用的时候GC就可以将对象回收了。判断一个对象是否一定不再有用的标准就是没有任何的变量指向它。当一个变量设置为null的时候。

Person p1=new Person(“lilei”);//在内存中创建了Person对象
Person p2 = p1;//把p2指向p1指向的对象!这一刻p1指向着leilei,p2就顺着p1找到了lilei
p1=null;//p1不再指向lilei 。lilei不能回收,因此p2还在指着它
p2= new Person(“hanmeimei”);//lilei可以回收,因为没有任何变量指着它。
当没有任何变量指向lilei的时候,就没法再顺着任何的变量把p3指向lilei了。

GC会找合适的时机来回收(回收的时候,所有的线程都会暂停执行等待回收完毕)==》频繁的GC会影响系统的性能

class Program
{
static void Main(string[] args)
{
var p = new {Name="Tom",Value=12 };
Console.WriteLine(p.Name);
p = null;//指示GC可以被回收了
WeakReference wr = new WeakReference(p);//使用WeakRefrence记录指向的对象时候被回收
Console.WriteLine(wr.IsAlive);//查看时候回收(可能被回收)
GC.Collect();//强制让GC回收
Console.WriteLine(wr.IsAlive);
Console.ReadKey();
}
}

三、性能调优工具

使用性能调试工具:CLRProfiler(免费)、.NET.Memory.Profiler。分析对象的创建、回收,分析内存泄露的原因。
使用CLRProfiler看备注中的代码观察GC的情况,TimerLine。
WinDbg(*)成为牛人必备的工具。

四、IDisposable接口

GC只能对托管资源进行回收,对一些非托管资源(数据库连接字符串,文件句柄,流资源,Socket等)无法回收,为解决这一问题,提供了IDisposable接口

实现了IDisposable接口的类的对象使用using关键字的时候,在出其作用范围会自动调用Dispose方式释放资源

注:Dispose()和Close()的区别

Dispose()是键该资源释放;Close()是关闭资源,还是可以被打开的;通常Dispose()会判断有没有Close(),没有的话需要帮助调用Dispose()方法

 

四、string 与StringBuilder对比

string是不变的,因此每次运算都会重新创建一个string对象

大量的字符串相连会产生大量的中间字符串,字符串是对象,对象的产生是慢的,而且会占用大量的内存。所以要避免大量的字符串的连接操作。使用CLR Profiler进行分析

因此对于需要动态拼接字符串的场合(比如创建SQL语句)用StringBuilder来代替string,StringBuilder内部实现了字符串拼接不会有string的缺陷

 

五、CLR (Common Language Runtime)

可以将其理解为一个.NET虚拟机

传统的C++程序是直接向OS申请内存资源,C#程序托管在CLR环境中,申请资源是通过CLR向操作系统申请。

 

六、字符串拘留池

1.由CLR处理:只对字符串类型进行处理

因为字符串的不可变性,大量的使用字符串会占用很多的系统资源。所以值相同的字符串共享同一个实例

string s1 = "aa";
string s2 = "aa";
string s3 = "a" + "a";//一样的
string s4 = new string("aa".ToCharArray());//New出来的字符串一定会创建一个新的实例
//由于字符串拘留池的存在上述产生同一个对象,所以引用的地址是相同的
Console.WriteLine(s1 == s2);//比较的是内容
Console.WriteLine(object.ReferenceEquals(s1,s2));//比较的是引用地址true
Console.WriteLine(object.ReferenceEquals(s1, s3));//true
Console.WriteLine(object.ReferenceEquals(s1, s4));//false

//非字符串对象不会进入字符串拘留池

Point p1 = new Point(2,3);
Point p2 = new Point(2,3);
Console.WriteLine(p1==p2);//比较的是内容 true
Console.WriteLine(object.ReferenceEquals(p1,p2));//比较的是引用 false

 

七、结构体和类的对比:语法是一样的,除了值类型和引用类型的区别(值类型:DateTime int double char Struct :都是继承ValueType类)

structPerson sp1 = new structPerson() { Name="Tom",Age=20};//值类型
structPerson sp2 = sp1;
sp1.Name = "Fracis";
Console.WriteLine(object.ReferenceEquals(sp1,sp2));//false
Console.WriteLine(sp2.Name);//sp1的内容改变不会影响sp2的内容

classPerson cp1 = new classPerson() { Name="Jerry",Age=10};//引用类型
classPerson cp2 = cp1;//
cp1.Name = "Francis";//改变CP1的内容
Console.WriteLine(object.ReferenceEquals(sp1, sp2));//true
Console.WriteLine(cp2.Name);//cp2的内容随之改变
Console.ReadKey();

struct structPerson
{
public string Name { set; get; }
public int Age { set; get; }
}

class classPerson
{
public string Name { set; get; }
public int Age { set; get; }
}

 

八、运算符重载==

上述的Point类实现了==的重载,档X,Y的值分别相等的时候判断是true.

自己编写一个类没有实现==的重载使用==的时候默认比较的是引用地址

class Person
{
public string Name { set; get; }
public int Age { set; get; }
}

Person p1=new Person(){Name="Tom",Age=20};

Person p2=new Person(){Name="Tom",Age=20};

Console.WriteLine(p1 == p2);//由于没有实现重载。此处比较的是引用地址false ==》如何实现比较繁琐
Console.WriteLine(object.ReferenceEquals(p1,p2));//比较的是引用地址false

 

九。浅拷贝和深拷贝

浅层拷贝,就是只拷贝类的第一层成员,而且如果成员是引用类型,则引用同一份。
深层拷贝,把对象引用的所有直接、间接的对象都拷贝一份。完全的一份拷贝。如果实现深层拷贝?需要耐心或者使用IO流

class Program
{
static void Main(string[] args)
{
person p1 = new person() { Name = "Tom", Age = 20, Dog = new Dog() { Name="Piggy"} };
person p2 = (person)p1.Clone();
Console.WriteLine(object.ReferenceEquals(p1,p2));//浅拷贝重新回创建一个对象,引用不一样false
Console.WriteLine(object.ReferenceEquals(p1.Dog,p2.Dog));//浅拷贝成员部分的引用的是同一份,true
Console.ReadKey();
}
}

class person:ICloneable
{
public string Name { set; get; }
public int Age { set; get; }
public Dog Dog { set; get; }

public object Clone()
{
//将成员部分拷贝一份返回一个对象(浅拷贝)
return this.MemberwiseClone();
}
}

class Dog
{
public string Name { set; get; }
}

IO流的深度拷贝将在后序补充:对一个对象序列化再反序列化,返回的就是一个深层的拷贝对象(包括属性的子类对象)

static object DeepCopy(object src)
{
BinaryFormatter Formatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Clone));
using (MemoryStream stream = new MemoryStream())
{
Formatter.Serialize(stream, src);
stream.Position = 0;
return Formatter.Deserialize(stream);
}
}

posted @ 2018-12-24 15:55  Francis_Ray  阅读(205)  评论(0编辑  收藏  举报