Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.NET Remoting Singleton memory leak, TCP, Marshal by Reference

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.

like image 782
mlusby Avatar asked Dec 04 '09 19:12

mlusby


2 Answers

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.

like image 157
Jesse C. Slicer Avatar answered Oct 20 '22 01:10

Jesse C. Slicer


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; }
        }
    }
like image 22
SamuelWarren Avatar answered Oct 20 '22 00:10

SamuelWarren