.NET Remoting Singleton 内存泄漏、TCP、Marshal by Reference

发布于 2024-08-13 07:31:22 字数 2547 浏览 3 评论 0原文

我正在使用我能找到的最简单的远程处理示例,在同一台计算机上运行的 Windows 服务和 Windows 窗体程序(客户端)之间共享对象。

服务像这样实例化对象:

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

客户端建立这样的连接:

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

这个想法是让服务能够更改对象的某些参数,以便客户端能够读取这些更改。

对象本身是:

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; }
    }
}

当服务自行运行时,任务管理器中的内存使用情况保持不变,即使服务不断用状态信息更新对象。当客户端启动时,两者的内存使用量都开始增加,并且永远不会下降。

这是我在 我之前的问题是关于查找内存泄漏的。

它在不同机器上的表现有所不同,有些机器显示内存没有增加,但内存增加的机器将可靠地重现此问题。运行 .NET Memory Profiler 显示,服务上的“新实例”数量不断增加,“类型/资源”选项卡中只有一两个“已删除”实例,其中“命名空间/系统”为“内核”,名称/资源为“堆内存”。我仍在尝试学习如何使用内存分析器,因此,如果这是错误的信息,我深表歉意,并且也将不胜感激有关我还应该在哪里查找的提示。

该对象被实例化一次,只有几个参数可供读取和写入,没有文件 io,没有分配我可以看到的内存,但我的内存使用量似乎只在我开始从客户端连接到该对象并读取其值。任何和所有输入都将受到赞赏,因为我希望避免拉出此代码并将其替换为命名管道或类似的代码,但我很快就会将这一点作为我唯一的选择。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

半枫 2024-08-20 07:31:22

您的服务实例化对象的位置不应该

serviceConfigRemote = new serviceConfigDataRemote();

看起来像

serviceConfigRemote = serviceConfigDataRemote.Instance;

这样吗?

至少,按照您的方式,您在服务器端创建两个不同的实例,其中一个位于静态 instance 成员初始值设定项中,供 Instance 使用属性和另一个通过 new serviceConfigDataRemote() 显式构造实现的属性。向该类添加私有构造函数也可能对您很有帮助,这样除了静态初始化程序之外,没有其他东西可以实例化单例。

这可能不是解决不断增加的内存的方法,但它绝对是一个需要解决的问题。

编辑:

这里有一些我在搜索网络时发现的更多提示:

  • [MTAThread] 添加到主机服务的主要方法中。
  • RemotingServices.Disconnect(this.serviceConfigRemote); 当您关闭主机服务时。

希望这可以有所帮助。

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.

这个俗人 2024-08-20 07:31:22

您是否尝试过在您的单例上使用延迟实例化。它可能不喜欢您实例化它的方式。

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; }
        }
    }

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; }
        }
    }
烟酉 2024-08-20 07:31:22

由于您看到此错误的唯一操作系统是 XP,因此可能存在几个问题。

  1. XP 的传入连接限制为 10 个(专业版)或 5 个(家庭版),这可能会导致此问题。

  2. 确保安装了所有服务包/补丁。我知道这可能是对任何问题的老套和陈词滥调的答案,但事实上这个问题只出现在 XP 中意味着它与操作系统相关。

另外,不确定您如何使用该服务,但 Windows XP 是桌面操作系统,而不是服务器操作系统。如果您希望该服务成为某种类型的服务器,那么您确实应该使用 2000/2003/2008 等,特别是因为它仅在 XP 上有问题。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文