本帖最后由 lucky5168 于 2015-02-21 16:54:40 编辑

解决方案 »

  1.   

    应该是你代码的问题首先判断下CONN是否为空
      

  2.   

    既然用C#就好好看看C#的垃圾回收/资源释放的方式和原理。用着C的概念来写C#程序还大叫这是C#的缺陷。你也真是个人才。
      

  3.   

    难道java,C++中释放前不需要判断要释放的对象是否存在??
      

  4.   

    .NET代码中会自动释放对象,代码如下,这个是从组件基类 Component 中继承的
     
    public void Dispose() {
                Dispose(true);
                GC.SuppressFinalize(this);
            }
    所以你再在析构函数中执行的时候CONN已经为空了,C#中一般不提倡写析构
      

  5.   

    如楼上所述确实需要了解某种语言的规则,因此,在学习一种不同编程语言时,会去了解相关部分,然后去判断存在的问题,至少会去MSDN上搜索一番.
      

  6.   

    xu56180825: 应该是你代码的问题首先判断下CONN是否为空
    [A]:你的答案不靠谱。我的代码没问题,在程序中的构造函数中已经完成对conn构建,而且自此以后从没置空过。wjq:既然用C#就好好看看C#的垃圾回收/资源释放的方式和原理。用着C的概念来写C#程序还大叫这是C#的缺陷。你也真是个人才。
    [A]:如果C#有其特殊的垃圾回收/资源释放的方式和原理,那至少也应该遵循标准的面向对象的操作方法。微软自建的一套标准是进步了? 还是在技术上比C++,Java次一等?starfd: 难道java,C++中释放前不需要判断要释放的对象是否存在?? 
    [A]:是的, C++,Java在析构函数被调用的时候,类成员变量是不会被系统销毁释放的。既然C++,Java已经建立了一套面向对象的操作标准,为什么微软要自搞一套超复杂的操作标准困惑程序员?liucqa: 代码  写错了
    [A]:你的回答严重不靠谱xu56180825: .NET代码中会自动释放对象,代码如下...,这个是从组件基类 Component 中继承的,所以你再在析构函数中执行的时候CONN已经为空了,C#中一般不提倡写析构。
    [A]:您的回答是所有回复中最靠谱的。但您的回复也不能解决我的问题,因为conn是数据库连接对象,必须要做conn.Close(), 而不是简单的Dispose()和置空。否则会发生数据库连接用尽,系统崩溃的恶果。liucqa: 析构函数一般在C#调用了非托管资源之后才需要自己写,一般情况下是不需要自己写的
    [A]:请问在程序中的conn是托管资源,还是非托管资源? 如果不写析构函数,系统会智能地知道conn是数据库连接吗?并且自动做conn.Close()操作吗?
      

  7.   


    你说你熟悉一些 c++ 这我相信,说你熟悉 java 我觉得未必。在析构函数中,不“释放”释放什么托管资源,指使用来释放非托管资源的。对于托管资源也根本不存在什么“释放内存”这一说,释放内存是GC做的事情,你根本不知道怎么释放。你写的对托管资源的close跟dispose调用这两个东西也根本就跟“释放内存”毫无关系。在你的程序中,哪有什么非托管资源的影子?你写析构函数干什么?
    在过你真的需要及时关闭数据库连接,那么你可以写public class AccessDb : IDisposable
    {
        string connstr = "Provider=Microsoft.Jet.OLEDB.4.0 ;Data Source=C:\\test.mdb";
        System.Data.OleDb.OleDbConnection conn = null;
        public AccessDb()
        {
            conn = new System.Data.OleDb.OleDbConnection(connstr);
            conn.Open();
        }    public void Dispose()
        {
            conn.Dispose();
        }
    }public partial class Form1 : Form
    {
        AccessDb db = null;
        public Form1()
        {
            InitializeComponent();
            db = new AccessDb();
            db.Dispose();
        }
    }//    托管系统里的析构函数跟你所知道的c++的析构函数根本就是两回事。托管里的析构函数,被GC调用,而不是你的代码调用!它被调用之前有一大堆非常复杂的清理工作,然后把所有析构函数放到Finally队列里边去随机无序地处理,因此如果你写public class TA
    {
        ~TA()
        {
            int x = 0;
        }
    }public class AccessDb
    {
        TA a;
        string connstr = "Provider=Microsoft.Jet.OLEDB.4.0 ;Data Source=C:\\tmp\\finance.mdb";
        System.Data.OleDb.OleDbConnection conn = null;
        public AccessDb()
        {
            conn = new System.Data.OleDb.OleDbConnection(connstr);
            conn.Open();
            a = new TA();
        }    ~AccessDb()
        {
            conn.Close();
            conn.Dispose();
        }
    }
    你就会看到首先调用的是 TA 的析构函数,然后才是 AccessDb 的!由于你不知道这个,因此我相信你也不了解托管系统的析构函数,包括说,你肯定也不了解 java 的析构函数。
      

  8.   

    不“释放”释放什么托管资源,指使用来释放非托管资源的  -->  不“释放”什么托管资源,只使用来释放非托管资源的在自定义托管代码中,基本上不使用托管函数,而是使用 IDisposeable 接口,使用 using(){} 语法结构。析构函数纯粹是为了c++移植的部分代码准备的。纯粹托管代码如果写析构函数,对性能有害。所以一般来说类库中的各种类型往往都会使用 GC.SuppressFinalize(this) 代码来禁止调用析构函数
      

  9.   

    如果你有什么“数据路连接”必须要提前释放(而不是等着GC开始的时候自动对你的conn引用的对象调用析构函数),那么你就要手动调用Dispose()方法。这个手动调用Dispose()的机制才跟你的c++程序的思路相符的。实际上你如果那么担心 conn.Dispose() 方法没有及时被调用,那么你应该在 Form 的 Closed 事件中手动“做一些善后工作”!就你的代码而言,如果你不调用Close或者Dispose(或者干脆二者都重复调用)会有什么bug吗?如果你测试不出来bug,就不要这样写代码。因为在GC开始工作时会自动释放conn引用的对象的资源,用不着你来写close或者dispose代码。如果测不出bug,不要担心什么“执行一些释放资源的操作是很普通的”。告诉你一个事实:在托管代码程序中,不去手动释放资源的操作,才是很普通的!
      

  10.   

    c++没有真正稍微正规的垃圾回收机制,除非你非常小心翼翼地维护对各个对象的 delete() 调用代码,否则你随随便便就能搞出一堆“内存溢出”的程序来了。没有垃圾回收机制和有垃圾回收机制,在析构操作是不一样的,就好像是小龙虾跟大龙虾的味道的差别一样。不过记住一条就对了:如果你写托管代码,你不需要写析构函数!
      

  11.   

    数据库对像 不是必须要做close ,你可以看这个类的原码dispose 里面有包含了调用了Close这个方法了,如果你做了Close可以不必做Dispose,Dispose这个交给GC去处理,或你做了Dispose,就不必做Close。
    之所以出了问题,是因为底层的拉圾回收有个执行的顺序,你在Form窗体里定义了这个AccessDb类,类里定义的析构,GC的做法:
     在 System.WeakReference.set_Target(Object value) 这一句 
           在 System.Data.ProviderBase.DbConnectionInternal.CloseConnection(DbConnection owningObject, DbConnectionFactory connectionFactory)
           在 System.Data.OleDb.OleDbConnection.Close()
           在 System.Data.OleDb.OleDbConnection.Dispose(Boolean disposing)
           在 System.ComponentModel.Component.Dispose()
           在 WindowsFormsApplication1.AccessDb.Finalize()
    在执行GC 时, WeakReference的实列 this._owningObject.这个对象,他是序列化对象,是托管资源,就是说在执行GC执行时先把_owningObject 这个内存里的对象先回收了,然后再去执行析构函数里面的close方法,造成了变成了空引用了。就出现了上面的错误了。
      

  12.   


    SqlConnection由于实现了IDisposable接口,所以在垃圾回收阶段,会通过Dispose模式调用Close方法,但是这个时间我们是无法确定的,这一切都是由GC来确定的,所以为了保证在其他时候连接没有被占用,用完就关闭是个好习惯。你得先理解什么是IDisposable接口,不能用C++的路子来思考托管代码。
      

  13.   

    using(OleDbConnection connection = new OleDbConnection(connectionString)) 

    connection.Open(); 
    //一段代码 

    通常我们这么用。请参考下面的链接
    http://www.cnblogs.com/liuhaorain/archive/2012/02/15/2349886.html
      

  14.   

    http://www.cnblogs.com/freeton/archive/2012/08/19/2646511.html
    再看看这个
      

  15.   


    我做程序牢记一条圣经:数据库连接是宝贵的不可再生的不受托管的资源,必须手工显式释放!也就是你自己拉的屎,你自己必须擦干净,否则必死无疑!怎么着?到了C#,数据库连接就变成托管资源了?C#很智能?能把数据库连接作为托管资源智能地释放?不用程序员Care了?
    实践是检验真理的唯一标准。马上编程测试,测试程序如下:using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;namespace TestDb4
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
                //如果数据库不存在表cache,可以用如下语句生成一个表
                //AccessDb db=new AccessDb();
                //db.UpdateData("create table Cache(id int primary key,log varchar(50));");
            }        private void ButtonTest_Click(object sender, EventArgs e)
            {
                int i, testTimes = 60000;
                for (i = 0; i < testTimes; i++)
                {
                    try
                    {
                        AccessDb db = new AccessDb();
                        int flag=db.UpdateData("delete from Cache");
                        if(flag<0)
                        {
                            string sError = "Error in i=" + i + ",sql failed";
                            MessageBox.Show(sError);
                            Console.WriteLine(sError);
                            break;
                        }
                    }
                    catch (Exception ex)
                    {
                        string sError = "Error in i=" + i + ",msg=" + ex.Message;
                        MessageBox.Show(sError);
                        Console.WriteLine(sError);
                        break;
                    }
                }
                if (i >= testTimes)
                    MessageBox.Show("Succeed!");
            }
        }    public class AccessDb
        {
            string connstr = "Provider=Microsoft.Jet.OLEDB.4.0 ;Data Source=C:\\tmp\\finance.mdb";
            System.Data.OleDb.OleDbConnection conn = null;
            public AccessDb()
            {
                conn = new System.Data.OleDb.OleDbConnection(connstr);
                conn.Open();
            }        /*
                    ~AccessDb()
                    {
                        //UpdateData("delete from Cache");
                        conn.Close();
                        conn.Dispose();
                    }
            */
            public int UpdateData(string sSQL)
            {
                int iResult = 0;
                try
                {
                    System.Data.OleDb.OleDbCommand comm = new System.Data.OleDb.OleDbCommand(sSQL, conn);
                    iResult = comm.ExecuteNonQuery();
                    //comm.Dispose();
                }
                catch (Exception ex)
                {
                    Console.WriteLine("sql=" + sSQL + ",error=" + ex.Message);
                    iResult = -1;
                }
                return (iResult);
            }
        }
    }
    启动测试程序,在程序中点击“Test”按钮后就开始测试,根本做不到60000次循环。做到第64次时数据库资源就被耗尽并且报错了(报错信息:Error in i=64,msg=未指定的错误)。这说明在C#里数据库连接同样也是宝贵的不可再生的不受托管的资源。
      

  16.   

    确实无解,可能是CONN的某些弱引用对象已经被置空了,硬要说是BUG也可以,但MSDN中已经说了不要在析构中调用CLOSE或者Dispose,但未找到具体原因
      

  17.   

    但可以确定对象没被销毁,我已经测试了,CONN对象还是存在的,说CONN对象被释放是错误的,但CONN中其某弱引用对象被置空了
      

  18.   

    首先我说的被销毁是指被Dispose过了,不知你理解的销毁是什么意思。的确是被销毁了,不信你自己做实验看看。程序运行到析构函数的时候报错的。也就是程序无法运行析构函数里的第一条语句--“conn.Close();”。conn对象在析构函数被调用之前就被系统(非本人代码)Dispose了,所以做不成“conn.Close();”。
      

  19.   

    conn 在析构函数里没有还没被销毁,出错了原因是OledbConnection 里面this 他本身的这个类,赋值给了 一个序列化对象WeakReference 就是这个,在析构函数执行时,先被拉圾回收器给回收了,造成上面说的益常,楼主说的应在里面判断 if( this._owningObject!=null)这个 
      

  20.   

    internal virtual void CloseConnection(DbConnection owningObject, DbConnectionFactory connectionFactory)
    {
        Bid.PoolerTrace("<prov.DbConnectionInternal.CloseConnection|RES|CPOOL> %d# Closing.\n", this.ObjectID);
        if (connectionFactory.SetInnerConnectionFrom(owningObject, DbConnectionOpenBusy.SingletonInstance, this))
        {
            try
            {
                DbConnectionPool pool = this.Pool;
                Transaction enlistedTransaction = this.EnlistedTransaction;
                if ((null != enlistedTransaction) && (enlistedTransaction.TransactionInformation.Status != TransactionStatus.Active))
                {
                    this.DetachTransaction(enlistedTransaction);
                }
                if (pool != null)
                {
                    pool.PutObject(this, owningObject);
                }
                else
                {
                    this.Deactivate();
                    this.PerformanceCounters.HardDisconnectsPerSecond.Increment();
                    this._owningObject.Target = null;
                    if (this.IsTransactionRoot)
                    {
                        this.SetInStasis();
                    }
                    else
                    {
                        this.PerformanceCounters.NumberOfNonPooledConnections.Decrement();
                        if (base.GetType() != typeof(SqlInternalConnectionSmi))
                        {
                            this.Dispose();
                        }
                    }
                }
            }
            finally
            {
                connectionFactory.SetInnerConnectionEvent(owningObject, DbConnectionClosedPreviouslyOpened.SingletonInstance);
            }
        }
    }  
      internal virtual void CloseConnection(DbConnection owningObject, DbConnectionFactory connectionFactory)
    {
        Bid.PoolerTrace("<prov.DbConnectionInternal.CloseConnection|RES|CPOOL> %d# Closing.\n", this.ObjectID);
        if (connectionFactory.SetInnerConnectionFrom(owningObject, DbConnectionOpenBusy.SingletonInstance, this))
        {
            try
            {
                DbConnectionPool pool = this.Pool;
                Transaction enlistedTransaction = this.EnlistedTransaction;
                if ((null != enlistedTransaction) && (enlistedTransaction.TransactionInformation.Status != TransactionStatus.Active))
                {
                    this.DetachTransaction(enlistedTransaction);
                }
                if (pool != null)
                {
                    pool.PutObject(this, owningObject);
                }
                else
                {
                    this.Deactivate();
                    this.PerformanceCounters.HardDisconnectsPerSecond.Increment();
                    this._owningObject.Target = null;  <----这句出益常了, 因为_owningObject 这个已被拉圾回收给销毁了。
                    if (this.IsTransactionRoot)
                    {
                        this.SetInStasis();
                    }
                    else
                    {
                        this.PerformanceCounters.NumberOfNonPooledConnections.Decrement();
                        if (base.GetType() != typeof(SqlInternalConnectionSmi))
                        {
                            this.Dispose();
                        }
                    }
                }
            }
            finally
            {
                connectionFactory.SetInnerConnectionEvent(owningObject, DbConnectionClosedPreviouslyOpened.SingletonInstance);
            }
        }

      

  21.   

    谢谢您的见解。不过做了更多的测试后(请看我19楼的帖子),现在觉得在析构函数中释放数据库连接资源不是一个好办法,因为C#的垃圾回收机制有点懒,不会马上回收无效的资源。最佳的访问数据库的方法是随用随放,不依赖C#的回收机制。也就是需要时才去获得数据库的连接,一旦用完立即释放。下面是修改后的访问数据库的类,这个类再也不会造成数据库资源的泄漏了。
     
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;namespace TestDb4
    {
        public class AccessDb
        {
            string connstr = "Provider=Microsoft.Jet.OLEDB.4.0 ;Data Source=C:\\tmp\\finance.mdb";
            //System.Data.OleDb.OleDbConnection conn = null;
            public AccessDb()
            {
                //conn = new System.Data.OleDb.OleDbConnection(connstr);
                //conn.Open();
            }        /*
            ~AccessDb()
            {
                //UpdateData("delete from Cache");
                //conn.Close();
                //conn.Dispose();
            }
            */        private System.Data.OleDb.OleDbConnection GetConnection()
            {
                System.Data.OleDb.OleDbConnection conn = new System.Data.OleDb.OleDbConnection(connstr);
                conn.Open();
                return(conn);
            }        public int UpdateData(string sSQL)
            {
                int iResult = 0;
                System.Data.OleDb.OleDbConnection conn = null;
                System.Data.OleDb.OleDbCommand cmd = null;
                try
                {
                    conn = GetConnection();
                    cmd = new System.Data.OleDb.OleDbCommand(sSQL, conn);
                    iResult = cmd.ExecuteNonQuery();
                }
                catch (Exception ex)
                {
                    Console.WriteLine("sql=" + sSQL + ",error=" + ex.Message);
                    iResult = -1;
                }
                finally
                {
                    CloseConnection(conn,cmd);
                }
                return (iResult);
            }        public void CloseConnection(System.Data.OleDb.OleDbConnection conn, System.Data.OleDb.OleDbCommand cmd)
            {
                try
                {
                    if (cmd != null)
                    {
                        cmd.Dispose();
                    }
                }
                catch (Exception) { }
                try
                {
                    if (conn != null)
                    {
                        conn.Dispose();
                    }
                }
                catch (Exception) { }
            }
        }
    }没办法,C#的回收机制存在重大缺陷,只有绕过它才能产生没有Bug的代码。
      

  22.   

    谢谢您的见解。不过做了更多的测试后(请看我19楼的帖子),现在觉得在析构函数中释放数据库连接资源不是一个好办法,因为C#的垃圾回收机制有点懒,不会马上回收无效的资源。最佳的访问数据库的方法是随用随放,不依赖C#的回收机制。也就是需要时才去获得数据库的连接,一旦用完立即释放。下面是修改后的访问数据库的类,这个类再也不会造成数据库资源的泄漏了。
     
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;namespace TestDb4
    {
        public class AccessDb
        {
            string connstr = "Provider=Microsoft.Jet.OLEDB.4.0 ;Data Source=C:\\tmp\\finance.mdb";
            //System.Data.OleDb.OleDbConnection conn = null;
            public AccessDb()
            {
                //conn = new System.Data.OleDb.OleDbConnection(connstr);
                //conn.Open();
            }        /*
            ~AccessDb()
            {
                //UpdateData("delete from Cache");
                //conn.Close();
                //conn.Dispose();
            }
            */        private System.Data.OleDb.OleDbConnection GetConnection()
            {
                System.Data.OleDb.OleDbConnection conn = new System.Data.OleDb.OleDbConnection(connstr);
                conn.Open();
                return(conn);
            }        public int UpdateData(string sSQL)
            {
                int iResult = 0;
                System.Data.OleDb.OleDbConnection conn = null;
                System.Data.OleDb.OleDbCommand cmd = null;
                try
                {
                    conn = GetConnection();
                    cmd = new System.Data.OleDb.OleDbCommand(sSQL, conn);
                    iResult = cmd.ExecuteNonQuery();
                }
                catch (Exception ex)
                {
                    Console.WriteLine("sql=" + sSQL + ",error=" + ex.Message);
                    iResult = -1;
                }
                finally
                {
                    CloseConnection(conn,cmd);
                }
                return (iResult);
            }        public void CloseConnection(System.Data.OleDb.OleDbConnection conn, System.Data.OleDb.OleDbCommand cmd)
            {
                try
                {
                    if (cmd != null)
                    {
                        cmd.Dispose();
                    }
                }
                catch (Exception) { }
                try
                {
                    if (conn != null)
                    {
                        conn.Dispose();
                    }
                }
                catch (Exception) { }
            }
        }
    }没办法,C#的回收机制存在重大缺陷,只有绕过它才能产生没有Bug的代码。不是C#回收存在重大缺陷,他的回收几制是没问题的,是刚好你所用的那个OledbConnecion类里面判断出了问题,
    “因为C#的垃圾回收机制有点懒,不会马上回收无效的资源”这个不是C#的回收懒,回收会影响到性能,所以GC只有在必须要回收时才去回收,这是GC的回收策略,这样在影响系统资源最小的情况下回收。算你上面用了显式的Dispose,GC也不会马上自动回收内存的,也是要经过一段的条件后才处理的。除非你Dispose后自己马上手动执行了 GC.Collect(); 
      

  23.   

    这个其它也不叫BUG,你可以在析构函数中加入判断
    if(conn!=null)
    至于这个问题的发生应该是在你的析构函数之前C#已经将conn回收了。
      

  24.   

    IDisposable接口就是处理你遇到的问题的,不要什么都往析构函数里面塞托管代码和非托管代码的析构函数意义不完全相同的。
      

  25.   

    这个不是BUG,只是程序设计者有意而为之的!!
    因为.NET环境并不能保证析构操作的确切时间,因此不能将C++中的做法在C#中实现。在C#中,析构迟早都会执行,不过其执行时机却无法预料;
    以上摘自C#高效编程
      

  26.   

    .net 想给大家一个十分完美的世界,但是哪有那么容易,为此它提供了idispose接口,来给你们管理它实在管理不了的东西
      

  27.   


            ~AccessDb()
            {
                conn.Close();
                conn.Dispose();
            }这里可能是.net Bug 你确用这个BUG造就了一个更大的BUG.
      

  28.   

    When you are not sure if an object is existed, null check should be the first thing to do either with Java or C#. In fact, many bugs made by junior developer just like what you did, invoking a property or a method on an no longer existed object which should be definitely avoided. 
      

  29.   

    Sorry for the confusing, I mean #30 is correct. 
    When you are not sure if an object is existed, null check should be the first thing to do either with Java or C#. In fact, many bugs made by junior developer just like what lucky5168 did, invoking a property or a method on an no longer existed object which should be definitely avoided.
      

  30.   

    Sorry for the confusing, I mean #30 is correct. 
    When you are not sure if an object is existed, null check should be the first thing to do either with Java or C#. In fact, many bugs made by junior developer just like what lucky5168 did, invoking a property or a method on an no longer existed object which should be definitely avoided.

    Lao da please say ren hua....
    用翻译机很累的。
      

  31.   

    你想用c#,却遵行java的思想,然后说:c#错了。错的不是你,是世界
      

  32.   


    SqlConnection由于实现了IDisposable接口,所以在垃圾回收阶段,会通过Dispose模式调用Close方法,但是这个时间我们是无法确定的,这一切都是由GC来确定的,所以为了保证在其他时候连接没有被占用,用完就关闭是个好习惯。你得先理解什么是IDisposable接口,不能用C++的路子来思考托管代码。我对这个习惯有看法,我认为这个要看软件的架构和应用什么数据库。上文中用Access数据库没有连接池在使用ODBCConnection.Close时就关闭了。但是SQL Server即使调用了SQLConnection.Close也不是真正的Logout了,还是要等到Dispose后GC回收了才断开,而且,下一个SQLConnection.Open的时候,如果前一个已经Close了就会接续使用而不是频繁的打开关闭数据库连接。这个从数据库后台抓一下Profiler就看得到了,它会自动发“exec sp_reset_connection”这个语句的来重置连接的。
    所以,调用SQLconnection.close是需要的,但是手工调用Dispose就画蛇添足了。交给GC来处理就好了。而且,调用了Close只是表示把连接归还给了连接池,并没有真正的断开。
      

  33.   

    构造函数实例化之后,到析构函数被调用,conn应该不为null
    你的测试代码显示,可能是.net的bug;谁知道呢,根据msdn的解释,可能微软就是这么设计的
    https://msdn.microsoft.com/zh-cn/library/66x5fx1b.aspx
      

  34.   

    要释放资源应该继承自IDispose接口
      

  35.   

    析构函数一般确实很少用。。
    GC机制虽然有不及时的情况。但是对一般程序而言,自动释放资源可以满足。
    如果要自己释放,可以继承IDispose接口 去实现。
      

  36.   

    针对非托管资源释有IDisposable接口 
    回收器不支持这个接口,  不必担心为空 并且可以访问其他对象操作释放前所有方法 
    试试 IDisposable与GC.SuppressFinalize();并用
      

  37.   

    数据库连接是宝贵的不可再生的不受托管的资源,必须手工显式释放!楼主这句话是对的,但是显然,是你没做好. 
    释放资源可以用using 配合 Dispose 楼主对c#的理解很一般嘛.
    语气还这么冲...
      

  38.   

    我认为,如果C#的语法、规则和C++、Java一样,那还有存在的必要么??
    C#编写的程序,我认为很吃内存,尤其是读写数据库,所以常用GC.collect()来强制回收。
    至于你说的conn.close(),做这个之前都要做判断的,至少我在C#上基本不用析构方法。