最近,做了个服务程序,电信项目,需要保证7 ×18小时运行(0点到6点除开),采用事件驱动,结果,程序运行15天,内存占用1G,为了解决内存泄露的问题,因此,有了这篇文章。。。。。

      内存泄露,原因很多,因此,不同的情况有不同的解决办法。

      首先:说说本项目可能存在的内存泄露的原因。

             1:多线程,资源变量的读取,死循环(本程序不存在死循环。。)

             2:资源没有释放完全,当然,本程序是由事件驱动,呵呵,原因有可能是由于事件没有正确释放造成的。。。

 

对于第2种:事件的没正确释放,是指什么呢?

 比如说:如下的代码

 

ITPUB个人空间1T0w-bbw~
  public class EventTest1:IDisposable
4vm[4s2He:j j aVo0    
{
r%j"J;heM*`{r0        
private int[] testArr = new int[100000000];
'wYhLV q0A{0ITPUB个人空间CV6Ysc|F
        
public event TestDelegate TestDelegate = null;
?{$G-I~rs0
Sx|+Hb X$Y~0        
protected void OnTest()
f8W4d(D1Qd6d C0        
{
Wh Lc?3P^-L0            
if (TestDelegate != null)
*z+iNfGf0            
{
c!SInA%ei0                TestDelegate();
*Q:UDAT0            }

8Pz FM7q?*w#Jo1h0        }
ITPUB个人空间 `]I}0h\5q

Z+Nz2LF ^B!F0        
public void Add()
9DK p{ w x0        
{ITPUB个人空间6TR4tX9@*~
            SystemEvents.DisplaySettingsChanged 
+= SystemEvents_DisplaySettingsChanged;
C:t;j4h6y0            OnTest();
8?`4d%rn-KBg0        }

IMj(J;R*I0
8A h"nMNe0        
void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e)
:PV3hS{o;uf4l0        
{
v,Yr R,O0Lb0ITPUB个人空间Y"J wB5?}}u_*Z
        }

4~K?;h&}}7]Z0
v+S7CHn!GaA0        
IDisposable 成员
.F,[qHx7P#i.zd3I0    }

 

除非您显示代用Dispose这个方法,不然,绝对会出现内存泄露。。。

 

因此,为了解决内存泄露,考虑了3种办法:

 1:再编写一个服务程序,每天定时关闭和重启内存泄露的服务程序。

 可行性描述:

     (1):这样做,最简单,最有可能达到效果的办法。为什么说是最有可能达到效果呢?原因在于,如果用服务程序来重新启动或者关闭一个服务程序的话,有可能关闭和启动的之间得时间过短,后面启动的服务程序,显示出的内存占用,还是那么多。。。。到底是什么原因造成这样的情况,还在研究中。。。

     (2):虽然这样最简单,但是,是最没办法的办法,不推荐使用,况且,就算解决问题后,自己没能够获得多少经验和能力提升。

     (3):除非您的客户要求您马上解决内存泄露的问题,不然,请别采用这个方法

2:最正宗也最符合开发设计思想的办法,重构代码,及时释放掉需要释放的资源   

    可行性描述:

        (1)时间问题:当出现内存泄露后,客户当然是希望能够最快时间得到开发组已经解决问题的确认,显然,重构一个程序,把代码全部在重新清理一遍,显然不是一下就能够完成的。

     解决问题:

        (1):在这,也就说说Dispose模式了(不知道到底有没有这个模式,好象自己看的java与设计模式上没看到过。。)。

                 Dispose模式,代码简单,但是,方法绝对的经典。

        

ITPUB个人空间:bf4[ Oifr e.y ],e ]O
       #region 释放资源 ,实现 IDisposableITPUB个人空间)c5Y FA\C
ITPUB个人空间(U1AcaL
        
bool mIsDisposed = false;ITPUB个人空间XZNE|5r;i
ITPUB个人空间~m5N+aBUK
        
public void Dispose()ITPUB个人空间Dh5eYG"J'EG
        
{ITPUB个人空间'Yz B6B{
            Dispose(
true);ITPUB个人空间 ~8_#h8M%R
            GC.SuppressFinalize(
this);
7Y h6B+]K3En'~0        }
ITPUB个人空间Q%xRP4Y~&r$l&b
ITPUB个人空间m [T jW&j(D.b
        
protected void Dispose(bool disposing)ITPUB个人空间^kgI.~zm
        
{
|4cb_-_bZ;t0            
if (!mIsDisposed)
U;f:U+q r p0            
{
Jj-Kkqc A+]0                
if (disposing)
@*`7uH8Y[C0                
{ITPUB个人空间#b l ^f&])g,D
                    DisposeOverride(
true);ITPUB个人空间uz4G9R8BA OH
                }
ITPUB个人空间k)Q^0xZnu0BP!S
                DisposeOverride(
false);
3MV&{p:is#mG"h$[J0                mIsDisposed 
= true;
*SUS8v `9{0            }
ITPUB个人空间9`B#E8u;W"H @%Al!U
        }

4I)ZWh4gqm0
8X/SQ&q|7U w)G"M4Y"V0        
protected virtual void DisposeOverride(bool p)
F7x u+@/A$V b{0        
{
fzz a7{,j0            
if (p)ITPUB个人空间PX-]^kfeSu0~+s3\v4@
            
{
.?gF#^C7i0                
// 释放托管资源ITPUB个人空间7Oa I?;|-R(N:Y
            }
ITPUB个人空间l#|d6sz
            
else
"]#H!k4|+AmX:J aY5h0            
{
^x2y.x(No;W\A0                
// 释放非托管资源ITPUB个人空间"}3UR|(BS8[/u1a |R
            }
ITPUB个人空间H*@!Qa/l;F8R7Vt
        }

f{E AI+@0        
#endregion

7U!tdY)Y yNKc0
MAM,Th^5x0析构函数:
_on^C2JZ uXN0 
this.Dispose(false);

 

当然,如果您程序里面所有的该释放的资源都释放了,还有内存泄露,那。。。我也没办法。。。。

 

3:使用应用程序域

    引用官方的话,给点曙光:

  • 使用应用程序域隔离可能终止进程的任务。如果正在执行任务的AppDomain的状态变得不稳定,则可以卸载AppDomain,但不会影响进程。当进程必须不重新启动而长时间运行时,这一点很重要。还可使用应用程序域隔离不应共享数据的任务。

  • 如果程序集被加载到默认应用程序域中,则当进程运行时将无法从内存中卸载该程序集。但是,如果打开另一个应用程序域来加载和执行程序集,则卸载该应用程序域时也会同时卸载程序集。使用此技术最小化长时间运行的进程的工作集,这些进程偶尔会使用大型 DLL。

       

            最后一句话,没看懂,但是,只要第一句,就足够了。。

     

    本人也是第一次使用AppDomain这东西,因此,只能给出代码了:

             1:首先,把所有的业务代码等,都用一个类封装起来。

             2:开启一个新的AppDomain。

          


    !r(c8~?g*cb0            Type testType = Type.GetType("BoCo.Hubei.HuangShi.AlarmLocation.Server.BtsAlarmLocationService");ITPUB个人空间2HZ i)ys7i c Mx$F
                Assembly ass 
    = testType.Assembly;ITPUB个人空间Qc(Aw&r2iAs5G'y
                
    string exeAssembly = ass.FullName;
    #f9M-n`D1Rh0ITPUB个人空间3Q0\7P3T0aI [
                AppDomainSetup ads 
    = new AppDomainSetup();ITPUB个人空间x'_"Hwb4O])d0Gf
                ads.ApplicationBase 
    =
    6z[e6\,F'l3U0               AppDomain.CurrentDomain.BaseDirectory;ITPUB个人空间&V2s5| {c(I
                ads.DisallowBindingRedirects 
    = false;ITPUB个人空间[{%c R_
                ads.DisallowCodeDownload 
    = true;
    ~ c u)]9g0            ads.ConfigurationFile 
    =ITPUB个人空间Wco,K*d-~'ke5o%W
                    AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
    9]5o@7t3jz"EY#o0ITPUB个人空间 cx!xG+tj oE@"y(M
                userCreateAppDomain 
    = AppDomain.CreateDomain("BoCo.Hubei.HuangShi.AlarmLocation.Server.exe"null, ads);//吗的,实在是没明白,这样建立的一个AppDomain后,在新的AppDomain中,用AppDomain.CurrentDomain.FriendlyName取得的名字,怎么会被截去了后面4位,搞得我在这个名称后,加了.exe
    ,^ ]:b5z R}.w1L0
                ObjectHandle objectHandle = userCreateAppDomain.CreateInstance(exeAssembly, "BoCo.Hubei.HuangShi.AlarmLocation.Server.BtsAlarmLocationService");ITPUB个人空间8}@V@0hv
                BtsAlarmLocationService btsAlarmLocationService 
    = (BtsAlarmLocationService)objectHandle.Unwrap();

     

    对于第1,3种方法,只是治标不治本,只是第3种比第1种,高深了那么一点点。。。。

    本文中,所有的代码等,只提供了一个思路,程序到底怎么操作,是您说了算。。。

    当然,为了国庆能够安心放假,本程序也就只有采用第3种方法,况且,没使用过的方法,当然要用下。

     

    最后,说下为什么本程序会出现内存泄露而没解决掉的原因:

          1:客户原因

              本程序实现的功能,在局方来说,是第一次,因此,局方人员希望快速出结果,可以向领导汇报,来增加其业绩。

          2:本公司原因

              (1)项目经理在明知道程序没有测试完全的情况下,也催促开发组提供程序上线运行测试,结果,上线后,程序的任何一个小小的修改,都需要经过局方领导签字确认后,才能够实施,况且,恰逢奥运,一个签字流程走下来,时间花费太多。。。

              (2)项目经理没有认识到本系统对局方使用人员业绩上带来的影响,程序某天只要出现丁点问题,则局方当天的使用人员将暴跳如雷。。。

          3:开发组原因

             当然,这个是最重要的。

                (1)开发组组长没有强势的态度去左右项目经理不上没有测试通过的程序。

                (2)开发组组员代码编写能力以及系统的全面考虑上,还存在一定问题。

         

    废话一堆,也只是先以简单办法解决当前问题。

  • posted on 2009-06-01 19:23  钱途无梁  阅读(1302)  评论(1编辑  收藏  举报