Skip to content

Potential Bug: mi_free() Fails to Reduce Committed Memory When Called from Different Thread #1187

@Niteip

Description

@Niteip

Hello, our team has encountered an issue while using the mimalloc library:
Under certain conditions, the total committed memory does not decrease after calling std::vector::shrink_to_fit().

Background information:
Our development environment:
Windows 10, VS2022, C++17, mimalloc 2.1.7 release
We have overloaded the global new operator to our own memory allocator, GlobalMemory.
And internally, GlobalMemory uses mi_malloc_aligned() and mi_free() to perform its tasks.
Recently, while conducting memory optimization, I found that a vector should be released, so I wrote the release code:

some_data.clear();
some_data.shrink_to_fit();
// some_data is defined as std::vector<SomeStruct>

To my surprise, the total committed memory of the process did not decrease.
Through our project's internal memory statistics system, I clearly saw that 1.5GB was released, but in the Windows Task Manager, our total committed memory did not decrease.
(In our project, there are approximately 500 some_data instances, each about several megabytes, totaling 1.5GB)

Subsequently, I conducted a series of tests:

Test 1:
Changing the mimalloc dynamic library to a static library did not resolve the issue.

Test 2:
Searching for information on GitHub, I found in the README: "2025-06-09, v1.9.4, v2.2.4, v3.1.4 (beta): Some important bug fixes, including a case where OS memory was not always fully released."
I thought this was crucial, so I upgraded mimalloc to v2.2.4, BUT the issue persisted.

Test 3:
Instead of using mimalloc, I replaced mi_malloc_aligned() and mi_free() inside GlobalMemory with _aligned_malloc() and _aligned_free().
The issue disappeared, and I saw that the total committed memory of the process also decreased by approximately 1.5GB.

Test 4:
I attempted to debug and followed shrink_to_fit() into mi_free.
I found that the call chain is:
mi_free() -> mi_free_generic_mt() -> mi_free_block_mt() -> mi_free_block_delayed_mt()
At this point, I realized something: the allocating thread and the freeing thread were different, and in mi_free(), the bool is_local was false.
I suspected this was the cause, so I tried to make the freeing thread the same as the allocating thread, and it succeeded; the total committed memory correctly decreased.

Conclusion:
When calling mi_free(), if the calling thread is different from the allocating thread, there may be a bug that causes the release to fail.

I hope you can take a look at this issue.

Additionally, Christmas is approaching, and I wish you a Merry Christmas.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions