본문 바로가기

C# 코딩 기초

C# 기초 03.2 - 스택(Stack)과 힙(Heap)

안녕하세요. "투명 나비" 입니다

 

지난 시간에는 박싱(Boxing)과 언박싱(Unboxin)을 배웠습니다. 

 

박싱(Boxing)은 스택에서 힙으로 메모리가 옮겨 가는 것,

 

언박싱(UnBoxing)은 힙에서 스텍으로 메모리가 옮겨 가는 것이라고 설명을 드렸습니다.

 

이번시간에는 스택(Stack)과 힙(Heap)에 대해서 조금 더 자세히 알아 보도록 하겠습니다.


컴퓨터 메모리

 

자세한 내용을 알기 전에 메모리는 마치 HashTable 처럼 한 쌍(Pair)의 "메모리 주소"와 "값"이

하나의 "세트"로 존재한다고 알고 있어야 합니다.

 

아주 간략한 그림

 

"주소"는 메모리 안의 위치 라고 기억하시고, "값"은 그 위치에 저장된 정보라고 이해하시면 됩니다.

 

컴퓨터가 "주소"를 모르면, 찾고자 하는 정확한 정보를 찾지 못합니다.

 

다른말로는 컴퓨터 내부 메모리에 정보가 있으나 사용하는 사람의 입장에서는 찾을 방법이 없으니 "잃어버렸다"든지 "삭제" 되었다는 말과 같습니다.

 

 

"값"은 어떤것이든 될수 있습니다.

 

예로들면, "어떠한 값을 가지고 있는 메모리 주소", "숫자", "문자", "아무것도 가지고 있지 않다" 등이 가능합니다.

 

 

마지막으로 컴퓨터가 어떠한 작업을 수행하기 위해서는 먼저 메모리에 정보를 저장해야 합니다

 

마치 레서피 대로 요리를 시작하기 전 재료를 먼저 준비하는 것으로 생각하시면 됩니다.

 


스택(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가 나중에 삭제함
(사용자가 직접 메모리를 관리해야하는
경우가 생길수도 있음)

변수 접근 해당 메소드 내에서 만 접근 가능 전역 적으로 접근이 가능

 


 

투명나비의 후기

 

역시나 빨리 끝날 줄 알았던 이번 포스팅은 생각보다 더 오래 걸렸습니다.

 

프로젝트를 개발할 때는 뭐가 가능하고, 뭐가 안되는 지 확인한 후 되는 코드만 사용을했는데,

 

이번에 복습하면서 왜 가능하고, 어떻게 코드를 써야 하는지 알게된 좋은 계기 였던 것 같습니다.

 

그리고 계속 그림판에서 예시를 고치고, 다시 그리고 하는 시간이 오래걸려 

 

다음 포스팅에서는 포토샵을 사용해야 할꺼 같네요ㅎㅎ

잡다한 기술이 늘어나는 거 같네요

 

이번 포스팅은 저번 포스팅 박싱, 언박싱에 대해서 쓰다가, 스택과 힙의 차이를 적어보고 싶어서 

 

만들었습니다. 

 

처음으로 2명의 방문자가 생겨 신기하고, 기쁘네요ㅎㅎㅎㅎㅎㅎ

 

빨리 다음글을 쓰러 가야겠습니다. 

 

참고로 인터넷에서 여러 자료를 참고해서 제 입맛 데로 만들었기 때문에,

 

누락된 부분이나, 간략하게 설명한 부분도 있습니다

 

만약 틀린 부분이나 추가해야 하는 부분이 있으면 알려주셨으면 좋겠습니다.


참고

https://blog.naver.com/cjej1004/110100887961

 

C# Stack & Heap Memory (스택 & 힙 메모리)

Stack (스택) 이란? - 프로그램을 실행하는데 필요한 메모리 공간으로 메소드가 호출되는데 필요한 메모리...

blog.naver.com

https://m.blog.naver.com/PostView.nhn?blogId=codingspecialist&logNo=221195242403&proxyReferer=https%3A%2F%2Fwww.google.com%2F

 

스택(stack)과 힙(heap) 이해

Stack vs HeapStack은 메소드가 실행될 때 변수가 push(입력)되며 실행이 종료될 때 pop(해제)된다. hea...

blog.naver.com

https://guslabview.tistory.com/186

 

[.NET] C# 스택(Stack)과 힙(Heap) 메모리의 차이

오늘은 스택과 힙메모리의 차이에 대해서 알아보도록 하겠습니다. C#에서도 당연히 스택기반의 메모리와 힙 기반의 메모리를 제공합니다. C#에서 이 메모리들의 구조를 확인하기 위해서는 먼저 값 형식(Value Type..

guslabview.tistory.com