6

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.

Community
  • 1
  • 1
mlusby
  • 641
  • 8
  • 21
  • What are the differences on the machines that exhibit the memory leak vs. those that don't? Is there a significant difference in hardware or OS, or is it seemingly random? – Crowe T. Robot Dec 04 '09 at 20:38
  • Can you give more details about the results of the memory profiler, and what else is being done in the code server side? The code you gave, does not have anything in it that should cause a memory leak, so I think the bug must be elsewhere. – Brett Allen Dec 04 '09 at 20:39
  • 2
    I wonder if thss really is a memory leak or just the unpredictable garbage collector. The profiler should reveal what objects were not garbage collected. – tobsen Dec 04 '09 at 20:41
  • It is seemingly random, though the only ones exhibiting the bug are XP. Unfortunately I'm inexperienced with the memory profiler, and nothing I'm seeing indicates an object not being garbage collected, I will update on that. I'm also using YourKit Profiler now, used its explicit Garbage Collect feature, and it did not impact the Memory used in Task manager. – mlusby Dec 04 '09 at 21:04
  • In .NET Memory Profiler, after an initial startup, the only new types that are not removed are as described above (HeapMemory, Kernel), the Call stack displays information I'm unsure about, but reference System.Runtime.Remoting!System.Runtime.Remoting.Channels – mlusby Dec 04 '09 at 21:24
  • What version .NET are you using? Are you using any Reflection? – JML Dec 04 '09 at 22:11
  • When the server is running on XP there is a memory leak, or when the client is? – Brett Allen Dec 05 '09 at 00:36
  • Updates to all comments: Server and client are on same machine. Version of .NET is 3.5 on all machines. Reflection only used on startup of the service to get version number. Keep in mind, this memory leak will cause the service to continue using more memory until all available memory is used, causing either the crash of the application itself, or something else on the machine. Vista machines have been observed to have a substantially smaller memory leak as well. – mlusby Dec 07 '09 at 14:48

3 Answers3

3

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
  • 19,901
  • 3
  • 68
  • 87
  • 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 Dec 07 '09 at 19:00
  • Agreed, made that change. Doesn't affect memory, but good catch. – mlusby Dec 07 '09 at 19:55
  • Added a couple things that couple potentially help. Haven't tried them myself though, FYI. – Jesse C. Slicer Dec 08 '09 at 00:07
  • What was it that fixed the problem? (Why is this the accepted answer?) Cheers. – Ian Goldby Nov 19 '10 at 10:13
  • Sorry Ian, I didn't notice the activity on this thread. I accepted this answer only because I believe it is a true statement, though it doesn't help. The solution was that I tore all the remoting code out and just used named pipes to pass strings back and forth. – mlusby Jan 13 '11 at 15:13
1

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; }
        }
    }
SamuelWarren
  • 1,449
  • 13
  • 28
0

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.

Brett Allen
  • 5,297
  • 5
  • 32
  • 62
  • 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 Dec 07 '09 at 20:15