← Atrás

Утечки памяти при тестировании на исторических данных

Обнаружил утечки памяти при тестировании на исторических данных, при многократном повторении. Сначал грешил на свою стратегию, потом попробовал запускать пример SampleHistoryTesting многократно, При достижении 30-40 повторений программа подвисала, не выдавая никаких сообщений.

Далее, для чистоты эксперимента подправил код SampleHistoryTesting:

		private void StartBtn_Click( object sender, RoutedEventArgs e ) {
				
			for( int i = 1; i < 1000; i++ ) {
				Run();

				if( _trader.State != EmulationStates.Started ) { Thread.Sleep( 10 );  }
				if( _trader.State != EmulationStates.Stopped ) { Thread.Sleep( 10 ); }
			}

			MessageBox.Show( "Закончено" );
		}
		
		
		private void Run()
		{
		
		// Ну а тут сам код стратегии, что был раньше в StartBtn_Click 
		
		...
		
		// изменил только период, для более быстрого тестирования
			var startTime = new DateTime(2009, 6, 1);
			var stopTime = new DateTime(2009, 6, 3);
		
		....
		
		}

После запуска данный код через некоторое время выдает Out of memory эксепшн. (Пробовал, как с включенным выводом инфы в форму, так и выключеным)

на 100 итерациях уже все нормально проходит.

Памяти достаточно - 4 гб озу

Текст исключения:

System.OutOfMemoryException: Выдано исключение типа "System.OutOfMemoryException".

в System.Linq.Buffer1.ToArray() в System.Linq.Enumerable.ToArray[TSource](IEnumerable1 source) в Ecng.Common.RandomArray1..ctor(T min, T max, Int32 count) в StockSharp.Algo.Testing.MarketDataGenerator1.Init() в StockSharp.Algo.Testing.TrendMarketDepthGenerator.Init() в StockSharp.Algo.Testing.EmulationTrader.Start(DateTime startDate, DateTime stopDate) в SampleHistoryTesting.MainWindow.Run() в C:\Projects\VS\OrigSampleHistoryTesting\MainWindow.xaml.cs:строка 219 в SampleHistoryTesting.MainWindow.StartBtn_Click(Object sender, RoutedEventArgs e) в C:\Projects\VS\OrigSampleHistoryTesting\MainWindow.xaml.cs:строка 63 в System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs) в System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised) в System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args) в System.Windows.UIElement.RaiseEvent(RoutedEventArgs e) в System.Windows.Controls.Primitives.ButtonBase.OnClick() в System.Windows.Controls.Button.OnClick() в System.Windows.Controls.Primitives.ButtonBase.OnMouseLeftButtonUp(MouseButtonEventArgs e) в System.Windows.UIElement.OnMouseLeftButtonUpThunk(Object sender, MouseButtonEventArgs e) в System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget) в System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target) в System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs) в System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised) в System.Windows.UIElement.ReRaiseEventAs(DependencyObject sender, RoutedEventArgs args, RoutedEvent newEvent) в System.Windows.UIElement.OnMouseUpThunk(Object sender, MouseButtonEventArgs e) в System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget) в System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target) в System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs) в System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised) в System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args) в System.Windows.UIElement.RaiseTrustedEvent(RoutedEventArgs args) в System.Windows.UIElement.RaiseEvent(RoutedEventArgs args, Boolean trusted) в System.Windows.Input.InputManager.ProcessStagingArea() в System.Windows.Input.InputManager.ProcessInput(InputEventArgs input) в System.Windows.Input.InputProviderSite.ReportInput(InputReport inputReport) в System.Windows.Interop.HwndMouseInputProvider.ReportInput(IntPtr hwnd, InputMode mode, Int32 timestamp, RawMouseActions actions, Int32 x, Int32 y, Int32 wheel) в System.Windows.Interop.HwndMouseInputProvider.FilterMessage(IntPtr hwnd, WindowMessage msg, IntPtr wParam, IntPtr lParam, Boolean& handled) в System.Windows.Interop.HwndSource.InputFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled) в MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled) в MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o) в System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs) в MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)

¿Le resultó útil este tema?

Compartir tema

Comentarios (25)

Iniciar sesión o Crear cuenta, Inicie sesión o regístrese para dejar un comentario

Mikhail Sukhov
Mikhail Sukhov

zorran: Обнаружил утечки памяти при тестировании на исторических данных, при многократном повторении.

Спасибо посмотрим. Это какая версия S#? В 4.1 у нас новый тестер. Лучше его использовать. Называется LowMemEmulationTrader

art.tsgnet
art.tsgnet

Mikhail Sukhov:

zorran: Обнаружил утечки памяти при тестировании на исторических данных, при многократном повторении.

Спасибо посмотрим. Это какая версия S#? В 4.1 у нас новый тестер. Лучше его использовать. Называется LowMemEmulationTrader в 4.1 не нашел такого

Alexander
Alexander

art.tsgnet:

Mikhail Sukhov:

zorran: Обнаружил утечки памяти при тестировании на исторических данных, при многократном повторении.

