即使退出函数,.NET内存仍然没有释放

3
我有一些C#代码,它读取了一个巨大的文件,并在进行一些操作后,将其引用设置为null并退出函数,但内存不会释放。
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xmlString);
XmlService.ConvertExcelToXML(xmlDoc);
int sdfid = 320;
XmlService.CompareXML(xmlDoc, ref sdfid, pkid);
xmlDoc.RemoveAll();
xmlDoc = null;

xmlDoc 是一个非常大的字符串,通常约为 50MB。在退出函数时,该内存被永久占用,我不得不每天重新启动我的服务几次,否则它的内存使用量会达到 1GB。

我尝试使用 GC.Collect,但是没有效果。

提前谢谢。

编辑

这是 XmlService 的类声明。它没有变量。所有方法都是静态的。

 public class XmlService

将Excel转换为XML的函数代码

public static bool ConvertExcelToXML(XmlDocument xmlDoc) {
        XmlNamespaceManager nm = new XmlNamespaceManager(xmlDoc.NameTable);
        nm.AddNamespace("z", "urn:schemas-microsoft-com:office:spreadsheet");
        nm.AddNamespace("o", "urn:schemas-microsoft-com:office:office");
        nm.AddNamespace("x", "urn:schemas-microsoft-com:office:excel");
        nm.AddNamespace("ss", "urn:schemas-microsoft-com:office:spreadsheet");
        nm.AddNamespace("html", "http://www.w3.org/TR/REC-html40");
        XmlNodeList rows = xmlDoc.DocumentElement.SelectNodes("//z:Worksheet/z:Table/z:Row", nm);
        if (rows != null && rows.Count > 0)
        {
            XmlNode nodeNames = rows[0];
            XmlNode nodeValues = rows[1];

            XmlNode destRootNode = xmlDoc.CreateNode(XmlNodeType.Element, "ParentNode", null);
            XmlNode fieldNode = null;
            XmlNode dataNode = null;
                for (int i = 0; i < nodeNames.ChildNodes.Count; i++)
                {
                    if (nodeNames.ChildNodes[i].HasChildNodes)
                    {
                        string nodeName = nodeNames.ChildNodes[i].ChildNodes[0].InnerXml;
                        //string nodeValue = nodeValues.ChildNodes[i].ChildNodes[0].InnerXml;
                        string nodeValue = "DataField" + i.ToString();

                        fieldNode = xmlDoc.CreateNode(XmlNodeType.Element, "Field", null);
                        dataNode = xmlDoc.CreateNode(XmlNodeType.Element, "Data", null);
                        dataNode.InnerXml = nodeName;
                        fieldNode.AppendChild(dataNode);
                        destRootNode.AppendChild(fieldNode);

                        fieldNode = xmlDoc.CreateNode(XmlNodeType.Element, "Field", null);
                        dataNode = xmlDoc.CreateNode(XmlNodeType.Element, "Data", null);
                        dataNode.InnerXml = nodeValue;
                        fieldNode.AppendChild(dataNode);
                        destRootNode.AppendChild(fieldNode);
                    }
                }
            xmlDoc.LoadXml("<ParentNode>" + destRootNode.InnerXml + "</ParentNode>");
            return true;
            } 
            return false;

}

并且比较XML的代码

        public static void CompareXML(XmlDocument filexmlDoc, ref int maxSDFID, string PKID)
    {
        FieldsListBO tmpFieldListBO = null;

        ResponseDTO responseDTO = DbService.getConnection();
        DbConnection con = (DbConnection)responseDTO.ReturnedObjects[Constants.CONNECTION_OBJECT];
        DbProviderFactory factory = (DbProviderFactory)responseDTO.ReturnedObjects[Constants.FACTORY_OBJECT];
        DbCommand cmd = factory.CreateCommand();

        cmd.CommandText = "select * from tree_store";
        cmd.Connection = con;
        con.Open();

        DbDataReader dr = cmd.ExecuteReader();
        dr.Read();
        String pXmlizedString = (String)dr["TransactionTree"];
        dr.Dispose();
        cmd.Dispose();
        con.Dispose();
        XmlSerializer xs = new XmlSerializer(typeof(FieldsListBO));
        MemoryStream memoryStream = new MemoryStream(StringToUTF8ByteArray(pXmlizedString));
        XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.ASCII);
        tmpFieldListBO = (FieldsListBO)xs.Deserialize(memoryStream);
        memoryStream.Dispose();
        xmlTextWriter.Close();
        if (tmpFieldListBO.FieldsList.Count < 1)
        {
            maxSDFID = 0;
            return;
        }

        FieldsListBO fieldListBO = new FieldsListBO();

        for (int i = 0; i < tmpFieldListBO.FieldsList.Count; i++)
        {
            if (tmpFieldListBO.FieldsList[i]._pkid.Equals(PKID))
            {
                fieldListBO.FieldsList.Add(tmpFieldListBO.FieldsList[i]);
            }
        }

        GetMaxSDFID(filexmlDoc, ref maxSDFID, fieldListBO);
    }

传递给GetMaxSDFID的filexmlDoc仅仅是按节点遍历,没有进行更新/删除操作


1
请发布 XmlService.ConvertExcelToXML 和 XmlService.CompareXML 的代码。 - Mitch Wheat
2
我怀疑问题出在 XmlService 的方法上 - 有没有可能是不安全/非托管代码?另一个选项是它正在使用 static 变量,从而将其保留在内存中。 - Shadow The Spring Wizard
XmlService是做什么的?它是否操作xmlDoc?你能展示整个函数吗? - Emond
@Shadow:我怀疑你说得对。XmlService的方法都是静态的……打赌这个类是有状态的?唉。 - corlettk
1
你是如何测量内存使用的?即使临时内存分配被垃圾回收器释放,该内存通常也不会从进程中释放到操作系统,但将可用于进程内的进一步分配(这对于大对象堆来说尤其如此)。 - Richard
显示剩余3条评论
2个回答

4

很抱歉要这么说,我可能错了,但是看起来你正在猜测问题的源头。

我这样说是因为你尝试过的事情。将本地变量置空并调用GC.Collect希望解决问题。有经验的人会告诉你,这不是解决方法,也不会有所帮助。

猜测通常是一个好方法(例如其他人猜测XmlService.ConvertExcelToXML可能是个问题),但是为什么要猜测呢?当应用程序消耗了大量内存时,获取进程的内存转储。您可以使用ProcDump,但还有许多其他方法可以做到这一点。

安装WinDbg。使用此工具,您可以使用诸如!dumpheap -stat之类的命令分析内存转储,该命令可以告诉您1 GB的确切位置。


2

为了更好地了解情况,我们需要知道 XmlService 做了什么。它可能仅仅是为了某些原因(或者可能只是设计不良)而存储数据。

假设方法在你展示的代码之后很快就会结束,你绝对不需要将变量设置为 null。如果 XmlService 没有对对象进行引用,你也不需要调用 RemoveAll

请注意,XmlDocument 对象本身不会非常大,尽管其对象图可能很大。但是,xmlString 本身可能很大。如果您在大型对象堆上拥有许多对象,则可能会为您带来问题,因为即使在释放空间后,LOH 也不会进行压缩。不过,我没有预料到它会表现得像这样。

您是否必须首先将 XML 放入字符串变量中?您可以使用流(例如从文件中)来代替吗?


大对象堆 :) - 用于大小超过80K的对象 - Mitch Wheat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接