none
Memory leak in Windows Mobile WCF proxy client?

    Question

  • Hi Guys,

    Today I found what appear to be a memory leak in the WCF client proxy for Windows Mobile. This is the proxy class generated by the NetCFSvcUtil.exe program power toy. The way I wrote our code for regular Windows WCF clients is we create the proxy once and keep it around for future calls. We also handle any errors that might occur on the proxy class and re-create it as necessary, but for the most part unless we get communication exceptions etc, we keep using the same proxy class to make it as efficient as possible.

    For Windows Mobile, we implemented basically the same thing. Although on mobile there is no error handling, but unlike on regular Windows when we get an error, the proxy is still perfectly valid. So once again we would create our proxy class, and then simply re-use it over and over again throughout the code. The code to create the proxy class is as follows:

        public static AdminServiceClient CreateAdminService()
        {
          CustomBinding binding = new CustomBinding();
          binding.Elements.Add(new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8));
          binding.Elements.Add(new HttpsTransportBindingElement {
            RequireClientCertificate = false,
            MaxReceivedMessageSize = 1048576,
          });
          return new AdminServiceClient(binding, new EndpointAddress(WebSite.WebService));
        }
    
    

    and we have a simple global getter property that returns it, and will create it the first time it is needed.

    Well everything was working just great and the software was finally working pretty well (except for super slow network communications on Windows Mobile, but that is probably just the speed of the CPU). But after many hours of use out in the warehouse, suddenly the whole program would lock up. When we examined the program, it was using up to 26Mb of memory, which we quickly discovered meant that the program had actually run out of memory due to the 32Mb process memory limit in Windows Mobile. So clearly we had a memory leak, so I set about trying to work out what it was.

    I did eventually find it, but it was in the place I least expected it! The WCF client proxy appears to be either keeping track of the entities it is materializing over the wire, or is leaking some other kind of resource internally. To test this theory out I simplified my code until all I was doing was loading some data over the wire (about 700 records) using the following code:

     void GoodTestFunc()
     {
       AdminServiceClient client = GT.CreateAdminService();
       long count = client.SupplierInvoices_CountLineItems(GT.SessionKey, 19050, DateTime.MinValue, true, true);
       var items = client.SupplierInvoices_GetLineItems(GT.SessionKey, 19050, DateTime.MinValue, true, true, 1000, 0);
     }
    
     AdminServiceClient client = null;
     
     void BadTestFunc()
     {
       if (client == null) {
         client = GT.CreateAdminService();
       }
       long count = client.SupplierInvoices_CountLineItems(GT.SessionKey, 19050, DateTime.MinValue, true, true);
       var items = client.SupplierInvoices_GetLineItems(GT.SessionKey, 19050, DateTime.MinValue, true, true, 1000, 0);
     }
    
    

    If I repeatedly call BadTestFunc() in a loop (actually I do it in a timer callback so I can at least quit the app still :), it will just keep leaking memory until eventually it will crash with an out of memory exception when it gets to about 26Mb of memory used by the process.

    On the other hand, if I repeatedly all GoodTestFunc() from the same loop, there is no memory leaks. THe difference is that I am creating the proxy class every time I need to make a call, and then simply throwing it away (for some reason the Windows Mobile proxy class is not disposable, so I just have to let it get garbage collected).

    The problem is I simply do not expect the proxy code to be leaking memory like that. The proxy code itself is pretty simple and I cannot see where it could be leaking memory from looking over the code, so I suspect it has to be somewhere in the XML serializer that is used by the proxy class itself, or perhaps in the channel factory? Either way it is pretty clear it leaks memory, and quite badly.

    I have run the exact same test on .NET 4 and in a Windows Forms client, and there are no memory leaks. So it is something specific to Windows Mobile. For now I can work around this by creating a new proxy class for every call to the server, but it seems that is rather inefficient and I would much prefer to somehow avoid this.

    Perhaps I should post this as bug report on Microsoft Connect?

    Wednesday, June 8, 2011 2:25 AM