Спасибо посмотрим. Это какая версия S#? В 4.1 у нас новый тестер. Лучше его использовать. Называется LowMemEmulationTrader в 4.1 не нашел такого

Уже переименован в EmulationTrader

zorran
zorran Autor

Проверял на 4.0.19 и 4.0.23

art.tsgnet
art.tsgnet

А где взять версию 4.1 ? в box.com пока вижу последнюю версию 4.0.23

Alexander
Alexander

art.tsgnet: А где взять версию 4.1 ? в box.com пока вижу последнюю версию 4.0.23

Codeplex, папка dev

gaifredo
gaifredo

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

Mikhail Sukhov
Mikhail Sukhov

gaifredo: Скачал новую версию, и запустил в ней SampleHistoryTesting. В ответ тишина, т.е. кривая эквилити не обновляется. Сообщение о том, что расчет закончен не отображается. На предыдущих версиях данный пример работал

4.1? Архив с данными тоже был скачал новый?

gaifredo
gaifredo

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

Alexander
Alexander

gaifredo: архив данных от 4.0

данные менялись, используйте новые

paveld
paveld

У меня на 4.1 в примере SampleHistoryTesting утечка памяти тоже возникает. Может нужно какие-нть данные представить по использованию памяти?

Alexander
Alexander

paveld: У меня на 4.1 в примере SampleHistoryTesting утечка памяти тоже возникает. Может нужно какие-нть данные представить по использованию памяти?

Конечно нужно. Какой интервал тестирования, сколько памяти потребляло в 4.0, сколько потребляет в 4.1. Какая конкретно версия с codeplex.

paveld
paveld

Alexander Mukhanchikov:

paveld: У меня на 4.1 в примере SampleHistoryTesting утечка памяти тоже возникает. Может нужно какие-нть данные представить по использованию памяти?

Конечно нужно. Какой интервал тестирования, сколько памяти потребляло в 4.0, сколько потребляет в 4.1. Какая конкретно версия с codeplex.

Интервал тестирования не менял (стоит с 01.06.2009 по 01.09.2009) На 4.0.0.23 в пике 2174012 Кб На 4.1 (с codeplex stocksharp-17261.zip) в пике 2117100 Кб

alexeev.evg
alexeev.evg

Аналогичная ситуация. Если запускать трейдер много раз, возникало исключение OutOfMemory. С каждой новой итерацией оказывалось съеденным все больше памяти.


static void Main(string[] args)
{
	for (int i = 1; i <= 100; i ++)
	{
		using (var temp = new MyClass())
		{
			temp.StartTest(date1, date2);
		}
		GC.Collect();
		GC.WaitForFullGCComplete();
		//с каждой новой итерацией занятая память в этой точке растет
	}
	Console.ReadLine();
}

public class MyClass : IDisposable
{
	private Security _security1;
	private Security _security2;
	private Portfolio _portfolio;
	private MarketDepth _depth1;
	private MarketDepth _depth2;
	private BaseTrader _trader;

	public MyClass()
	{
	}

	public void StartTest(DateTime date1, DateTime date2)
	{
		//запуск трейдера
	}

	public void Dispose()
	{
		if (_trader != null)
		_	trader.Dispose();
		_security1 = null;
		_security2 = null;
		_portfolio = null;
		_depth1 = null;
		_depth2 = null;
		_trader = null;
	}
}

После танцев с бубном удалось выяснить, что проблема в буфере экземпляра EmulationTrader - приватное поле типа Ecng.Collections.BlockingQueue<>. Стало ясно, что объекты данной коллекции остаются в памяти, сборщик мусора почему-то их не трогает. При запуске каждого нового экземпляра EmulationTrader объем занимаемой памяти растет. Уменьшение свойства BufferSize приводит к уменьшению размера прироста памяти, но проблема остается. Решил проблему очисткой буфера в методе Dispose() MyClass (прирост занятой памяти или вообще прекратился или сильно замедлился):

