개요
메모리를 동적으로 할당하고 해제하는 과정에서 성능을 최적화하기 위한 기술
일반적으로 동적 메모리 할당이 자주 일어나는 시스템에서 성능 문제를 해결하려고 사용된다.
메모리 풀은 메모리 블록을 미리 할당해두고 필요한 곳에서 이를 재사용하는 방식으로 동작한다.
메모리 풀을 사용하면 메모리 할당과 해제의 비용을 줄일 수 있다.
장점
동적 메모리 할당과 해제를 반복적으로 호출하는 경우, 시스템에 의한 오버헤드가 발생한다.
동적 메모리 할당이 반복되면 메모리 단편화가 발생할 수 있다.
하지만 메모리 풀을 사용하면 이러한 오버헤드와 단편화를 감소 시킬 수 있다.
또한 메모리 풀은 고정된 크기의 블록을 사용하기 때문에 메모리 사용량을 예측할 수 있다.
동작 원리
메모리 풀은 미리 일정량의 메모리 블록을 할당한다, 따라서 크기가 같은 메모리 블록을 준비한다.
필요한 메모리 블록이 있을 때, 메모리 풀에서 미리 준비된 블록을 제공한다.
작업이 끝나면 메모리 풀에 해당 블록을 반환한다.
사용 예시
- 게임 개발: 매 프레임마다 객체가 생성되고 파괴되는 경우, 메모리 풀을 사용하여 객체를 재사용할 수 있다.
- 실시간 시스템: 메모리 풀을 통해 메모리 할당과 해제를 관리하여 동적 메모리 할당에 따른 시스템의 응답 속도가 저하를 최소화 한다.
- 대형 소프트웨어 시스템: 메모리 풀을 사용하면 예측 가능한 메모리 관리와 성능 최적화를 할 수 있다.
예제
1. 클래스 형식
#include <iostream>
#include <vector>
class MemoryPool {
public:
// 메모리 풀을 초기화하고 지정된 크기의 메모리 블록을 미리 할당
MemoryPool(size_t blockSize, size_t blockCount)
: m_blockSize(blockSize), m_blockCount(blockCount) {
m_pool.resize(blockCount);
m_freeList.reserve(blockCount);
// 자유 목록에 모든 블록을 추가
for (size_t i = 0; i < blockCount; ++i) {
m_freeList.push_back(&m_pool[i]);
}
}
// 메모리 할당
void* allocate() {
if (m_freeList.empty()) {
std::cout << "메모리 부족!" << std::endl;
return nullptr;
}
void* block = m_freeList.back();
m_freeList.pop_back();
return block;
}
// 메모리 해제
void deallocate(void* ptr) {
m_freeList.push_back(ptr);
}
private:
size_t m_blockSize; // 각 블록의 크기
size_t m_blockCount; // 블록의 개수
std::vector<char> m_pool; // 메모리 풀
std::vector<void*> m_freeList; // 자유 목록
};
int main() {
// 크기가 32바이트인 블록을 10개 할당하는 메모리 풀 생성
MemoryPool pool(32, 10);
// 메모리 할당
void* block1 = pool.allocate();
void* block2 = pool.allocate();
std::cout << "할당된 메모리 블록 주소: " << block1 << ", " << block2 << std::endl;
// 메모리 해제
pool.deallocate(block1);
pool.deallocate(block2);
return 0;
}
2. 구조체 형식
#include <iostream>
#include <vector>
#include <cstdlib>
struct MemoryPool {
size_t blockSize;
std::vector<void*> freeList;
// 메모리 풀 초기화
void init(size_t blockSize, size_t blockCount) {
this->blockSize = blockSize;
resizePool(blockCount); // 메모리 풀 초기화
}
// 메모리 풀 크기 동적으로 조정
void resizePool(size_t blockCount) {
for (size_t i = freeList.size(); i < blockCount; ++i) {
freeList.push_back(malloc(blockSize)); // 새 블록 할당
}
for (size_t i = freeList.size(); i > blockCount; --i) {
free(malloc(blockSize)); // 필요없는 블록 해제
}
}
// 메모리 할당
void* allocate() {
if (freeList.empty()) {
std::cout << "메모리 부족!" << std::endl;
return nullptr;
}
void* block = freeList.back();
freeList.pop_back();
return block;
}
// 메모리 해제
void deallocate(void* ptr) {
freeList.push_back(ptr);
}
~MemoryPool() {
// 모든 동적 메모리 해제
for (void* block : freeList) {
free(block);
}
}
};
int main() {
MemoryPool pool;
pool.init(4, 10); // 4바이트 크기 블록을 10개로 초기화
// 할당 및 동적 조정
void* ptr1 = pool.allocate();
std::cout << "할당된 블록 주소: " << ptr1 << std::endl;
// 메모리 풀 크기 동적 증가
pool.resizePool(20); // 블록 수를 20으로 확장
// 더 많은 블록 할당
void* ptr2 = pool.allocate();
std::cout << "할당된 두 번째 블록 주소: " << ptr2 << std::endl;
// 메모리 해제
pool.deallocate(ptr1);
pool.deallocate(ptr2);
return 0;
}
위 예시에서의 클래스 형식의 메모리 풀은 char타입으로 1바이트를 나타내므로 메모리 크기 * 블럭의 수이다.
구조체 형식의 메모리 풀은 void*타입으로 포인터 자체를 가르키고, 메모리 크기 * 블럭의 수의 개수를 가진다.
또한 메모리 풀은 벡터 자료 구조를 사용하고 있지만 실제로는 스택과 같이 사용되고 잇으며, 큐를 사용해도 무방하다.
728x90
'자료 구조' 카테고리의 다른 글
[자료 구조] 멀티셋 C++ (0) | 2025.02.17 |
---|