메모리 할당 : 스택 대 힙?


84

Stack vs Heap 사이의 메모리 할당 기본 사항과 혼동되고 있습니다. 표준 정의에 따라 (사물은 모두가 말한다), 모든 값 유형 에 할당 얻을 것이다 스택참조 로 이동합니다 유형 .

이제 다음 예를 고려하십시오.

class MyClass
{
    int myInt = 0;    
    string myString = "Something";
}

class Program
{
    static void Main(string[] args)
    {
       MyClass m = new MyClass();
    }
}

이제 메모리 할당은 C #에서 어떻게 발생합니까? MyClass(즉, m) 의 객체가 힙에 완전히 할당됩니까? 즉, int myInt그리고 string myString모두가 힙에 갈 것인가?

또는 객체가 두 부분으로 나뉘어 두 메모리 위치 인 스택 및 힙에 할당됩니까?


나는 오랫동안 어리석은 말이 틀렸음에도 불구하고 좋은 답변이있을 것이라고 믿기 때문에 단순히 찬성했다. 그러나 제안 된 "이중 할당"에 대한 사소한 반론을 제시하는 것은 매우 쉽습니다 (힌트 : 클래스 객체는 함수 호출 경계를 넘어 살 수 있으며 많은 경우가 있습니다).

이것이 귀하의 질문에 대답합니까? 스택과 힙은 무엇이며 어디에 있습니까?
Olivier Rogier

답변:


55

m힙에 할당되며 myInt. 기본 유형 (및 구조체)이 스택에 할당되는 상황은 메서드 호출 중이며 스택에서 로컬 변수를위한 공간을 할당합니다 (더 빠르기 때문). 예를 들면 :

class MyClass
{
    int myInt = 0;

    string myString = "Something";

    void Foo(int x, int y) {
       int rv = x + y + myInt;
       myInt = 2^rv;
    }
}

rv, x은 ( y는) 모두 스택에 있습니다. myInt힙 어딘가에 있습니다 ( this포인터 를 통해 액세스해야 함 ).


7
중요한 추가 사항은 "스택"과 "힙"이 실제로 .NET의 구현 세부 사항이라는 점을 기억하는 것입니다. 스택 기반 할당을 전혀 사용하지 않는 C #의 합법적 인 구현을 만드는 것은 완벽하게 가능합니다.
JSB ձոգչ 2010

5
나는 그것들이 그렇게 취급 되어야한다는 데 동의 하지만 그것들이 순전히 구현 세부 사항이라는 것은 전적으로 사실이 아닙니다. 공개 API 문서 및 언어 표준 (EMCA-334, ISO / IEC 23270 : 2006) (예 : "구조 값은 '스택에'저장 됨"에 명시되어 있습니다. 신중한 프로그래머는 구조를 신중하게 사용하여 성능을 향상시킬 수 있습니다. ") 그러나 예, 힙 할당 속도가 애플리케이션의 병목 현상 인 경우 아마도 잘못하고있는 것입니다 (또는 잘못된 언어 사용).
Mud

65

구현 세부 사항으로 객체가 할당되는 위치에 대한 질문을 고려해야합니다 . 객체의 비트가 정확히 어디에 저장되어 있는지는 중요하지 않습니다. 객체가 참조 유형인지 값 유형인지는 중요 할 수 있지만 가비지 수집 동작을 최적화해야하기 전까지는 객체가 저장 될 위치에 대해 걱정할 필요가 없습니다.

현재 구현에서는 참조 유형이 항상 힙에 할당되지만 값 유형 스택에 할당 수 있지만 반드시 그런 것은 아닙니다. 값 유형은 참조 유형에 포함되지 않고 레지스터에 할당되지 않은 박싱되지 않은 비 이스케이프 로컬 또는 임시 변수 인 경우에만 스택에 할당됩니다.

  • 값 유형이 클래스의 일부인 경우 (예제에서와 같이) 힙에있게됩니다.
  • 박스형이면 힙에있게됩니다.
  • 배열에 있으면 힙에서 끝납니다.
  • 정적 변수이면 힙에서 끝납니다.
  • 클로저에 의해 캡처되면 힙에있게됩니다.
  • 반복기 또는 비동기 블록에서 사용되는 경우 힙에서 끝납니다.
  • 안전하지 않거나 관리되지 않는 코드로 만든 경우 모든 유형의 데이터 구조 (스택 또는 힙일 필요는 없음)에 할당 될 수 있습니다.

내가 놓친 것이 있습니까?

물론 주제에 대한 Eric Lippert의 게시물에 링크하지 않았다면 나는 실망 할 것입니다.


1
Ed : 정확히 언제 중요합니까?
Gabe

1
@Gabe : 비트가 저장되는 위치는 중요합니다. 예를 들어 크래시 덤프를 디버깅하는 경우 개체 / 데이터를 찾을 위치를 알지 못하면 멀리 가지 않을 것입니다.
Brian Rasmussen

14
놓친 상황은 다음과 같습니다. 값 형식이 안전하지 않은 포인터를 통해 액세스되는 관리되지 않는 코드에서 가져온 경우 스택이나 관리되는 힙에 없을 수 있습니다. 관리되지 않는 힙 또는 힙이 아닌 일부 데이터 구조에있을 수 있습니다. "무더기"가 있다는 전체 아이디어도 신화입니다. 수십 개의 힙이있을 수 있습니다. 또한 지터가 값을 등록하도록 선택하면 스택이나 힙에 있지 않고 레지스터에 있습니다.
Eric Lippert

1
Eric Lippert의 Part Two는 환상적인 책이었습니다. 링크에 감사드립니다!
Dan Bechard 2013 년

2
이것은 인터뷰에서 요구되지만 실제 생활에서는 요구되지 않기 때문에 중요합니다. :)
Mayank 2015 년

