bds 2006 Конфликты диспетчера скрытой памяти C (класс new/delete[] против AnsiString)

Я использую BDS 2006 Turbo C++ в течение длительного времени, и некоторые из моих более крупных проектов (CAD/CAM, 3D gfx-движки и астрономические вычисления) иногда выдают исключение (для например один раз в 3-12 месяцев при интенсивной эксплуатации 24/7). После обширной отладки я нашел это:

//code1:
struct _s { int i; }    // any struct
_s *s=new _s[1024];     // dynamic allocation
delete[] s;             // free up memory

этот код обычно находится внутри шаблона, где _s может быть также классом, поэтому delete[] этот код должен работать правильно, но delete[] не работает должным образом для структур (классы выглядят нормально). Никаких исключений не генерируется, память освобождается, но это каким-то образом повреждает таблицы распределения диспетчера памяти, и после этого любое новое распределение может быть неправильным (новое может создавать перекрывающиеся распределения с уже выделенным пространством или даже нераспределенным пространством, следовательно, случайные исключения)

Я обнаружил, что если я добавляю пустой деструктор к _s, то вдруг кажется, что все в порядке

struct _s { int i; ~_s(){}; }

Что ж, теперь самое странное. После того, как я обновил это в своих проектах, я обнаружил, что класс AnsiString также имеет плохие перераспределения. Например:

//code2:
int i;
_s *dat=new _s[1024];
AnsiString txt="";
// setting of dat
for (i=0;i<1024;i++) txt+="bla bla bla\r\n";
// usage of dat
delete[] dat;

В этом коде dat содержит некоторые полезные данные, а затем некоторая строка txt создается путем добавления строк, поэтому txt необходимо перераспределять несколько раз, а иногда данные dat перезаписываются txt (даже если они не перекрываются, мне кажется, что временная AnsiString необходима для перераспределения txt перекрывается с dat)

Итак, мои вопросы:

  1. Я делаю что-то не так в code1, code2?
  2. Есть ли способ избежать ошибок AnsiString (пере)распределения? (но до сих пор пользуюсь)

    • After extensive debugging (after posting question 2) I have found that AnsiString do not cause problems. They only occur while using them. The real problem is probably in switching between OpenGL clients. I have Open/Save dialogs with preview for vector graphics. If I disable OpenGL usage for these VCL sub-windows than AnsiString memory management errors disappears completely. I am not shore what is the problem (incompatibility between MFC/VCL windows or more likely I made some mistake in switching contexts, will further investigate). Concern OpenGL windows are:
    • главная форма VCL + OpenGL внутри Canvas клиентской области
    • дочерний элемент основного диалогового окна MFC Open/Save + закрепленная форма предварительного просмотра VCL + OpenGL внутри Canvas клиентской области

P.S.

  1. эти ошибки зависят от количества new/delete/delete[] использований, а не от выделенных размеров
  2. ошибки code1 и code2 повторяются (например, есть анализатор для загрузки сложного ini-файла, и ошибка возникает в той же строке, если ini не изменен)
  3. Я обнаруживаю эти ошибки только в больших проектах (обычный исходный код > 1 МБ) с комбинированным использованием AnsiString и шаблонов с внутренними динамическими выделениями, но возможно, что они есть и в более простых проектах, но происходят так редко, что я их пропускаю.
  4. Infected projects specs:
    • win32 noinstall standalone (using Win7sp1 x64 but on XPsp3 x32 behaves the same)
    • не измеряет при использовании GDI или OpenGl/GLSL
    • не измеряет, используются ли драйверы устройств DLL или нет
    • нет компонента OCX или нестандартного компонента VCL
    • нет DirectX
    • Компиляция/ссылка с выравниванием по 1 байту
    • не используйте RTL, пакеты или фреймворки (отдельно)

Извините за плохой английский/грамматику... любая помощь/вывод/предложение приветствуется.


person Spektre    schedule 17.07.2013    source источник
comment
Возможно, вы захотите использовать инструменты, которые менее сильно устарели.   -  person David Schwartz    schedule 17.07.2013


Ответы (1)


После обширной отладки я точно изолировал проблему. Управление памятью bds2006 Turbo C++ стало поврежденным после того, как вы попытались вызвать любое удаление для уже удаленного указателя. Например:

BYTE *dat=new BYTE[10],*tmp=dat;
delete[] dat;
delete[] tmp;

После этого управление памятью ненадежно. («новый» может выделять уже выделенное пространство)

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

//---------------------------------------------------------------------------
class test
    {
public:
    int siz;
    BYTE *dat;
    test()
        {
        siz=10;
        dat=new BYTE[siz];
        }
    ~test()
        {
        delete[] dat;   // <- add breakpoint here
        siz=0;
        dat=NULL;
        }
    test& operator = (const test& x)
        {
        int i;
        for (i=0;i<siz;i++) if (i<x.siz) dat[i]=x.dat[i];
        for (   ;i<siz;i++) dat[i]=0;
        return *this;
        }
    };
//---------------------------------------------------------------------------
test get()
    {
    test a;
    return a;   // here call a.~test();
    }           // here second call a.~test(); 
//---------------------------------------------------------------------------
void main()
    {
    get();
    }
//---------------------------------------------------------------------------

В функции get() дважды вызывается деструктор класса a. Один раз для реального a и один раз для его копии, потому что я забыл создать конструктор

test::test(test &x);

[Edit1] дальнейшие обновления кода

Хорошо, я усовершенствовал код инициализации как для шаблонов классов, так и для структур, чтобы исправить еще больше ошибок. Добавьте этот код в любую структуру/класс/шаблон и, при необходимости, добавьте функциональность.

T()     {}
T(T& a) { *this=a; }
~T()    {}
T* operator = (const T *a) { *this=*a; return this; }
//T* operator = (const T &a) { ...copy... return this; }
  • T — это имя структуры/класса
  • последний оператор нужен только в том случае, если T использует динамические выделения внутри него, если никакие распределения не используются, вы можете оставить его как есть

Это также решает другие проблемы с компилятором, такие как:

Если у кого-то есть подобные проблемы, надеюсь, это поможет.

Также посмотрите на трассировку указателя в коде С++ mmap, если вам нужно отладить выделение памяти...

person Spektre    schedule 02.08.2013