Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > .NET Frameworks (точка нет фреймворки) > C# (си шарп)
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 14.09.2015, 16:28   #1
coNsept
Форумчанин
 
Аватар для coNsept
 
Регистрация: 14.12.2009
Сообщений: 716
По умолчанию Большая нагрузка и утечка памяти

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

Проблема следующая:

Существует некое событие которое принимает около 200-300 сообщений в секунду.
Метод ProcessSubmitSm это NHibernate который вызывает хранимую процедуру а она в свою очередь производит некоторые манипуляции с данными и в итоге добавляет данные в таблицу. Всего данный запрос вместе с обработкой занимает около 1-1.5 секунд.
Если вызывать метод который добавляет данные в базу в потоке по итогу приложение съедает очень большое количество памяти но добавляет собственно все быстро. (количество минимальных и максимальных потоков в ThreadPool ограничил)
Если данный метод не вызывать в потоке тогда утечки не происходит но соответственно падает производительность приложения.
Использовать BULK в этом случае нельзя так как MS SQL не поддерживает асинхронность внутри себя за исключением Service Broker и создание кучи JOB'ов. Но и данные варианты я пробовал и они очень требовательные к ресурсам.
Так же пробовал использовать коллекции ConcurrentQueue/Bag, BlockingCollection и по таймеру добавлять в базу данные но так же коллекции растут и уходить память.

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

Второй пост скинул код так как все не уместилось в одно сообщение.

з.ы. Если где-то неясно объяснился, укажите пожалуйста, я попробую более развернуто разложить вопрос.

Последний раз редактировалось coNsept; 14.09.2015 в 16:30.
coNsept вне форума Ответить с цитированием
Старый 14.09.2015, 16:28   #2
coNsept
Форумчанин
 
Аватар для coNsept
 
Регистрация: 14.12.2009
Сообщений: 716
По умолчанию

Код:
  private static void SmppServerEvClientSubmitSm(object sender, SmppServerClient client, SubmitSm data)
  {
   try
   {
    var smppServer = (SmppServer) sender;

    Guid clientMessageId;

    if (!Guid.TryParse(data.Response.MessageId, out clientMessageId))
    {
     clientMessageId = Guid.NewGuid();
    }

    byte total;
    byte sequenceNumber;
    ushort referenceNumber;
    string concatenationKey;

    if (data.Concatenation == null)
    {
     total = 1;
     sequenceNumber = 0;
     referenceNumber = 0;
     concatenationKey = clientMessageId.ToString();
    }
    else
    {
     total = data.Concatenation.Total;
     sequenceNumber = data.Concatenation.SequenceNumber;
     referenceNumber = data.Concatenation.ReferenceNumber;
     concatenationKey = data.ConcatenationKey;
    }

    #region SubmitSmExtension Class

    //    var submitSmExtension = new SubmitSmExtension
    //     {
    //      SystemId = data.Client.SystemID,
    //      PortNumber = smppServer.Port,
    //      DestAddr = long.Parse(data.DestAddr),
    //      DestAddrTon = data.DestAddrTon,
    //      DestAddrNpi = data.DestAddrNpi,
    //      SourceAddr = data.SourceAddr,
    //      SourceAddrTon = data.SourceAddrTon,
    //      SourceAddrNpi = data.SourceAddrNpi,
    //      MessageText = data.MessageText,
    //      ConcatenationKey = concatenationKey,
    //      SequenceNumber = sequenceNumber,
    //      ReferenceNumber = referenceNumber,
    //      Total = total,
    //      DataCoding = data.DataCoding.ToString(),
    //      RegisteredDelivery = data.RegisteredDelivery,
    //      ClientMessageId = clientMessageId
    //     };

    #endregion

Task.Factory.StartNew(() => {
ProcessSubmitSm(data.Client.SystemID, smppServer.Port, long.Parse(data.DestAddr), data.DestAddrNpi, data.DestAddrTon, data.SourceAddr, 
data.SourceAddrNpi, data.SourceAddrTon, data.MessageText, concatenationKey, sequenceNumber, referenceNumber, total, 
data.DataCoding.ToString(), data.RegisteredDelivery, clientMessageId);
});

   }
   catch (Exception ex)
   {
    DbLayer.AddToLog("EXCEPTION", "Controller::SmppServerEvClientSubmitSm", ex.Message, ex.StackTrace, ex.Source);
   }
  }



