.NET Remoting Утечка памяти Singleton, TCP, Marshal по ссылке - PullRequest
6 голосов
/ 04 декабря 2009

Я использую самый простой пример удаленного взаимодействия, который я могу найти, разделяя объект между службой Windows и программой Windows Form (клиентом), работающей на той же машине.

Служба создает экземпляр объекта следующим образом:

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 показывает, что в службе постоянно увеличивается число «новых экземпляров», причем только один или два «Удалено» на вкладке Типы / Ресурсы, где Пространство имен / Система - это Ядро, а Имя / Ресурс - это HeapMemory. Я все еще пытаюсь научиться использовать Memory Profiler, поэтому я прошу прощения, если это неверная информация, и я также буду благодарен за подсказку, где еще я должен искать.

Этот объект создается один раз, с помощью пары параметров для чтения и записи, без файла io, без выделения памяти, которое я вижу, и все же использование моей памяти только увеличивается, как только я начинаю соединение с клиент к этому объекту и читать его значения. Буду признателен за любые входные данные, так как я хотел бы избежать вытягивания этого кода и замены его именованными каналами или аналогичными, но я быстро подхожу к этому вопросу как к единственному варианту.

Ответы [ 3 ]

3 голосов
/ 07 декабря 2009

Не должно там, где ваш сервис создает объект,

serviceConfigRemote = new serviceConfigDataRemote();

похоже на

serviceConfigRemote = serviceConfigDataRemote.Instance;

вместо

По крайней мере, так, как вы это делаете, вы создаете два разных экземпляра на стороне сервера, один в статическом инициализаторе instance, который будет использоваться свойством Instance, а другой через new serviceConfigDataRemote() явная конструкция. Также может быть полезно добавить в этот класс приватный конструктор, чтобы ничто иное не могло создать экземпляр синглтона, кроме статического инициализатора.

Возможно, это не решение постоянно растущей памяти, но определенно кажется, что это проблема, которую нужно решить.

EDIT:

Вот еще пара советов, которые я нашел, прочесывая сети:

  • Добавьте [MTAThread] к основному методу службы хоста.
  • RemotingServices.Disconnect(this.serviceConfigRemote); при закрытии службы хоста.

Надеюсь, это поможет.

1 голос
/ 07 декабря 2009

Вы пытались использовать ленивый экземпляр на вашем синглтоне. Возможно, вам не нравится, как вы его создаете.

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; }
        }
    }
0 голосов
/ 07 декабря 2009

Поскольку единственная ОС, в которой вы видите эту ошибку, - это XP, существует несколько возможных проблем.

  1. XP имеет ограничение на входящее соединение 10 (для профи) или 5 (для дома), и это может сыграть роль в проблеме.

  2. Убедитесь, что установлены все пакеты обновления / исправления. Я знаю, что это может быть банальным и клише ответом на любые проблемы, но тот факт, что эта проблема появляется только в XP, подразумевает, что это связано с ОС.

Кроме того, вы не знаете, как вы используете службу, но Windows XP - это настольная ОС, а не серверная ОС. Если вы хотите, чтобы служба была сервером какого-то типа, вам действительно следует использовать 2000/2003/2008 и т. Д., Тем более что она имеет проблемы только с XP.

...