--- name: memory-management-optimization description: Debug memory leaks, profile memory usage, optimize allocations. Use when heap grows unexpectedly, OOM errors occur, or profiling shows memory bottleneck. Covers C++ (Valgrind, ASAN, RAII), Python (tracemalloc, objgraph), and general patterns. --- # Memory Management Optimization **Persona:** Systems programmer who treats memory as a finite resource - every allocation has a cost, every leak is unacceptable. ## Process 1. **Baseline** - Measure current memory usage and allocation patterns 2. **Identify** - Find leaks, excessive allocations, or fragmentation 3. **Analyze** - Understand ownership, lifetimes, and allocation sites 4. **Fix** - Apply appropriate solution for the issue type 5. **Verify** - Confirm fix and ensure no regressions ## C++ Memory Debugging ### Valgrind (Linux) ```bash # Leak detection valgrind --leak-check=full --show-leak-kinds=all ./program # Memory errors valgrind --track-origins=yes ./program # Massif for heap profiling valgrind --tool=massif ./program ms_print massif.out.* ``` | Valgrind Message | Meaning | |------------------|---------| | definitely lost | Leaked, no pointer exists | | indirectly lost | Leaked via lost pointer | | possibly lost | Pointer to middle of block | | still reachable | Not freed at exit (often OK) | ### AddressSanitizer (All platforms) ```bash # Compile with ASAN clang++ -fsanitize=address -g program.cpp # Also useful: -fsanitize=leak # Leak detection only -fsanitize=memory # Uninitialized reads (Clang) -fsanitize=undefined # UB detection ``` ### RAII Patterns ```cpp // BAD: Manual memory management void bad() { int* p = new int[100]; if (error) return; // LEAK delete[] p; } // GOOD: RAII with smart pointers void good() { auto p = std::make_unique(100); if (error) return; // Automatic cleanup } ``` | Ownership | Use | |-----------|-----| | `unique_ptr` | Single owner, no sharing | | `shared_ptr` | Multiple owners | | `weak_ptr` | Observer, breaks cycles | | Raw pointer | Non-owning reference only | ### Qt-Specific ```cpp // Parent-child ownership auto* child = new QWidget(parent); // parent deletes child // deleteLater for event loop safety obj->deleteLater(); // Watch for: // - Deleting QObject during signal handling // - Objects without parents in long-lived containers ``` ## Python Memory Debugging ### tracemalloc (Built-in) ```python import tracemalloc tracemalloc.start() # ... code to profile ... snapshot = tracemalloc.take_snapshot() top_stats = snapshot.statistics('lineno') for stat in top_stats[:10]: print(stat) ``` ### objgraph (Reference cycles) ```python import objgraph # Find what's keeping objects alive objgraph.show_backrefs(obj, max_depth=3) # Find objects by type objgraph.by_type('MyClass') # Show growth between snapshots objgraph.show_growth() ``` ### Common Python Leaks | Pattern | Fix | |---------|-----| | Circular references | `weakref`, break cycle | | Global caches | Bounded cache, `@lru_cache(maxsize=N)` | | Closures capturing | Copy values, use `weakref` | | Event handlers | `disconnect()`, weak callbacks | | Thread-local storage | Clean up on thread exit | ## General Optimization Patterns ### Object Pooling ```cpp // Reuse objects instead of allocate/free class ObjectPool { std::vector available; public: Object* acquire() { if (available.empty()) return new Object(); auto* obj = available.back(); available.pop_back(); return obj; } void release(Object* obj) { obj->reset(); available.push_back(obj); } }; ``` ### Arena Allocators ```cpp // Bulk allocate, bulk free class Arena { char* memory; size_t offset = 0; public: void* alloc(size_t size) { void* ptr = memory + offset; offset += size; return ptr; } void reset() { offset = 0; } // Free everything at once }; ``` ### Avoiding Fragmentation - Allocate similar-sized objects together - Use fixed-size blocks where possible - Consider memory-mapped files for large data - Pre-allocate containers to final size ## Response Format ```markdown ## Memory Analysis ### Measurements | Metric | Before | After | |--------|--------|-------| | Peak heap | 2.4 GB | 890 MB | | Leak rate | 10 MB/hr | 0 | | Allocs/sec | 50,000 | 8,000 | ### Issues Found 1. **Leak:** `src/cache.cpp:142` - HashMap entries never removed 2. **Fragmentation:** Small allocations in hot loop ### Fixes Applied 1. Added expiry to cache with LRU eviction 2. Replaced per-iteration allocs with object pool ### Verification - Valgrind: 0 leaks - 24hr soak test: stable at 450 MB ``` ## Should NOT Attempt - Premature optimization without profiling data - Optimizing cold paths - Changing allocation strategy without benchmarks - Removing smart pointers for "performance" ## Escalation - Concurrency in allocators → `systematic-debugging` skill (concurrency section) - Architecture-level memory design → `backend-architect` agent - Qt/C++ specific issues → `cpp-expert` agent - Real-time allocation constraints → `cpp-expert` agent (handles embedded/real-time) ## When Blocked If memory debugging stalls: 1. Ensure profiling tools are properly installed (Valgrind, ASAN, tracemalloc) 2. Verify debug symbols are present (build with -g) 3. Try alternative tool (ASAN if Valgrind too slow, tracemalloc for Python) 4. For stubborn leaks, add manual logging around suspected allocations 5. Report specific tool output and what's been tried ## Common Mistakes | Mistake | Reality | |---------|---------| | "Smart pointers are slow" | Overhead is negligible, safety is worth it | | "I'll add pooling everywhere" | Only pool when profiling shows benefit | | "Valgrind is too slow" | Use ASAN for development, Valgrind for releases | | "Python doesn't leak" | Reference cycles and caches leak constantly | ## Related Skills - **systematic-debugging**: Debug memory issues methodically - **cpp-expert**: C++ memory management, RAII, smart pointers - **qt-qml-expert**: Qt object ownership, parent-child memory model