Answers

  • Hello.  I don't know if you're still tracking this thread, but I've just encountered the same problem, and I was able to find the leak using the NetCFCLRProfiler.  Are you familiar with the netcfsvcutil-generated file CFClientBase.cs?  In the CFClientBase<TChannel> class is the member

    Dictionary<CFContractSerializerInfo, XmlObjectSerializer> serializers
    

    If you find the method GetContractSerializer, you can see that the client base attempts to cache its serializers for reuse, using the CFContractSerializerInfo as the key to the dictionary.  Unfortunately, a new instance of CFContractSerializerInfo is created every time GetContractSerializer is called (you can see this by finding all references to the method).  Since CFContractSerializerInfo does not override Equals() and GetHashCode(), the dictionary lookup never finds a match, so a new XmlObjectSerializer is created and added to the dictionary with every service call.  Actually, two serializers are added: one for the request, and one for the response.  The dictionary persists for the lifetime of the client object, so eventually you will have 26 MB of XmlObjectSerializers in memory.

    There are several ways you can work around this, but all of them require modifying the CFClientBase.cs file.  I know this is distasteful, because that code is auto-generated by the netcfsvcutil tool, but consider that the CFClientBase is the same every time you use the tool: it doesn't change when the service contract changes.  So you can generate it once, make your modifications, and put the modified version in your source control.  Whenever you update the service client file for a service contract change, just ignore the CFClientBase that was generated.

    The most correct way to fix the client base is probably to have CFContractSerializerInfo override Equals() and GetHashCode(), so that two instances with identical property values will be equivalent for the purposes of the dictionary.  I also tried a shortcut method that works well enough for my purposes: use the info.MessageContractType as the key of the dictionary.  The CFContractSerializerInfo is created in exactly the same way for each call to a given service method, so the message contract type (TREQUEST or TRESPONSE) is unique enough to ensure that the appropriate serializer will be used.

    Kimberly


    Thursday, August 18, 2011 10:04 PM

