메모리 구조란?
-
프로그램이 실행되기 위해서는
먼저 프로그램이 메모리에 로드(load)되어야 한다.
또한 프로그램에서 사용되는 변수들을 저장할 메모리도 필요하다.
-
따라서 컴퓨터의 운영체제는
프로그램의 실행을 위해
다양한 메모리 공간을 제공하고 있다.
프로그램이 운영체제로부터 할당받는 대표적인 메모리 공간(RAM)은 다음과 같다.
-
코드(code) 영역
-
데이터(data) 영역
-
힙(Heap) 영역
-
스택(Stack) 영역
코드(Code) 영역
-
메모리의 코드(code) 영역은
실행할 프로그램의 코드가 저장되는 영역으로
텍스트(code) 영역이라고도 부른다.
-
실행할 프로그램의 코드는
프로그래머가 작성한 소스 코드이다.
-
프로그램이 시작하고
끝날 때까지 메모리에 계속 남아있는다.
-
또한 상수도 여기에 들어간다.
물론 컴파일 된 기계어가 들어간다.
-
CPU는
코드 영역에 저장된 명령어를
하나씩 가져가서 처리하게 된다.
데이터(Data) 영역
-
메모리의 데이터(data) 영역은
프로그램의 전역 변수와 정적(Static) 변수가 저장되는 영역이다.
-
프로그램이 시작하고
끝날 때까지 메모리에 계속 남아있는다.
스택(Stack) 영역
-
메모리의 스택(stack) 영역은
함수의 호출과 관계되는
지역 변수와 매개변수가 저장되는 영역이다.
-
스택 영역은 함수의 호출과 함께 할당되며
함수의 호출이 완료되면 소멸한다.
-
이렇게 스택 영역에 저장되는 함수의 호출 정보를
스택 프레임(Stack frame)이라고 한다.
-
스택 영역은
메모리의 높은 주소에서 낮은 주소의 방향으로 할당된다.
-
Stack 영역이 클 수록 Heap 영역이 작아지고
Heap 영역이 클 수록 Stack 영역이 작아진다.
-
스택 영역은
컴파일 시에 크기가 결정된다.
스택 할당 속도 vs 힙 할당 속도
-
스택이 훨씬 더 빠르다.
-
스택은 이미 할당 되어 있는 공간을 사용하는 것이고
힙은 따로 할당해서 사용하는 공간이다.
-
다만 스택은 공간이 매우 적기 때문에
모든 응용 프로그램에서 스택을 사용할 수는 없다.
-
좀 더 자세히 알아보자.
스택에서 할당의 의미는
이미 생성되어 있는
스택에 대해
포인터의 위치만 바꿔주는
단순한 CPU Instruction(단순히 덧셈과 뺄셈 연산, 일반적으로 단일 Instruction)이다.
-
반면 힙에서의 할당은
요청된 chunk의 크기, 현재 메모리의 fragmentation 상황 등
다양한 요소를 고려하기 때문에
더 많은 CPU Instruction이 필요하다.
장점
-
낭비되는 공간이 없다.
-
하나의 명령만으로
메모리 조작과 어드레스 조작이 가능하다.
단점
-
한계가 있어
한계를 초과하도록 삽입할 수 없다.
-
유연성이 부족하다.
힙(Heap) 영역
-
메모리의 힙(Heap) 영역은
사용자가 직접 관리할 수 있고
해야만하는 영역이다.
-
힙 영역은
사용자에 의해
메모리 공간이 동적으로
할당되고 해제된다.
-
힙 영역은
메모리의 낮은 주소에서 높은 주소의 방향으로 할당된다.
-
malloc() 또는 new 연산자를 통해 할당하고
free() 또는 delete 연산자를 통해서 해제가 가능하다.
-
Java에서는 가비지 컬렉터가
자동으로 해제하는 곳이다.
-
Stack 영역이 클 수록 Heap 영역이 작아지고
Heap 영역이 클 수록 Stack 영역이 작아진다.
- 런타임 시에 크기가 결정된다.
장점
-
프로그램에 필요한
개체의 개수나 크기를
미리 알 수 없는 경우 사용 가능하다.
-
개체가 너무 커서
스택 할당자에 맞지 않는 경우 사용 가능하다.
단점
-
할당 작업으로 인한 속도 저하
- 단지 할당하는 데 시간이 많이 소요될 수 있다.
-
힙 손상으로 인한 속도 저하
-
응용 프로그램에서
힙 블록을 적절하게 사용하지 않을 경우 힙이 손상된다.
-
가장 많이 발생할 수 있는 힙 손상 문제로는
이중 해제, 해제 후 블록 사용, 블록 경계를 벗어나 덮어쓰기 등이 있다.
-
-
힙 경합으로 인한 속도 저하
-
두 개 이상의 쓰레드에서
동시에 데이터에 액세스하려고 하면
경합이 발생하여
한 쪽 쓰레드의 작업이 완료되어야
다른 쪽 쓰레드의 작업이 진행될 수 있다.
-
경합으로 인해 항상 문제가 발생하며
이 문제는 현재 다중 프로세서 시스템에서
일어나는 문제 중 가장 큰 문제다.
-
이 문제를 해결하는 일반적인 방법은
해당 힙을 사용하는 모든 작업을 Serialize한다.
이러한 Serialization으로 인해
쓰레드에서는 잠금을 기다리는 동안
컨텍스트를 스위칭 할 수 있다.
-
그럼 Heap 영역, 동적 할당은 왜 필요한 것일까?
- 메모리를 효율적으로 관리할 수 있기 때문이다.
-
임베디드 시스템을 개발하다보면
하드웨어 크기가 매우 작은 경우가 많다.
-
하드웨어 크기가 작다는 것은
메모리의 용량도 작음을 의미한다.
-
그렇게 작은 메모리 공간을
컴파일러가 자동으로 관리해주는 것보다
프로그래머가 관리한다면
더 효율적인 관리가 가능하다.
Overflow
Heap overflow
-
Heap이 위에서부터 주소값을 채워져 내려오다가
Stack영역을 침범하는 경우
Stack overflow
- Stack영역이 Heap을 침범하는 경우
-
Stack의 지역변수는 사용되고 소멸하므로
데이터 용량의 불확실성을 가지므로
밑에서부터 채워 올리고
Heap은 위에서부터 채워 내려진다.
-
Stack 영역에서의
주소값은 밑에서부터(먼저선언된 순서) 정해지며
그 다음 주소는 순서대로 정해진다.