Skip to content

Fix memory leak when LeapMap is dropped after a resize (issue #16)#21

Open
mandrean wants to merge 1 commit intorobclu:mainfrom
mandrean:fix/memory-leak-drop-source-tables
Open

Fix memory leak when LeapMap is dropped after a resize (issue #16)#21
mandrean wants to merge 1 commit intorobclu:mainfrom
mandrean:fix/memory-leak-drop-source-tables

Conversation

@mandrean
Copy link

Two bugs in the Drop impl caused memory to leak on every create-insert-drop cycle that triggered a resize:

  1. After a successful migration the old source table stays in migrator.sources and is only moved to migrator.stale_sources on the next initialize_migrator call. If the map is dropped before a second resize those tables are never freed (~218 MB per cycle as reported in the issue). Fix: iterate migrator.sources in Drop and free each source table, mirroring what cleanup_stale_table does for stale_sources.

  2. Migrator owns sources: Vec<...> and stale_sources: Vec<...>. The previous code called deallocate on the raw struct bytes without first calling drop_in_place, so the Vec backing buffers were never freed. Fix: call std::ptr::drop_in_place(migrator_ptr) before deallocate.

Both fixes are in Drop only. The hot path (insert, get, remove, resize) is unchanged, so there is no performance regression.

A regression test using a custom counting allocator verifies this directly:

Bytes leaked
Before fix (main) ~218 MB per resize cycle
After fix 0

Closes #16.

Two bugs caused memory to leak after each create-insert-drop cycle
that triggered a migration (resize):

1. Source tables never freed on drop: After a successful migration the
   old source table is stored in `migrator.sources` and is only moved
   to `migrator.stale_sources` during the *next* call to
   `initialize_migrator`. If the map is dropped before a second
   migration the source tables are never freed, accounting for the
   ~218 MB per-round leak reported in the issue.

   Fix: iterate `migrator.sources` in `Drop` and explicitly free each
   source table's buckets and struct, mirroring what `cleanup_stale_table`
   does for entries in `stale_sources`.

2. Migrator Vec fields leaked via missing drop_in_place: `Migrator`
   contains `sources: Vec<MigrationSource<K,V>>` and
   `stale_sources: Vec<AtomicPtr<Table<K,V>>>`. The previous code called
   `deallocate` on the raw Migrator memory without first calling
   `drop_in_place`, so those Vecs' backing heap buffers were never freed.

   Fix: call `std::ptr::drop_in_place(migrator_ptr)` immediately before
   `deallocate` so that the Vec destructors run correctly.

A regression test using a custom counting allocator is added to
`leapmap.rs` that asserts zero bytes remain allocated after the map is
dropped following a resize.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fastest, but with memory leaks.

1 participant