23

"모든 VALUE 유형이 스택에 할당됩니다"는 매우 잘못되었습니다. 구조체 변수 메서드 변수로 스택에 있을 수 있습니다 . 그러나 유형의 필드는 해당 유형 을 사용합니다 . 필드의 선언 유형이 클래스 인 경우 값은 해당 개체의 일부로 힙에 있습니다. 필드의 선언 유형이 구조체 인 경우 필드는 해당 구조체가 있는 모든 곳에서 해당 구조체의 일부입니다 .

비록 방법 변수가 있다 이러한 경우 힙에있을 캡처 (λ / 아논-방법), 또는 (예를 들어) 반복기 블록의 일부.


1
그리고 boxing을 잊지 마세요. object x = 12;메소드에있는 경우 12는 정수 (값 유형)이더라도 힙에 저장됩니다.
Gabe

@Gabe : 값 유형 저장 위치는 값 유형의 필드 (공개 및 개인)를 자체적으로 보유합니다. 참조 유형 저장 위치는을 보유 null하거나 적절한 유형의 힙 개체에 대한 참조입니다. 모든 값 유형에 해당하는 힙 오브젝트 유형이 있습니다. 참조 유형 저장 위치에 값 유형을 저장하려고 시도하면 해당 힙 개체 유형의 새 개체가 생성되고 모든 필드가 해당 새 개체에 복사되며 참조 유형 저장 위치에 개체에 대한 참조가 저장됩니다. C #은 값 유형과 객체 유형이 동일한 척하지만 ...
supercat

... 이러한 관점은 이해보다는 혼란을 더합니다. List<T>.Enumerator해당 유형의 변수에 저장된 unboxed 는 값 유형이기 때문에 값 의미를 나타냅니다. 그러나 List<T>.Enumerator유형의 변수에 저장된 A IEnumerator<T>는 참조 유형처럼 작동합니다. 후자를 전자와 다른 유형으로 간주하면 행동의 차이를 쉽게 설명 할 수 있습니다. 같은 유형 인 척하면 추론하기가 훨씬 더 어려워집니다.
supercat 2012-08-20

12

2

스택

stack저장 메모리 블록 local variablesparameters. 스택은 함수가 입력 및 종료됨에 따라 논리적으로 확장 및 축소됩니다.

다음 방법을 고려하십시오.

public static int Factorial (int x)
{
    if (x == 0) 
    {
        return 1;
    }

    return x * Factorial (x - 1);
}

이 메서드는 재귀 적이므로 자신을 호출합니다. 메소드가 입력 될 때마다, 새로운 INT는 스택에 할당 하고, 때마다 메소드 종료는 INT는 해제됩니다 .


