views:

674

answers:

3

I am using the simplest example of remoting that I could find, sharing an object between a windows service and a windows forms program (client), running on the same machine.

The service instantiates the object like this:

serviceConfigRemote = new serviceConfigDataRemote();
serverChannel = new TcpServerChannel(9090);
ChannelServices.RegisterChannel(serverChannel, false);
RemotingServices.Marshal(this.serviceConfigRemote, "ServiceConfigData");

The client establishes a connection like this:

TcpClientChannel channel = new TcpClientChannel();
ChannelServices.RegisterChannel(channel, false);
configData = (serviceConfigDataRemote)Activator.GetObject(typeof(serviceConfigDataRemote), "tcp://localhost:9090/ServiceConfigData");

The idea is for the service to be able to make changes to some of the parameters of the object, for the client to be able to read those changes.

The object itself is:

public sealed class serviceConfigDataRemote : MarshalByRefObject
{
    private bool myConnectedFlag;
    private bool mySendingFlag;
    private bool myUpdateFlag;
    private string myClientConfiguration;

    static readonly serviceConfigDataRemote instance = new serviceConfigDataRemote();

    static serviceConfigDataRemote()
    {
    }

    public serviceConfigDataRemote()
    {
        myConnectedFlag = false;
        mySendingFlag = false;
        myUpdateFlag = false;
        myClientConfiguration = "";
    }

    public static serviceConfigDataRemote Instance
    {
        get
        {
            return instance;
        }
    }

    public override object InitializeLifetimeService()
    {
        return (null);
    }


    public bool Connected
    {
        get { return myConnectedFlag; }
        set { myConnectedFlag = value; }
    }

    public bool Sending
    {
        get { return mySendingFlag; }
        set { mySendingFlag = value; }
    }

    public bool CheckForUpdates
    {
        get{return myUpdateFlag;}
        set { myUpdateFlag = value; }
    }

    public string ClientConfiguration
    {
        get { return myClientConfiguration; }
        set { myClientConfiguration = value; }
    }
}

While the service is running by itself, the Mem Usage in Task Manager stays constant, even though the service is continually updating the object with status information. When the client is started, both begin to increase in Mem Usage, and never go down.

This is the problem that I referred to in My Previous Question about finding memory leaks.

It is appearing differently on different machines, some show no memory increases, but the machines that do will reliably reproduce this problem. Running .NET Memory Profiler shows that on the service, there is an ever increasing number of "New instances", with only one or two "Removed" in the tab Types/Resources where Namespace/System is Kernel and Name/Resource is HeapMemory. I'm still trying to learn how to use the Memory Profiler, so I apologize if this is the wrong information, and tip on where else I should be looking would also be appreciated.

This object is instantiated once, with just a couple of parameters to read and write, no file io, no allocating of memory that I can see, and yet my memory usage only appears to go up the moment I start a connection from the client to that object and read its values. Any and all input would be appreciated, as I would like to avoid pulling this code and replacing it with named pipes or similar, but I'm quickly approaching that point as my only option.

+2  A: 

Shouldn't where your service instantiates the object,

serviceConfigRemote = new serviceConfigDataRemote();

look like

serviceConfigRemote = serviceConfigDataRemote.Instance;

instead?

At the very least, the way you have it, you're creating two different instances on the server side, one in the static instance member initializer to be used by the Instance property and another one via the new serviceConfigDataRemote() explicit construction. It may also serve you well to add a private constructor to that class so nothing else can instantiate the singleton other than the static initializer.

This may not be the solution to the ever-increasing memory, but it definitely appears to be something of an issue to address.

EDIT:

Here are a couple more tips I found scouring the 'nets:

  • Add [MTAThread] to the main method of the host service.
  • RemotingServices.Disconnect(this.serviceConfigRemote); when you're shutting down the host service.

Hope this may assist.

Jesse C. Slicer
As per my statement to "add a private constructor", it should be that the current public default constructor should be made private to preserve the Singleton pattern.
Jesse C. Slicer
Agreed, made that change. Doesn't affect memory, but good catch.
mlusby
Added a couple things that couple potentially help. Haven't tried them myself though, FYI.
Jesse C. Slicer
A: 

Since the only OS you are seeing this bug in is XP, there are a couple possible issues.

  1. XP has a incoming connection limit of 10 (on pro) or 5 (on home) , and this could play a part in the issue.

  2. Ensure that all service packs/patches are installed. I know this may be a corny and cliche answer to any problems, but the fact this issue only appears in XP implies it is OS related.

Also, not sure how you're using the service, but Windows XP is a desktop OS, not a server OS. If you intend the service to be a server of some type, you really should be using 2000/2003/2008 etc, especially since it only has issues on XP.

Aequitarum Custos
All service packs/patches are installed. This is only a single connection, and is only local, on top of being TCP which it looks like is not strictly limited:http://www.codeguru.com/forum/showthread.php?t=326643
mlusby
+1  A: 

Have you tried using lazy instantiation on your Singleton. It's possible that it doesn't like the way you're instantiating it.

public sealed class serviceConfigDataRemote : MarshalByRefObject
    {
     private bool myConnectedFlag;
     private bool mySendingFlag;
     private bool myUpdateFlag;
     private string myClientConfiguration;

     static serviceConfigDataRemote instance;

     static serviceConfigDataRemote()
     {
     }

     public serviceConfigDataRemote()
     {
      myConnectedFlag = false;
      mySendingFlag = false;
      myUpdateFlag = false;
      myClientConfiguration = "";
     }

     public static serviceConfigDataRemote Instance
     {
      get
      {
       if (instance == null)
       {
        lock (new Object())
        {
         if (instance == null)
         {
          instance = new serviceConfigDataRemote();
         }
         return instance;
        }
       }
       return instance;
      }
     }

     public override object InitializeLifetimeService()
     {
      return (null);
     }


     public bool Connected
     {
      get { return myConnectedFlag; }
      set { myConnectedFlag = value; }
     }

     public bool Sending
     {
      get { return mySendingFlag; }
      set { mySendingFlag = value; }
     }

     public bool CheckForUpdates
     {
      get { return myUpdateFlag; }
      set { myUpdateFlag = value; }
     }

     public string ClientConfiguration
     {
      get { return myClientConfiguration; }
      set { myClientConfiguration = value; }
     }
    }
highphilosopher
Gave that a try, no difference in behavior.
mlusby