Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is the memory created via unique_ptr not being deleted properly when reset() is called?

I have an application that receives packets at a fast rate, and every time it receives packets, some objects are created to handle them and for the object creation I am using std::unique_ptr.

For some reason they don't seem to be getting cleaned up properly as I can see the memory usage of the application constantly rise.

enter image description here

I took a snapshot to see where the allocations are coming from and it was as expected

enter image description here

Here is the code that is creating these PacketIn and PacketHeader objects

while (!server->BufferEmpty()) {
        std::shared_ptr<Stream> inStream = std::make_shared<Stream>();
        std::vector<unsigned char> buffer = inStream->GetBuffer();

        std::size_t n = server->receive(boost::asio::buffer(buffer),
            boost::posix_time::milliseconds(-1), ec);

        if (ec)
        {
            std::cout << "Receive error: " << ec.message() << "\n";
        }
        else
        {
            std::unique_ptr<IPacketIn> incomingPacket = std::make_unique<IPacketIn>();

            incomingPacket->ReadHeader(inStream);
            std::cout << "Received a buffer! ";
            //std::cout.write(buffer, n);
            std::cout << "\n";

            incomingPacket.reset();
        }

        ++packetsRead;

        inStream.reset();

}

PacketIn

class IPacketIn {
public:
    IPacketIn() {
        m_packetHeader = std::make_unique<PacketHeader>();
    }

    ~IPacketIn() {
        m_packetHeader.reset();
    }

    void ReadHeader(std::shared_ptr<Stream> stream) {
        m_packetHeader->ReadHeader(stream);
    }

private:
    std::unique_ptr<IPacketHeader> m_packetHeader;
};

PacketHeader

class PacketHeader : public IPacketHeader {
public:
    PacketHeader() {

    }

    ~PacketHeader() {

    }

    void ReadHeader(std::shared_ptr<Stream> stream) override {
        //m_uuid = stream->ReadUUID(10);
        //m_timestamp = stream->ReadInt64();
        //m_packetId = stream->ReadShort();
    }

private:
    std::string m_uuid;
    //long m_timestamp;
    //unsigned short m_packetId;

I've stepped through the code and it seems calling reset is clearing the unique_ptr but is it actually deleting the memory it has created or am I missing something?


Edit

So it seems it is not related to the unique_ptr as I have tried swapping to using new and delete with the same issue.

What I have noticed is that the issue occurs when the PacketHeader class has member variables

std::string m_uuid;
long m_timestamp;
unsigned short m_packetId;

When these variables are removed, the issue no longer occurs.

I have narrowed it down to being the std::string uuid;. When this is present in the PacketHeader class it causes the memory to rise but when it is removed it is fine. Why is this?

Are these somehow not being removed when the object is destroyed?

like image 277
jjmcc Avatar asked Mar 06 '23 13:03

jjmcc


2 Answers

It turns out that ownership of instances of PacketHeader class is held through a pointer to base class IPacketHeader which lacks a virtual destructor. So the std::unique_ptr<IPacketHeader> was unable to perform cleanup properly.

like image 73
user7860670 Avatar answered Mar 08 '23 03:03

user7860670


Yes, it is deleting the memory.

Note that neither of the calls to reset are needed - the destructor of the pointer is about to be called in both cases, and that will delete the memory.

Note that monitoring process memory is a very unreliable way to tell if you have a memory leak. Up to some limit, system libraries quite often try not to reuse recently released memory - in order to reduce the impact of use-after-free bugs.

Try using valgrind to see if you have an actual memory leak.

Edit: VTT has clarified that the OP wasn't just monitoring process memory, but using VS memory profiler (which is very similar to valgrind).

like image 25
Martin Bonner supports Monica Avatar answered Mar 08 '23 01:03

Martin Bonner supports Monica