스파르타 게임개발종합반(Unity)/기술 면접 대비 꾸준 실습

04. 스택 메모리 vs 힙 메모리

테크러너 2024. 7. 5.

확인 문제

1. 다음 `struct` 코드와 `class` 코드의 결과를 예측해보고, 이유를 생각해봅시다.

public struct Point
{
    public int X;
    public int Y;

    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }
}

public class Program
{
    public static void Main()
    {
        Point point1 = new Point(10, 20);
        Point point2 = point1;
        point2.X = 30;

        Console.WriteLine("Point 1 X: " + point1.X);
        Console.WriteLine("Point 2 X: " + point2.X);
    }
}
public class Point
{
    public int X;
    public int Y;

    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }
}

public class Program
{
    public static void Main()
    {
        Point point1 = new Point(10, 20);
        Point point2 = point1;
        point2.X = 30;

        Console.WriteLine("Point 1 X: " + point1.X);
        Console.WriteLine("Point 2 X: " + point2.X);
    }
}

 

`struct`는 값 형식(value type)입니다. 따라서 `point1`을 `point2`에 할당하면 두 개의 독립적인 복사본이 생성됩니다.

`point2.X`를 `30`으로 변경해도 `point1.X`에는 영향을 미치지 않습니다.

 

`class`는 참조 형식(reference type)입니다. 따라서 `point1`을 `point2`에 할당하면 두 변수가 같은 객체를 참조하게 됩니다.

`point2.X`를 `30`으로 변경하면 `point1.X`도 변경됩니다. `point1`과 `point2`가 같은 객체를 가리키기 때문입니다.

 

 

 

 

설명 문제

1. 참조 형식과 값 형식에 대해 설명해주세요.

참조 형식은 데이터가 저장된 메모리 위치(주소)를 저장하고, 값 형식은 데이터를 직접 저장합니다.

그래서 참조 형식을 가진 변수는 실제 데이터가 있는 힙(heap) 메모리의 주소를 참조하고, 값 형식은 보통 스택(stack) 메모리에 저장됩니다.

참조 형식을 복사할 때는 데이터의 참조(주소)가 복사되고, 값 형식은 실제 데이터가 복사됩니다.(두 변수는 서로 독립적인 값을 갖습니다)

 

2. 메모리에서 스택과 힙의 차이점에 대해 설명해주세요.

스택은 Last-In-First-Out(LIFO) 후입선출 구조를 따릅니다. 힙은 자유 형식 메로리 영역으로, 메모리 블록이 필요에 따라 동적으로 할당되고 해제됩니다.

스택은 함수 호출 시 자동으로 메모리가 할당되고, 함수가 종료되면 자동으로 해제됩니다. 이는 컴파일 타임에 결정됩니다.

힙 메모리는 new 연산자 등 명시적으로 할당되고 프로그래머가 직접 해제해야 합니다.

스택 메모리 할당 및 해제는 매우 빠릅니다. 단순히 스택 포인터를 이동하기 때문입니다.

힙 메모리 할당 및 해제는 스택보다 느립니다. 더 복잡한 메모리 관리 작업이 필요하기 때문입니다.

스택은 제한된 크기를 가지며 너무 많은 메모리를 사용하면 스택 오버플로(Stack Overflow) 오류가 발생할 수 있습니다.

힙은 스택보다 훨씬 큰 메모리 영역을 가지며, 필요한 만큼 할당할 수 있습니다. 그러나 과도한 메모리 사용은 메모리 부족 문제를 일으킬 수 있습니다.

스택에 저장된 데이터는 메모리 지역 변수와 같이 함수 내에서만 유효합니다.

힙에 저장된 데이터는 전역적으로 접근 가능하며, 참조에 의해 접근됩니다. 객체가 필요 없어지면 가비지 컬렉터(Garbage Collector)가 메모리를 회수합니다.

 

