Обработка вложенных указателей QVector
Я унаследовал существенный проект Qt5, где накопительная утечка памяти становится серьезной проблемой. (Да, утечка памяти редко допускается, но с реальным бюджетом и временными ограничениями...).
Этот графический интерфейс считывает данные изображения в объекты класса вокселей для графического отображения. Данные поступают либо из файла, либо из буфера (если они получены в реальном времени) и хранятся как гнездо qvector, то есть:
QVector < QVector <Voxel *> > cVoxel;
Когда изображение читается из файла, cVoxel
инициализируется с помощью QVector.resize(0)
,
cVoxel.resize(0);
Когда изображение, сохраненное в файл, открывается, локальный Voxel
указатель создан и помещен в конец cVoxel
, один раз для каждого пикселя, так что цикл за все строки и столбцы:
for (iRow = 0; iRow < nRows; ++iRow)
{
for (iCol = 0; iCol < nCols; ++iCol)
{
Voxel *v = new Voxel;
cVoxel[iRow].push_back(v);
// Code for reading data into cVoxel removed here
...
}
}
Благодаря приведенным ниже полезным комментариям, я теперь добился некоторого успеха в уменьшении использования памяти в диспетчере задач Windows, вложив разрушение cVoxel QVector в мой CTOR. По линии:
for (iRow = 0; iRow < nRows; iRow++)
{
for (iCol = 0; iCol < nCols; iCol++)
{
delete cVoxel[iRow][iCol];
}
}
В идеале, большая перезапись является лучшим решением. Но в реальном мире мне придется попытаться исправить большие утечки и надеяться, что этого будет достаточно, пока не будет достаточно ресурсов для более идеального решения.
- Я смотрел на утечки памяти в
Voxel
сам по себе, но там нет ничего очевидного. - Мои исследования показывают, что просмотр диспетчера задач Windows на предмет потребления памяти не совсем надежен (Win7 не является ОС реального времени...), но если открытие файла увеличивает потребление памяти приложением с 16 до 81,5 млн, то, несомненно, должно быть некоторое уменьшение памяти, если выделенная память в
cVoxel
успешно выпущен? Если я продолжу открывать и закрывать изображения, потребление памяти приложением будет увеличиваться на том же этапе. Он никогда не уменьшается после закрытия всех / всех открытых изображений. - Сейчас нет попыток освободить память, назначенную (используя новый оператор)
cVoxel
, Я попробовал несколько подходов (и прочитал, чтобы узнать больше), но пока что немного повезло. - QVector отлично справляется со своей собственной обработкой памяти, но я застрял с этой настройкой гнезда QVector и просто полагаясь на squeeze(), resize() или аналогичные QVector, будет только утечка памяти (что уже имеет место для других Переменные в проекте. Я запустил проект с помощью Visual Leak Detector, поэтому у меня есть идея, кто из них является серьезным, а кто - мелкой рыбой)
----РЕДАКТИРОВАТЬ ----
Приносим извинения за беспорядочные комментарии, которые комментируют ниже, но это, безусловно, помогает мне уменьшить утечки памяти (полная остановка, надеюсь, произойдет в должное время...).
Я отредактировал in-line выше, чтобы (надеюсь) сделать этот пост более понятным, и удалил мое лучшее дело, так как оно не повлияло на утечку памяти. Значительное изменение выше - это (2) краткие абзацы, выделенные курсивом.
Мне также нужно исследовать предположение, связанное с полиморфизмом @richardcitter (sp?).
--- EDIT3 ---
Убрал Edit2, разместил этот (новый) вопрос отдельно здесь.
Кроме того, я уверен, что ответ ниже должен исправить этот вопрос - мне просто нужно выяснить, как использовать qvector.resize()
или найти обходной путь к этому.
2 ответа
@SomeProgrammerDude: Вы дали мне 9/10 к решению. Я не знаю, должен ли я отредактировать ваш ответ или использовать это, поэтому модераторы должны редактировать соответственно.
Как обрисовано в общих чертах в сообщении SO, в конце концов я решил отказаться от умных указателей. Вышеупомянутое решение ввело (для меня) проблему компилятора, пытающегося ссылаться на удаленную функцию. В противном случае это правильно с парой модификаций:
QVector < QVector <Voxel *> > cVoxel;
Инициализация:
cVoxel.resize(0);
Назначение памяти:
{
for (int i = 0; i < rows; ++i)
{
cVoxel.push_back( QVector <Voxel *> () );
for (int j = 0 ; j < cols ; ++j)
{
Voxel *v = new Voxel;
cVoxel[i].push_back(v);
}
}
}
Наконец DTOR освобождает память:
int iRow, iCol;
for (iRow = 0; iRow < rows; iRow++)
{
for (iCol = 0; iCol < cols; iCol++)
{
delete cVoxel[iRow][iCol];
}
}
Трудно отформатировать код в комментариях, поэтому я добавляю его как ответ, хотя это может не решить вашу проблему. В любом случае, комментарии тоже были довольно длинными.
Чтобы решить неопределенное поведение и должным образом убедиться, что вам не нужно делать никаких дополнительных выделений, вы можете предварительно выделить количество элементов в векторах. Вы уже делаете это, когда звоните resize(0)
, но вместо того, чтобы установить нужный вам размер, вы устанавливаете нулевой размер, вы делаете вектор пустым.
Я бы предложил что-то вроде этого:
Первое использование std::unique_ptr
как предложил Ричард Криттен:
QVector < QVector < std::unique_ptr <Voxel> > > cVoxel;
Если у Qt есть собственный уникальный тип указателя, вы можете использовать его вместо этого.
Затем, когда вы создаете, вы используете resize
чтобы установить фактический размер векторов:
cVoxel.resize(nRows);
Тогда вы можете использовать простые индексы в векторе. Установите размер для внутренних векторов:
for (iRow = 0; iRow < nRows; ++iRow)
{
cVoxel[iRow].resize(nCols); // Resize to the number of columns
for (iCol = 0; iCol < nCols; ++iCol)
{
cVoxel[iRow][iCol].reset(new Voxel); // Create the actual Voxel object
// Code for reading data into cVoxel here
...
}
}
Так как вы используете std::unique_ptr
(или эквивалент Qt) памяти, управляемой std::unique_ptr
объект будет автоматически освобожден после уничтожения объекта. Так что нет больше утечек памяти, когда cVoxel
вектор выходит за рамки или иным образом разрушается, так что ваш Voxel
объекты будут.