Stomp Allocator의 목적 : 메모리 오염버그를 잡아보자.
// 메모리 오염 예시1
Knight k1 = new Knight();
k1->hp = 200;
k1->mp = 150;
delete k1
// ...
k1->hp = 100; // ??? -> 여기서 crash가 날 수도 안 날 수도 있다.
// 만약 k1->hp 메모리 공간에 다른 메모리가 써진다면??? -> 그 데이터를 오염시킬 수 있다.
// 메모리 오염 예시2
vector<int32> v{1,2,3,4,5};
for(int32 i = 0; i < 5; i++)
{
int32 value = v[i];
// ...
if(/*something*/)
{
v.clear(); // clear했는데 break를 안해서 위에서 잘못된 메모리에 접근
}
}
// 메모리 오염 예시3
class Player
{
};
class Knight : public Player
{
};
Player* p = new Player();
Knight* k = static_cast<Knight*>(p);
// dynamic_cast로 잡아야하지만 성능이슈로 static_cast로 잡았는데 잘못 캐스팅함.
다양한 이유가 있구나… 그래서 어떻게 Stomp로 해결할껀데?
우선 OS의 메모리에 대해 이해해야한다.
// 가상 메모리 기본!
int* num = new int;
int64 add = reinterpret_cast<int64>(num);
// 여기서 나온주소대로 다른 프로세스에서 접근이 가능할까?
// 당연하지만 안된다. 메모리 주소(가상주소)는 각 프로세스마다 관리하게 된다.
delete num;
// 어떤 프로세스에서 2GB메모리를 쓴다고 가정해보자.
// 2GB [ ]
// 그럼 가상메모리에 접근하기 위해서 메모리주소가 몇개가 필요할까?
// 1바이트에 접근하기 위해 1바이트가 필요하다면 해당프로세스에 2GB메모리할당을 위해 접근을 위한 메모리를 2GB할당해야하는 바보 같은 일이 발생한다.
// 따라서 메모리 공간보다 크게 접근 메모리를 할당하는데 이를 페이징이라한다.
// -> 보통 4kb이다.
// 페이징 마다 read/write 권한을 줄 수 있다.
// 실제로 확인해보자.
SYSTEM_INFO info;
::GetSystemInfo(&info);
info.dwPageSize; // 4KB
info.dwAllocationGranularity; // 64KB
class StompAllocator
{
enum { PAGE_SIZE = 0x1000 }; // 4096
//...
void* StompAllocator::Alloc(int32 size)
{
const int64 pageCount = (size + PAGE_SIZE - 1) / PAGE_SIZE; // 몇개의 페이지를 할당할까?
const int64 dataOffset = pageCount * PAGE_SIZE - size; // 메모리 오버플로 방지용
void* baseAddress = ::VirtualAlloc(NULL, // 알아서 메모리 공간을 잡아달라
pageCount * PAGE_SIZE, // 메모리 크기
MEM_RESERVE | MEM_COMMIT, // MEM_RESERVE(예약), MEM_COMMIT(사용)
PAGE_READWRITE); // 권한
return static_cast<void*>(static_cast<int8*>(baseAddress) + dataOffset);
// 실제 메모리 사용은 할당된 메모리의 마지막 부분만 쓰게 한다.
// [ [ 여기만 쓴다 ]]
}
void StompAllocator::Release(void* ptr)
{
const int64 address = reinterpret_cast<int64>(ptr);
const int64 baseAddress = address - (address % PAGE_SIZE);
::VirtualFree(reinterpret_cast<void*>(baseAddress), 0, MEM_RELEASE);
}
// new, delete와 차이점은?
// VirtualFree는 실제로 메모리 공간을 밀어버린다.
// 메모리 오염문제를 방지할 수 있다.
- 메모리 오염의 문제는 해결됐지만
- 메모리 파편화, 재사용의 문제는 해결되지 않았다.
- 역시 뒤에서 다시 설명한다.