Подтвердить что ты не робот

Qt 4.7: поток TCP, передача данных вызывает утечку памяти

Я сам решил эту проблему, и награда не будет присуждена. Проблема возникла в результате операции GUI, инициированной потоком без GUI.

Qt 4,7 OSX 10.6.8

В приложении много кода, но не очень много связано с тем, что происходит.

Утечка памяти данных происходит в контексте единственного соединения, которое открывается, считывается, записывается и закрывается в пределах одного потока Qt. Я использую фиксированный объект памяти (pMsg) для хранения моих сообщений, а затем отправляю их на внешнее устройство следующим образом:

m_pTcpSocket->write((char*)pMsg->Buf8, (qint64)pMsg->GetLength());

Buf8 представляет собой статический массив длиной в 2048 байт. GetLength - это первые 16 бит сообщения и имеет значение 0xFF, поэтому число от 0 до 255. Если возврат 4 для этих сообщений, всегда есть в моей диагностике. Обе операции окружены их собственными мьютексами (что означает разные мьютексы). Длина сообщений обычно составляет 4 байта. Сообщения надежно попадают на принимающее устройство в другом месте в нашей проводной локальной сети; они верны, когда они приходят, и устройство отвечает соответствующим образом ACK, характерным только для этих сообщений. Я попытался добавить вызов flush() после этого; не помогает (и не должно быть ничего флеша, но...) Я не знаю, что утечка находится в write().

Отправка этих сообщений в свою очередь заставляет меня получать сообщение ACK с устройства. Я прочитал это следующим образом:

if (m_pTcpSocket->waitForReadyRead(100))
{
    while ((bytesavailable = m_pTcpSocket->bytesAvailable()))
    {
        m_pTcpSocket->read(RBuf, bytesavailable);
        AssembleMsg(Buf, bytesavailable); // state machine empties Buf
    }
}

После цикла bytesavailable равен нулю (конечно.) Buf - это неподписанный char указатель на 2048 статический массив беззнаковых символов, после которого после каждой части данных я запускаю простой конечный автомат, который собирает Сообщения. Длины сообщений: 4. Сообщения принимаются и собираются, как ожидалось, не выделяются выделения памяти и не объявляются объекты. Обе операции окружены их собственными мьютексами (что означает разные мьютексы, поэтому они не могут взаимодействовать между rx и tx.) После того, как сообщение собрано, все, что он делает, это reset счетчик, который устанавливает задержку на следующее сообщение keepalive ( это то, что они есть, без них устройство отключит соединение.) Задержка накапливается путем подсчета после waitforreadyread (100), который рассчитывает интервалы этой длины, пока устройство ничего не отправляет на этот порт, что типично поведение. Таким образом, таймер не требуется. Время работает отлично. Сообщения считываются, как только они поступают, или, по крайней мере, в течение 100 мс. Они не накапливаются. Поэтому я думал, что буфер чтения не станет большим (er). Но... я не знаю. Что-то увеличивается!

Итак, чтобы прочитать. Но я не знаю, что утечка находится в read().

НО он должен быть тем или иным. Если я не отправляю эти сообщения (это значит, что я тоже не получаю сообщения ACK), тогда утечки не происходит. В приложении ничего не меняется. Это режим, в котором он включен, и никакой другой активности не происходит, я просто поддерживаю соединение, поэтому, когда он запускает радио, порт готов к работе.

Оба они работают в одном потоке, и оба они запускаются из одного и того же сокета. Поток работает непрерывно, и тот же сокет остается открытым (на самом деле.) Так что это не проблема сокета, проблема удаления.

Проблема усугубляется определенными брендами SDR-радиостанций, так как они требуют keepalive во время операции приема, что означает, что приложение сидит там и перегрызает память, как сумасшедшая, когда принимается как ХОРОШО, когда она сидит там только и ждет.

Я теряю около 250 мегабайт примерно через 12 часов, в кусках где-то менее 100 тыс. Я могу наблюдать увеличение памяти приложения 1 мб за раз, примерно раз в секунду.