All replies

  • The client is IDisposable, so you need to call close/abort on it in a try/finally block. Something which looks like this:

    IClientChannel client = null;
          try
          {
            client = GetClient();
    
            // do stuff
          }
          catch (FaultException<yyy> f)
          {
    
          }
          catch (FaultException<xxx> f)
          {
    
          }
          catch (FaultException f)
          {
    
          }
          catch (TimeoutException c)
          {
          }
          catch (CommunicationException c)
          {
          }
          finally
          {
            if (client != null)
            {
              if (client.State == CommunicationState.Faulted)
              {
                client.Abort();
              }
              else if (client.State != CommunicationState.Closed)
              {
                try
                {
                  client.Close();
                }
                catch
                {
                  client.Abort();
                  throw;
                }
              }
            }
          }
    

    Wednesday, June 8, 2011 1:53 PM
  • Sorry, I think you are getting confused with the destkop version of the WCF client proxy, which does have all these features. The mobile version of the client proxy is different in that it does NOT have client.Close(), client.Abort() or client.State. More importantly, as I mentioned before, if you get a communication exception, timeout exception or other exception that proxy continues to function correctly.

    It is also not disposable, as I have said above.



    Wednesday, June 8, 2011 5:57 PM
  • My apologies, your text was so long that I skipped a few sections
    Wednesday, June 8, 2011 5:59 PM
  • The only real issue with the Windows Mobile client proxy is simply that it leaks memory. Badly. As far as I am concerned, it should never leak memory. Thankfully it is not that expensive to re-create a new one as needed, as it appears that internally it caches stuff, but still, I was not expecting such a memory leak.
    Wednesday, June 8, 2011 6:56 PM
  • Hello, I think the problem is in NetCFSvcUtil, which is not part of WCF core. I don't know much about Windows Mobile. But can you try to use the channel programming model directly, without generating a client proxy? I'm not sure if it's supported in Windows Mobile. But it is supported in Windows Phone.
    Lante, shanaolanxing This posting is provided "AS IS" with no warranties, and confers no rights.
    Windows Azure Technical Forum Support Team Blog
    Thursday, June 9, 2011 2:19 AM
  • I have looked over the generated proxy code and I cannot seem to figure out where the potential memory leak might be. For the most part the proxy class is a pretty thin veneer over the XML serialization classes, and the channel programming model classes mentioned above. Given the size of the leak, it would seem to me it is something to do with XML serialization, but I cannot be sure at the moment without debugging the proxy class some more.
    Thursday, June 9, 2011 3:14 AM
  • Hello.  I don't know if you're still tracking this thread, but I've just encountered the same problem, and I was able to find the leak using the NetCFCLRProfiler.  Are you familiar with the netcfsvcutil-generated file CFClientBase.cs?  In the CFClientBase<TChannel> class is the member

    Dictionary<CFContractSerializerInfo, XmlObjectSerializer> serializers
    

    If you find the method GetContractSerializer, you can see that the client base attempts to cache its serializers for reuse, using the CFContractSerializerInfo as the key to the dictionary.  Unfortunately, a new instance of CFContractSerializerInfo is created every time GetContractSerializer is called (you can see this by finding all references to the method).  Since CFContractSerializerInfo does not override Equals() and GetHashCode(), the dictionary lookup never finds a match, so a new XmlObjectSerializer is created and added to the dictionary with every service call.  Actually, two serializers are added: one for the request, and one for the response.  The dictionary persists for the lifetime of the client object, so eventually you will have 26 MB of XmlObjectSerializers in memory.

    There are several ways you can work around this, but all of them require modifying the CFClientBase.cs file.  I know this is distasteful, because that code is auto-generated by the netcfsvcutil tool, but consider that the CFClientBase is the same every time you use the tool: it doesn't change when the service contract changes.  So you can generate it once, make your modifications, and put the modified version in your source control.  Whenever you update the service client file for a service contract change, just ignore the CFClientBase that was generated.

    The most correct way to fix the client base is probably to have CFContractSerializerInfo override Equals() and GetHashCode(), so that two instances with identical property values will be equivalent for the purposes of the dictionary.  I also tried a shortcut method that works well enough for my purposes: use the info.MessageContractType as the key of the dictionary.  The CFContractSerializerInfo is created in exactly the same way for each call to a given service method, so the message contract type (TREQUEST or TRESPONSE) is unique enough to ensure that the appropriate serializer will be used.

    Kimberly


    Thursday, August 18, 2011 10:04 PM
  • Interesting info! Do you have a fixed version of CFClientBase.cs that we could use? It would be nice if the fix could be included in an updated NetCFSvcUtil, but I won't hold my breath waiting for that :)
    Thursday, August 18, 2011 10:43 PM
  • I haven't checked, but if the generated class is a partial class, you could even put the Equals, and HashCode methods in a separate file which will never be overwritten when regenerating the proxy.
    Friday, August 19, 2011 12:01 PM
  • Jesse, great idea, but the type in question is a protected struct nested within CFClientBase.

    Kendall, I simply added these two methods to the CFContractSerializerInfo struct:

    public override bool Equals(object obj)
    {
      if ((obj != null)
        && (obj.GetType() == typeof(CFContractSerializerInfo)))
      {
        return (this.MessageContractType == ((CFContractSerializerInfo)obj).MessageContractType);
      }
      return false;
    }
    
    public override int GetHashCode()
    {
      if ((this.MessageContractType != null))
      {
        return this.MessageContractType.GetHashCode();
      }
      else
      {
        return base.GetHashCode();
      }
    }
    


    Clearly this does not take any fields besides MessageContractType into account.  As I said above, the utility appears to generate the client code in such a way that the MessageContractType is unique enough.  You will want to verify this for your specific uses, though, and extend the methods if necessary.

    Friday, August 19, 2011 1:01 PM
  • Hi All,

    I am using Intermec device, NetCFSvcUtil client proxy and calling IIS wcf services...

    Main problem is when we call Services more then 2 times from mobile device,its given error-"CFFaultException"

    please anyone help me ...,regarding this.............

    Thanks,

    Kumar.

      

    Monday, April 8, 2013 11:14 AM
  • What are the details of the exception you are getting? And how long between the two calls?
    Monday, April 8, 2013 2:09 PM
  • I just wanted to say that your solution is brilliant. Here it is 2013 and I had a big performance problem. Thanks to your solution the problem was solved.  Thanks!
    Thursday, July 25, 2013 2:58 PM