我自己解决了这个问题,赏金不会得到奖励。该问题是由于由非 GUI 线程启动的 GUI 操作引起的。
Qt 4.7OSX 10.6.8
应用程序中有很多代码,但与正在发生的事情无关。
数据存储器泄漏发生在单个连接的上下文中,该连接在单个Qt线程中打开,读取,写入和关闭。我使用固定内存对象 (pMsg( 来保存我的消息,然后将它们发送到外部设备,如下所示:
m_pTcpSocket->write((char*)pMsg->Buf8, (qint64)pMsg->GetLength());
Buf8 是一个 2048 字节的静态数组。GetLength 是消息的前 16 位,针对 0xFF,因此是一个介于 0 到 255 之间的数字。这些消息应该返回 4,在我的诊断中总是有。这两个操作都被各自的互斥锁(意味着不同的互斥锁(包围。消息长度通常为 4 个字节。消息可靠地到达有线 LAN 上其他地方的接收设备;它们到达时是正确的,并且设备会使用特定于这些消息的 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
}
}
循环后,可用字节数为零(当然(。Buf 是一个无符号字符指针,指向 2048 个无符号字符的静态数组,在收到数据的每一部分后,我都会运行一个简单的状态机来组装消息。消息长度为 4。按预期接收和组合消息,不进行内存分配,也不声明对象。这两个操作都被它们自己的互斥锁包围(这意味着不同的互斥锁,因此它们无法在 rx 和 tx 之间进行交互。一旦消息被组装,它所做的就是重置一个计数器,该计数器将延迟设置为下一个保持连接消息(这就是这些消息。 没有它们,设备将断开连接。延迟是通过在 waitforreadyread(100( 之后计数来累积的,只要设备不向此端口发送任何内容,它就会计算该长度的间隔,这是典型行为。这样,就不需要计时器。时间安排正常。消息到达后立即读取,或者至少在 100 毫秒内读取。它们不会累积。所以我认为读取缓冲区不会变大(呃(。但。。。我不知道。东西越来越大了!
所以这就是阅读。但我也不知道泄漏是否在 read(( 中。
但它必须是其中之一。如果我不发送这些消息(这意味着我也没有收到 ACK 消息(,那么就不会泄漏。应用程序中的任何位置都不会发生任何其他更改。这是它启动的模式,没有其他活动正在进行,我只是保持连接打开,所以当需要运行无线电时,端口已准备就绪。
它们都在同一线程中运行,并且它们都从同一个套接字运行。线程连续运行,并且同一个套接字保持打开状态(实际上是几个小时(。所以这不是套接字对象删除问题。
某些品牌的 SDR 无线电加剧了这个问题,因为它们在接收操作期间需要保持连接,这意味着应用程序坐在那里,在接收时以及坐在那里等待时疯狂地咀嚼内存。
我在大约 250 小时内丢失了大约 12 兆字节,块数低于 100k。我可以看到应用程序内存一次增加 1 mb,大约每秒一次。
我已经用谷歌搜索了大量内容,我能找到的只是无法通过多个连接删除 tcp 对象,这绝对不是这里的问题。
我真的很茫然。问题是否与我在线程中使用套接字有关?该应用程序(一个非常复杂的软件定义无线电应用程序(在 10 到 16 个线程的任何位置运行,具体取决于它正在做什么;我在它们自己的线程中运行数据传输,这样它们就不会受到任何束缚主事件循环的东西的影响。
我已经尝试过 valgrind,但它在尝试启动应用程序后会终止应用程序,远远早于任何这一切开始。我认为它不喜欢线程或其他东西。或者也许是 10.6.8,但无论如何,它不起作用。无论如何,Qt 4.7都没有集成它。我知道没有办法从应用程序内部跟踪内存使用情况,以便我可以包装每个发送和接收,并至少找出哪个(或两个?(负责。
编辑:通过更改激活消息的速率,我直接更改了内存泄漏的速率,正如我上面所说,如果没有发送激活消息,则根本没有内存丢失。
这就是我能想到的告诉你们的全部内容;欢迎任何建议,任何关于Qt中TCP怪癖的启发都将受到欢迎,基本上是任何东西。我花了很多天的时间,在这个关头我只是被阻挠了。
我找到了。从非 gui 线程中提取以一种非常间接的方式破坏了 Qt。停止这样做,它停止泄漏。谢谢大家。
值得称赞的是@Shf,但遗憾的是,我并没有那么了解赏金,我可能告诉他进入这里并回答得太晚了。当他收到我的信息时,我会弥补他,对他实际上提供了关键提示的问题提供赏金。赏金将包括我的堆栈溢出代表的其余部分,包括这个问题所获得的收益。我现在能做的最好的事情;下次我会知道得更好。这绝对是有教育意义的。
代码方面还不够,但我会看看这些东西:-
- 你怎么知道你有内存泄漏?
- 你怎么知道它实际上不是堆损坏 看不到
- 任何地方的"新"或"删除"。 如果您不使用它们,则"泄漏"很可能在TCP处理中。
- 套接字 :尝试关闭它并经常重新打开。这样做时泄漏会得到清洁吗?
- 你读
RBuf
然后从Buf
组装......? RBuf
是什么类型?为什么没有限制检查您阅读的数量?- Wireshark - 查看您的套接字上正在发送/接收的内容 - 那里发生的任何异常情况。或者,任何进入其他插座的东西。
- 您实际上是从套接字读取字节吗?检查
read
的返回值,并查看此问题。
影响泄漏率的事物中可以找到重要的线索。您提到keepalive消息就是这样一件事,我认为这些消息是由您的应用程序发送的,而不是接收的。
在发送端,您可以显示如何发送单个消息,但不显示如何管理传出消息队列。因此,我的建议是检查发送后消息是否被正确删除,或者在管理该数据结构时是否存在其他问题。
您显示和描述的代码中似乎没有泄漏。
由于Valgrind不起作用,下一个最好的办法是尝试LeakSanitizer(http://clang.llvm.org/docs/LeakSanitizer.html(和/或地址消毒器(http://clang.llvm.org/docs/AddressSanitizer.html(。见鬼,尽可能运行所有消毒剂,也许会出现一些事情。
除此之外,我从代码中得到的唯一线索是pMsg
的处理:它是如何分配和解除分配的?我们没有看到这方面的代码。如果您愿意,可以检查它或共享它。
使用多线程。看看我的另一个答案。它与您的问题完美匹配,并将终止您的多线程问题。
同样在Qt中始终使用信号和插槽。默认情况下,它们可以保护代码免受跨线程问题的影响,并具有更多优势。