Я широко использую googled, и все, что я могу найти, говорит о том, что не удается удалить объект tcp через несколько соединений, что определенно не является проблемой здесь.

Я действительно в недоумении. Является ли проблема связана с моим использованием сокета в потоке? Приложение (очень сложное программное обеспечение, установленное для радиоприложения) работает от 10 до 16 потоков, в зависимости от того, что он делает; Я запускаю передачи данных в своем потоке, чтобы они не были скомпрометированы ничем, что связывает основной цикл событий.

Я пробовал valgrind, но он немного прекратил приложение после того, как он попытается запустить его, задолго до того, как это произойдет. Я не думаю, что ему нравится нить или что-то в этом роде. Или, может быть, 10.6.8, но в любом случае это не работает. Qt 4.7 в любом случае не интегрирует его. Я не знаю, как отслеживать использование памяти внутри приложения, чтобы я мог обернуть каждую отправку и получение и, по крайней мере, выяснить, какой из них (или обоих?) Несет ответственность.

*** edit: меняя скорость сообщения keepalive, я напрямую меняю скорость утечки памяти, и, как я думаю, я сказал выше, если keepalive не отправляется, потери памяти вообще нет.

Это все, что я могу придумать, чтобы рассказать вам людей; любые предложения приветствуются, любое освещение о трюках TCP в Qt приветствуется, в основном что угодно. Я потратил много дней на это, и я просто обструкционирован на этом этапе.

4b9b3361

Ответ 1

Я нашел его. Опираясь на не-gui-нить, он очень сильно нарушал Qt. Прекратил это делать, и он прекратил протекать. Спасибо всем.

Это @Shf, который заслуживает кредит, но, к сожалению, я не очень хорошо понимал щедрости, и я, вероятно, сказал ему войти сюда и ответить слишком поздно. Я сделаю это с ним - когда он получит мое сообщение - предложив щедрость на вопрос, где он действительно предоставил критический намек. Баунти будет состоять из остальной части моего, включая то, что было заработано этим вопросом. Лучшее, что я могу сделать сейчас; Я буду знать лучше в следующий раз. Это определенно было образовательным.

Ответ 2

Не достаточно, чтобы работать с точки зрения кода, но я бы посмотрел на эти вещи: -

  • Откуда вы знаете, что у вас есть утечка памяти?
  • Как вы знаете, что это не фактические повреждения кучи.
  • Там нет "нового" или "удалить" в любом месте. если вы не используете их, то "утечка" вероятна в обработке TCP.
  • Розетки: попробуйте закрыть это и повторно открыть так часто. Устраняет ли утечка, когда вы это делаете?
  • Вы читаете RBuf, но затем собираетесь из Buf...?
  • Какой тип RBuf? Почему нет ограничений на количество, которое вы читаете в нем?
  • Wireshark - Посмотрите, что отправлено/получено в вашей розетке - что-то необычное происходит там. Или, что угодно, чтобы ДРУГИЕ сокеты.
  • Вы действительно читаете байты из сокета? Проверьте возвращаемое значение из read и посмотрите этот вопрос.

Ответ 3

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

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

Ответ 4

В коде, который вы показываете и описываете, нет утечки.

Поскольку Valgrind не работает, лучше всего попробовать LeakSanitizer (http://clang.llvm.org/docs/LeakSanitizer.html) и/или AddressSanitizer (http://clang.llvm.org/docs/AddressSanitizer.html). Черт, запустите все дезинфицирующие средства, которые вы можете, может быть, что-то придумает.

Кроме того, единственным ключом, который я получаю от кода, является обработка pMsg: как он распределяется и освобождается? Мы не видим кода для этого. Изучите его или поделитесь им, если хотите.

Ответ 5

Нет необходимости использовать многопоточность. Оформите мой другой ответ. Он идеально подходит для вашей проблемы и прекратит многопоточность.

Также в Qt всегда используются сигналы и слоты. По умолчанию они защищают код от проблем с поперечными потоками и имеют гораздо больше преимуществ.