LINUX.ORG.RU

Qt Linux memory leaks

 , ,


2

7

Здравствуйте

Имеется программа, написанная на Qt (С++), которая парсит входящие файлы и запихивает их в базу данных. Проблема заключается в том, что по каким-то непонятным причинам обьект, хранящий результат, не удаляется. А самое интересное заключается в том, что проблема происходит исключительно на линуксе (fedora).

Немного дополнительной информации:
1. Обработка каждого файла происходит в разных потоках. Создание нового потока реализовано в таком виде:

            QThread *_t = new QThread();

            QObject::connect(_t, SIGNAL(started()), task.data(), SLOT(run()), Qt::DirectConnection);
            QObject::connect(task.data(), SIGNAL(finished()), this, SLOT(taskFinished()));
            QObject::connect(_t, SIGNAL(finished()), _t, SLOT(quit()));
            QObject::connect(_t, SIGNAL(finished()), _t, SLOT(deleteLater()));
            QObject::connect(task.data(), SIGNAL(finished()), _t, SLOT(quit()));

            _t->start();
2. Расширение файла - CSV. Обработка файла осуществляется этой библиотекой
3. Обработанные данные кладутся в
    struct LineParseResult { QString article, brand, price, stock, multiplicity, __article, price_id; QByteArray key; };
    typedef QList<LineParseResult> PResult;
4. После того, как файл обработался и ParserTask заканчивает свою работу - эмитится сигнал finished(). Мы его ловим и получаем объект ParserTask'a:
        auto _task = qobject_cast<ParserTask *>(QObject::sender());
после чего ищем его в списке тасков, которые работают на данный момент и очищаем. Список выглядит таким образом:
QList<QSharedPointer<ParserTask>> m_currentRunningTasks;
и очистка памяти осуществляется таким образом:
                auto task = m_currentRunningTasks.takeAt(i);
                task.clear();
4. Платформо-зависимого кода в программе не имеется, за исключением того, что присутствует в самом Qt'e

После всех проделанных операций происходят разные вещи на Windows 10 и Linux, а именно программа, которая работает на Linux (диструбтив указан в начале) потребляет гораздо больше памяти, что не так страшно, как то, что память, которая занята после обработки файла, не очищается даже принудительно. На Windows же обратное, программа ест мало памяти и очищает ее после отработки

Программа была проверена валгриндом, который говорит, что утечек не имеется

Что это может быть и как с этим бороться?



Последнее исправление: Flassie (всего исправлений: 1)

А с чего вы вообще взяли, что это утечка? Есть конечно шанс, что valgrind сбоит. Но скорее всего, это просто кеш. Вы 100500 файлов откройте и тогда смотрите на потребление.

RazrFalcon ★★★★★
()
Ответ на: комментарий от RazrFalcon

Если таких файлов добавлено много - приходит оом-киллер и убивает софтину. Т.е. это не буфферы, это занимаемая память

Flassie
() автор топика
Ответ на: комментарий от bass

под линукс собирается бинарник с qt, который собран с опцией static Компилятор - gcc 5.3.0 mingw32 (4.9.2) - windows

Flassie
() автор топика

а именно программа, которая работает на Linux (диструбтив указан в начале) потребляет гораздо больше памяти, что не так страшно, как то, что память, которая занята после обработки файла, не очищается даже принудительно

Прежде чем бороться, ждем подробного описания как была выявлена утечка, что как проверялось и где смотрелось.

I-Love-Microsoft ★★★★★
()
Ответ на: комментарий от Flassie

так эта ... если логика такая, что кучу файлов одновременно в обработку пускаешь, то, как бы, не стоит этому удивляться. сунул миллион файлов, вот тебе и пришел OOM, потому что памяти только на пол-ляма хватило. делай очереди, один хрен дофига потоков это не решение проблемы, а ее усугубление.

anonymous
()
Ответ на: комментарий от anonymous

Стоит ограничение потоков. 2-4 потока одновременно работающих. Файлы в очереди постепенно забивают память в любом случае

Flassie
() автор топика
Ответ на: комментарий от I-Love-Microsoft

