티스토리 뷰

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


  • 값 형식 : 값을 변수에 넣는 데이터 형식
  • 참조 형식 : 변수에 대한 위치(메모리 위치)를 담는 데이터 형식


간단하게 값 형식과 참조 형식에 대해서 알아보았습니다. 그럼 변수를 값에 넣을 때 메모리 구조에서 어떻게 처리되는지를 알아 보겠습니다. 먼저 값 형식을 통해서 변수에 값을 넣게 되면 스택 메모리에 변수의 값이 저장됩니다.


그럼 스택 구조를 먼저 알아보도록 하겠습니다. 스택은 컴퓨터 사이언스 전공에서 배우는 자료구조의 한 종류입니다. 전형적인 LIFO(Last In First Out)구조로써, 가장 나중에 들어온 데이터가 자료구조에서 가장 먼저 나가는 구조로 이루어 져 있습니다. 이러한 구조를 한국말로는 후입선출이라고 부르고 있습니다. 이와 반대되는 자료구조는 FIFO(First In First Output)구조로 대표적인 예로 링크드리스트(Linked List)가 있습니다. 






스택 구조가 실질적으로 코드상에서 어떻게 처리가 되는지 알아볼까요. 아래와 같은 간단한 코드가 있다고 가정을 해 봅시다.


public void Init()

{


int a  = 10; // 1번 과정

int b = a;

b = 20; // 2번 과정

a = 30;


} // 3번 과정


1번 과정을 통해서 정수형의 a, b가 생성이 됩니다. a, b는 스택 메모리의 어딘가에 할당이 되며 초기화한 값이 할당된 곳에 저장이 됩니다. 그림으로 나타내면 아래와 같습니다. 




2번 과정이 진행되면, 1번 과정에서 할당된 각각의 위치에 값이 할당이 됩니다. 



스택메모리를 이용하면 변수가 할당 된 위치에 값만 변경이 되는 것입니다. 그리고 마지막으로 3번 과정을 거치게 되면 할당된 a, b가 역순대로 메모리에서 자동적으로 정리가 됩니다. 


값 형식을 통해서 메모리에 데이터를 넣게 되면, 스택 메모리를 사용하기 때문에 LIFO 방식으로 데이터가 없어진다고 설명드렸습니다. 그럼 값 형식에 할당된 데이터가 사라지는 시점은 언제일까요? 그 시점은 위의 코드에서 3번 과정 }를 만날 때 메모리에 할당된 데이터가 정리가 됩니다. 스택 메모리는 전형적으로 변수가 선언된 Scope를 벗어나게 되면 할당이 해제가 됩니다. 


스택 메모리에서 중요한 점은 1) 할당된 위치에 값만 지속적으로 변경되는 점 2) LIFO 방식이며, 3) 해제시점은 변수선언 영역을 벗어나는 때라는 것만 이해하고 계시면 됩니다. 


그럼 지금부터는 참조 형식의 변수 할당은 어떻게 되는지 알아보도록 하겠습니다. 참조 형식의 변수 할당이 어떻게 되는지 알아보기 위한 예제코드는 아래와 같습니다.


class Program

{

    public int value;

    static void Main(string[] args)

    {

        Program prog = new Program(); // 1번과정

        prog.value = 100;


        Program prog1 = prog; // 2번 과정


        prog1.value = 200; // 3번 과정

    }

}


위의 코드 1번과정을 거쳐서 prog 객체를 생성하고, 이에 대한 값을 100으로 설정합니다. 이렇게 되면 스택에는 prog에 대한 메모리 주소가 들어가게 됩니다. 그리고 힙에는 실질적인 값이 들어갑니다.


이 말의 의미는 prog는 5000번지를 참조하고 있는데, 5000번지에 저장된 값이 100이라는 의미입니다. 




2번 과정을 통해서 prog1 객체에 prog 객체를 넣습니다. 이러한 경우 단순히 Stack에서의 복사만 이루어 집니다. 스택메모리에는 Prog1에 대한 변수가 할당이 되지만, Stack에 들어있는 값은 prog가 참조하고 있는 주소인 5000이 들어갑니다. 따라서 prog, prog1 변수는 둘다 5000번지에 있는 값 100을 참조하고 있는 것입니다. 이해해야 하는 포인트는 값이 복사가 되는게 아니라 주소가 복사가 된다는 점 입니다.


3번 과정을 통해서 prog의 값을 200으로 변경합니다. 값을 변경하면 힙 메모리에 저장된 값이 변경이 되겠죠. 현재 prog와 prog1은 모두 동일한 메모리를 참조하고 있습니다. 따라서 prog와 prog1의 value 값은 모두 200으로 변경이 됩니다. 


힙 메모리에서 중요한 점은 1) Stack에는 메모리 주소가, Heap에는 실질적인 값이 들어간다는 점 2) 새로운 객체 변수에 기존의 변수를 할당하면 Stack의 메모리 주소만 복사된다는 점 3) 값을 변경하면 값은 메모리를 참조하고 있는 모든 변수의 값은 동일해 진다는 점을 이해하고 계시면됩니다. 


스택 메모리는 해당 함수가 종료되면 할당된 값이 없어진다고 설명드렸습니다. 그럼 힙 메모리 영역은 언제 없어질까요? 이에 대한 답은 알 수 없다 입니다. 스택 메모리와 달리 힙 메모리 기반은 Garbage Collector(GC, 가비지 콜렉터)라는 녀석이 알아서 할당을 해제합니다. 내부적으로 할당 해제 알고리즘이 있긴 하지만 개발자 및 사용자 입장에서는 알 필요가 없습니다. 그 이유는 CLR에서 알아서 해제를 해주기 때문입니다.


CLR의 GC는 기존의 C/C++과 C#의 가장 큰 차이점입니다. C/C++에서는 메모리 할당 및 해제를 개발자가 알아서 처리해줘야 했습니다. 그러지 않으면 Memory Leak(메모리 릭)이 발생을 하여 메모리 처리 부분에 큰 문제점을 야기하였습니다. Java에서 부터 큰 인기를 얻은 GC를 C#에도 도입함으로써 개발자의 오류를 줄여주는 역할을 하고 있습니다. 하지만 GC가 있으면 성능상으로 느려진다는 단점도 가지게 됩니다. 


이 글에서는 스택과 힙 메모리의 차이 그리고 값 형식과 타입 형식에 대해서 설명드렸습니다.


이 글이 도움이 되셨나요?

그렇다면 아래의 그림을 클릭해주세요.



댓글