더미

  • 힙은 objects(예 reference-type instances:) 상주 하는 메모리 블록입니다 . 새 개체가 생성 될 때마다 힙에 할당되고 해당 개체에 대한 참조가 반환됩니다. 프로그램이 실행되는 동안 새 개체가 생성되면 힙이 채워지기 시작합니다. 런타임에는 힙에서 개체를 주기적으로 할당 해제하는 가비지 수집기가 있으므로 프로그램이 실행되지 않습니다 Out Of Memory. 객체는 그 자체로 참조되지 않는 즉시 할당 해제 대상이됩니다 alive.
  • 힙은 또한 static fields. 힙에 할당 된 객체 (가비지 수집 될 수 있음)와 달리 these live until the application domain is torn down.

다음 방법을 고려하십시오.

using System;
using System.Text;

class Test
{
    public static void Main()
    {
        StringBuilder ref1 = new StringBuilder ("object1");
        Console.WriteLine (ref1);
        // The StringBuilder referenced by ref1 is now eligible for GC.

        StringBuilder ref2 = new StringBuilder ("object2");
        StringBuilder ref3 = ref2;
        // The StringBuilder referenced by ref2 is NOT yet eligible for GC.
        Console.WriteLine (ref3); // object2
    }
}    

위의 예에서는 변수 ref1이 참조하는 StringBuilder 객체를 만든 다음 해당 내용을 작성합니다. 그 StringBuilder 객체는 이후에 그것을 사용하는 것이 없기 때문에 즉시 가비지 수집에 적합합니다. 그런 다음 ref2 변수가 참조하는 또 다른 StringBuilder를 만들고 해당 참조를 ref3에 복사합니다. 해당 시점 이후에 ref2가 사용되지 않더라도 ref3은 동일한 StringBuilder 객체를 유지하여 ref3 사용을 완료 할 때까지 수집 대상이되지 않도록합니다.

값 유형 인스턴스 (및 개체 참조)는 변수가 선언 된 모든 위치에 있습니다. 인스턴스가 클래스 유형 내의 필드 또는 배열 요소로 선언 된 경우 해당 인스턴스는 힙에 있습니다.


1

간단한 조치

값 유형은 THE STACK에서 확장 될 수 있으며, 일부 미래주의 데이터 구조에 할당 할 수있는 구현 세부 사항입니다.

따라서 값과 참조 유형이 작동하는 방식을 이해하는 것이 더 낫습니다. 값 유형은 값으로 복사됩니다. 즉, 값 유형을 FUNCTION에 매개 변수로 전달할 때 자연적으로 복사되는 것보다 전체 새 사본이 있음을 의미합니다. .

참조 유형은 참조로 전달됩니다 (참조가 일부 향후 버전에서 주소를 다시 저장한다는 점을 고려하지 마십시오. 다른 데이터 구조에 저장 될 수 있음).

그래서 당신의 경우

myInt는 참조 유형을 벗어난 클래스에서 캡슐화 된 int이므로 'THE HEAP'에 저장 될 클래스의 인스턴스에 연결됩니다.

ERIC LIPPERTS가 작성한 블로그를 읽을 수 있습니다.

Eric의 블로그


1

객체가 생성 될 때마다 힙이라는 메모리 영역으로 이동합니다. int 및 double과 같은 기본 변수는 로컬 메서드 변수 인 경우 스택에 할당되고 멤버 변수 인 경우 힙에 할당됩니다. 메서드에서 로컬 변수는 메서드가 호출 될 때 스택으로 푸시되고 메서드 호출이 완료되면 스택 포인터가 감소합니다. 다중 스레드 응용 프로그램에서 각 스레드는 자체 스택을 가지지 만 동일한 힙을 공유합니다. 이것이 힙 공간에서 동시 액세스 문제를 방지하기 위해 코드에서주의를 기울여야하는 이유입니다. 스택은 스레드로부터 안전하지만 (각 스레드에는 자체 스택이 있음) 코드를 통한 동기화로 보호되지 않는 한 힙은 스레드로부터 안전하지 않습니다.

이 링크는 http://www.programmerinterview.com/index.php/data-structures/difference-between-stack-and-heap/ 도 유용합니다 .


0

m은 MyClass의 객체에 대한 참조이므로 m은 메인 스레드의 스택에 저장되지만 MyClass의 객체는 힙에 저장됩니다. 따라서 myInt 및 myString은 힙에 저장됩니다. m은 참조 (메모리 주소) 일 뿐이며 메인 스택에 있습니다. m 할당이 해제되면 GC는 힙에서 MyClass 객체를 지 웁니다. 자세한 내용은이 기사의 네 부분을 모두 읽으십시오. https://www.c-sharpcorner.com/article/C-Sharp-heaping-vs-stacking-in-net- 이상적 상대/

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.