구조 LIFO 구조, 함수 호출 시 사용 자유 형식, 동적 메모리 할당
속도 매우 빠름 상대적으로 느림
메모리 할당 컴파일 타임에 자동 할당 런타임에 명시적 할당 (new 등)
메모리 해제 함수 종료 시 자동 해제 명시적 해제 (delete, 가비지 컬렉션)
용량 제한적 (스택 오버플로 가능) 더 큰 용량 (메모리 부족 가능)
데이터 접근 지역 변수, 함수 내에서만 유효 전역적으로 접근 가능
사용 예 지역 변수, 함수 매개변수 동적 객체, 대규모 데이터 구조

 

 

3. 1번과 2번 질문의 답안을 기반으로 struct와 class의 차이점에 대해 설명해주세요.

 

Struct는 값 형식(Value Type)입니다. 데이터 자체를 직접 저장하며, 변수에 할당될 때 값이 복사됩니다. 이로 인해 각 변수는 독립적인 데이터를 가집니다.

Class는 참조 형식(Reference Type)입니다. 데이터의 참조(메모리 주소)를 저장하며, 변수에 할당될 때 참조가 복사됩니다. 따라서 여러 변수가 동일한 객체를 참조할 수 있습니다.

 

형식 값 형식 (Value Type) 참조 형식 (Reference Type)
메모리 할당 주로 스택 (Stack) 힙 (Heap)
성능 메모리 할당/해제 빠름 메모리 할당/해제 상대적으로 느림
데이터 복사 값 복사 (독립적) 참조 복사 (데이터 공유)
용도 간단한 데이터 구조 복잡한 데이터 구조 및 객체 지향
상속 상속 불가 상속 가능
기본 생성자 명시적 정의 불가 명시적 정의 가능

 

 

 

4. 얕은 복사와 깊은 복사의 차이점은 무엇인가요?

얕은 복사는 객체의 '참조 값(주소)'만 복사하는 것입니다.

깊은 복사는 객체의 모든 수준의 필드와 하위 객체까지 재귀적으로 복사하는 것입니다. 원본 객체가 참조하는 모든 객체를 새로운 메모리 공간에 복사합니다. 즉, 원본 객체와 복사된 객체가 독립적인 객체를 갖게 합니다.

 

5. 박싱과 언박싱이 일어나는 과정을 메모리 관점에서 설명해주세요.

박싱은 값 형식을 참조 형식으로 변환하는 과정입니다. 값 형식 데이터를 힙(heap) 메모리에 할당하고, 해당 메모리의 참조를 생성하는 과정입니다.

언박싱은 참조 형식을 다시 값 형식으로 변환하는 과정입니다. 힙 메모리에 있는 객체에서 값을 꺼내어 스택 메모리에 저장하는 과정입니다.

 

 

실습 문제

[깊은 복사 구현]

여러 종류의 깊은 복사를 구현해봅시다.

1. 새로운 인스턴스를 생성하여 모든 필드들을 복사한 뒤 반환해주는 Clone() 메서드

2. 복사 생성자를 이용한 깊은 복사

public class Character
{
    public string name;
    public int hp;
    public int attack;

    public Character()
    {
        name = "";
        hp = 10;
        attack = 5;
    }

    public Character Clone()
    {
        // ****** CODE HERE ******
        
        // ***********************
    }

    public Character(Character original)
    {
        // ****** CODE HERE ******
        
        // ***********************
    }
}

 

 

작성한 코드▼

더보기
public class Character
{
    public string name;
    public int hp;
    public int attack;

    public Character()
    {
        name = "";
        hp = 10;
        attack = 5;
    }

    public Character Clone()
    {
        // ****** CODE HERE ******
        return new Character(this);
        // ***********************
    }

    public Character(Character original)
    {
        // ****** CODE HERE ******
        this.name = original.name;
        this.hp = original.hp;
        this.attack = original.attack;
        // ***********************
    }
}

 

반응형

댓글