Смотрим top, скармливаем файл в соседенем окошке. Программа рапортует что закончила с файлом, память остаётся занята. Скармливаем таким образом по очереди (т.е ждём окончания работы с предыдущим) много файлов - приходит oom-killer.

Flassie
() автор топика
Ответ на: комментарий от Flassie

ну есть разница - список из файлов и куча выделенных буферов.

В любом случае проблема где-то в логике самой софтины. По выходу буферы, очевидно, сбрасываются, есть vg говорит, что все норм, но вот в самой софтине они «забываются» и выделяются новые на обработку каждого файла.

А вот происходит это в твоей поделке или в сторонней, это уже нужно более детально смотреть.

anonymous
()
Ответ на: комментарий от anonymous

вощим что я могу сказать. Выноси пример обработки в отдельный тест-проект и гоняй либу, скармливая ей файлы подряд. Если утекает — то проблема в ней, если нет — то где-то у тебя.

anonymous
()
Ответ на: комментарий от anonymous

В логике софтины, но при этом на двух разных ОС программа работает по разному, причем на одной из них - стабильно и без проблем? Сомневаюсь

Flassie
() автор топика
Ответ на: комментарий от Flassie

Легко.

Ты же понимаешь, что чудес не бывает?

anonymous
()
Ответ на: комментарий от Flassie

Никто и не спорит, что обвить линукс проще, чем искать проблему в своей проге.

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

RazrFalcon ★★★★★
()
Ответ на: комментарий от RazrFalcon

Не первый день ищется ошибка именно в своей проге. Был бы результат - не было бы тут вопроса :)

Flassie
() автор топика
Ответ на: комментарий от intelfx

Можно поподробнее? И как понять, с ним-ли это ошибка и как вообще это исправлять, если это именно из-за этого?

Flassie
() автор топика
Ответ на: комментарий от Flassie

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

Чо спрашиваю то? У нас прям вот 1:1 задача была. И тоже на Qt изначально. Обрабатывала файлы сутками, утечек и OOM килера не случалось.

Так что в чудеса и баги Qt в этом месте верится слабо. Вот бага в cccsvparser или в чудесатой логике работы с потоками и заданиями весьма вероятна.

anonymous
()

Программа была проверена валгриндом, который говорит, что утечек не имеется

Утечки бывают разные. Бывает ситуация, когда программа хранит указатели на память, а при выходе вся память освобождается. Тогда memcheck ничего не найдёт. В таком случае помогает massif (valgrind --tool=massif ...). Полученный лог обрабатываешь утилитой ms_print. Она выдаст тебе места, в которых выделяется больше всего памяти. Среди них и ищи утечки.

i-rinat ★★★★★
()
Ответ на: комментарий от Flassie

mingw32

ммм где-то вот тут твоя ошибка, для начала возьми mingw64 вместо этого deprecated. А вообще это изврат, для венды собирай компилятором студии и шлангом прежде чем говорить об ошибках в линуксовом билде. Вполне вероятно что ошибка в твоём ьштпц32 накладывается на другие ошибки и ограничения и скрывается.

anonymous
()
Ответ на: комментарий от anonymous

выносили либу отдельно? гоняли?

В общем я тебе щас и так расскажу. Поскольку этот парсер оказался всего 2 файлами, я его таки прогнал. Обработал миллиона 2 раз файлы (которые там в примерах идут), ни утечек, ни ООМ не получил.

Так что вариантов ровно один...

anonymous
()
Ответ на: комментарий от RazrFalcon

Какие ваши доказательства? Msvc актуальной версии? Если не считать вот это http://clang.llvm.org/docs/MSVCCompatibility.html то шланг и msvc — основные и официальные компиляторы при разработке для венды, gcc с mingw и всем прочим практически не используется.

anonymous
()
Ответ на: комментарий от RazrFalcon

И продуцируемые gcc бинарники кстати не совместимы на уровне ABI с msvc, насколько я помню. Одного этого уже достаточно. Есть конечно костыли какие-то.

anonymous
()

по описанию задачи тебе нужны еще QThreadPool и QRunnable это заменит ловлю сигналов для отслеживания готовых заданий

HighwayStar ★★★★★
()
Ответ на: комментарий от Flassie

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

