-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
HashMap reports zero capacity without freeing memory #79178
Comments
I don’t exactly remember the API for |
@Diggsey I looked into this, and it kind of seems intentional?
if guard.len() * 3 < guard.capacity() {
guard.shrink_to_fit();
}
That condition is never true, and is not executed at all. So the line that's commented out is the first time it is actually called. Regardless, So when the last
I was curious so I wanted to test this out so I tried creating a
Update: Nevermind, I completely missed setting the default hasher. |
Yes... that's the issue. The expression should evaluate to true, but it's not because there is a situation where hashbrown reports a capacity of zero, but the backing memory has not been freed.
That's not the issue: the issue is that
The method is |
Why should that evaluate to true? Are you saying that the capacity should reduce appropriately when items are removed? |
The way hashbrown works, the capacity already reduces (in some cases) when items are removed, and that's what causes the issue. The logic of the condition is this: "if the hashmap is less than 1/3 full, then shrink it to fit the number of items". At some point whilst removing items, this should result in the hashmap becoming empty, since at some point the number of items should hit zero whilst the capacity is non-zero (in which case the hashmap is less than 1/3 full, since 0 < 1/3). The problem is that very occasionally, when you remove the last item from the hashmap, the capacity reported by the map also drops to zero, even though no memory has been freed. |
Ah! gotcha, sorry I was understanding this the wrong way around |
It's generally expected that when std collections return a capacity of zero, that they are not backed by any allocation. I believe this was also true for the HashMap prior to hashbrown, although I could be wrong.
In any case, whilst
HashMap::capacity()
is documented as a lower bound I still find it extremely surprising that it reports a capacity of zero without freeing its memory.You can reproduce the issue with the following program (using mockalloc to detect the leaked memory):
Given these lines:
I expected all the memory to be reclaimed by the end of the loop. However, this is not the case: the memory is only reclaimed if
shrink_to_fit()
is called an additional time.IMO, the fix should be two-fold:
When the last element of a HashMap is removed, it should automatically clear out any tombstones so that the
capacity()
method is accurate again.A new method should be introduced to obtain the upper bound on the capacity. At the moment code which attempts to shrink under-utilized hashmaps is broken, and it's not possible to fix this code without a new method.
The text was updated successfully, but these errors were encountered: