Skip to content

🧩 Quản lý bộ nhớ nâng cao trong C++

📖 Giới thiệu

Quản lý bộ nhớ nâng cao là một kỹ năng quan trọng để viết ứng dụng C++ hiệu suất cao và ổn định. Ngoài smart pointers cơ bản, còn có nhiều techniques và tools để tối ưu hóa memory usage, tránh memory leaks, và cải thiện performance.

Vấn đề với Default Memory Management:

  • Fragmentation: Bộ nhớ bị phân mảnh sau nhiều alloc/free
  • Performance: new/delete có overhead
  • Cache misses: Objects không được arrange tối ưu
  • Memory leaks: Khó detect và debug

Advanced Techniques:

  • Custom Allocators: Tối ưu allocation patterns
  • Memory Pools: Pre-allocated memory blocks
  • Object Pools: Reuse objects thay vì allocate mới
  • Stack Allocators: Linear allocation cho temporary objects
  • Memory Mapping: Sử dụng OS memory mapping

Tools và Debugging:

  • Valgrind: Memory leak detection trên Linux
  • AddressSanitizer: Compiler-based memory error detection
  • Memory profilers: Analyze memory usage patterns
  • Static analysis: Compile-time memory safety checks

🔧 Cú pháp

Custom Allocators

cpp
#include <iostream>
#include <memory>
#include <vector>
#include <cstdlib>
using namespace std;

// Simple custom allocator
template<typename T>
class DebugAllocator {
public:
    using value_type = T;
    using size_type = size_t;
    using difference_type = ptrdiff_t;
    
    static size_t allocations;
    static size_t deallocations;
    static size_t bytesAllocated;
    
    DebugAllocator() = default;
    
    template<typename U>
    DebugAllocator(const DebugAllocator<U>&) noexcept {}
    
    T* allocate(size_t n) {
        size_t bytes = n * sizeof(T);
        T* ptr = static_cast<T*>(malloc(bytes));
        
        allocations++;
        bytesAllocated += bytes;
        
        cout << "🔧 Allocated " << bytes << " bytes at " << ptr << endl;
        return ptr;
    }
    
    void deallocate(T* ptr, size_t n) {
        size_t bytes = n * sizeof(T);
        deallocations++;
        bytesAllocated -= bytes;
        
        cout << "🗑️ Deallocated " << bytes << " bytes at " << ptr << endl;
        free(ptr);
    }
    
    template<typename U>
    bool operator==(const DebugAllocator<U>&) const noexcept {
        return true;
    }
    
    template<typename U>
    bool operator!=(const DebugAllocator<U>&) const noexcept {
        return false;
    }
    
    static void printStats() {
        cout << "📊 Allocations: " << allocations 
             << ", Deallocations: " << deallocations 
             << ", Active bytes: " << bytesAllocated << endl;
    }
};

template<typename T>
size_t DebugAllocator<T>::allocations = 0;

template<typename T>
size_t DebugAllocator<T>::deallocations = 0;

template<typename T>
size_t DebugAllocator<T>::bytesAllocated = 0;

// Usage with STL containers
using DebugVector = vector<int, DebugAllocator<int>>;

Memory Pool Implementation

cpp
#include <cassert>
#include <new>

class MemoryPool {
private:
    struct Block {
        Block* next;
    };
    
    void* memory_;
    Block* freeList_;
    size_t blockSize_;
    size_t blockCount_;
    size_t allocatedBlocks_;
    
public:
    MemoryPool(size_t blockSize, size_t blockCount) 
        : blockSize_(blockSize), blockCount_(blockCount), allocatedBlocks_(0) {
        
        // Ensure minimum block size
        if (blockSize_ < sizeof(Block*)) {
            blockSize_ = sizeof(Block*);
        }
        
        // Allocate memory
        memory_ = malloc(blockSize_ * blockCount_);
        if (!memory_) {
            throw bad_alloc();
        }
        
        // Initialize free list
        freeList_ = static_cast<Block*>(memory_);
        Block* current = freeList_;
        
        for (size_t i = 0; i < blockCount_ - 1; i++) {
            current->next = reinterpret_cast<Block*>(
                reinterpret_cast<char*>(current) + blockSize_
            );
            current = current->next;
        }
        current->next = nullptr;
        
        cout << "🏊 MemoryPool created: " << blockCount_ 
             << " blocks of " << blockSize_ << " bytes" << endl;
    }
    
    ~MemoryPool() {
        if (allocatedBlocks_ > 0) {
            cout << "⚠️ Warning: " << allocatedBlocks_ 
                 << " blocks still allocated!" << endl;
        }
        free(memory_);
        cout << "🏊 MemoryPool destroyed" << endl;
    }
    
    void* allocate() {
        if (!freeList_) {
            cout << "❌ MemoryPool exhausted!" << endl;
            return nullptr;
        }
        
        Block* block = freeList_;
        freeList_ = freeList_->next;
        allocatedBlocks_++;
        
        cout << "🔧 Pool allocated block " << allocatedBlocks_ 
             << " at " << block << endl;
        return block;
    }
    
    void deallocate(void* ptr) {
        if (!ptr) return;
        
        Block* block = static_cast<Block*>(ptr);
        block->next = freeList_;
        freeList_ = block;
        allocatedBlocks_--;
        
        cout << "🗑️ Pool deallocated block, " << allocatedBlocks_ 
             << " blocks remaining" << endl;
    }
    
    size_t getBlockSize() const { return blockSize_; }
    size_t getTotalBlocks() const { return blockCount_; }
    size_t getAllocatedBlocks() const { return allocatedBlocks_; }
    size_t getFreeBlocks() const { return blockCount_ - allocatedBlocks_; }
    
    bool isEmpty() const { return allocatedBlocks_ == 0; }
    bool isFull() const { return allocatedBlocks_ == blockCount_; }
};

Stack Allocator

cpp
class StackAllocator {
private:
    char* memory_;
    size_t size_;
    size_t offset_;
    
public:
    StackAllocator(size_t size) : size_(size), offset_(0) {
        memory_ = static_cast<char*>(malloc(size));
        if (!memory_) {
            throw bad_alloc();
        }
        cout << "📚 StackAllocator created: " << size << " bytes" << endl;
    }
    
    ~StackAllocator() {
        if (offset_ > 0) {
            cout << "⚠️ Warning: " << offset_ << " bytes not deallocated!" << endl;
        }
        free(memory_);
        cout << "📚 StackAllocator destroyed" << endl;
    }
    
    void* allocate(size_t bytes, size_t alignment = alignof(max_align_t)) {
        // Align offset
        size_t aligned_offset = (offset_ + alignment - 1) & ~(alignment - 1);
        
        if (aligned_offset + bytes > size_) {
            cout << "❌ StackAllocator: Not enough space!" << endl;
            return nullptr;
        }
        
        void* ptr = memory_ + aligned_offset;
        offset_ = aligned_offset + bytes;
        
        cout << "📚 Stack allocated " << bytes << " bytes at offset " 
             << aligned_offset << endl;
        return ptr;
    }
    
    void deallocateToMarker(size_t marker) {
        if (marker <= size_) {
            offset_ = marker;
            cout << "📚 Stack deallocated to marker " << marker << endl;
        }
    }
    
    size_t getCurrentMarker() const { return offset_; }
    size_t getRemainingSpace() const { return size_ - offset_; }
    
    void reset() {
        offset_ = 0;
        cout << "📚 Stack allocator reset" << endl;
    }
};

RAII Memory Guards

cpp
#include <functional>

// RAII guard for automatic cleanup
template<typename T>
class MemoryGuard {
private:
    T* ptr_;
    function<void(T*)> deleter_;
    
public:
    explicit MemoryGuard(T* ptr, function<void(T*)> deleter = [](T* p) { delete p; })
        : ptr_(ptr), deleter_(deleter) {}
    
    ~MemoryGuard() {
        if (ptr_) {
            deleter_(ptr_);
        }
    }
    
    // Move-only semantics
    MemoryGuard(const MemoryGuard&) = delete;
    MemoryGuard& operator=(const MemoryGuard&) = delete;
    
    MemoryGuard(MemoryGuard&& other) noexcept 
        : ptr_(other.ptr_), deleter_(move(other.deleter_)) {
        other.ptr_ = nullptr;
    }
    
    MemoryGuard& operator=(MemoryGuard&& other) noexcept {
        if (this != &other) {
            if (ptr_) deleter_(ptr_);
            ptr_ = other.ptr_;
            deleter_ = move(other.deleter_);
            other.ptr_ = nullptr;
        }
        return *this;
    }
    
    T* get() const { return ptr_; }
    T* release() {
        T* temp = ptr_;
        ptr_ = nullptr;
        return temp;
    }
    
    void reset(T* ptr = nullptr) {
        if (ptr_) deleter_(ptr_);
        ptr_ = ptr;
    }
    
    explicit operator bool() const { return ptr_ != nullptr; }
    T& operator*() const { return *ptr_; }
    T* operator->() const { return ptr_; }
};

// Factory function
template<typename T, typename... Args>
MemoryGuard<T> makeGuarded(Args&&... args) {
    return MemoryGuard<T>(new T(forward<Args>(args)...));
}

🔬 Phân tích & Giải thích chi tiết

Memory Layout và Alignment

Memory Alignment:

cpp
#include <iostream>
#include <cstddef>

struct AlignmentDemo {
    char c;     // 1 byte
    int i;      // 4 bytes (aligned to 4-byte boundary)
    double d;   // 8 bytes (aligned to 8-byte boundary)
};

void demonstrateAlignment() {
    cout << "sizeof(char): " << sizeof(char) << endl;
    cout << "sizeof(int): " << sizeof(int) << endl;
    cout << "sizeof(double): " << sizeof(double) << endl;
    cout << "sizeof(AlignmentDemo): " << sizeof(AlignmentDemo) << endl;
    
    cout << "alignof(char): " << alignof(char) << endl;
    cout << "alignof(int): " << alignof(int) << endl;
    cout << "alignof(double): " << alignof(double) << endl;
    cout << "alignof(AlignmentDemo): " << alignof(AlignmentDemo) << endl;
    
    AlignmentDemo demo;
    cout << "Address of c: " << (void*)&demo.c << endl;
    cout << "Address of i: " << (void*)&demo.i << endl;
    cout << "Address of d: " << (void*)&demo.d << endl;
}

Cache-Friendly Memory Layout

Data Structure of Arrays vs Array of Structures:

cpp
// Array of Structures (AoS) - not cache friendly
struct Particle_AoS {
    float x, y, z;     // Position
    float vx, vy, vz;  // Velocity
};

// Structure of Arrays (SoA) - cache friendly
struct ParticleSystem_SoA {
    vector<float> x, y, z;        // Positions
    vector<float> vx, vy, vz;     // Velocities
};

void updateParticles_AoS(vector<Particle_AoS>& particles) {
    for (auto& p : particles) {
        p.x += p.vx;  // Cache miss when jumping between particles
        p.y += p.vy;
        p.z += p.vz;
    }
}

void updateParticles_SoA(ParticleSystem_SoA& system) {
    size_t count = system.x.size();
    for (size_t i = 0; i < count; i++) {
        system.x[i] += system.vx[i];  // Better cache locality
        system.y[i] += system.vy[i];
        system.z[i] += system.vz[i];
    }
}

Memory Debugging Techniques

Custom new/delete Operators:

cpp
#include <cstdio>

class TrackedObject {
private:
    static size_t objectCount;
    static size_t totalMemory;
    
public:
    static void* operator new(size_t size) {
        void* ptr = malloc(size);
        objectCount++;
        totalMemory += size;
        printf("🔧 new: %zu bytes at %p (total: %zu objects, %zu bytes)\n", 
               size, ptr, objectCount, totalMemory);
        return ptr;
    }
    
    static void operator delete(void* ptr, size_t size) {
        objectCount--;
        totalMemory -= size;
        printf("🗑️ delete: %zu bytes at %p (remaining: %zu objects, %zu bytes)\n", 
               size, ptr, objectCount, totalMemory);
        free(ptr);
    }
    
    static void printStats() {
        printf("📊 Objects: %zu, Memory: %zu bytes\n", objectCount, totalMemory);
    }
};

size_t TrackedObject::objectCount = 0;
size_t TrackedObject::totalMemory = 0;

💻 Ví dụ minh họa

Ví dụ 1: Game Entity Manager với Object Pool

cpp
#include <iostream>
#include <vector>
#include <queue>
#include <memory>
#include <string>
#include <chrono>
using namespace std;

// Base Entity class
class Entity {
protected:
    static int nextId;
    int id;
    bool active;
    string name;
    
public:
    Entity() : id(nextId++), active(false) {
        cout << "🎮 Entity[" << id << "] constructed" << endl;
    }
    
    virtual ~Entity() {
        cout << "🎮 Entity[" << id << "] \"" << name << "\" destructed" << endl;
    }
    
    virtual void initialize(const string& entityName) {
        name = entityName;
        active = true;
        cout << "✅ Entity[" << id << "] \"" << name << "\" initialized" << endl;
    }
    
    virtual void reset() {
        active = false;
        name = "";
        cout << "🔄 Entity[" << id << "] reset" << endl;
    }
    
    virtual void update(float deltaTime) = 0;
    
    int getId() const { return id; }
    bool isActive() const { return active; }
    const string& getName() const { return name; }
    void setActive(bool state) { active = state; }
};

int Entity::nextId = 1;

// Specific entity types
class Player : public Entity {
private:
    float health;
    float score;
    
public:
    void initialize(const string& entityName) override {
        Entity::initialize(entityName);
        health = 100.0f;
        score = 0.0f;
    }
    
    void reset() override {
        Entity::reset();
        health = 0.0f;
        score = 0.0f;
    }
    
    void update(float deltaTime) override {
        if (active) {
            // Simple player update logic
            score += deltaTime * 10.0f;
        }
    }
    
    float getHealth() const { return health; }
    float getScore() const { return score; }
    
    void takeDamage(float damage) {
        health -= damage;
        if (health <= 0) {
            cout << "💀 Player[" << id << "] died!" << endl;
            active = false;
        }
    }
};

class Enemy : public Entity {
private:
    float health;
    float speed;
    
public:
    void initialize(const string& entityName) override {
        Entity::initialize(entityName);
        health = 50.0f;
        speed = 2.0f;
    }
    
    void reset() override {
        Entity::reset();
        health = 0.0f;
        speed = 0.0f;
    }
    
    void update(float deltaTime) override {
        if (active) {
            // Simple AI logic
            // Move towards player, attack, etc.
        }
    }
    
    float getHealth() const { return health; }
    float getSpeed() const { return speed; }
};

// Object Pool for efficient entity management
template<typename T>
class ObjectPool {
private:
    vector<unique_ptr<T>> pool;
    queue<T*> available;
    size_t poolSize;
    size_t activeObjects;
    
public:
    ObjectPool(size_t size) : poolSize(size), activeObjects(0) {
        pool.reserve(size);
        
        // Pre-allocate objects
        for (size_t i = 0; i < size; i++) {
            auto obj = make_unique<T>();
            available.push(obj.get());
            pool.push_back(move(obj));
        }
        
        cout << "🏊 ObjectPool<" << typeid(T).name() << "> created with " 
             << size << " objects" << endl;
    }
    
    ~ObjectPool() {
        cout << "🏊 ObjectPool<" << typeid(T).name() << "> destroyed" << endl;
    }
    
    T* acquire() {
        if (available.empty()) {
            cout << "❌ ObjectPool exhausted!" << endl;
            return nullptr;
        }
        
        T* obj = available.front();
        available.pop();
        activeObjects++;
        
        cout << "🔧 Acquired object from pool (" << activeObjects 
             << "/" << poolSize << " active)" << endl;
        return obj;
    }
    
    void release(T* obj) {
        if (obj) {
            obj->reset();
            available.push(obj);
            activeObjects--;
            
            cout << "🔄 Released object to pool (" << activeObjects 
                 << "/" << poolSize << " active)" << endl;
        }
    }
    
    size_t getActiveCount() const { return activeObjects; }
    size_t getAvailableCount() const { return available.size(); }
    size_t getTotalCount() const { return poolSize; }
    
    void printStats() const {
        cout << "📊 Pool Stats - Active: " << activeObjects 
             << ", Available: " << available.size() 
             << ", Total: " << poolSize << endl;
    }
};

// Entity Manager using object pools
class EntityManager {
private:
    ObjectPool<Player> playerPool;
    ObjectPool<Enemy> enemyPool;
    vector<Entity*> activeEntities;
    
public:
    EntityManager(size_t maxPlayers = 10, size_t maxEnemies = 100) 
        : playerPool(maxPlayers), enemyPool(maxEnemies) {
        cout << "🎮 EntityManager created" << endl;
    }
    
    ~EntityManager() {
        // Release all active entities
        for (auto entity : activeEntities) {
            releaseEntity(entity);
        }
        cout << "🎮 EntityManager destroyed" << endl;
    }
    
    Player* createPlayer(const string& name) {
        Player* player = playerPool.acquire();
        if (player) {
            player->initialize(name);
            activeEntities.push_back(player);
        }
        return player;
    }
    
    Enemy* createEnemy(const string& name) {
        Enemy* enemy = enemyPool.acquire();
        if (enemy) {
            enemy->initialize(name);
            activeEntities.push_back(enemy);
        }
        return enemy;
    }
    
    void releaseEntity(Entity* entity) {
        if (!entity) return;
        
        // Remove from active entities
        auto it = find(activeEntities.begin(), activeEntities.end(), entity);
        if (it != activeEntities.end()) {
            activeEntities.erase(it);
        }
        
        // Return to appropriate pool
        if (auto player = dynamic_cast<Player*>(entity)) {
            playerPool.release(player);
        } else if (auto enemy = dynamic_cast<Enemy*>(entity)) {
            enemyPool.release(enemy);
        }
    }
    
    void updateAll(float deltaTime) {
        cout << "\n🔄 Updating " << activeEntities.size() << " entities..." << endl;
        
        // Update all active entities
        for (auto entity : activeEntities) {
            if (entity->isActive()) {
                entity->update(deltaTime);
            }
        }
        
        // Remove inactive entities
        auto it = activeEntities.begin();
        while (it != activeEntities.end()) {
            if (!(*it)->isActive()) {
                cout << "🗑️ Removing inactive entity: " << (*it)->getName() << endl;
                releaseEntity(*it);
                it = activeEntities.begin(); // Restart iteration after erase
            } else {
                ++it;
            }
        }
    }
    
    void printStats() const {
        cout << "\n📊 ENTITY MANAGER STATS:" << endl;
        cout << "Active entities: " << activeEntities.size() << endl;
        playerPool.printStats();
        enemyPool.printStats();
        
        cout << "Active entities list:" << endl;
        for (const auto entity : activeEntities) {
            cout << "- " << entity->getName() << " (ID: " << entity->getId() << ")" << endl;
        }
    }
};

int main() {
    cout << "=== GAME ENTITY MANAGER WITH OBJECT POOL ===" << endl;
    
    EntityManager manager(5, 20);
    
    // Create some entities
    cout << "\n=== CREATING ENTITIES ===" << endl;
    auto player1 = manager.createPlayer("Hero");
    auto player2 = manager.createPlayer("Sidekick");
    
    auto enemy1 = manager.createEnemy("Goblin_1");
    auto enemy2 = manager.createEnemy("Goblin_2");
    auto enemy3 = manager.createEnemy("Orc_1");
    
    manager.printStats();
    
    // Simulate game loop
    cout << "\n=== GAME SIMULATION ===" << endl;
    for (int frame = 1; frame <= 5; frame++) {
        cout << "\n--- Frame " << frame << " ---" << endl;
        manager.updateAll(0.016f);  // 60 FPS
        
        // Some entities die randomly
        if (frame == 3 && enemy1) {
            cout << "💥 Enemy1 killed!" << endl;
            enemy1->setActive(false);
        }
        
        if (frame == 4 && player2) {
            cout << "💀 Player2 died!" << endl;
            player2->setActive(false);
        }
    }
    
    // Create more entities to test pool reuse
    cout << "\n=== CREATING MORE ENTITIES ===" << endl;
    auto enemy4 = manager.createEnemy("Dragon");
    auto player3 = manager.createPlayer("NewHero");
    
    manager.printStats();
    
    // Final cleanup happens automatically in destructor
    cout << "\n=== CLEANUP ===" << endl;
    
    return 0;
}

Ví dụ 2: Memory Pool Allocator cho High-Performance Computing

cpp
#include <iostream>
#include <vector>
#include <chrono>
#include <random>
#include <algorithm>
#include <memory>
using namespace std;

// Performance-critical data structure
struct Vector3D {
    float x, y, z;
    
    Vector3D(float x = 0, float y = 0, float z = 0) : x(x), y(y), z(z) {}
    
    Vector3D operator+(const Vector3D& other) const {
        return Vector3D(x + other.x, y + other.y, z + other.z);
    }
    
    Vector3D operator*(float scalar) const {
        return Vector3D(x * scalar, y * scalar, z * scalar);
    }
    
    float magnitude() const {
        return sqrt(x * x + y * y + z * z);
    }
};

// Custom allocator using memory pool
template<typename T>
class PoolAllocator {
private:
    static constexpr size_t POOL_SIZE = 10000;
    static constexpr size_t BLOCK_SIZE = sizeof(T);
    
    struct Pool {
        alignas(T) char memory[POOL_SIZE * BLOCK_SIZE];
        bool used[POOL_SIZE];
        size_t allocatedCount;
        
        Pool() : allocatedCount(0) {
            fill(used, used + POOL_SIZE, false);
        }
    };
    
    static vector<unique_ptr<Pool>> pools;
    static size_t currentPool;
    
public:
    using value_type = T;
    
    PoolAllocator() = default;
    
    template<typename U>
    PoolAllocator(const PoolAllocator<U>&) noexcept {}
    
    T* allocate(size_t n) {
        if (n != 1) {
            throw bad_alloc(); // This allocator only handles single objects
        }
        
        // Try to find free slot in existing pools
        for (auto& pool : pools) {
            for (size_t i = 0; i < POOL_SIZE; i++) {
                if (!pool->used[i]) {
                    pool->used[i] = true;
                    pool->allocatedCount++;
                    return reinterpret_cast<T*>(pool->memory + i * BLOCK_SIZE);
                }
            }
        }
        
        // Create new pool if no space available
        pools.push_back(make_unique<Pool>());
        auto& newPool = pools.back();
        newPool->used[0] = true;
        newPool->allocatedCount++;
        return reinterpret_cast<T*>(newPool->memory);
    }
    
    void deallocate(T* ptr, size_t n) {
        if (!ptr) return;
        
        // Find which pool this pointer belongs to
        for (auto& pool : pools) {
            char* poolStart = pool->memory;
            char* poolEnd = poolStart + POOL_SIZE * BLOCK_SIZE;
            
            if (ptr >= reinterpret_cast<T*>(poolStart) && 
                ptr < reinterpret_cast<T*>(poolEnd)) {
                
                size_t index = (reinterpret_cast<char*>(ptr) - poolStart) / BLOCK_SIZE;
                if (pool->used[index]) {
                    pool->used[index] = false;
                    pool->allocatedCount--;
                }
                return;
            }
        }
    }
    
    template<typename U>
    bool operator==(const PoolAllocator<U>&) const noexcept {
        return true;
    }
    
    template<typename U>
    bool operator!=(const PoolAllocator<U>&) const noexcept {
        return false;
    }
    
    static void printStats() {
        cout << "📊 Pool Allocator Stats:" << endl;
        cout << "Total pools: " << pools.size() << endl;
        
        size_t totalAllocated = 0;
        for (size_t i = 0; i < pools.size(); i++) {
            totalAllocated += pools[i]->allocatedCount;
            cout << "Pool " << i << ": " << pools[i]->allocatedCount 
                 << "/" << POOL_SIZE << " allocated" << endl;
        }
        
        cout << "Total allocated objects: " << totalAllocated << endl;
        cout << "Memory usage: " << pools.size() * POOL_SIZE * BLOCK_SIZE 
             << " bytes (" << pools.size() * POOL_SIZE * BLOCK_SIZE / 1024 
             << " KB)" << endl;
    }
    
    static void cleanup() {
        pools.clear();
    }
};

template<typename T>
vector<unique_ptr<typename PoolAllocator<T>::Pool>> PoolAllocator<T>::pools;

template<typename T>
size_t PoolAllocator<T>::currentPool = 0;

// Performance testing
class PerformanceTester {
private:
    mt19937 rng;
    uniform_real_distribution<float> dist;
    
public:
    PerformanceTester() : rng(42), dist(-100.0f, 100.0f) {}
    
    Vector3D randomVector() {
        return Vector3D(dist(rng), dist(rng), dist(rng));
    }
    
    template<typename Allocator>
    chrono::microseconds testVectorOperations(const string& testName, size_t count) {
        cout << "\n🧪 Testing " << testName << " with " << count << " vectors..." << endl;
        
        using VectorContainer = vector<Vector3D, Allocator>;
        
        auto start = chrono::high_resolution_clock::now();
        
        {
            VectorContainer vectors;
            vectors.reserve(count);
            
            // Create vectors
            for (size_t i = 0; i < count; i++) {
                vectors.push_back(randomVector());
            }
            
            // Perform calculations
            Vector3D sum(0, 0, 0);
            for (const auto& v : vectors) {
                sum = sum + v * 0.5f;
            }
            
            // More operations
            for (auto& v : vectors) {
                v = v * 1.1f;
            }
            
            // Calculate magnitudes
            float totalMagnitude = 0;
            for (const auto& v : vectors) {
                totalMagnitude += v.magnitude();
            }
            
            cout << "Final sum: (" << sum.x << ", " << sum.y << ", " << sum.z << ")" << endl;
            cout << "Total magnitude: " << totalMagnitude << endl;
        }
        
        auto end = chrono::high_resolution_clock::now();
        auto duration = chrono::duration_cast<chrono::microseconds>(end - start);
        
        cout << "⏱️ " << testName << " completed in " << duration.count() << " μs" << endl;
        
        return duration;
    }
    
    void runComparison(size_t vectorCount) {
        cout << "=== MEMORY ALLOCATOR PERFORMANCE COMPARISON ===" << endl;
        
        // Test standard allocator
        auto standardTime = testVectorOperations<allocator<Vector3D>>(
            "Standard Allocator", vectorCount
        );
        
        // Test pool allocator
        auto poolTime = testVectorOperations<PoolAllocator<Vector3D>>(
            "Pool Allocator", vectorCount
        );
        
        PoolAllocator<Vector3D>::printStats();
        
        // Compare results
        cout << "\n📊 PERFORMANCE COMPARISON:" << endl;
        cout << "Standard Allocator: " << standardTime.count() << " μs" << endl;
        cout << "Pool Allocator: " << poolTime.count() << " μs" << endl;
        
        if (poolTime < standardTime) {
            double improvement = (double)(standardTime - poolTime).count() / standardTime.count() * 100;
            cout << "🚀 Pool allocator is " << improvement << "% faster!" << endl;
        } else {
            double degradation = (double)(poolTime - standardTime).count() / standardTime.count() * 100;
            cout << "📉 Pool allocator is " << degradation << "% slower" << endl;
        }
    }
};

// Memory usage monitoring
class MemoryMonitor {
private:
    static size_t allocations;
    static size_t deallocations;
    static size_t currentMemory;
    static size_t peakMemory;
    
public:
    static void recordAllocation(size_t bytes) {
        allocations++;
        currentMemory += bytes;
        if (currentMemory > peakMemory) {
            peakMemory = currentMemory;
        }
    }
    
    static void recordDeallocation(size_t bytes) {
        deallocations++;
        currentMemory -= bytes;
    }
    
    static void printReport() {
        cout << "\n📊 MEMORY MONITOR REPORT:" << endl;
        cout << "Total allocations: " << allocations << endl;
        cout << "Total deallocations: " << deallocations << endl;
        cout << "Current memory usage: " << currentMemory << " bytes" << endl;
        cout << "Peak memory usage: " << peakMemory << " bytes (" 
             << peakMemory / 1024 << " KB)" << endl;
        cout << "Memory leaks: " << (currentMemory > 0 ? "YES" : "NO") << endl;
    }
    
    static void reset() {
        allocations = deallocations = currentMemory = peakMemory = 0;
    }
};

size_t MemoryMonitor::allocations = 0;
size_t MemoryMonitor::deallocations = 0;
size_t MemoryMonitor::currentMemory = 0;
size_t MemoryMonitor::peakMemory = 0;

int main() {
    PerformanceTester tester;
    
    // Test with different sizes
    vector<size_t> testSizes = {1000, 10000, 50000};
    
    for (size_t size : testSizes) {
        cout << "\n" << string(50, '=') << endl;
        tester.runComparison(size);
        
        // Cleanup between tests
        PoolAllocator<Vector3D>::cleanup();
    }
    
    // Memory monitoring demo
    cout << "\n=== MEMORY MONITORING DEMO ===" << endl;
    MemoryMonitor::reset();
    
    {
        vector<Vector3D*> pointers;
        
        // Allocate some vectors
        for (int i = 0; i < 1000; i++) {
            Vector3D* v = new Vector3D(i, i * 2, i * 3);
            pointers.push_back(v);
            MemoryMonitor::recordAllocation(sizeof(Vector3D));
        }
        
        // Deallocate half
        for (size_t i = 0; i < pointers.size() / 2; i++) {
            delete pointers[i];
            MemoryMonitor::recordDeallocation(sizeof(Vector3D));
        }
        
        MemoryMonitor::printReport();
        
        // Cleanup remaining
        for (size_t i = pointers.size() / 2; i < pointers.size(); i++) {
            delete pointers[i];
            MemoryMonitor::recordDeallocation(sizeof(Vector3D));
        }
    }
    
    MemoryMonitor::printReport();
    
    return 0;
}

Ví dụ 3: RAII Resource Manager với Custom Deleter

cpp
#include <iostream>
#include <memory>
#include <fstream>
#include <string>
#include <vector>
#include <functional>
#include <cstdio>
using namespace std;

// Generic RAII wrapper with custom cleanup
template<typename T>
class ResourceWrapper {
private:
    T resource_;
    function<void(T&)> cleanup_;
    bool valid_;
    
public:
    template<typename... Args>
    ResourceWrapper(function<void(T&)> cleanup, Args&&... args) 
        : resource_(forward<Args>(args)...), cleanup_(cleanup), valid_(true) {
        cout << "🔧 Resource acquired" << endl;
    }
    
    ~ResourceWrapper() {
        if (valid_ && cleanup_) {
            cleanup_(resource_);
            cout << "🧹 Resource cleaned up" << endl;
        }
    }
    
    // Move-only semantics
    ResourceWrapper(const ResourceWrapper&) = delete;
    ResourceWrapper& operator=(const ResourceWrapper&) = delete;
    
    ResourceWrapper(ResourceWrapper&& other) noexcept 
        : resource_(move(other.resource_)), cleanup_(move(other.cleanup_)), valid_(other.valid_) {
        other.valid_ = false;
    }
    
    ResourceWrapper& operator=(ResourceWrapper&& other) noexcept {
        if (this != &other) {
            if (valid_ && cleanup_) {
                cleanup_(resource_);
            }
            resource_ = move(other.resource_);
            cleanup_ = move(other.cleanup_);
            valid_ = other.valid_;
            other.valid_ = false;
        }
        return *this;
    }
    
    T& get() { return resource_; }
    const T& get() const { return resource_; }
    
    T* operator->() { return &resource_; }
    const T* operator->() const { return &resource_; }
    
    T& operator*() { return resource_; }
    const T& operator*() const { return resource_; }
    
    bool isValid() const { return valid_; }
    
    void release() {
        valid_ = false;
    }
};

// Specialized resource managers
class FileManager {
public:
    using FileHandle = ResourceWrapper<FILE*>;
    
    static FileHandle openFile(const string& filename, const string& mode) {
        FILE* file = fopen(filename.c_str(), mode.c_str());
        if (!file) {
            throw runtime_error("Cannot open file: " + filename);
        }
        
        return FileHandle([filename](FILE*& f) {
            if (f) {
                fclose(f);
                cout << "📁 Closed file: " << filename << endl;
                f = nullptr;
            }
        }, file);
    }
    
    static void writeToFile(FileHandle& handle, const string& data) {
        if (handle.isValid() && *handle) {
            fprintf(*handle, "%s\n", data.c_str());
            fflush(*handle);
        }
    }
    
    static string readLine(FileHandle& handle) {
        if (!handle.isValid() || !*handle) {
            return "";
        }
        
        char buffer[1024];
        if (fgets(buffer, sizeof(buffer), *handle)) {
            string result(buffer);
            // Remove newline if present
            if (!result.empty() && result.back() == '\n') {
                result.pop_back();
            }
            return result;
        }
        return "";
    }
};

class MemoryManager {
public:
    using MemoryBlock = ResourceWrapper<void*>;
    