var value= _trader.GetType().GetField("#=qtcoS5HXJh4KLzrXhXg6zgg==", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(_trader); value.GetType().GetMethod("Clear").Invoke(value, null); _trader.Dispose();

Думаю, если очищать буфер в методе Dispose класса EmulationTrader, хуже не станет.

pyhta4og
pyhta4og

alexeev.evg,

из вашего кода несовсем ясно, сколько экземпляров EmulationTrader используется?

конструкция <code> using (var temp = new MyClass()) { temp.StartTest(date1, date2); } </code>

как мне кажется стартует EmulationTrader и сразу его останавливает в Dispose не дожидаясь пока он дотестирует.

Вы можете привести полный код примера чтобы я мог воспроизвести утечку?

ak
ak

А между тем, проблема с EmulationTrader осталась и в последней версии S# (4.1.7.0). Если запускать тестирование с включенной генерацией стакана - обычно ±150 созданных и затем даже принудительно удаленных EmulationTrader'ов хватает чтобы заполнить ±1200 MB оперативы (после очень скоро следует OutOfMemoryEx).

Виновником, как я думаю, является TrendMarketDepthGenerator, содержащий объект(ы) типа Ecng.Common.RandomArray, которые по какой-то причине не очищаются GC, но при этом содержат и накапливают огромное количество [int]. На картинке - состояние приложения после прогона на 48 EmulationTrader'ах и удалении каждого. 89% занимают неудаленные массивы:

Click for large view - Uploaded with Skitch

Если сделать нечто, предложенное alexeev.evg выше, при удалении EmulationTrader'а (взял первый попавшийся объект содержащий Ecng.Common.RandomArray), для последней версии S# это:


// try to cleanup memory, private field in EmulationTrader
//{Ecng.Collections.CachedSynchronizedDictionary<StockSharp.BusinessEntities.Security,StockSharp.Algo.Testing.MarketDepthGenerator>} 
var value = context.Value.Trader.GetType().GetField("#=qHvivsYU2tNspR3_h$VF0nqA$yDC50HFX_RHAxeUi6UE=", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(context.Value.Trader);
value.GetType().GetMethod("Clear").Invoke(value, null);

context.Value.Trader.Dispose();
context.Value.Trader = null;

То занимая память приложения уменьшится примерно вдвое.

Коллеги, можете посмотреть, что с этим можно сделать?

Геннадий Ванин (Gennady Vanin)
FiNick
FiNick

Та же фигня. Пол дня искал в своем коде утечки, пока на форум не зашел=/

Геннадий Ванин (Gennady Vanin)
Mikhail Sukhov
Mikhail Sukhov

FiNick: Та же фигня. Пол дня искал в своем коде утечки, пока на форум не зашел=/

На последней версии воспроизводиться?

Den
Den

Mikhail Sukhov: На последней версии воспроизводиться?

ak выше писал "А между тем, проблема с EmulationTrader осталась и в последней версии S# (4.1.7.0). "

Mikhail Sukhov
Mikhail Sukhov

Den:

Mikhail Sukhov: На последней версии воспроизводиться?

ak выше писал "А между тем, проблема с EmulationTrader осталась и в последней версии S# (4.1.7.0). "

Я про кодеплекс.

VassilSanych
VassilSanych

Хорошая статейка http://msdn.microsoft.com/en-us/magazine/jj863136.aspx В самом низу есть список новых потоконезависимых коллекций .net 4.0 Как вариант замены деревянных велосипедов.

Den Геннадий Ванин (Gennady Vanin)
Den
Den

VassilSanych: Хорошая статейка http://msdn.microsoft.com/en-us/magazine/jj863136.aspx В самом низу есть список новых потоконезависимых коллекций .net 4.0 Как вариант замены деревянных велосипедов.

А кто-нить в S# тестил разницу между SynchronizedDictionary и ConcurrentDictionary?

ak
ak

Потестил на последней сборке #22766 - но моих тестах ситуация изменилась кардинально.

Тестировал на 48 EmulationTrader'ах и 3 торговых днях.

Предыдущая версия (не уверен, что 4.1.7.0, возможно я обновлялся из trunk'а): Пиковое потребление: 1200+ МБ Потребление после завершения теста и удаления EmulationTrader'ов: ~600 МБ

Новая версия: #22766 Пиковое потребление: ~500 МБ Потребление после завершения теста и удаления EmulationTrader'ов: ~140 МБ

Спасибо. Позже я еще внимательно посмотрю на CandleManagerContainer, может и он подтекает )

FiNick
FiNick

Мне не помогло. Взял с кодеплекса последний changeset 22848, референсы с транка, версия 4.1.8. Один прогон теста на данных ордерлога за месяц занимают 2Гб, затем делаю Dispose всему чему могу, экземпляру трейдера в том числе, делаю GC.Collect() на всякий случай, память занимаемая приложением не уменьшается. Затем запускается новый прогон теста на том же месяце, память уходит за 7Гб, комп помирает

ak
ak

Нужно убедиться, что все объекты входящие в конструирование трейдера нигде не остаются висеть, внимательно просмотрите у себя:

  1. security
  2. storage
  3. portfolio
  4. marketDepthGenerator
  5. candleManager
  6. candleSeries
  7. strategy Я еще делаю это, записав результат прогона:
trader.StateChanged += (oldState, newState) =>
                {
                    if (trader.State == EmulationStates.Stopped)
                    {
                        trader.UnRegisterMarketDepth(marketDepthGenerator);
                        marketDepthGenerator = null;
                    }
                };

....

trader.StateChanged += (oldState, newState) =>
            {
                if (trader.State == EmulationStates.Stopped)
                {
                    strategy.Stop();
                    candleManager = null;
                    storage = null;
                }        
            };

....

strategy.Trader.Dispose();
strategy.Trader = null;
strategy.Dispose();

Особенно обратите внимание на marketDepthGenerator = null; - из объекта трейдера его не достать никак, поэтому я прицепил этот обработчик в scope'е где у меня есть доступ к только что созданному локальному объекту marketDepthGenerator.

Воспользуйтесь CLR Profiler (http://www.microsoft.com/en-us/download/details.aspx?id=16273), он позволяет очень наглядно посмотреть Managed Heap для запущенного приложения - сразу станет понятно, где течет.