垃圾回收器 Dispose 和 Finalize 的互补作用

假如我们程序有两个窗口 Form1、Form2; 当我们关闭一个窗口的时候,会发出一个 终止响应,并将该窗口对象送入终止队列,公共语言运行库的垃圾回收器跟踪着这个对象的生存期,此时就会调用此对象的基类,比如 Form2的Dispose方法,用于销毁对象并收回资源。

如果我们在 Form2 窗体中建立一个关闭后显示 Form1 的窗体,代码如下:

Public Class Form2
    Private Sub Form2_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed
        Frm1.Show()
    End Sub
End Class

这里我们用了 Form2 窗体的 FormClosed 事件可以解决 关闭 Form2 窗体后,Form1 没显示出来而导致程序进入永久运行状体。

然而,大家可能回发现一个问题,如果我们再次点击 Button1 按钮后,Form2 它能原模原样的显示出来吗?

答案是否定的,它不仅不显示,还会导致程序出错:错误如下图:

 

构造函数的作用:

其实方法很简单:

我只要判断一下 Form2 是否被销毁就行了,如果销毁了我们就用 New 函数构造 一个 Form2 窗口实例,这样,我们又能重新把 Form2 窗口给显示出来。

所以我们用 form2.IsDisposed就可以来判断

Public Class Form1
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        If frm2 Is Nothing Or frm2.IsDisposed Then  '判断对象是否被销毁
            frm2 = New Form2()
        End If
        Me.Hide()
        frm2.Show()
    End Sub
End Class
Public Class Form2
    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        Me.Hide()
        frm1.Show()
    End Sub
    Private Sub Form2_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed
        Frm1.Show()
    End Sub
End Class

这下完善多了,两个窗体之间的切换也不会有这么多 别扭的问题了。

对VB.NET的窗体实例的创建与销毁的过程我们来分析一下

一个窗体类,比如 Form1 类是通过调用其基类,就是 Form 类的 New 方法来创建实例、 Dispose方法来销毁实例。

我们来看看 Windows  窗体设计器生成的代码 :

Public Sub New()
    MyBase.New()
     '该调用是 Windows 窗体设计器所必需的。
    InitializeComponent()
    '在 InitializeComponent() 调用之后添加任何初始化
End Sub
    '窗体重写处置以清理组件列表。
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
    If disposing Then
       If Not (components Is Nothing) Then
          components.Dispose()
       End If
    End If
    MyBase.Dispose(disposing)
End Sub

MyBase 关键字的行为类似 于引用类的当前实例的基类的对象变量。

MyBase 常用于访问在派生类中被重写或 隐藏的基类成员。在这段代码中,MyBase 指的当然就是 System.Windows.Forms.Form 类了。构造对象时用的New方法是显式调用的,没什么好解说的。

 

析构函数但善后工作 :

所谓析构:析构函数(destructor) 与构造函数相反,当对象脱离其作用域时(例如对象所在的函数已调用完毕),系统自动执行析构函数。析构函数往往用来做“清理善后” 的工作(例如在建立对象时用new开辟了一片内存空间,应在退出前在析构函数中用delete释放)。

备注                             

  • 不能在结构中定义析构函数,只能对类使用析构函数。 

  • 一个类只能有一个析构函数。                               

  • 无法继承或重载析构函数。                               

  • 无法调用析构函数,它们是被自动调用的。 

  • 析构函数既没有修饰符,也没有参数。

程序员无法控制何时调用析构函数,因为这是由垃圾回收器决定的。  垃圾回收器检查是否存在应用程序不再使用的对象。  如果垃圾回收器认为某个对象符合析构,则调用析构函数(如果有)并回收用来存储此对象的内存。  程序退出时也会调用析构函数。 

可以通过调用 Collect 强制进行垃圾回收,但大多数情况下应避免这样做,因为这样会导致性能问题。

在 vb.net 中,使用名称为 Finalize 的“垃圾清理” Sub 过程类创建析构函数。访问修饰符为 Protected(受保护的 )和  Overrides 关键字。通常变量为 Nothing 时,表示该变量被置空,从内存清除对象,回收对象占用的系统资源。

总之:不要在类的 Finalize 方法中对 Connection、DataReader 或任何其他托管对象调用 CloseCollect Dispose。在终结器中,应该仅释放类直接拥有的非托管资源。如果您的类不拥有任何非托管资源,则不要在类定义中包括 Finalize 方法。

一句话:托管的就不要管,但非托管的你就非管不可

如例:

Module Module1
    Private Class First
        Protected a As Object = New Object  '用 Protected 声明,好让派生类也能方法此变量
        Public Sub New() '无参数的构造函数
            a = "析构函数被调用"
            Console.WriteLine("First 类中的{0}", a)
        End Sub
        Protected Overrides Sub Finalize() '析构函数 
            If a IsNot Nothing Then
                a = Nothing '把引用类型变量清空                 
                GC.Collect() '程序退出后,由 CLR 的垃圾回收机制 GC 自己确认何时清理回收
            End If
        End Sub
    End Class
    Private Class Second
        Inherits First
        Public Sub New()
            Console.WriteLine("Second 类中的{0}", a)
        End Sub
    End Class
    Private Class Third
        Inherits Second
        Public Sub New()
            Console.WriteLine("Third 类中的{0}", a)
        End Sub
    End Class
    Sub Main()
        Dim t As New Third
        Console.Read()
    End Sub
End Module

如图:

 

Form.Dispose 方法是重写自 Control.Dispose 方法的,那么 Control.Dispose 方法的含义又是怎么样的?

它的作用就是:释放由 Control 占用的非托管资源,还可以另外再释放托管资源。

当它参数中的 disposing 为 true 时,则释放 托管资源 和 非托管资源;为 false 则仅释放 非托管资源。

Form 类的 disposing 为 true。在关闭窗体时自动调用dispose的功能是得益于.net的公共语 言运行库,运行库自动处理对象布局和管理对对象的引用,当不再使用对象时释放它们。其生存期以这种方式来管理的对象称为托管数据。

自动内存管理消除内存泄漏以及其他一些常见的编程错误。任何类型的 Dispose 方法都应该释放它拥有的所有资源。它还应该通过调用其父类型的 Dispose 方法释放其基类型拥有的所有资源。该父类型的 Dispose 方法应该释放它拥有的所有资源并同样也调用 其父类型的 Dispose 方法,从而在整个基类型层次结构中传播该模式。要确保始终正确地清理资源,Dispose 方法应该可以被多次安全调用而不引发任何异常。

可是,如果系统问题或应用程序调用上出了问题,不能正常调用Dispose怎么办?

这时,如果通过 Dispose 还释放不干净或没有调用Dispose,系统的垃圾回收器会调用对象的 Finalize 方法进行清除。由于执行 Finalize 方法会大大减损性能,所以我们不 会一开始就用它去进行清除工作。

这是我们能想起了一个重要的问题:“如果总是在模块、类和窗体中定义的全局变量来处理,由于访问范围太大,会不会有安全性的问题?

当然,我们可以试试其他的解决方案。

posted on 2015-11-06 16:00  I_am  阅读(1090)  评论(0编辑  收藏  举报

导航