    static MemoryBlock allocateAligned(size_t size, size_t alignment = 16) {
        void* ptr = aligned_alloc(alignment, size);
        if (!ptr) {
            throw bad_alloc();
        }
        
        cout << "🧠 Allocated " << size << " bytes (aligned to " << alignment << ") at " << ptr << endl;
        
        return MemoryBlock([size](void*& p) {
            if (p) {
                cout << "🧠 Freeing " << size << " bytes at " << p << endl;
                free(p);
                p = nullptr;
            }
        }, ptr);
    }
    
    template<typename T>
    static T* getTypedPointer(MemoryBlock& block) {
        return static_cast<T*>(*block);
    }
};

class DatabaseConnection {
private:
    string connectionString_;
    bool connected_;
    
public:
    DatabaseConnection(const string& connStr) 
        : connectionString_(connStr), connected_(false) {
        connect();
    }
    
    ~DatabaseConnection() {
        disconnect();
    }
    
    void connect() {
        // Simulate database connection
        cout << "🔌 Connecting to database: " << connectionString_ << endl;
        connected_ = true;
    }
    
    void disconnect() {
        if (connected_) {
            cout << "🔌 Disconnecting from database: " << connectionString_ << endl;
            connected_ = false;
        }
    }
    
    bool isConnected() const { return connected_; }
    
    void executeQuery(const string& query) {
        if (connected_) {
            cout << "📊 Executing query: " << query << endl;
        } else {
            throw runtime_error("Database not connected");
        }
    }
};

class ConnectionManager {
public:
    using DBConnection = ResourceWrapper<DatabaseConnection>;
    
    static DBConnection createConnection(const string& connectionString) {
        return DBConnection([](DatabaseConnection& db) {
            db.disconnect();
            cout << "🔌 Database connection cleaned up" << endl;
        }, connectionString);
    }
};

// Demo application
class ResourceDemo {
private:
    vector<string> logMessages;
    
public:
    void demonstrateFileOperations() {
        cout << "\n=== FILE OPERATIONS DEMO ===" << endl;
        
        try {
            // Create and write to file
            {
                auto writeFile = FileManager::openFile("demo_output.txt", "w");
                
                vector<string> messages = {
                    "Hello, RAII World!",
                    "This file is managed automatically",
                    "No need to worry about closing files",
                    "RAII handles everything!"
                };
                
                for (const auto& msg : messages) {
                    FileManager::writeToFile(writeFile, msg);
                    logMessages.push_back("Wrote: " + msg);
                }
                
                cout << "✅ File written successfully" << endl;
            } // File automatically closed here
            
            // Read from file
            {
                auto readFile = FileManager::openFile("demo_output.txt", "r");
                
                cout << "\n📖 Reading file contents:" << endl;
                string line;
                while (!(line = FileManager::readLine(readFile)).empty()) {
                    cout << "Read: " << line << endl;
                }
            } // File automatically closed here
            
        } catch (const exception& e) {
            cout << "❌ File operation error: " << e.what() << endl;
        }
    }
    
    void demonstrateMemoryOperations() {
        cout << "\n=== MEMORY OPERATIONS DEMO ===" << endl;
        
        try {
            // Allocate memory for array of integers
            const size_t arraySize = 1000;
            auto memory = MemoryManager::allocateAligned(arraySize * sizeof(int), 32);
            
            int* array = MemoryManager::getTypedPointer<int>(memory);
            
            // Initialize array
            for (size_t i = 0; i < arraySize; i++) {
                array[i] = static_cast<int>(i * i);
            }
            
            // Calculate sum
            long long sum = 0;
            for (size_t i = 0; i < arraySize; i++) {
                sum += array[i];
            }
            
            cout << "✅ Array initialized and sum calculated: " << sum << endl;
            
            // Memory automatically freed when 'memory' goes out of scope
            
        } catch (const exception& e) {
            cout << "❌ Memory operation error: " << e.what() << endl;
        }
    }
    
    void demonstrateDatabaseOperations() {
        cout << "\n=== DATABASE OPERATIONS DEMO ===" << endl;
        
        try {
            auto db = ConnectionManager::createConnection("localhost:5432/testdb");
            
            if (db->isConnected()) {
                vector<string> queries = {
                    "CREATE TABLE users (id INT, name VARCHAR(50))",
                    "INSERT INTO users VALUES (1, 'Alice')",
                    "INSERT INTO users VALUES (2, 'Bob')",
                    "SELECT * FROM users WHERE id > 0"
                };
                
                for (const auto& query : queries) {
                    db->executeQuery(query);
                }
                
                cout << "✅ Database operations completed" << endl;
            }
            
            // Database connection automatically cleaned up
            
        } catch (const exception& e) {
            cout << "❌ Database operation error: " << e.what() << endl;
        }
    }
    
    void demonstrateExceptionSafety() {
        cout << "\n=== EXCEPTION SAFETY DEMO ===" << endl;
        
        try {
            auto memory1 = MemoryManager::allocateAligned(1024);
            auto file1 = FileManager::openFile("temp1.txt", "w");
            auto db1 = ConnectionManager::createConnection("test_connection");
            
            // Simulate an error
            throw runtime_error("Simulated error for testing");
            
        } catch (const exception& e) {
            cout << "🛡️ Exception caught: " << e.what() << endl;
            cout << "🛡️ All resources automatically cleaned up despite exception!" << endl;
        }
    }
    
    void printLogs() const {
        cout << "\n📋 Operation logs:" << endl;
        for (const auto& msg : logMessages) {
            cout << "- " << msg << endl;
        }
    }
};

