C#反射調用WebService引起內存泄漏

最近寫了一個小工具,用來定時檢測公司各臺服務器上的WebService是否工作正常.如果無法訪問則報警.

開發思路也很簡單, 設定一個Timer, 定時啟動多個線程(每個線程負責N臺服務器訪問任務)去動態訪問各服務器上的WebService的指定方法. 然後對異常信息進行報警.


動態訪問WebService技術採用大家都熟悉的反射(Reflection), 如下:

/// < summary>          
/// 動態調用WebService
/// < /summary>          
/// < param name="url">WSDL服務地址< /param>
/// < param name="classname">類名< /param>  
/// < param name="methodname">方法名< /param>  
/// < param name="args">參數< /param> 
/// < returns>< /returns>
public static object InvokeWebService(string url, string classname, string methodname, object[] args)
{
    string @namespace = "EnterpriseServerBase.WebService.DynamicWebCalling";
    if ((classname == null) || (classname == ""))
    {
        classname = WSHelperReflection.GetWsClassName(url);
    }
    try
    {                   
        //獲取WSDL   
        WebClient wc = new WebClient();
        Stream stream = wc.OpenRead(url + "?WSDL");
        ServiceDescription sd = ServiceDescription.Read(stream);
        ServiceDescriptionImporter sdi = new ServiceDescriptionImporter();
        sdi.AddServiceDescription(sd, "", "");
        CodeNamespace cn = new CodeNamespace(@namespace);
        //生成客戶端代理類代碼
        CodeCompileUnit ccu = new CodeCompileUnit();
        ccu.Namespaces.Add(cn);
        sdi.Import(cn, ccu);
                
        CSharpCodeProvider icc = new CSharpCodeProvider();
        //設定編譯參數
        CompilerParameters cplist = new CompilerParameters();
        cplist.GenerateExecutable = false;
        cplist.GenerateInMemory = true;
        cplist.ReferencedAssemblies.Add("System.dll");
        cplist.ReferencedAssemblies.Add("System.XML.dll");
        cplist.ReferencedAssemblies.Add("System.Web.Services.dll");
        cplist.ReferencedAssemblies.Add("System.Data.dll");
        //編譯代理類
        CompilerResults cr = icc.CompileAssemblyFromDom(cplist, ccu);
        if (true == cr.Errors.HasErrors)
        {
            System.Text.StringBuilder sb = new System.Text.StringBuilder();
            foreach (System.CodeDom.Compiler.CompilerError ce in cr.Errors)
            {
                sb.Append(ce.ToString());
                sb.Append(System.Environment.NewLine);
            }
            throw new Exception(sb.ToString());
        }
        //生成代理實例,并調用方法
        System.Reflection.Assembly assembly = cr.CompiledAssembly;
        Type t = assembly.GetType(@namespace + "." + classname, true, true);
        object obj = Activator.CreateInstance(t);
        System.Reflection.MethodInfo mi = t.GetMethod(methodname);
        return mi.Invoke(obj, args);
    }
    catch (Exception ex)
    {
        throw new Exception(ex.InnerException.Message, new Exception(ex.InnerException.StackTrace));
    }
}

完成編碼并簡單測試通過后就投入了使用.大約8小時后發現Run這個工具的電腦出現 內存不足 的報警并導致檢測工具Crash !

用dotMemory檢測發現, 每一輪調用訪問就會產生大量Unmanaged Memory 未被釋放(即:  內存泄漏)

圖1: 灰色區塊中包含了Unmanaged memory

C#反射調用WebService引起內存泄漏_第1张图片

圖2: 灰色區塊就是 Unmanaged memory

C#反射調用WebService引起內存泄漏_第2张图片


問題應該出在反射上!

因為通過反射加載到內存中的DLL只有在關閉程序時才會被釋放, 而這個工具會一直開著并且定時使用反射去動態調用WebService, 這樣就會不斷佔用內存直到資源耗盡.

我曾嘗試在調用完成后把能釋放的一切資源都釋放掉但沒有任何幫助. 對於這種反射的情況,微軟似乎也沒有提供一個很好的辦法.


後記: 由於目前沒有找到解決方案, 最終放棄使用動態調用WebService改為調用固定方法(Method), 然后在初始化時依次替換訪問服務器的IP來實現檢測任務. 

改用此方案后, 再未出現 內存泄漏 的問題.

你可能感兴趣的:(C#)