I've recently taken ownership of a WCF Windows Service that makes heavy use of the following static Utility class to retrieve lookup data:
public static class Utility
{
//begin code that causes increased memory consumption
private static Dictionary<string, ErrorData> _errorData;
internal static Dictionary<string, ErrorData> ErrorData
{
get
{
if (_errorData == null)
{
_errorData = GetErrorData();
}
return _errorData;
}
}
//end code that causes increased memory consumption
/// GetErrorData method to get error messages from error xml
/// </summary>
/// <returns>Dictionary of Error messages value for different fields.</returns>
internal static Dictionary<string, ErrorData> GetErrorData()
{
Dictionary<string, ErrorData> data = null;
XmlDocument doc = LoadXmlDocument(Constants.ErrorMessagesFileName);
XmlNodeList errorNode = doc.SelectNodes("/ErrorMessages/Error");
data = new Dictionary<string, ErrorData>();
foreach (XmlNode node in errorNode)
{
ErrorData errorValues = new ErrorData();
errorValues.FieldName = node.Attributes["FieldName"].Value;
errorValues.ErrorMessage = node.Attributes["ErrorMessage"].Value;
data.Add(node.Attributes["code"].Value, errorValues);
}
return data;
}
internal static XmlDocument LoadXmlDocument(string xmlFileName)
{
XmlDocument doc = null;
try
{
if (HttpRuntime.Cache[xmlFileName] == null)
{
doc = new XmlDocument();
doc.Load(Constants.Folderpath + "\\" + xmlFileName);
HttpRuntime.Cache.Insert(xmlFileName, doc);
}
else
{
doc = (XmlDocument)HttpRuntime.Cache[xmlFileName];
}
}
catch (Exception ex)
{
//log
}
return doc;
}
}
As you can see, the static ErrorData property makes use of a private backing field. ErrorData is a Dictionary that is constructed using an XML resource on the filesystem, which is why the contents of the file are stored in HttpRuntime.Cache upon initial retrieval.
Under normal load, the service consumes about 120 MB of RAM.
At some point, a team member felt the need to introduce another level of optimization by creating a static property backed by a lazily loaded static field. Anyway, the presence of said static field causes a rather severe memory leak (500MB+ ) after just a few calls to the service.
The minute I remove the static field and property (clients instead call Utility.GetErrorData()), memory consumption goes back to normal levels.
Can anyone explain why the presence of this static field is causing a memory leak? The WCF service is running with InstanceContextMode.PerCall, if that makes a difference.
Many thanks.
Memory leakage occurs in C++ when programmers allocates memory by using new keyword and forgets to deallocate the memory by using delete() function or delete[] operator. One of the most memory leakage occurs in C++ by using wrong delete operator.
The primary tools for detecting memory leaks are the C/C++ debugger and the C Run-time Library (CRT) debug heap functions. The #define statement maps a base version of the CRT heap functions to the corresponding debug version. If you leave out the #define statement, the memory leak dump will be less detailed.
The main cause of memory leaks in an application is due to unwanted references. The garbage collector finds the memory that is no longer in use by the program and releases it back to the operating system for further allocation.
If the error file is very large, then the static version loads the huge XML document into memory and never releases it. Previously, if clients had called GetErrorData(), then the data would have been loaded into memory and returned, cleaing memory.
There is no synchronization here, so if the static variable were not loaded, several simultaneous requests would have begun loading the error document separately. Only one Dictionary would win and get saved to the static variable. However, if the error file is large, multiple threads loading it simultaneously would increase memory pressure. If this were the case, I would expect the next garbage collection would reclaim the extra instances and release much of this memory.
Also note that the static instance version loads the error file once. So if additional errors were created, these would never be returned to the client.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With