티스토리 뷰

C++ 환경에서 접할 수 있는 대표적인 메모리 오류

1. 메모리 누수(Memory Leak)

  • 'new'로 할당한 메모리를 해제하지 않을 때 쓰이지 않는 메모리가 지속적으로 쌓이면서 시스템 메모리를 고갈시킴
  • 시간이 지날수록 프로그램 속도가 느려지고 나중에는 강제 종료가 되기도 함

2. 댕글링 포인터(Dangling Pointer) 

  • 메모리를 해제할 때 이미 해제한 메모리에 접근하다 발생하는 접근 오류 
  • 프로그램이 강제 종료될 수도 있음

3. 메모리 단편화(Memory Fragmentation)

  • 잦은 할당과 해제로 만들어진 메모리 파편들이 후에 새롭게 할당 하는 것을 방해하는 문제
  • 극단적으로 파편화된 메모리는 새로운 메모리 공간을 찾는 데 CPU 소모량이 높아지거나 결국 할당에 실패함 

 

스택 메모리를 좀 더 똑똑하게 사용하자

스택 메모리 구조 이해하기 

스택 메모리 특성 

  • 메모리의 시작점과 끝점, 마지막 할당지점이 있음
  • 메모리를 할당할 때는 마지막 할당 지점 이후로 순차적으로 할당
  • 메모리를 해제할 때는 마지막 할당한 지점부터 순차적으로 해제
  • 메모리는 높은 주소에서 낮은 주소로 할당
  • 메모리의 시작점과 끝점 뒤에 가드 영역이 존재하므로 가드영역에 접근하면 프로그램이 강제 종료
  • 가드 영역디버그 모드릴리즈 모드에 따라 크기가 다름

**메모리 가드의 존재를 알려주는 예시**

두 지역변수 간 메모리 영역 차이를 출력하기 위한 코드

 

Debug 환경에서 12가 나오고 Release 환경에서 4가 나옴
메모리 가드가 포함된 스택 메모리

 

스택 메모리 크기도 한계가 있다

문제점

  • 스택 메모리는 스레드마다 기본적으로 1 MB 씩 주어짐
  • 만약 큰 지역변수를 선언하게 되면 스택 오버플로우 오류가 발생하게 됨

예시 

배열구조에 1 MB 공간을 확보했을 때 stack overflow 발생

 

스택 메모리를 현명하게 사용하기

1. 크기가 큰 데이터를 만들지 말 것

가급적 모든 것을 작게 만드는 습관을 가져야 한다. 보통 데이터의 사이즈가 커지는 이유는 문자열이나 배열 때문이다.

STL의  string과 vector는 내부에서 동적 메모리인 힙을 이용하므로 데이터의 크기를 줄일 수 있다. 

2. 크기가 큰 데이터는 전역변수나 정적변수를 활용

  • 지역변수로 생성했을 때 오버플로우가 발생하면 전역변수나 정적변수로 바꾸면 해당 문제는 해결된다.

  • 싱글톤 패턴을 사용해서 크기가 큰 데이터를 관리할 수 있다. 
class CBigMemory
{
private:
	CBigMemory() {}
	~CBigMemory() {}

public:
	char szBigSizeArr[10 * 1000 * 1000];

	static CBigMemory* GetInstance()
	{
		static CBigMemory instance;
		return &instance;
	}
};

inline CBigMemory* Doc()
{
	return CBigMemory::GetInstance();
}

int main()
{
	memset(Doc()->szBigSizeArr, 0, sizeof(Doc()->szBigSizeArr));
}

3. 재귀함수를 만들지 말 것

재귀함수의 문제점 

  1. 코드를 해석하기 어렵다.
  2. 스택 메모리 낭비가 크다.
  3. 너무 큰 값을 요청한 경우 스택 메모리 고갈로 프로그램이 강제 종료될 수 있다. 

4. 링 메모리의 활용

class CRingMemory
{
	unsigned char* m_pMemory;	// 링 메모리 포인터
	int m_nMemorySize;			// 멤버 메모리 변수 사이즈
	int m_nLastAllocPos;		// 마지막 할당 위치

public:
	CRingMemory() :m_pMemory(nullptr), m_nMemorySize(0), m_nLastAllocPos(0) {}
	~CRingMemory() {}

	// 링 메모리 생성 함수
	// 원하는 사이즈의 링메모리를 생성
	// 만약 생성이 되지 않으면 false 반환
	bool Create(int nMaxSize)
	{
		m_pMemory = new(std::nothrow) unsigned char[nMaxSize];
		if (nullptr == m_pMemory) return false;
		m_nMemorySize = 0;
		m_nLastAllocPos = 0;
		return true;
	}
	// 링 메모리 파괴 함수
	void Destroy()
		{
			if (m_pMemory) delete[] m_pMemory;
			m_pMemory = nullptr;		
		}

	// 링 메모리 할당함수 
	// 할당할 사이즈를 인수로 받아와서 데이터 할당 
	unsigned char* Alloc(int nSize)
	{
		if (m_nMemorySize < (m_nLastAllocPos + nSize)) m_nLastAllocPos = 0;

		unsigned char* pNewMemory = m_pMemory + m_nLastAllocPos;
		return pNewMemory;
	}
	
};
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함