I-Love-Microsoft ★★★★★
()
Ответ на: комментарий от RazrFalcon

В посте который рядом есть ссылка, первое предложение на той странице гласит

When Clang compiles C++ code for Windows, it attempts to be compatible with MSVC.

anonymous
()
Ответ на: комментарий от RazrFalcon

Это не отменяет того что оно совместимо и многие разработчки отдают предпочтение ему, у msvc своеобразные представления об отладке и лично мне он тоже не нравится совсем.

anonymous
()
Ответ на: комментарий от anonymous

Увы, без понятия. Последний раз, когда тыкал clang под винду, пару лет назад, ничего нормального не выходило.

RazrFalcon ★★★★★
()
Ответ на: комментарий от anonymous

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

Надо WinDBG пользовать для отладки, а не этого кастрата.

anonymous
()
Ответ на: комментарий от anonymous

Что за своеобразные представления? Остановка по брекпойнту, по записи в память, просмотр переменных, запись истории выполнения. Что ещё надо?

red75prim ★★★
()
Ответ на: комментарий от intelfx

И почему же тогда я в своем линуксе, написав примитивный сниппет с malloc и прогнав его под strace, вижу кучу вызовов mmap и всего три brk где-то в начале процесса?

Актуальные пруфы по sbrk() можно?

devsdc ★★
()
Ответ на: комментарий от RazrFalcon

Да переключение потоков между ядрами, они не учли что в Linux это немного как-то иначе, я в детали не лез, они пофиксили эту мелочь, которая оказывала серьезное воздействие.

Я был в той первой тысяче что тестировали Steam/Linux и писали багрепорты, а я отважно тестировал Serious Sam: BFE )))))

I-Love-Microsoft ★★★★★
()
Ответ на: комментарий от RazrFalcon

90% софта на Qt собрана mingw.

Оооооочень сильно ошибаешься. На вскидку: дефолтный Qt Creator, распространяемый во всех виндовых сборках Qt — собран именно с помощью MSVC.

qBittonrent — MSVC

VirtualBox — MSVC

И ещё что-то значимое было, не вспомню уже.

А вот Clementine на MinGW.

EXL ★★★★★
()
Ответ на: комментарий от intelfx

Охвау, погонял еще сниппеты — аллокация кусков размеров до 2^16 включительно действительно приводит к вызову brk(). 2016 год, блин.

devsdc ★★
()
Ответ на: комментарий от intelfx

Может быть, дело в классной реализации malloc() в линуксе

причем тут Linux (ядро) ? man libc и не позорься.

anonymous
()
Ответ на: комментарий от anonymous

При том, что я оговорился — s/линуксе/глибц/, да.

intelfx ★★★★★
()

QObject::connect(_t, SIGNAL(started()), task.data(), SLOT(run()), Qt::DirectConnection);

а почему указан именно Qt::DirectConnection ?

x905 ★★★★★
()

QThread

В архитектуре дыра. Инструмент выбран неверно.

http://doc.qt.io/qt-5/threads-technologies.html

QtConcurrent::run()+QFutureWatcher{если нужен возврат результата из QtConcurrent::run}

ЗЫ. неправильный порядок коннектов, нету moveToThread.

А самое интересное заключается в том, что проблема происходит исключительно на линуксе (fedora).

Сей факт совсем неудивителен.

dhampire ★★★
()
Ответ на: комментарий от dhampire

Огромное спасибо за ответ.
Вчера покопавшись убедился, что что-то не то с QThread. Решил, что возможно это какие-то особенности его реализации на линуксе

Хотелось бы больше уточнений на тему неправильного порядка коннектов и уточнений про «Сей факт совсем неудивителен»

Flassie
() автор топика
Ответ на: комментарий от Flassie
QObject* object;
QThread* thread = new QThread;

QObject::connect(thread, SIGNAL(started()), object, SLOT(onStarted()));
QObject::connect(object, SIGNAL(objectFinished()), thread, SLOT(quit()));
QObject::connect(thread, SIGNAL(finished()), object, SLOT(deleteLater()));
QObject::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));

object->moveToThread(thread);
thread->start();

как-то так.

dhampire ★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.