Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,11 @@ Consider this migration scenario:
```rust
use ic_stable_structures::{
memory_manager::{MemoryId, MemoryManager},
BTreeMap, DefaultMemoryImpl,
BTreeMap, DefaultMemoryImpl, Memory,
};

let mem_mgr = MemoryManager::init(DefaultMemoryImpl::default());
let mem = DefaultMemoryImpl::default();
let mem_mgr = MemoryManager::init(mem.clone());
let (mem_id_a, mem_id_b) = (MemoryId::new(0), MemoryId::new(1));

// ========================================
Expand All @@ -122,23 +123,32 @@ map_a.insert(1, b'A'); // Populate map A with data
let data = map_a.get(&1); // Extract data for migration
map_a.clear_new(); // A is now empty
drop(map_a); // Memory stays allocated to mem_id_a
let actual_size_before_migration = mem.size();

let mut map_b: BTreeMap<u64, u8, _> = BTreeMap::init(mem_mgr.get(mem_id_b));
map_b.insert(1, data.unwrap()); // B allocates NEW memory
// Result: 2x memory usage
let actual_size_after_migration = mem.size();
// Result: ~2x memory usage
// Memory allocation grew (waste)
assert!(actual_size_before_migration < actual_size_after_migration);

// ========================================
// Scenario 2: WITH reclamation
// ========================================
let mut map_a: BTreeMap<u64, u8, _> = BTreeMap::init(mem_mgr.get(mem_id_a));
map_a.insert(1, b'A'); // Populate map A with data
let data = map_a.get(&1); // Extract data for migration
map_a.clear_new(); // A is now empty
drop(map_a); // Drop A completely
let actual_size_before_migration = mem.size();
mem_mgr.reclaim_memory(mem_id_a); // Free A's memory buckets for reuse

let mut map_b: BTreeMap<u64, u8, _> = BTreeMap::init(mem_mgr.get(mem_id_b));
map_b.insert(1, data.unwrap()); // B reuses A's reclaimed memory buckets
let actual_size_after_migration = mem.size();
// Result: 1x memory usage
// Memory allocation stayed the same (no waste)
assert!(actual_size_before_migration == actual_size_after_migration);
```

**Important**: Always drop the original structure before calling `reclaim_memory`.
Expand Down
16 changes: 13 additions & 3 deletions docs/src/concepts/memory-manager.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,11 @@ The `MemoryManager` provides a `reclaim_memory` method to efficiently handle the
```rust
use ic_stable_structures::{
memory_manager::{MemoryId, MemoryManager},
BTreeMap, DefaultMemoryImpl,
BTreeMap, DefaultMemoryImpl, Memory,
};

let mem_mgr = MemoryManager::init(DefaultMemoryImpl::default());
let mem = DefaultMemoryImpl::default();
let mem_mgr = MemoryManager::init(mem.clone());
let (mem_id_a, mem_id_b) = (MemoryId::new(0), MemoryId::new(1));

// ========================================
Expand All @@ -61,23 +62,32 @@ map_a.insert(1, b'A'); // Populate map A with data
let data = map_a.get(&1); // Extract data for migration
map_a.clear_new(); // A is now empty
drop(map_a); // Memory stays allocated to mem_id_a
let actual_size_before_migration = mem.size();

let mut map_b: BTreeMap<u64, u8, _> = BTreeMap::init(mem_mgr.get(mem_id_b));
map_b.insert(1, data.unwrap()); // B allocates NEW memory
// Result: 2x memory usage
let actual_size_after_migration = mem.size();
// Result: ~2x memory usage
// Memory allocation grew (waste)
assert!(actual_size_before_migration < actual_size_after_migration);

// ========================================
// Scenario 2: WITH reclamation
// ========================================
let mut map_a: BTreeMap<u64, u8, _> = BTreeMap::init(mem_mgr.get(mem_id_a));
map_a.insert(1, b'A'); // Populate map A with data
let data = map_a.get(&1); // Extract data for migration
map_a.clear_new(); // A is now empty
drop(map_a); // Drop A completely
let actual_size_before_migration = mem.size();
mem_mgr.reclaim_memory(mem_id_a); // Free A's memory buckets for reuse

let mut map_b: BTreeMap<u64, u8, _> = BTreeMap::init(mem_mgr.get(mem_id_b));
map_b.insert(1, data.unwrap()); // B reuses A's reclaimed memory buckets
let actual_size_after_migration = mem.size();
// Result: 1x memory usage
// Memory allocation stayed the same (no waste)
assert!(actual_size_before_migration == actual_size_after_migration);
```

```admonish info ""
Expand Down