Почему происходит утечка памяти с динамическим массивом пользовательского класса?

Я создаю индикатор, который распознает формы свечей.

Для этого я создал отдельный class Candlestick и включил его в файл индикатора.

Проблема в том, что я страдаю от утечек памяти.

Я новичок в указателях, и после того, как я много прочитал/просмотрел, я все еще, кажется, что-то здесь упускаю.

Это класс индикатора. Содержимое класса Candlestick не имеет значения, поэтому я его опускаю.

Candlestick *candles[]; 

void OnDeinit(const int reason)
{
    for(int i = 0; i < ArraySize(candles); i++ ){
      delete(candles[i]);
    }
}

int OnCalculate(args here)
{
    ArrayResize(candles, Bars);

    for(int i = MathMax(Bars-2-IndicatorCounted(), 1); i >= 0; i--)
    {            
        candles[i] = new Candlestick();

        // Do stuff with this candle (and other candles) here e.g.
        if(candles[i+1].type == BULLISH) Print("Last candle was Bullish");
    }
}

Когда я это делаю, я получаю ошибки утечки памяти. Кажется, мне нужно удалить указатели на свечи в этом динамическом массиве. Проблема в том, когда и где? потому что они нужны мне в следующей итерации цикла for(){...}. Так что я не могу удалить его там.

Когда я удаляю его в функции OnDeinit(), там все еще есть свечи, и я все еще получаю ошибку утечки.

Почему?


person Nique    schedule 12.04.2016    source источник


Ответы (1)


Прежде всего, Ник, добро пожаловать в Worlds of MQL4.

Возможно, вы уже поняли, что код MQL4 не является кодом C.

Среди многих важных отличий ключевым здесь является то, что и в какой момент делает платформа исполнения кода ( MetaTrader Terminal 4 ).

OnCalculate() — это процесс, похожий на зомби, который запускается много раз, но в любом случае определенно не находится под вашим контролем.

Далее, OnCalculate() по дизайну не означает новый Bar.


Как?

MQL4 концептуально восходит к временам, когда вычислительные ресурсы были на много порядков меньше и намного дороже с точки зрения их разделения времени между ЦП и мультиплексированием на этапе выполнения кода.

Таким образом, язык домена пользователя MQL4 сохраняет преимущества некоторых скрытых жемчужин, которые недоступны напрямую. Одним из них является очень эффективная обработка обновлений на основе регистров и поддержание минимального динамического распределения ресурсов из-за их разрушительного неблагоприятного воздействия на предсказуемость выполнения в реальном времени.

Это поможет вам понять, как проектировать концептуальные объекты и обращаться с ними более разумно, лучше всего имитируя поведение «каменного века»-но-ОЧЕНЬ-эффективно (в обоих случаях- мудро и с точки зрения памяти ), вместо того, чтобы заполнять пул памяти бесконечным количеством неуправляемых экземпляров при каждом вызове OnCalulate(), который разбрызгивает бесконечное количество new Candlestick(); // *--> candles[]


Лучший следующий шаг:

Если вы сомневаетесь, просто прочитайте о лучших практиках для ArrayResize() в платформе localhost-help/documentation, чтобы начать осознавать вещи, которые вызывают накладные расходы (если не блоки) в домене, где nano$econd$ считается и вредит. в профессиональном дизайне программного обеспечения.

person user3666197    schedule 12.04.2016
comment
Задав этот вопрос прошлой ночью, я пошел спать и подумал: неужели я вставил этот код не в то место? Я думаю, что ваш ответ соответствует этому. Правильный? - person Nique; 12.04.2016
comment
Я также заметил, что MQL4 сдвигает последний бар вверх при создании нового бара. (0 -> 1). Мне нужно имитировать это поведение. Спасибо, что указали мне правильное направление. - person Nique; 12.04.2016
comment
У меня это работает сейчас. Я удалил все, кроме ArrayResize(candles, Bars) из OnCalculate(). Теперь в OnInit() я звоню ArraySetAsSeries(candles, true), а потом ArrayRezise(candles, Bars). Я изменил код в DeInit(), чтобы после завершения он удалял все указатели Candlestick в массиве. Ура.. ! Еще раз большое спасибо, что указали на меня. Лучше просто вставить пример. Я действительно учусь у него :). Я сделал скриншот всего этого. - person Nique; 12.04.2016
comment
Спасибо, @Nique. Также внимательно прочитайте о предварительном распределении памяти для размера массива, это необходимо прочитать, если задержка имеет значение. Может обрабатывать более нескольких сотен тысяч записей (как db.Pool() DMA-кэш — вещь, которая убила бы OrderSelect() и другие стандартные db.Pool() операции в [StrategyTester] в HFT- стратегии) - person user3666197; 12.04.2016
comment
Кроме того, небольшое замечание: все [Пользовательские индикаторы] совместно используют один единственный поток, поэтому будьте очень осторожны с любой задержкой, тем больше любая потенциальная блокировка при входе в распределенная обработка (и лучше забудьте о файловом вводе-выводе, который всегда блокируется примерно на несколько десятков миллисекунд). - person user3666197; 12.04.2016
comment
Я углублюсь в это. На данный момент мой индикатор очень простой и не будет выполнять ордера. Это просто для анализа баров и распознавания закономерностей. Никаких автозаказов. Но я думаю, что полезно узнать о задержке. Когда я включаю только (встроенный) индикатор полос Боллинджера, таймер обратного отсчета, кажется, уже отстает. (поверьте, у меня отличный интернет и зверский комп, так что это не мой комп. Я думаю, что-то не так с индикатором. Но это совсем другая тема. - person Nique; 12.04.2016
comment
К вашему сведению: пользовательскому индикатору явно запрещено отправлять заказы (из-за вышеупомянутого узкого места в производительности + блокировка единой обработки транзакций на локальном хосте, подобная синхронной службе, до тех пор, пока не возникнет задержка двойного транспорта и обработки на стороне сервера) - person user3666197; 13.04.2016