C#with firebird数据库:内存不足,Dispose()的正确使用是什么?

时间:2017-08-04 01:24:49

标签: c# visual-studio firebird

我有一个使用visual studio 2012 C#和Firebird作为数据库的桌面应用程序。当程序在没有关闭的情况下使用一个小时时,就会有时间内存内存对话框出现,程序将不再响应。

在我的课堂上,我正在使用Dispose(),但我不认为我做得对,因为它得到了相同的结果。平台目标也设置为x64但仍然得到相同的结果。

这是我班级的一个例子

using DevExpress.XtraEditors;
using FirebirdSql.Data.FirebirdClient;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace DepEdZDSMS.Class
{
    class ClsAppointmntPic
    {
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                FirebirdService.Close();
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

         public MemoryStream APP_FRONT { get; set; }
         public MemoryStream APP_BACK { get; set; }
         public DateTime EventTimestamp { get; set; } //for accomplished date and for log date

         public void DeleteAppImage()
        {
            try
            {
                var del = new FbConnection(ClsConnectionImages.FirebirdSQL);
                var fbcmd = new FbCommand("APP_DELETE", del);
                fbcmd.Parameters.Add("@WORKXP_PK", FbDbType.Integer).Value = ClsEmployee.UpdateHandler2;
                fbcmd.CommandType = CommandType.StoredProcedure;
                fbcmd.Connection.Open();
                fbcmd.ExecuteNonQuery();
                fbcmd.Connection.Close();
             }
            catch (Exception errorcode)
            {
                XtraMessageBox.Show(String.Format("Error in connection: {0} Process failed.", errorcode.Message), @"Server Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
        }

        public void UpdateAppImage() 
        {
            byte[] x = null; //front image
            byte[] y = null; //back image
            try
            {
                var adder = new FbConnection(ClsConnectionImages.FirebirdSQL);
                var fbcmd = new FbCommand("APP_UPDATE", adder)
                {
                    CommandType = CommandType.StoredProcedure
                };

                if (APP_FRONT != null) x = APP_FRONT.ToArray();
                fbcmd.Parameters.Add("@APP_FRONT", FbDbType.Binary).Value = x;
                if (APP_BACK != null) y = APP_BACK.ToArray();
                fbcmd.Parameters.Add("@APP_BACK", FbDbType.Binary).Value = y;
                fbcmd.Parameters.Add("@USER_PK", FbDbType.SmallInt).Value = ClsEmployee.USER_PK;
                fbcmd.Parameters.Add("@APP_UPDATETIME", FbDbType.VarChar).Value = EventTimestamp;
                fbcmd.Parameters.Add("@WORKXP_PK", FbDbType.VarChar).Value = ClsEmployee.UpdateHandler2;
                fbcmd.Connection.Open();
                fbcmd.ExecuteNonQuery();
                fbcmd.Connection.Close();
            }
            catch (Exception errorcode)
            {
                XtraMessageBox.Show(String.Format("Error in connection: {0}. Saving failed.", errorcode.Message), @"Server Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
        }

        public void SaveAppImage() //122
        {
            byte[] x = null; //front image
            byte[] y = null; //back image

            try
            {
                var adder = new FbConnection(ClsConnectionImages.FirebirdSQL);
                var fbcmd = new FbCommand("APP_INSERT", adder)
                {
                    CommandType = CommandType.StoredProcedure
                };
                fbcmd.Parameters.Add("@WORKXP_PK", FbDbType.VarChar).Value = ClsEmployee.UpdateHandler2;
                fbcmd.Parameters.Add("@EMP_PK", FbDbType.VarChar).Value = ClsEmployee.UpdateHandler;
                if (APP_FRONT != null) x = APP_FRONT.ToArray();
                fbcmd.Parameters.Add("@APP_FRONT", FbDbType.Binary).Value = x;
                if (APP_BACK != null) y = APP_BACK.ToArray();
                fbcmd.Parameters.Add("@APP_BACK", FbDbType.Binary).Value = y;
                fbcmd.Parameters.Add("@USER_PK", FbDbType.SmallInt).Value = ClsEmployee.USER_PK;
                fbcmd.Parameters.Add("@APP_UPDATETIME", FbDbType.VarChar).Value = EventTimestamp;

                fbcmd.Connection.Open();
                fbcmd.ExecuteNonQuery();
                fbcmd.Connection.Close();
            }
            catch (Exception errorcode)
            {
                XtraMessageBox.Show(String.Format("Error in connection: {0}. Saving failed.", errorcode.Message), @"Server Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
        }

        public ClsAppointmntPic(int refID)
        {
            try
            {
                var app = new ClsAppointmntPic();
                var x = new DataSet();
                var y = new FbDataAdapter();

                var f = new FbCommand("IMAGE_APPOINTMENT", FirebirdService);

                f.Parameters.Add("@X", FbDbType.Integer).Value = refID;

                f.CommandType = CommandType.StoredProcedure;
                y.SelectCommand = f;
                y.Fill(x, "APPOINTMENT");

                if (x.Tables[0].Rows.Count > 0)
                {
                    var fx = x.Tables[0].Rows[0];

                    if (!fx["APP_FRONT"].Equals(DBNull.Value))
                    {
                        var i = (byte[])fx["APP_FRONT"];
                        var fx2 = new MemoryStream(i);
                        APP_FRONT = fx2;
                    }
                    if (!fx["APP_BACK"].Equals(DBNull.Value))
                    {
                        var j = (byte[])fx["APP_BACK"];
                        var fx2 = new MemoryStream(j);
                        APP_BACK = fx2;
                    }
                }
                app.FirebirdService.Close();
            }
            catch (Exception e)
            {
                MessageBox.Show(@"Error: " + e.Message, @"DepEdZDSMS", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);

            }
        }


        public ClsAppointmntPic()
    {
        // TODO: Complete member initialization
    }


        private void Openconnection()
        {
            if (FirebirdService.State == ConnectionState.Open)
            {
                FirebirdService.Close();
            }
            FirebirdService.Open();
        }

        private void Closeconnection()
        {
            FirebirdService.Close();
        }
        private static readonly string Firebird = ClsConnectionImages.FirebirdSQL;
        /// <summary>
        /// 
        /// </summary>
        private readonly FbConnection FirebirdService = new FbConnection(Firebird);
    }
}

2 个答案:

答案 0 :(得分:2)

再次感受:

Dispose()与内存无关。

内存由垃圾收集器管理。 Dispose()是关于非托管资源,如网络套接字,文件句柄,数据库连接和gdi资源。

正确使用Dispose()的一个示例是修改现有的DeleteApp()方法,如下所示:

public void DeleteAppImage()
{
    try
    {
        using (var del = new FbConnection(ClsConnectionImages.FirebirdSQL))
        using(var fbcmd = new FbCommand("APP_DELETE", del))
        {
            fbcmd.Parameters.Add("@WORKXP_PK", FbDbType.Integer).Value = ClsEmployee.UpdateHandler2;
            fbcmd.CommandType = CommandType.StoredProcedure;
            del.Open();
            fbcmd.ExecuteNonQuery();
         }
    }
    catch (Exception errorcode)
    {
        XtraMessageBox.Show(String.Format("Error in connection: {0} Process failed.", errorcode.Message), @"Server Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
    }
}

请注意,我甚至从未直接致电Dispose()。我让框架通过使用块来执行它,这确保即使抛出异常也会调用它 。 FWIW,我也倾向于删除此级别的错误处理;这样做更接近表示层,而不是数据层。

现在关于内存问题。问题与这样的行有关:

if (APP_FRONT != null) x = APP_FRONT.ToArray();

该行占用(可能是大型的)内存流,并将其中的所有内存复制到字节数组中。

.Net使用分代垃圾收集器,它有一个称为大对象堆(LOH)的特殊代。超过一定大小的对象自动最终在大对象堆上(它曾经是85,000字节,在最近的版本中可能已经改变)。是什么让这一代更特别的是它不经常收集。即使收集了它,它也几乎从不压缩

您可以将压缩视为对硬盘进行碎片整理。当一个对象被回收时,它会留下地址空间。它使用的物理内存返回到操作系统,可供其他进程使用,但分配给进程的地址空间仍有漏洞。压缩将剩余的对象向前移动到内存中以填充那些空洞...除了大对象堆不经常这样做。

对LOH重复分配的应用程序将随着时间的推移耗尽其进程可用的所有地址空间。操作系统仍有大量可用内存,但该进程不能再请求,因为它无法为其分配地址。此时,您将看到OutOfMemory异常。

要解决这个问题,你需要避免这些分配...要么找到一种方法来重用同一组内存(即:分配一个字节数组缓冲区,你可以在从MemoryStreams复制时重复使用),使用足够小的块来避免LOH,或者保留流而不是数组。从.Net 4.5.1开始,您还可以通过LargeObjectHeapCompactionMode属性告诉垃圾收集器执行完整的压缩,并为您的服务定期设置此方法。但那是治疗症状,而不是疾病。

答案 1 :(得分:0)

您需要声明您的类实现了IDisposable,否则任何using语句都不知道如何找到Dispose方法并且不会发布任何内容。即使方法存在。

class ClsAppointmentPic : IDisposable