Код:
  private static void ProcessSubmitSm(string systemId, int portNumber, long destAddr, byte destAddrNpi, byte destAddrTon, string sourceAddr,
 byte sourceAddrNpi, byte sourceAddrTon, string messageText, string concatenationKey, byte sequenceNumber, int referenceNumber, byte total,
 string dataCoding, byte registeredDelivery, Guid clientMessageId)
  {
   using (var session = OpenSession())
   {
    using (var transaction = session.BeginTransaction())
    {
     var query = session.CreateSQLQuery(@"EXEC sp_ProcessSubmitSm @systemId=:systemId, @portNumber=:portNumber, @destAddr=:destAddr, 
@destAddrNpi=:destAddrNpi, @destAddrTon=:destAddrTon, @sourceAddr=:sourceAddr, @sourceAddrNpi=:sourceAddrNpi, 
@sourceAddrTon=:sourceAddrTon, @messageText=:messageText, @concatenationKey=:concatenationKey, @sequenceNumber=:sequenceNumber,
 @referenceNumber=:referenceNumber, @total=:total, @dataCoding=:dataCoding, @registeredDelivery=:registeredDelivery, 
@clientMessageId=:clientMessageId");
     query.SetString("systemId", systemId);
     query.SetInt32("portNumber", portNumber);
     query.SetInt64("destAddr", destAddr);
     query.SetByte("destAddrNpi", destAddrNpi);
     query.SetByte("destAddrTon", destAddrTon);
     query.SetString("sourceAddr", sourceAddr);
     query.SetByte("sourceAddrNpi", sourceAddrNpi);
     query.SetByte("sourceAddrTon", sourceAddrTon);
     query.SetString("messageText", messageText);
     query.SetString("concatenationKey", concatenationKey);
     query.SetByte("sequenceNumber", sequenceNumber);
     query.SetInt32("referenceNumber", referenceNumber);
     query.SetByte("total", total);
     query.SetString("dataCoding", dataCoding);
     query.SetByte("registeredDelivery", registeredDelivery);
     query.SetGuid("clientMessageId", clientMessageId);
     query.ExecuteUpdate();
     transaction.Commit();
    }
   }
  }

Последний раз редактировалось Stilet; 14.09.2015 в 16:52.
coNsept вне форума Ответить с цитированием
Старый 14.09.2015, 23:34   #3
Smitt&Wesson
Старожил
 
Аватар для Smitt&Wesson
 
Регистрация: 31.05.2010
Сообщений: 13,543
По умолчанию

Тип byte, это не то, что Вы подумали. Для него нужно указывать количество бит. примерно так - byte <имя переменной>: 3;
В Вашем случае, выделяется секция размером в 256 байт по умолчанию. Дальше смотреть не стал, т.к. и этот ляп - очевиден.
Пиши пьяным, редактируй трезвым.
Справочник по алгоритмам С++ Builder
Smitt&Wesson вне форума Ответить с цитированием
Старый 14.09.2015, 23:39   #4
Пепел Феникса
Старожил
 
Аватар для Пепел Феникса
 
Регистрация: 28.01.2009
Сообщений: 21,000
По умолчанию

Цитата:
Сообщение от Smitt&Wesson Посмотреть сообщение
Тип byte, это не то, что Вы подумали. Для него нужно указывать количество бит. примерно так - byte <имя переменной>: 3;
В Вашем случае, выделяется секция размером в 256 байт по умолчанию. Дальше смотреть не стал, т.к. и этот ляп - очевиден.
не несите чушь.
это шарп а не си.
Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
Программа делает то что написал программист, а не то что он хотел.
Функции/утилиты ждут в параметрах то что им надо, а не то что вы хотите.
Пепел Феникса вне форума Ответить с цитированием
Старый 14.09.2015, 23:44   #5
Smitt&Wesson
Старожил
 
Аватар для Smitt&Wesson
 
Регистрация: 31.05.2010
Сообщений: 13,543
По умолчанию

Цитата:
Сообщение от Пепел Феникса Посмотреть сообщение
не несите чушь.
это шарп а не си.
Во, блин. Не усмотрел. Шарп не знаю, ничего сказать не могу. Облажался по-полной . Но в Си именно так.
Пиши пьяным, редактируй трезвым.
Справочник по алгоритмам С++ Builder

Последний раз редактировалось Smitt&Wesson; 14.09.2015 в 23:55.
Smitt&Wesson вне форума Ответить с цитированием
Старый 14.09.2015, 23:49   #6
coNsept
Форумчанин
 
Аватар для coNsept
 
Регистрация: 14.12.2009
Сообщений: 716
По умолчанию

В общем заметил еще один такой момент.
Если убираю из потока метод который записывает данные в базу и оставляю просто создание потоков в итоге утечки не происходит даже при нагрузке 400-500 сообщений в секунду.
То есть, вызывая метод ProcessSubmitSm (в хранимой процедуре оставил обычный INSERT в таблицу) происходит утечка.
Я в полном недоумение.. Даж не знаю что уже предпринять в этой ситуации так как кучу вариантов перепробовал.
coNsept вне форума Ответить с цитированием
Старый 15.09.2015, 00:38   #7
Luuzuk
Форумчанин
 
Аватар для Luuzuk
 
Регистрация: 18.01.2012
Сообщений: 975
По умолчанию

Уважаемый, а как вы определили, что "произошла утечка"? Приложение упало с OutOfMemoryException? Профайлером памяти воспользовались и он её определил?
.NET не обязан освобождать память моментально, сборщик мусора может тянуть со сборкой сколь угодно долго. Попробуйте исключительно в исследовательских целях пару раз (да, именно 2 раза подряд) вызвать GC.Collect();
Если объем занятой приложением памяти резко сократится, то не забивайте голову ерундой, управление памятью идет в штатном режиме. И вызовы GC.Collect() тоже удалите

P.S. и да, зачем вам 400-500 раз в секунду NHibernate'овскую сессию создавать, если поток сообщений настолько велик? Может имеет смысл её закешировать? Да и ценность создаваемой NHibernate транзакции для выполнения одной единственной хранимки тоже весьма сомнительна. А каждый создаваемый объект - это лишние килобайты той самой памяти, о потреблении которой вы беспокоитесь
Благодарить в репутацию. Проклинать — туда же

Последний раз редактировалось Luuzuk; 15.09.2015 в 00:43.
Luuzuk вне форума Ответить с цитированием
Старый 15.09.2015, 17:56   #8
coNsept
Форумчанин
 
Аватар для coNsept
 
Регистрация: 14.12.2009
Сообщений: 716
По умолчанию

Можете привести пример как закешировать?
coNsept вне форума Ответить с цитированием
Старый 15.09.2015, 18:44   #9
pu4koff
Старожил
 
Аватар для pu4koff
 
Регистрация: 22.05.2007
Сообщений: 9,065
По умолчанию

Не увидел для чего тут NHibernate в принципе нужен, тем более раз уж такая битва за память идёт. На ADO.NET код будет почти такой же, а прослойка тоньше и меньше лишнего и ненужного здесь хлама.
pu4koff вне форума Ответить с цитированием
Старый 15.09.2015, 20:27   #10
coNsept
Форумчанин
 
Аватар для coNsept
 
Регистрация: 14.12.2009
Сообщений: 716
По умолчанию

Угум, я уже обычным ADO.NET делаю, более производительнее выходит.

Вообщем проблема собственно в потоках, вот такой пример приводит к утечки.
В моем случае тоже самое, я создаю поток и внутри выполняю SQL операцию 1-1.5 сек и в итоге получаю утечку, как избавиться от этой заразы хз.

Код:
for (int i = 0; i < n; i++)
{
 Task.Factory.StartNew(() => Thread.Sleep(2000));
}
coNsept вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
утечка памяти polin11 Общие вопросы C/C++ 10 18.08.2015 18:12
Утечка памяти Juffin Общие вопросы Delphi 3 02.11.2010 12:11
Анимация (большая Нагрузка на процессор) BuT@JL Мультимедиа в Delphi 6 24.08.2009 09:43
Преобразование типов - большая нагрузка на ЦП ArtUrlWWW Общие вопросы .NET 1 19.05.2009 14:41
утечка памяти в С++ vengo Общие вопросы C/C++ 9 10.06.2008 21:24