Stack 과 Heap의 차이 및 사용 방법

 스택(Stack)과 힙(Heap) 차이점 – JungHyun Baek – Developer from South Korea (junghyun100.github.io)

[유니티, C#] 스택과 값, 힙과 참조 : 네이버 블로그 (naver.com)

c# 기초 03.2 - 스택(Stack)과 힙(Heap) (tistory.com)


class Simple{}int main()
{    Simple simple(1);
    cout << simple.GetID() << endl;

    Simple* pSimple = new Simple(2);
    cout << pSimple->GetID() << endl;

    delete pSimple;    // 스택에 할당할 경우 객체를 별도로 지정하지 않고 단순 할당 하면 된다.    Simple simple(1);    // 힙영역에 할당해서 동적으로 할당 시킬경우 객체생성한다.    // 힙영역 할당의 경우 동적으로 생성 했기 때문에 마지막에     // 메모리에서 삭제해야 한다    Simple* pSimple = new Simple(2);}

메모리 구조

프로그램이 실행되기 위해서는 먼저 프로그램이 메모리에 로드(load)되어야 합니다.

또한, 프로그램에서 사용되는 변수들을 저장할 메모리도 필요합니다.

따라서 컴퓨터의 운영체제는 프로그램의 실행을 위해 다양한 메모리 공간을 제공하고 있습니다.

프로그램이 운영체제로부터 할당받는 대표적인 메모리 공간은 4가지 있습니다.

  1. 코드(code) 영역

  2. 데이터(data) 영역

  3. 스택(stack) 영역

  4. 힙(heap) 영역

My Image

코드(code) 영역

메모리의 코드(code) 영역은 실행할 프로그램의 코드가 저장되는 영역으로 텍스트(code) 영역이라고도 부릅니다.

CPU는 코드 영역에 저장된 명령어를 하나씩 가져가서 처리하게 됩니다.

데이터(data) 영역

메모리의 데이터(data) 영역은 프로그램의 전역 변수와 정적(static) 변수가 저장되는 영역입니다.

데이터 영역은 프로그램의 시작과 함께 할당되며, 프로그램이 종료되면 소멸합니다.

스택(stack) 영역

메모리의 스택(stack) 영역은 함수의 호출과 관계되는 지역 변수와 매개변수가 저장되는 영역입니다.

스택 영역은 함수의 호출과 함께 할당되며, 함수의 호출이 완료되면 소멸합니다.

이렇게 스택 영역에 저장되는 함수의 호출 정보를 스택 프레임(stack frame)이라고 합니다.

스택 영역은 푸시(push) 동작으로 데이터를 저장하고, 팝(pop) 동작으로 데이터를 인출합니다.

이러한 스택은 후입선출(LIFO, Last-In First-Out) 방식에 따라 동작하므로, 가장 늦게 저장된 데이터가 가장 먼저 인출됩니다.

스택 영역은 메모리의 높은 주소에서 낮은 주소의 방향으로 할당됩니다.

힙(heap) 영역

메모리의 힙(heap) 영역은 사용자가 직접 관리할 수 있는 ‘그리고 해야만 하는’ 메모리 영역입니다.

힙 영역은 사용자에 의해 메모리 공간이 동적으로 할당되고 해제됩니다.

힙 영역은 메모리의 낮은 주소에서 높은 주소의 방향으로 할당됩니다.

스택과 힙의 장단점

스택

매우 빠른 액세스

변수를 명시 적으로 할당 해제 할 필요가 없습니다.

공간은 CPU에 의해 효율적으로 관리되고 메모리는 단편화되지 않습니다.

지역 변수 만

스택 크기 제한 (OS에 따라 다름)

변수의 크기를 조정할 수 없습니다.

변수는 전역 적으로 액세스 할 수 있습니다.

메모리 크기 제한 없음

(상대적으로) 느린 액세스

효율적인 공간 사용을 보장하지 못하면 메모리 블록이 할당 된 후 시간이 지남에 따라 메모리가 조각화되어 해제 될 수 있습니다.

메모리를 관리해야합니다 (변수를 할당하고 해제하는 책임이 있습니다)

변수는 C언어 realloc() or 자바 new


스택(Stack)

스택(Stack) 메모리도 "주소"와 "값"이 똑같이 한 세트로 있습니다.

 

기본 메모리와 다른 점은 메모리에 무엇을 저장할지는 지 미리 정해놓는 점과, 이를 관리 하는 방식입니다. 

 

먼저 스택(Stack)은 c# 기초 03.1 - 박싱(Boxing)과 언박싱(UnBoxing)에서도 설명했듯이 값 형식(Value Type)이 저장됩니다.

 

조금 자세히 설명 하면 프로그램을 실행하는데 필요한 메모리 공간으로,

 

메소드가 호출되는데 필요한 메모리가 스텍에 저장됩니다.

 

필요한 메모리란 메소드가 사용하는 코드 즉 지역변수, 매개변수(파라미터), 리턴값 등이 있습니다.

 

 

그리고 컴퓨터 메모리는 한계가 존재하기 때문에, 무작정 모든 코드를 메모리에 저장하면

 

메모리 한계에 부딪혀 더 이상의 정보를 저장하기 불가능 합니다.  

 

그렇기에 메모리를 효율적으로 사용하기 위해서,

 

기초03 - 컬렉션(Collection)에서 배운 스택(Stack)과 똑같은 방식으로 작동 됩니다.

 

바로 LIFO(Last-In First-Out : 후입선출)의 방식으로 가장 나중에 저장된 값을 가장 먼저 반환하는 것 입니다.

 

총알을 탄창에 장전하는 것과 같다고 생각하시면 됩니다.

 

아래의 코드를 보시겠습니다.

public void StackEx(){
	
    int a = 1;
    int b = 2;
	
    a = 10;
    b = 20; 
    
}

 

 

먼저 int a = 1;이 실행되고, 그 다음에 int b = 2;가 실행되기 때문에,

 

맨 아래에 값 : 1이 들어가고, 그 위에 값 : 2가 들어갑니다. 

 

그후 "값 형식"이 저장되기 때문에 스택메모리 안에 값만 변하게 됩니다.

 

그리고 함수 종료시 마지막에 들어갔던 "b변수의 메모리"가 삭제되고, 그후 "a변수의 메모리"가 삭제 됩니다.

 

위 그림과 같이 메소드의 호출이 끝나는 순간, 스택메모리에서 LIFO 순서대로 삭제 합니다.

 

스택 프레임(Stack Frame)

위에서 설명했듯이 마치 음식을 만들기 전에 재료를 준비하는 것처럼

 

메소드를 호출하기 전에, 메소드에서 사용될 메모리(정보)가 스텍에 저장되는데,

 

이것을 하나의 묶음으로 표현한게 스택 프레임(Stack Frame)입니다.

 

하나의 메소드에는 하나의 스택프레임이 존재합니다.

 

여기서 사용될 메모리란 메소드가 사용하는 코드 즉 지역변수, 매개변수(파라미터), 리턴값 등이 있습니다.

 

만약 Sum() 메소드 안에, 지역변수, 매개변수, 리턴값 등이 있으면 전부 스택프레임으로 묶어서 스택에 저장합니다.

 

이렇게 하면 실행 순서는 Main() -> Calc() -> Sum() 순으로 시작하지만,

 

먼저 끝나는 순서는 그 반대인 Sum() -> Calc() -> Main()이기 때문에,

 

메소드가 끝나는 순간 메모리에서 삭제하여, 메모리 공간을 효율적으로 사용할수 있게 됩니다.

 


힙(Heap)

힙(Heap) 메모리도 "주소"와 "값"이 똑같이 한 세트로 있습니다.

 

스택과 같은점은 무엇을 저장할지는 지 미리 정해놓는 점과, 이를 관리 하는 방식입니다. 

 

먼저 힙(Heap)은 c# 기초 03.1 - 박싱(Boxing)과 언박싱(UnBoxing)에서도 설명했듯이 참조 형식(Reference Type)이 저장됩니다.

 

힙(Heap)에서는 키워드 new를 이용해서 생성한 메모리가 저장됩니다.

 

위 그림에서 전역변수 "x"와 "y"도 힙(Heap)에 저장됩니다.

 

이것은 클래스를 생성할때 new 키워드로 생성하기 때문에, 메소드가 아닌 전역변수도 힙(Heap)에 저장됩니다.

 

우리가 배운 배열(Array), 컬렉션(Collection), 일반화 컬랙션(Generial Collection)도 new 키워드로 생성하기 때문에,

힙(Heap)에 저장이 됩니다.

 

 

힙(Heap)은 메모리 제한이 없기 때문에 클래스가 여기에 저장이 됩니다. 

 

 

아래의 코드를 보시겠습니다.

public void HeapEx(){

	//전역변수
	public int num;

	// 메소드
	public void Heap(){
    	
            int[] array = new int[3]{0, 1, 2};
		
            HeapEx ex01 = new HeapEx();
            ex01.num = 10;
 		
            HeapEx ex02 = ex01;
            ex02.num = 20;
        
    }
    
}

HeapEx 클래스는 전역변수 num을 가지고 있습니다.

 

위의 그림을 보시면 new로 생성된 array와 클래스 ex01의 "값"은 힙(Heap)에 저장되어 있고, 

 

스택(Stack)은 값을 가지고 있는 힙(Heap)에 "주소"를 저장합니다.

 

ex02의 경우 ex01의 값(주소 : 5001)을 복사하여 스택(Stack)에 저장합니다.

 

그렇기 때문에 ex02.num = 20; 가 실행될때 힙(Heap) 주소:5001에 저장되어 있는 값이 바뀝니다.

 

ex01과 ex02는 같은 주소를 가지고 있기 때문에,

 

ex01.num의 값 또한 "10"이 아닌 "20"으로 나옵니다

 

 

 

가비지 컬랙터 (GC : Garbage Collector)

스택(Stack)에서는 LIFO방식으로 메모리를 삭제 하는데,  그럼 힙(Heap)에서는 어떻게 메모리가 삭제가 될까요?

 

메모리는 주소를 모르면 값을 찾을 수가 없습니다.

 

힙(Heap)의 값을 가리키는 주소가 스택(Stack)에 저장되어 있지 않으면, 

값은 존재하는데 접근이 불가능 하다는 말입니다. 

 

C#에서는 이렇게 주소가 삭제되 접근이 불가능하게된 힙(Heap)메모리들을

가비지 컬랙터(GC : Garbage Colector)가 알아서 삭제를 해줍니다.

다만, 언제 GC가 메모리에서 삭제할지는 모릅니다. 

 

 

여담

여러 매체에서 억울하게 누명을 쓴 사람이 카카오톡 대화내용을 복구해 누명을 벗었다는 이야기를 

다들 한번씩은 들어보셨을꺼 같습니다. 

 

이러한 데이터 복구가 가능한 이유중 하나가 바로 힙(Heap) 메모리 때문입니다. 

 

우리가 어떠한 정보를 삭제할 때 그 정보가 힙(Heap)에 저장되어 있으면, 

 

컴퓨터는 스택(Stack)메모리안 해당 주소값만 삭제를 합니다. 

 

그렇게 되면 사용자의 입장에서는 값을 가지고 있는 주소가 사라졌기 때문에,

 

접근이 불가능하지만, 그 정보는 힙(Heap)안에서 살아있습니다. 

 

물론, 가비지 컬랙터(GC)가 삭제하기 전까지 말이죠.

 

이렇게 주소가 살아져 접근이 불가능한 힙(Heap)메모리에 저장된 값들을 일일이 찾아서, 

해당 값의 주소를 다시 스택(Stack)에 저장하는게 메모리 복구라고 생각하시면 됩니다.

 


스택(Stack) vs 힙(Heap)

이제 두 메모리를 비교해 보겠습니다. 

 스택(Stack)힙(Heap)
접근 속도매우 빠른 접근

(상대적으로) 느린 접근

메모리 크기크기 제한 있음(OS에 따라 다름)

메모리 크기 제한이 없음

삭제효율적인 공간 관리를 위해 LIFO로 삭제됨

GC가 나중에 삭제함

(사용자가 직접 메모리를 관리해야하는

경우가 생길수도 있음)

 

변수 접근해당 메소드 내에서 만 접근 가능

전역 적으로 접근이 가능

 








댓글

이 블로그의 인기 게시물

About AActor!!! "UObject" has no member "BeginPlay"

UNREAL Android build information

Shader informations nice blog ~ ~