代码改变世界

[原创]c#中的内存管理

2007-08-02 13:33  Virus-BeautyCode  阅读(852)  评论(0编辑  收藏  举报

一、
 C#编程的一个优点是程序员不需要担心具体的内存管理,尤其是垃圾收集器会处理所有的内存清理

工作。用户可以得到像C++语言那样的效率,而不需要考虑像在C++中那样内存管理工作的复杂性。虽然不必

手工管理内存,但如果要编写高效的代码,就仍需理解后台发生的事情。

在解除变量时,其顺序总是与给它们分配内存的顺序相反,这就是堆栈的工作方式。

c++中的栈在c#中叫堆栈,c++中的堆在c#中变成堆和托管堆,但是托管堆的工件方式有点像堆栈。

栈的分配是向低地址扩展,而且堆栈中的内存块总是按照4B的倍数进行分配,堆的分配是向高地址扩展。

下一行代码声明了一个TestClass引用,并实例化一个TestClass对象。在这个例子中,需要在堆栈上为

MyClass引用分配空间,同时,也需要在堆上为它分配空间:

  TestClass MyClass = new TestClass();

二、对未托管的资源的释放

1,析构函数,由于垃圾收集器的工作方式,无法确定C#对象的析构函数何时执行。所以,不能在析构函数中

放置需要在某一时刻运行的代码,也不应使用能以任意顺序对不同类实例调用的析构函数。如果对象占用了

宝贵而重要的资源,应尽可能快地释放这些资源,此时就不能等待垃圾收集器来释放了。

2,实现IDisposable接囗,IDisposable接口声明了一个方法Dispose(),它不带参数,返回void。Dispose()

的执行代码显式释放由对象直接使用的所有未托管资源,并在所有实现IDisposable接口的封装对象上调用

Dispose()。这样,Dispose()方法在释放未托管资源时提供了精确的控制。


三、c#中的指针

因为使用指针会带来相关的风险,所以C#只允许在特别标记的代码块中使用指针。标记代码所用的关键字是

unsafe。下面的代码把一个方法标记为unsafe:

unsafe int GetSomeNumber()

{

// code that can use pointers

}

任何方法都可以标记为unsafe—— 无论该方法是否应用了其他修饰符(例如,静态方法、虚拟方法等)。在这

种方法中,unsafe修饰符还会应用到方法的参数上,允许把指针用作参数。还可以把整个类或结构标记为

unsafe,表示所有的成员都是不安全的:

unsafe class MyClass

{

// any method in this class can now use pointers

}

同样,可以把成员标记为unsafe:

class MyClass

{

unsafe int *pX; // declaration of a pointer field in a class

}

也可以把方法中的一个代码块标记为unsafe:

void MyMethod()

{

// code that doesn~t use pointers

unsafe

{

  // unsafe code that uses pointers here

}

// more ~safe~ code that doesn~t use pointers

}

但要注意,不能把局部变量本身标记为unsafe:

int MyMethod()

{

unsafe int *pX; // WRONG

}

如果要使用不安全的局部变量,就需要在方法或不安全的语句块中声明和使用它。在使用指针前还有一步要

完成。C#编译器会拒绝不安全的代码,除非告诉编译器代码包含不安全的代码块。

可以把指针声明为任意一种数据类型—— 即任何预定义的数据类型uint、int和byte等,也可以声明为一个

结构。但是不能把指针声明为一个类或数组,这是因为这么做会使垃圾收集器出现问题。为了正常工作,垃

圾收集器需要知道在堆上创建了什么类实例,它们在什么地方。但如果代码使用指针处理类,将很容易破坏

堆中.NET运行库为垃圾收集器维护的、与类相关的信息。在这里,垃圾收集器可以访问的数据类型称为托管

类型,而指针只能声明为非托管类型,因为垃圾收集器不能处理它们。


四、使用指针优化性能

1、创建基于堆栈的数组

    指针的一个主要应用领域:在堆栈中创建高性能、低系统开销的数组。有时,我们希望创建一个使用时

间比较短的高性能数组,不希望有引用对象的系统开销。而使用指针就可以做到,但只能用于一维数组。

为了创建一个高性能的数组,需要使用另一个关键字:stackalloc。stackalloc命令指示.NET运行库分配堆

栈上一定量的内存。在调用它时,需要为它提供两条信息:

● 要存储的数据类型

● 需要存储的数据个数。

例如,分配足够的内存,以存储10个decimal数据,可以编写下面的代码:

decimal* pDecimals = stackalloc decimal [10];

同样,可以用表达式*(pDoubles+X)或者pDoubles[X]获得数组中下标为X的元素。