int main() {
    cout << "=== RAII RESOURCE MANAGER DEMO ===" << endl;
    
    ResourceDemo demo;
    
    demo.demonstrateFileOperations();
    demo.demonstrateMemoryOperations();
    demo.demonstrateDatabaseOperations();
    demo.demonstrateExceptionSafety();
    
    demo.printLogs();
    
    cout << "\n🎉 All demos completed successfully!" << endl;
    cout << "🎉 Notice how all resources were automatically cleaned up!" << endl;
    
    return 0;
}

🏋️ Thực hành

Bài tập 1: Ring Buffer Allocator

Implement ring buffer allocator cho real-time systems:

  • Fixed-size circular buffer
  • O(1) allocation/deallocation
  • Thread-safe operations
  • Memory alignment support

Bài tập 2: Garbage Collector

Tạo simple mark-and-sweep garbage collector:

  • Object graph traversal
  • Mark reachable objects
  • Sweep unreachable objects
  • Handle circular references

Bài tập 3: Memory Profiler

Implement memory profiler để analyze memory usage:

  • Track allocations/deallocations
  • Call stack recording
  • Memory leak detection
  • Performance statistics

Bài tập 4: Cache-Aware Data Structure

Design cache-friendly data structure:

  • Minimize cache misses
  • Data locality optimization
  • Prefetching strategies
  • Performance benchmarking

Lời giải chi tiết

Bài tập 1 - Ring Buffer Allocator:

cpp
#include <iostream>
#include <atomic>
#include <mutex>
#include <cassert>
using namespace std;

class RingBufferAllocator {
private:
    char* buffer_;
    size_t size_;
    atomic<size_t> head_;
    atomic<size_t> tail_;
    mutable mutex mutex_;
    
public:
    RingBufferAllocator(size_t size) : size_(size), head_(0), tail_(0) {
        buffer_ = static_cast<char*>(aligned_alloc(64, size));
        if (!buffer_) {
            throw bad_alloc();
        }
        cout << "🔄 RingBuffer created: " << size << " bytes" << endl;
    }
    
    ~RingBufferAllocator() {
        free(buffer_);
        cout << "🔄 RingBuffer destroyed" << endl;
    }
    
    void* allocate(size_t bytes, size_t alignment = alignof(max_align_t)) {
        lock_guard<mutex> lock(mutex_);
        
        // Align bytes to boundary
        size_t alignedBytes = (bytes + alignment - 1) & ~(alignment - 1);
        
        size_t currentTail = tail_.load();
        size_t newTail = (currentTail + alignedBytes) % size_;
        
        // Check if allocation would overlap with head
        size_t currentHead = head_.load();
        if (wouldOverlap(currentTail, newTail, currentHead)) {
            cout << "❌ RingBuffer: Out of space!" << endl;
            return nullptr;
        }
        
        void* ptr = buffer_ + currentTail;
        tail_.store(newTail);
        
        cout << "🔧 RingBuffer allocated " << alignedBytes 
             << " bytes at offset " << currentTail << endl;
        return ptr;
    }
    
    void deallocateTo(void* ptr) {
        lock_guard<mutex> lock(mutex_);
        
        if (ptr < buffer_ || ptr >= buffer_ + size_) {
            cout << "❌ Invalid pointer for deallocation" << endl;
            return;
        }
        
        size_t offset = static_cast<char*>(ptr) - buffer_;
        head_.store(offset);
        
        cout << "🗑️ RingBuffer deallocated to offset " << offset << endl;
    }
    
    void reset() {
        lock_guard<mutex> lock(mutex_);
        head_.store(0);
        tail_.store(0);
        cout << "🔄 RingBuffer reset" << endl;
    }
    
private:
    bool wouldOverlap(size_t tail, size_t newTail, size_t head) const {
        if (tail < head) {
            return newTail >= head;
        } else if (tail > head) {
            return newTail >= head && newTail <= tail;
        }
        return false; // Empty buffer
    }
};

📋 Tóm tắt

Các điểm quan trọng

Advanced Memory Techniques:

  • Custom Allocators: Tối ưu cho specific use cases
  • Memory Pools: Pre-allocated memory để tránh fragmentation
  • Object Pools: Reuse objects thay vì allocate/deallocate
  • Stack Allocators: Linear allocation cho temporary data

Memory Layout Optimization:

  • Alignment: Tối ưu memory access performance
  • Cache Locality: Arrange data để minimize cache misses
  • SoA vs AoS: Choose layout phù hợp với access patterns
  • Padding: Understand memory layout impacts

Debugging và Profiling:

  • Memory Leaks: Tools và techniques để detect
  • AddressSanitizer: Compiler-based error detection
  • Custom Tracking: Implement allocation tracking
  • Profiling Tools: Analyze memory usage patterns

Best Practices

Performance:

  • Minimize allocations trong hot paths
  • Use object pools cho frequently created/destroyed objects
  • Consider memory layout cho cache efficiency
  • Profile memory usage để identify bottlenecks

Safety:

  • Use RAII cho automatic resource management
  • Implement proper exception safety
  • Avoid memory leaks với smart pointers
  • Test với memory debugging tools

Scalability:

  • Design allocators cho specific workloads
  • Consider multi-threading implications
  • Plan for memory growth patterns
  • Monitor memory usage trong production

Chuẩn bị cho bài tiếp theo

Bài học cuối cùng sẽ tìm hiểu về Design Patterns - các mẫu thiết kế phổ biến:

  • Creational Patterns: Singleton, Factory, Builder
  • Structural Patterns: Adapter, Decorator, Facade
  • Behavioral Patterns: Observer, Strategy, Command
  • Modern C++ Implementation: Using smart pointers, RAII, templates
  • Real-world Examples: Practical applications của từng pattern

Advanced memory management là kỹ năng quan trọng cho high-performance applications. Hãy luyện tập để master các techniques này!

Khóa học C++ miễn phí