I have the following scenario, when starting my application, I subscribe to a middleware and download the latest state (snapshot), I need to do some filtering logic and then process the snapshot. To that aim, I put the initial snapshot in a std::unordered_map
. After that I no longer need the map so I'd like its memory to be released back to the OS. The unserialized snapshot records take a considerable amount of memory, up to 15GB. Thus, it's quite important to release that memory. This is the code I have.
class Consumer
{
public:
Consumer(Processor& p)
: m_processor(p)
, m_snapshot(false)
, m_sub([this](const auto& msg) {
if (m_snapshot)
addToCache(msg);
else
m_processor.process(Order(msg));
},
[this](const auto& state) {
if (state == State::BEGIN)
m_snapshot = true;
else if (state == State::END)
m_snapshot = false;
playCache();
})
{
}
private:
void addToCache(const Input& input)
{
// if logic
m_cache[input.header.key()] = Order(input);
}
void playCache()
{
for (auto& [_, order] : m_cache)
m_processor(std::move(order));
m_cache.clear();
}
private:
Processor m_processor;
bool m_snapshot;
Sub m_sub;
std::unordered_map<std::string, Order> m_cache;
};
This clearly doesn't release the memory back to the OS (checking in htop
for my process it still uses the same amount of memory).
The 'm_processor' "consumes" the Order
message, storing only a serialized version in a string.
I am pretty sure the memory isn't released and it's coming from this map, because I did the following tests:
- No doing any filtering (not adding to
m_cache
at all) and simply processing all messages (even if they are more) yields a significantly lower memory consumption around 5GB vs 15GB of having the cache to filter and then processing. - I am pretty sure there's no memory leak on the upstream
m_sub
. I tried commenting out the part of adding to the map and processing. Aka, still getting the snapshot but throwing that away and again I get much less memory usage.
I know that .clear()
doesn't imply deallocating the memory, thus as suggested in other post I tried the following:
- Doing a swap on
playCache
ie:
void playCache()
{
for (auto& [_, order] : m_cache)
m_processor(std::move(order));
m_cache.clear();
std::unordered_map<std::string, Order> empty;
std::swap(m_cache, empty);
}
same result, memory is not released back to the OS.
- Making
m_cache
astd::unique_ptr
and calling reset once I am done:
void playCache()
{
for (auto& [_, order] : *m_cache)
m_processor(std::move(order));
m_cache->clear();
m_cache.reset();
}
Still same result.
Am I missing something? Why is the memory not released back to the OS?
If it helps the data type is simply:
struct Order {
Order() = default;
Order(const Input& i)
: header_(i.header())
, body_(deserializeProto(i.body())
{
}
protobuf::Header header_;
protobuf::Body body_;
};
Thanks in advance.