메모리 구조란?
-
프로그램이 실행되기 위해서는
-
먼저 프로그램이 메모리에 로드(load)되어야 한다.
-
또한 프로그램에서 사용되는 변수들을 저장할 메모리도 필요하다.
-
따라서 컴퓨터의 운영체제는
-
프로그램의 실행을 위해
-
다양한 메모리 공간을
-
제공하고 있다.
-
프로그램이 운영체제로부터 할당받는 대표적인 메모리 공간(RAM)은 다음과 같다.
-
코드(code) 영역
-
데이터(data) 영역
-
스택(Stack) 영역
-
힙(Heap) 영역
코드(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 영역이 작아진다.
- 런타임 시에 크기가 결정된다.
장점
-
프로그램에 필요한
-
개체의 개수나 크기를
-
미리 알 수 없는 경우 사용 가능하다.
-
개체가 너무 커서
-
스택 할당자에 맞지 않는 경우 사용 가능하다.
단점
-
할당 작업으로 인한 속도 저하
- 단지 할당하는 데 시간이 많이 소요될 수 있다.
-
해제 작업으로 인한 속도 저하
-
주로 병합을 사용할 때
-
해제 작업에 더 많은 주기가 소요된다.
-
병합하는 동안
-
각 해제 작업에서는
-
해당 인접 항목을 찾아내어 더 큰 블록을 만들고
-
그 블록을 해제 목록에 다시 삽입한다.
-
그러한 찾기가 수행되는 동안에는
-
메모리가 임의의 순서로 액세스되어
-
캐시 누락이 발생하고
-
성능이 저하될 수 있다.
-
-
힙 손상으로 인한 속도 저하
-
응용 프로그램에서
-
힙 블록을 적절하게 사용하지 않을 경우
-
힙이 손상된다.
-
가장 많이 발생할 수 있는 힙 손상 문제로는
-
이중 해제, 해제 후 블록 사용, 블록 경계를 벗어나 덮어쓰기 등이 있다.
-
-
힙 경합으로 인한 속도 저하
-
두 개 이상의 쓰레드에서
-
동시에 데이터에 액세스하려고 하면
-
경합이 발생하여
-
한 쪽 쓰레드의 작업이 완료되어야
-
다른 쪽 쓰레드의 작업이 진행될 수 있다.
-
경합으로 인해 항상 문제가 발생하며
-
이 문제는 현재 다중 프로세서 시스템에서
-
일어나는 문제 중 가장 큰 문제다.
-
메모리 블록을 아주 많이 사용하는 응용 프로그램이나
-
DLL이 여러 개의 쓰레드로 실행되거나
-
다중 프로세서 시스템에서 실행되면 속도가 느려진다.
-
이 문제를 해결하려면
-
일반적으로 단일 잠금 방법을 사용하여
-
해당 힙을 사용하는 모든 작업을 Serialize한다.
-
이러한 Serialization으로 인해
-
쓰레드에서는 잠금을 기다리는 동안
-
컨텍스트를 스위칭 할 수 있다.
-
경합은 일반적으로
-
쓰레드와 프로세스의 컨텍스트 스위칭을 가져온다.
-
컨텍스트 스위칭에도 리소스가 많이 소모되지만
-
프로세서 캐시에서 데이터가 손실되어
-
나중에 해당 쓰레드가 다시 살아날 때
-
이 데이터를 다시 작성하는 데에
-
리소스가 훨씬 많이 소모된다.
-
그럼 Heap 영역, 동적 할당은 왜 필요한 것일까?
-
메모리를
-
효율적으로 관리할 수 있기 때문이다.
-
임베디드 시스템을 개발하다보면
-
하드웨어 크기가 매우 작은 경우가 많다.
-
하드웨어 크기가 작다는 것은
-
메모리의 용량도 작음을 의미한다.
-
그러한 작은 메모리 공간에
-
프로그래머가 메모리 관리의 달인이라면
-
컴파일러가 자동으로 할당해주는 것보다
-
더 효율적인 관리가 가능하다.
-
물론 메모리 용량이 클 수도 있지만
-
가격이 매우 비싸질 것이다.
Overflow
Heap overflow
-
Heap이 위에서부터 주소값을 채워져 내려오다가
-
Stack영역을 침범하는 경우
Stack overflow
- Stack영역이 Heap을 침범하는 경우
-
Stack의 지역변수는 사용되고 소멸하므로
-
데이터 용량의 불확실성을 가지므로
-
밑에서부터 채워 올리고
-
Heap은 위에서부터 채워 내려진다.
-
Stack 영역에서의
-
주소값은
-
밑에서부터(먼저선언된 순서) 정해지며
-
그 다음 주소는 순서대로 정해진다.