3

I believe I have memory leak with some of my code that uses the XmlDocument class.

My program runs on a Windows 6.1.4 device (C#) and reads from a database on another server to see if any programs installed on the device need to be uninstalled and then reads from an XmlDocument to get the names of the programs that are uninstallable. The program then matches the lists and uninstalls accordingly, if necessary. This process is looped infinitely and runs in the background but what I'm noticing is that the memory creeps up slowly over time and the program eventually catches an OutOfMemoryException.

If I comment out everything and do nothing in the loop, the memory stays right around 2MB consistantly. If I leave everything but the following code commented out then the memory usage goes up .05 megabytes every minute or so continuously. Those results are with having the loop sleep for 1 second. The regular sleep speed is about 10 minutes. Any thoughts as to what could be causing the leak and if it has anything to do with the XmlDocument class?

foreach (string programName in uninstallPrograms)
{
    XmlDocument xmlDoc1 = new XmlDocument();
    xmlDoc1.LoadXml("<wap-provisioningdoc>" +
          "  <characteristic type=\"UnInstall\">" +
          "    <characteristic type=\"" + programName + "\">" +
          "     <parm name=\"uninstall\" value=\"1\"/>" +
          "    </characteristic>" +
          "  </characteristic>" +
          "</wap-provisioningdoc>");

    xmlDoc1 = ConfigurationManager.ProcessConfiguration(xmlDoc1, true);

    cmdStr += "DELETE FROM DEVICE_APPS WHERE ID = " + deviceAppIDList[count++] + "; ";

    xmlDoc1 = null;
}

// Check for pre-installed apps to uninstall
count = 0;

XmlDocument xmlDoc2 = new XmlDocument();

xmlDoc2.LoadXml("<wap-provisioningdoc><characteristic-query type=\"UnInstall\"/>" +
        "</wap-provisioningdoc>");

/**** The line below seems to be the cause of the memory leak ****/
//xmlDoc2 = ConfigurationManager.ProcessConfiguration(xmlDoc2, true);

XmlNodeList xmlNodeList = xmlDoc2.SelectNodes("wap-provisioningdoc/" +
        "characteristic[@type='UnInstall']/characteristic/@type");

xmlDoc2 = null;

cmdStr does eventually get used and is set to string.Empty at the end of the loop. At first I had didn't have xmlDoc = null; in my code but it didn't help either way. I've tried adding GC.Collect(); at the end of my loop and that seemed to help slow down the leak but it doesn't fix it entirely. Plus I've read it's not good practice to use it anyway.

Edit: So it seems to be the ConfigurationManager line that I commented out in my code above that has the memory leak. As soon as I comment out that line of code the memory leak stops. It starts back up when I uncomment this line. Is there something I need to do after making the call to ProcessConfiguration to release the memory?

Also, I am using the Microsoft.WindowsMobile.Configuration namespace for ConfigurationManager runtime version 1.1.4322 since System.Configuration does not exist in CF.

Zac
  • 2,325
  • 3
  • 23
  • 33
  • 4
    btw, the moment someone adds a non-trivial programName, your code is toast; concatenating strings into xml without encoding them is hazardous - corrupt xml is certain. – Marc Gravell Jul 29 '11 at 21:07
  • FYI. Note how I edited the code sections of your question. You only need to prefix code lines by four spaces. No need for the *pre* and *code* HTML tags. –  Jul 29 '11 at 21:09
  • That's true Marc, but the data that's populating programName is retrieved from a database, and the database is populated from the handheld itself. So yeah if the database were to become corrupted there would definitely be an issue but a bigger one at that. Thanks Inuyasha for the fix! – Zac Jul 29 '11 at 21:30

5 Answers5

2

I had similar problem while iterating over many ~100 MB xml files. I tried all the things written above but none of them helped. Finally I separated xml processing into a separate dummy function and then garbage collection worked properly. I did something like this:

function f()
{
    FileInfo[] rgFiles = di.GetFiles("*.xml.gz", SearchOption.TopDirectoryOnly);
    //process all *.xml.gz files in folder
    foreach (FileInfo fi in rgFiles)
    {
        forGC(fi);
    }
}

Function forGC did all the work. Now garbage collection understood when it was ok to remove an xml from memory

0

Duplicated question https://stackoverflow.com/questions/42226993/how-to-dispose-xmldocument Look for György Kőszeg answer.

The using statement will call Dispose to clean up the resources. The XmlDocument isn't IDisposable but the FileStream is.

Rubarb
  • 179
  • 1
  • 6
0

In the code you show cmdStr gets bigger and bigger but I don't see anything ever happening with it... so this would cause your memory comsumption to grow indefinitely and result in OutOfMemory-Exception...

Yahia
  • 69,653
  • 9
  • 115
  • 144
  • Sorry, I should've mentioned that that variable does get used later on and is reset at the end of the loop with cmdStr = string.Empty;. I didn't put all the code from within my loop just because there's a lot of code in there. I've narrowed it down to this chunk though. – Zac Jul 29 '11 at 21:08
  • what does `Global.WriteToFile` do ? could you either comment these calls out and check again or show the source code of this method ? – Yahia Jul 29 '11 at 21:15
  • I removed those lines out, they were just to write out to a log file that I was keeping. – Zac Jul 29 '11 at 21:26
  • yes - but did anything change ? the memory growth still the same or slower ? – Yahia Jul 29 '11 at 21:43
  • Memory growth is still the same. – Zac Jul 29 '11 at 22:14
  • ok... then the only thing left to try for narrowing it down is commenting out the calls to ConfigurationManager... and check again... – Yahia Jul 29 '11 at 22:18
  • Interesting... it does appear to be the line of code with ConfigurationManager. As soon as I comment that line out the memory stabilizes and the leak stops. – Zac Aug 01 '11 at 20:16
0

You can change the type of cmdStr to a StringBuilder. Because string is immutable it gets copied around everytime you append something to the string.

R Quijano
  • 1,301
  • 9
  • 10
0

Looks like the answer to this is to do it natively using DMProcessConfigXML(). Using this method does not cause a memory leak. So there must be something within the wrapper that is not releasing its resources properly.

Zac
  • 2,325
  • 3
  • 23
  • 33