"Strange"内存泄漏 - TCP 网络

"Strange" memory leaks - TCP Network

本文关键字:TCP 网络 泄漏 Strange 内存      更新时间:2023-10-16

我使用boost::asioPacket S构建了很多类TCP网络 - 用于传输。(Packet的基类是std::vector<char>

我以为我已经修复了所有可能的内存泄漏,但是然后我只是在关闭客户端之前就关闭了服务器 - 这不会引起任何问题 - _CrtDumpMemoryLeaks报告了内存泄漏。因此,我将视觉泄漏检测器添加到了项目中,并检查了这些内存泄漏的位置。

void dumpMemoryLeaks(void) { CrtDumpMemoryLeaks(); }
//and in main:
atexit(dumpMemoryLeaks);

检查回到报告的内存泄漏的线路,这似乎有点...对我来说很奇怪。

我有一个类AsyncAcceptor,其中我在构造函数启动器列表中分配了new BoostSocket -RAW指针(适用于正常和SSL套接字的包装器)。在 AsyncAcceptor的击路仪中,如果有效,将正确删除原始指针。

_acceptor = new AsyncAcceptor(service, bindIP, port); //No memory leak for this pointer
//...
_acceptor->AsyncAcceptManaged(_OnSocketAccept); //Function pointer

AsyncAcceptManaged中,服务器正在等待连接并传递另一个指针 - 一个内存泄漏。

//Set socket properties
//Call handler function passed as parameter
handler(_socket); //Raw pointer passed to handler
_socket = nullptr;
_socket = new BoostSocket(_acceptor.get_io_service()); //with ctx for SSL, if defined
//If the acceptor is not closed, it calls `AsyncAcceptManaged` again.

现在到处理程序功能。

void OnSocketOpen(BoostSocket *sock)
{
    //set additional socket options and check for error
    //Note: SocketType is template parameter of class - allowing different socket types
    std::shared_ptr<SocketType> newSocket(new SocketType(sock));
    delete sock;
    sock = nullptr;
}

SocketType总是有一个基类,具有同一构造函数的 BoostSocket *

Socket(BoostSocket *socket)
    : _socket(std::move(*socket))
{
    //...
}

BoostSocket中,成员_socket是std::unique_ptr<SocketType>,其中socketType是tcp::socketboost::asio::ssl::stream<tcp::socket>的TypEdef。

BoostSocket(BoostSocket &&s)
    : _socket(std::move(s._socket))
{ }

视觉泄漏检测器指向此堆栈:

  1. AsyncAcceptor::AsyncAcceptManaged
  2. OnSocketOpen-处理程序
  3. shared_ptr-内存(452)-explicit shared_ptr(_Ux *_Px)
  4. void _Resetp(_Ux *_Px)

也有Smilar"奇怪"报告。


Packet p;
//add data & add Packet to a std::queue
QueuePacket(Packet &&p);
//Inside QueuePacket:
_queue.push(std::move(p));

内存泄漏:

  1. 队列(111)-void push(value_type&& _Val)
  2. Deque(1181)-void push_back(value_type&& _Val)

内存泄漏(_socketstd::unique_ptr):

BoostSocket(boost::asio::io_service &ioService)
    : _socket(new SocketType(ioService)) //<--- here
{ }

内存泄漏(m_Memberstd::shared_ptr):

m_Member = std::move(std::shared_ptr<StoredClass>(new StoredClass(shared_from_this()) ) );

大记忆泄漏(_readBufferstd::vector<char>):

_readBuffer.resize(static_cast<std::size_t>(DataUnits::DATA_UNIT_MB) * 5); //resize to 5 MB

我假设最后的内存泄漏是由于插座在内存泄漏报告时未正确破坏的。但是我无法想象问题可能是什么。我的原始指针适当处理,对于我使用智能指针的其他任何东西。

视觉泄漏检测器检测到17个内存泄漏(5244907字节)。

这是VLD?的正确报告,我似乎找不到导致所有内容的内存泄漏。

的任何提示或解决方案。

如果您担心它是否可能是假阳性,则需要在调试器中的循环中运行程序的"核心逻辑"。一次,在Megabytes上,应该很快确认内存是否泄漏。

关于您程序的实际设计:

我的原始指针经过正确处理,对于我使用智能指针的其他任何东西。

...

void OnSocketOpen(BoostSocket *sock)
{
    //set additional socket options and check for error
    //Note: SocketType is template parameter of class - allowing different socket types
    std::shared_ptr<SocketType> newSocket(new SocketType(sock));
    delete sock;
    sock = nullptr;
}

该语句以及我要采样的代码似乎相互矛盾。

一般而言,使用C 11/C 14,通常的建议是永远不要, ever 处理原始指针,并且永远不会使用明确的newdelete关键字。显然,没有规则是100%无误的(我建议您在计算机监视器上写的Sharpie),但是在这种情况下,我会说这仍然是真的99.99 ...%的时间。

因此,每次使用原始指针时,您都需要用std::unique_ptr替换它们(如果它拥有对象)或std::shared_ptr(如果有多个对象拥有),则。我看到您已经在某些对象中使用了std::unique_ptrstd::shared_ptr;确保您正在为指针的全部做此操作。

其次,用适当的std::make_uniquestd::make_shared替换new的每一次使用。除非您正在撰写客户删除,否则消除对delete的任何和所有引用;即使那样,请确保这实际上是您需要的。

第三,要非常小心您的代码语义:

m_Member = std::move(std::shared_ptr<StoredClass>(new StoredClass(shared_from_this()) ) );

作为一般经验法则,没有理由永远"移动" std::shared_ptrstd::shared_ptr存在的原因是允许使用指针的安全复制语义,而在移动是适当选择的情况下,编译器通常足够聪明,可以为您弄清楚。此代码本身可能很好(尽管STD ::移动是多余的),但是您可能需要查看其他代码区域。

我不能指出任何可能是罪魁祸首的特定代码(尤其是因为我们在此处没有完整的代码列表),但是如果您解决了所有这些问题,则很可能会解决内存泄漏(S)。

显然插座没有被自动破坏。我通过在处理新套接字/连接的线程后,在循环中手动关闭所有左插座的问题解决了这个问题。

代码看起来像这样:

//_sockets - std::set with std::shared_ptr<SocketType>
if (_stopped) //std::atomic
{
    for (i = _sockets.begin(); i != _sockets.end();)
    {
        if ((*i)->IsOpen() && (*i)->IsSocketOpen())
        {
            (*i)->CloseSocket();
            SocketRemoved(*i); //Do work when client gets disconnected
            --_connections; //std::atomic
        }
        _sockets.erase(i++);
    }
}

vld不会报告任何内存泄漏,尽管_CrtDumpMemoryLeaks有自己的意见。

未检测到内存泄漏。
视觉泄漏检测器现已退出。