A store had a third of its entries deleted an hour ago; its data total fell but the process never shrank — why?
answer
- freed to the process, not the machine
- free lists, not the kernel
- pages come back whole or not at all
- one survivor pins a whole page
- reuse, relocation, or restart
basics
~20 sFreeing an entry returns its bytes to the process's own allocator, not to the operating system. Those blocks wait in free lists for reuse, and the process shrinks only when a whole region happens to empty — which a deletion scattered across the keyspace rarely produces.
solid answer
~50 sThe bytes came back to the process, not to the machine. A deletion frees one entry's allocation, and that allocation goes onto the allocator's free lists so the next write can reuse it. The operating system, though, reclaims at page granularity and larger; a page that still holds a single live entry stays with the process no matter how empty the rest of it is. Because the deleted entries were scattered across the whole keyspace, almost every page keeps at least one survivor, so almost nothing is returnable. The space is not lost to the store — it will be reused by subsequent writes — it is lost to everything else on the machine. Allocators differ in how aggressively they return large empty spans, some stores offer a facility that relocates entries to consolidate free space, and where neither applies, a restart is what returns it.
go deeper
Remember that deleting entries makes the store's own total go down without making the machine's view of the process go down. The two numbers move independently, and that is normal rather than broken.
Explain the granularity mismatch: the store frees single entries of mixed sizes, the operating system reclaims whole pages, and a page with one live entry left on it cannot be returned. Note that the freed space is still fully usable by the store.
Read it against the trend rather than the snapshot, and know the remedies in order of bluntness — let the workload refill it, relocate entries where the store can, restart when it cannot. Say out loud what a restart costs before proposing one.
Frame the deletion pattern itself as the design lever: a keyspace purged in bulk will open a gap that the machine keeps paying for, so gradual deletion or a separate tier for the disposable entries changes the memory profile without any tuning at all.
## Where the freed bytes actually went Deleting an entry is a call into the process's own allocator, and it ends there. The allocator marks that block reusable and records it on a free list. Nothing has been said to the operating system, and the operating system's view of the process — its **resident size** — is unchanged. Meanwhile the store's own **data size**, which is its sum over the entries it believes it holds, drops immediately, because from the store's point of view those entries are gone. So one number falls and the other does not, and both are correct. That divergence is the whole phenomenon, and it is why the gap between the two figures widens sharply after a mass deletion or a large expiry wave and then stays wide. ## Why the operating system cannot take it back The mismatch is one of granularity: - The store frees **one entry at a time**, in whatever size that entry happened to need. - The operating system reclaims in **pages and larger spans**, all or nothing. - The deleted entries were **interleaved with the survivors**, because they were allocated as they arrived, not grouped by anything you deleted on. The result is arithmetic, not a defect: if a third of the entries on every page are gone, every page is a third empty and no page is empty. A page holding one live entry is a page the process must keep. The freed third is real, usable, and unreturnable. This is also why the shape of a deletion matters more than its size. A steady trickle of deletions is continuously refilled by new writes of broadly similar size, so blocks are reused almost as fast as they are freed and the gap never opens. A mass deletion frees space far faster than the workload refills it, and does so everywhere at once. ## What happens next | Time | Data size | Resident size | The gap | |---|---|---|---| | Before the deletion | High | High | Narrow | | Immediately after | Drops by a third | Unchanged | Opens sharply | | As new writes arrive | Climbs again | Roughly flat | Closes gradually | | If writes never come | Stays low | Stays high | Stays open indefinitely | The fourth row is the one that gets misread as a leak. A process whose workload never reclaims the freed space keeps holding it, with no defect anywhere and nothing to find. ## What actually returns it 1. **The workload itself.** New entries of similar sizes land in the free blocks. This is by far the most common resolution and requires nothing from you. 2. **The allocator, opportunistically.** If a large span does become entirely free, some allocators hand it back to the operating system and some hold on to it. This varies by allocator and is not a property of in-memory stores. 3. **Relocation, where the store offers it.** Some stores can move live entries together in the background so that whole regions empty and can be released. It costs serving time while it runs, and not every store in this class has such a facility at all. 4. **A restart.** The blunt instrument. It always works, because a fresh process starts with no history, and it costs you whatever the tier was holding that has no other copy, plus the period during which the tier is cold. ## Where implementations diverge Everything above assumes a general allocator carving variable-sized pieces. A store that allocates from **fixed-size classes** behaves differently after the same deletion: it hands out whole blocks from predetermined size buckets, so the freed blocks go back to the bucket they were cut for and the store counts them within its own total either way. On such a store the data-size-to-resident-size gap may barely move after a mass deletion, and the freed space can still be unusable — by values that need a different size. Same workload, completely different signature. So "deleting entries did not shrink the process" is expected on this whole class of systems, but the *reason* and the *evidence you can see* depend on which allocation scheme is underneath. ## The judgment to take away An hour after a mass deletion, an unchanged resident size is the aftermath, not a symptom. What would make it a symptom is the trend: a gap that opened at an event and then stays flat is that event; a gap that keeps climbing day after day while the entry count stays level is something else entirely. Treat the freed-but-unreturned memory as occupied for planning purposes — it is real memory the process is holding and other things on the machine cannot have — while not treating it as a defect to hunt.
- Is the freed memory wasted from the store's own point of view?No. Those blocks are on the allocator's free lists and the next writes will land in them, so the store can grow back to its previous entry count without asking the operating system for a byte. The memory is unavailable to everything else on the machine, which is a real cost, but it is not lost to the store.
- Why does a steady trickle of deletions not open the same gap?Because it is continuously refilled. New writes of broadly similar size claim freed blocks about as fast as deletions release them, so free space never accumulates. A mass deletion releases space far faster than the workload can consume it, and releases it across every region at once rather than in one place.
- What would make the process actually shrink without a restart?A whole region becoming entirely free and the allocator choosing to hand it back — which needs either luck in how the survivors are laid out, or a store that can relocate live entries in the background to consolidate free space. Allocators differ in how eagerly they return spans, and not every store offers relocation at all.
A warehouse that clears a third of its pallets from every aisle still needs the whole building. The floor space came back to the warehouse, not to the landlord, and only consolidating the remaining pallets into fewer aisles lets you hand a wing back.
saying these in an interview costs you the question
- Assumes deleting entries hands memory straight back to the operating system.
- Calls the unchanged resident size a leak an hour after a mass deletion.
- Believes the freed space is unusable by the store itself.
- Expects the process to shrink the moment its data total falls.
- Thinks a scattered deletion frees whole pages.