스택과 힙은 무엇과 어디에 있습니까?


8099

프로그래밍 언어 서적은 값 유형이 스택에 작성 되고 참조 유형이 힙에 작성 되며이 두 가지가 무엇인지 설명하지 않습니다. 나는 이것에 대한 명확한 설명을 읽지 못했습니다. 스택 이 무엇인지 이해합니다 . 그러나,

  • 그것들은 어디에 그리고 무엇입니까 (실제 컴퓨터의 메모리에서)?
  • OS 또는 언어 런타임에 의해 어느 정도까지 제어됩니까?
  • 그들의 범위는 무엇입니까?
  • 각각의 크기는 어떻게 결정됩니까?
  • 무엇이 더 빨라 집니까?

175
정말 좋은 설명은 여기에서 찾을 수 있습니다 . 스택과 힙의 차이점은 무엇입니까?
Songo

12
또한 (정말로) good : codeproject.com/Articles/76153/… (스택 / 힙 부분)
Ben


3
관련, 스택 충돌을 참조하십시오 . 스택 충돌 수정은 시스템 변수 및 동작과 같은 일부 측면에 영향을 미쳤습니다 rlimit_stack. 또한 Red Hat Issue 1463241
jww

3
@mattshane 스택 및 힙의 정의는 값 및 참조 유형에 의존하지 않습니다. 즉, 값 및 참조 유형이 존재하지 않더라도 스택 및 힙을 완전히 정의 할 수 있습니다. 또한 값과 참조 유형을 이해하면 스택은 구현 세부 사항 일뿐입니다. 에릭 리퍼 (Eric Lippert) : 스택은 구현 세부 사항, 1 부 .
Matthew

답변:


5961

스택은 실행 스레드를위한 스크래치 공간으로 남겨진 메모리입니다. 함수가 호출되면 블록은 로컬 변수 및 일부 부기 데이터를 위해 스택 맨 위에 예약됩니다. 해당 함수가 반환되면 블록이 사용되지 않고 다음에 함수를 호출 할 때 사용할 수 있습니다. 스택은 항상 LIFO (last in first out) 순서로 예약됩니다. 가장 최근에 예약 된 블록은 항상 해제 될 다음 블록입니다. 이렇게하면 스택을 추적하는 것이 정말 간단합니다. 스택에서 블록을 해제하는 것은 하나의 포인터를 조정하는 것입니다.

힙은 동적 할당을위한 메모리 세트입니다. 스택과 달리, 힙에서 블록을 할당하고 할당을 해제하는 데 적용되는 패턴은 없습니다. 언제든지 블록을 할당하고 언제든지 해제 할 수 있습니다. 이것은 주어진 시간에 힙의 어느 부분이 할당되거나 해제되는지 추적하는 것이 훨씬 더 복잡합니다. 다양한 사용 패턴에 대한 힙 성능을 조정하는 데 사용할 수있는 많은 사용자 지정 힙 할당자가 있습니다.

각 스레드는 스택을 가져 오지만 일반적으로 응용 프로그램에 대해 하나의 힙만 있습니다 (다양한 유형의 할당에 대해 여러 힙을 갖는 경우는 드물지만).

질문에 직접 대답하려면 :

OS 또는 언어 런타임에 의해 어느 정도까지 제어됩니까?

스레드가 작성 될 때 OS는 각 시스템 레벨 스레드에 스택을 할당합니다. 일반적으로 OS는 언어 런타임에 의해 호출되어 애플리케이션의 힙을 할당합니다.

그들의 범위는 무엇입니까?

스택은 스레드에 연결되므로 스레드가 종료되면 스택이 재생됩니다. 힙은 일반적으로 런타임에 의해 응용 프로그램 시작시 할당되며 응용 프로그램 (기술적으로 처리)이 종료 될 때 재생됩니다.

각각의 크기는 어떻게 결정됩니까?

스택의 크기는 스레드가 생성 될 때 설정됩니다. 힙 크기는 응용 프로그램 시작시 설정되지만 공간이 필요할 때 커질 수 있습니다 (할당자는 운영 체제에서 더 많은 메모리를 요청 함).

무엇이 더 빨라 집니까?

액세스 패턴이 메모리에서 메모리를 할당 및 할당 해제하는 것이 쉽지 않기 때문에 (포인터 / 정수는 단순히 증분되거나 감소됨), 힙은 할당 또는 할당 해제와 관련하여 훨씬 더 복잡한 부기를 유지하기 때문에 스택이 더 빠릅니다. 또한 스택의 각 바이트는 매우 자주 재사용되는 경향이 있으므로 프로세서 캐시에 매핑되는 경향이있어 매우 빠릅니다. 힙에 대한 또 다른 성능 히트는 대부분 글로벌 리소스 인 힙이 일반적으로 멀티 스레딩 안전해야한다는 것입니다. 즉, 각 할당 및 할당 해제는 일반적으로 프로그램의 "모든"다른 힙 액세스와 동기화되어야합니다.

명확한 데모 :
이미지 출처 : vikashazrati.wordpress.com


74
좋은 대답-프로세스가 시작될 때 (OS가 있다고 가정) 스택이 OS에 의해 할당되는 동안 프로그램에 의해 인라인으로 유지된다고 덧붙여 야한다고 생각합니다. 이는 스택이 더 빠른 또 다른 이유이기도합니다. 푸시 및 팝 작업은 일반적으로 하나의 머신 명령어이며, 최신 머신은 한 번의 사이클로 최소 3 번을 수행 할 수 있지만 힙 할당 또는 해제에는 OS 코드 호출이 포함됩니다.
sqykly

275
나는 결국 다이어그램에 혼란스러워합니다. 나는 그 이미지를 볼 때까지 그것을 얻었다 고 생각했다.
Sina Madani

10
@Anarelle 프로세서는 운영 체제와 함께 또는 운영 체제없이 명령을 실행합니다. 내 마음에 가까운 예로는 SNES가 있습니다. SNES는 API 호출이 없었으며 오늘날 우리가 알고있는 OS가 없지만 스택이 있습니다. 스택에 할당하는 것은 이러한 시스템에서 덧셈과 뺄셈이며 변수를 생성 한 함수에서 반환하여 변수가 튀어 나오면 파괴 된 변수에 적합하지만 결과는 그렇게 될 수는 없습니다. 버려진. 이를 위해 우리는 힙을 필요로하며, 이는 호출과 리턴에 묶이지 않습니다. 대부분의 OS는 API 힙을 가지고 있으며, 스스로 할 이유가 없습니다
sqykly

2
"스택은 스크래치 공간으로 남겨진 메모리입니다". 멋있는. 그러나 Java 메모리 구조와 관련하여 실제로 "제쳐두고"는 어디에 있습니까? 힙 메모리 / 힙이 아닌 메모리 / 기타 ( betsol.com/2017/06/…에 따른 Java 메모리 구조 )
Jatin Shashoo

4
바이트 코드 인터프리터 인 @JatinShashoo Java 런타임은 한 단계 더 높은 수준의 가상화를 추가하므로 Java 애플리케이션 관점 만 참조하십시오. 운영 체제 관점에서 볼 때 Java 런타임 프로세스는 일부 공간을 처리 된 바이트 코드에 대한 "힙이 아닌"메모리로 할당하는 힙일뿐입니다. 해당 OS 수준 힙의 나머지는 개체의 데이터가 저장되는 응용 프로그램 수준 힙으로 사용됩니다.
kbec

2349

스택:

  • 힙처럼 컴퓨터 RAM에 저장됩니다.
  • 스택에서 생성 된 변수는 범위를 벗어나 자동으로 할당이 해제됩니다.
  • 힙의 변수에 비해 할당 속도가 훨씬 빠릅니다.
  • 실제 스택 데이터 구조로 구현되었습니다.
  • 매개 변수 전달에 사용되는 로컬 데이터, 리턴 주소를 저장합니다.
  • 너무 많은 스택을 사용하면 스택 오버플로가 발생할 수 있습니다 (주로 무한 또는 너무 깊은 재귀, 매우 큰 할당).
  • 스택에서 생성 된 데이터는 포인터없이 사용될 수 있습니다.
  • 컴파일 시간 전에 할당해야하는 데이터의 양을 정확히 알고 너무 크지 않은 경우 스택을 사용합니다.
  • 일반적으로 프로그램이 시작될 때 이미 결정된 최대 크기를 갖습니다.

더미:

  • 스택처럼 컴퓨터 RAM에 저장됩니다.
  • C ++에서 힙의 변수는 수동으로 삭제해야하며 범위를 벗어나지 않아야합니다. 데이터는 함께 해제 delete, delete[]또는 free.
  • 스택의 변수에 비해 할당 속도가 느립니다.
  • 요청시 프로그램에서 사용할 데이터 블록을 할당하는 데 사용됩니다.
  • 할당 및 할당 해제가 많은 경우 조각화가 발생할 수 있습니다.
  • C ++ 또는 C에서 힙에서 작성된 데이터는 포인터로 가리키고 new또는 malloc각각으로 할당 됩니다.
  • 너무 큰 버퍼를 할당하도록 요청하면 할당이 실패 할 수 있습니다.
  • 런타임시 필요한 데이터 양을 정확히 모르거나 많은 데이터를 할당해야하는 경우 힙을 사용합니다.
  • 메모리 누수를 담당합니다.

예:

int foo()
{
  char *pBuffer; //<--nothing allocated yet (excluding the pointer itself, which is allocated here on the stack).
  bool b = true; // Allocated on the stack.
  if(b)
  {
    //Create 500 bytes on the stack
    char buffer[500];

    //Create 500 bytes on the heap
    pBuffer = new char[500];

   }//<-- buffer is deallocated here, pBuffer is not
}//<--- oops there's a memory leak, I should have called delete[] pBuffer;

31
포인터 pBuffer와 b의 값은 스택에 있으며 대부분 함수의 입구에 할당됩니다. 컴파일러에 따라 함수 입구에 버퍼도 할당 될 수 있습니다.
Andy

36
언어 표준 ( open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf 에서 사용 가능)에 C정의 된대로 C99언어 에 "스택"이 필요 하다는 것은 일반적인 오해입니다 . 실제로 '스택'이라는 단어는 표준에도 나타나지 않습니다. 이것은 wrt / to 의 스택 사용법이 일반적으로 사실이지만, 언어에 필요한 것은 아닙니다. 자세한 내용은 knosof.co.uk/cbook/cbook.html 을 참조하십시오 . 특히 en.wikipedia.org/wiki/Burroughs_large_systemsCC
johne

55
@Brian buffer []와 pBuffer 포인터가 스택에 생성되고 왜 pBuffer의 데이터가 힙에 생성되는지 설명해야합니다 . 나는 프로그램이 메모리를 스택 대 힙에 할당하도록 구체적으로 지시한다고 생각할 수 있기 때문에 일부 ppl은 귀하의 답변에 혼란 스러울 수 있다고 생각하지만 그렇지 않습니다. Buffer는 값 유형이고 pBuffer는 참조 유형이기 때문입니까?
Howiecamp

9
@Remover : 포인터가 주소를 가지고 있지 않으며 힙이나 스택의 무언가를 똑같이 가리킬 수 있습니다. malloc과 유사한 new, malloc 및 기타 함수는 힙에 할당하고 할당 된 메모리의 주소를 리턴합니다. 왜 힙에 할당 하시겠습니까? 따라서 메모리가 범위를 벗어나지 않고 원하는 때까지 해제되지 않습니다.
Brian R. Bondy

35
"메모리 누수에 대한 책임"-힙은 메모리 누수에 대한 책임이 없습니다! 쓰레기를 포기하지 않는 게으른 / 잊어 버린 / 전 자바 코더 / 코더는 있습니다!
Laz

1370

가장 중요한 점은 힙과 스택이 메모리를 할당 할 수있는 방법에 대한 일반적인 용어라는 것입니다. 그것들은 여러 가지 방법으로 구현 될 수 있으며, 용어는 기본 개념에 적용됩니다.

  • 항목 스택에서 항목은 순서대로 순서대로 배치되며 맨 위 항목 만 제거 할 수 있습니다 (전체를 뒤집지 않고).

    종이 더미처럼 쌓아 올리십시오

    스택의 단순성은 할당 된 메모리의 각 섹션에 대한 레코드가 포함 된 테이블을 유지 관리 할 필요가 없다는 것입니다. 필요한 유일한 상태 정보는 스택 끝을 가리키는 단일 포인터입니다. 할당하고 할당을 해제하려면 해당 단일 포인터를 늘리거나 줄이면됩니다. 참고 : 스택은 때때로 메모리 섹션의 상단에서 시작하여 위쪽으로 성장하지 않고 아래쪽으로 확장되도록 구현 될 수 있습니다.

  • 힙에는 항목을 배치하는 방법에 대한 특정 순서가 없습니다. 명확한 '상위'항목이 없기 때문에 어떤 순서로든 항목을 가져오고 제거 할 수 있습니다.

    감초 allsorts의 힙처럼 힙

    힙 할당에는 조각화를 줄이고 오버로드 된 메모리 세그먼트를 찾아 요청 된 크기에 맞도록 충분히 큰 메모리 등을 찾기 위해 할당 된 메모리와없는 메모리에 대한 전체 레코드를 유지 관리해야합니다. 여유 공간을 남겨두고 언제든지 메모리 할당을 해제 할 수 있습니다. 때때로 메모리 할당자는 할당 된 메모리를 이동하여 메모리 조각 모음 또는 가비지 수집-메모리가 더 이상 범위에 있지 않고 할당을 해제 할 때 런타임에 식별하는 유지 관리 작업을 수행합니다.

이러한 이미지는 스택과 힙에서 메모리를 할당하고 해제하는 두 가지 방법을 설명하는 데 상당히 효과적입니다. !!

  • OS 또는 언어 런타임에 의해 어느 정도까지 제어됩니까?

    언급 한 바와 같이 힙 및 스택은 일반적인 용어이며 여러 가지 방법으로 구현할 수 있습니다. 컴퓨터 프로그램에는 일반적으로 호출 스택이라고하는 스택이 있습니다.이 스택에는 호출 된 기능에 대한 포인터 및 로컬 변수와 같은 현재 기능과 관련된 정보를 저장합니다. 함수가 다른 함수를 호출 한 다음 반환하기 때문에 스택이 커지거나 줄어들어 함수의 정보를 호출 스택 아래로 유지합니다. 프로그램에는 실제로 런타임 제어 기능이 없습니다. 프로그래밍 언어, OS 및 시스템 아키텍처에 따라 결정됩니다.

    힙은 동적으로 무작위로 할당되는 모든 메모리에 사용되는 일반적인 용어입니다. 즉 순서가 잘못되었습니다. 메모리는 일반적으로 OS에서 할당하며 응용 프로그램은 API 함수를 호출하여이 할당을 수행합니다. 동적으로 할당 된 메모리를 관리하는 데 약간의 오버 헤드가 필요하며, 일반적으로 사용되는 프로그래밍 언어 또는 환경의 런타임 코드로 처리됩니다.

  • 그들의 범위는 무엇입니까?

    콜 스택은 프로그래밍의 관점에서 '범위'와 관련이없는 저수준 개념입니다. 일부 코드를 분해하면 스택의 일부에 대한 상대 포인터 스타일 참조가 표시되지만 고급 언어와 관련하여 언어에는 자체 범위 규칙이 적용됩니다. 그러나 스택의 중요한 측면 중 하나는 함수가 반환되면 해당 함수의 로컬 항목이 즉시 스택에서 해제된다는 것입니다. 프로그래밍 언어가 작동하는 방식에 따라 예상대로 작동합니다. 힙에서는 정의하기도 어렵습니다. 범위는 OS에 의해 노출되는 모든 것이지만 프로그래밍 언어는 아마도 응용 프로그램에 "범위"가 무엇인지에 대한 규칙을 추가합니다. 프로세서 아키텍처와 OS는 가상 주소 지정을 사용합니다. 프로세서는 물리적 주소로 변환하고 페이지 오류 등이 있습니다. 어떤 페이지가 어떤 응용 프로그램에 속하는지 추적합니다. 그러나 프로그래밍 언어가 메모리를 할당하고 해제하는 데 사용하는 방법을 사용하고 오류를 확인하기 때문에 (어떤 이유로 든 할당 / 해제가 실패하는 경우) 실제로 걱정할 필요가 없습니다.

  • 각각의 크기는 어떻게 결정됩니까?

    다시 말하지만 언어, 컴파일러, 운영 체제 및 아키텍처에 따라 다릅니다. 스택은 일반적으로 메모리가 연속적이어야하기 때문에 일반적으로 사전 할당됩니다. 언어 컴파일러 또는 OS가 크기를 결정합니다. 스택에 방대한 양의 데이터를 저장하지 않으므로 원치 않는 끝없는 재귀 ( "스택 오버플로") 또는 기타 비정상적인 프로그래밍 결정을 제외하고는 완전히 사용하지 않아야 할 정도로 커집니다.

    힙은 동적으로 할당 될 수있는 모든 것에 대한 일반적인 용어입니다. 당신이 그것을 보는 방법에 따라, 그것은 끊임없이 크기를 변화시키고 있습니다. 현대의 프로세서와 운영 체제에서 작동하는 정확한 방법은 매우 추상화되어 있으므로 일반적으로 (어떻게 할 수있는 언어로) 메모리를 사용하지 않아야 하는지를 제외하고는 어떻게 작동하는지에 대해 크게 걱정할 필요가 없습니다. 아직 할당하지 않았거나 해제 한 메모리가 없습니다.

  • 무엇이 더 빨라 집니까?

    사용 가능한 모든 메모리가 항상 연속적이기 때문에 스택이 더 빠릅니다. 사용 가능한 메모리의 모든 세그먼트에 대한 목록을 유지할 필요는 없으며 스택의 현재 상단에 대한 단일 포인터입니다. 컴파일러는 일반적 으로이 목적을 위해이 포인터를 특별하고 빠른 레지스터 에 저장합니다 . 또한 스택의 후속 작업은 일반적으로 메모리의 매우 가까운 영역에 집중되어 매우 낮은 수준에서 프로세서 온다이 캐시에 의한 최적화에 적합합니다.


20
David 저는 이것이 좋은 이미지이거나 "푸시 다운 스택"이 개념을 설명하기에 좋은 용어라는 데 동의하지 않습니다. 스택에 무언가를 추가하면 스택의 다른 내용은 아래로 밀리지 않고 원래 위치에 남아 있습니다.
thomasrutter

8
이 답변에는 큰 실수가 포함됩니다. 정적 변수는 스택에 할당되지 않습니다. 설명을 보려면 내 답변 [link] stackoverflow.com/a/13326916/1763801 을 참조하십시오 . "자동"변수와 "정적"변수를 동일시하고 있지만 전혀 같지 않습니다
davec davec

13
특히 "정적으로 할당 된 로컬 변수"가 스택에 할당되어 있다고 말합니다. 실제로 이들은 데이터 세그먼트에 할당됩니다. 자동으로 할당 된 변수 (모든 로컬 변수를 포함하지는 않지만 대부분 로컬 변수와 참조가 아닌 값으로 전달되는 함수 매개 변수와 같은 것) 만 스택에 할당됩니다.
davec 1

9
방금 C에서 정적 할당동적 이지 않은 것에 대한 용어가 아니라 별도의 것 입니다. 답변을 편집했습니다. 감사합니다.
thomasrutter

5
C, Java, Pascal, Python 등이 아니라 정적 대 자동 대 동적 할당이라는 개념이 있습니다. "정적 할당"이라고 말하는 것은 거의 모든 곳에서 같은 것을 의미합니다. 어떤 언어에서는 정적 할당이 "동적 아님"을 의미하지 않습니다. 설명하는 내용 (즉, 스택의 내용)에 대해 "자동"할당이라는 용어를 원합니다.
davec

727

(나는이 답변을 다소 속이는 다른 질문 에서이 답변을 옮겼습니다.)

귀하의 질문에 대한 답변은 구현에 따라 다르며 컴파일러와 프로세서 아키텍처에 따라 다를 수 있습니다. 그러나 여기 간단한 설명이 있습니다.

  • 스택과 힙은 모두 기본 운영 체제에서 할당 된 메모리 영역입니다 (요청시 실제 메모리에 매핑되는 가상 메모리).
  • 다중 스레드 환경에서 각 스레드는 완전히 독립적 인 스택을 갖지만 힙을 공유합니다. 동시 액세스는 힙에서 제어되어야하며 스택에서는 불가능합니다.

  • 힙에는 사용 된 블록과 사용 가능한 블록의 링크 된 목록이 포함됩니다. 사용 가능한 블록 중 하나에서 적합한 블록을 작성 하여 힙에 대한 새 할당 ( new또는으로 malloc)이 충족됩니다. 이를 위해서는 힙의 블록 목록을 업데이트해야합니다. 힙의 블록에 대한 이 메타 정보 는 종종 모든 블록 바로 앞의 작은 영역에 힙에 저장됩니다.
  • 힙이 커짐에 따라 새로운 블록이 종종 낮은 주소에서 높은 주소로 할당됩니다. 따라서 은 메모리가 할당됨에 따라 크기가 커지는 메모리 블록 의 힙으로 생각할 수 있습니다 . 힙이 할당에 비해 너무 작은 경우 기본 운영 체제에서 더 많은 메모리를 확보하여 크기를 늘릴 수 있습니다.
  • 많은 작은 블록을 할당하고 할당 해제하면 사용 된 블록 사이에 많은 작은 여유 블록이 산재 된 상태로 힙이 남을 수 있습니다. 자유 블록의 결합 된 크기가 충분히 클 수 있더라도 자유 블록 중 어느 것도 할당 요청을 만족시키기에 충분히 큰 것이 없기 때문에 큰 블록을 할당하는 요청은 실패 할 수있다. 이것을 힙 조각화 라고 합니다.
  • 빈 블록에 인접한 사용 된 블록이 할당 해제 될 때, 새로운 빈 블록은 인접한 빈 블록과 병합되어 더 큰 빈 블록을 생성하여 힙의 단편화를 효과적으로 감소시킬 수있다.

힙

스택

  • 스택은 종종 스택 포인터 라는 CPU의 특수 레지스터와 밀접하게 작동합니다 . 처음에 스택 포인터는 스택의 맨 위 (스택에서 가장 높은 주소)를 가리 킵니다.
  • CPU는 특별 지시가 밀어 스택에 값을하고 터지는 다시 스택에서 그들을. 각 푸시 는 스택 포인터의 현재 위치에 값을 저장하고 스택 포인터를 줄입니다. 취득이 값은 스택 포인터가 가리키는 다음 (사실에 의해 혼동하지 않는 스택 포인터를 증가 추가 스택에 값하는 감소 스택 포인터 및 제거이 증가 스택에 성장 기억을. 바닥). 저장 및 검색된 값은 CPU 레지스터의 값입니다.
  • 함수가 호출되면 CPU는 현재 명령어 포인터 (예 : 스택에서 실행되는 코드 주소) 를 푸시하는 특수 명령어를 사용합니다 . 그런 다음 CPU는 명령 포인터를 호출 된 함수의 주소로 설정하여 함수로 이동합니다. 나중에 함수가 반환되면 이전 명령 포인터가 스택에서 팝되고 함수를 호출 한 직후 코드에서 실행이 다시 시작됩니다.
  • 함수가 입력되면 스택 포인터는 로컬 (자동) 변수를 위해 스택에 더 많은 공간을 할당하기 위해 줄어 듭니다. 함수에 하나의 로컬 32 비트 변수가 있으면 스택에 4 바이트가 따로 설정됩니다. 함수가 반환되면 스택 포인터가 다시 이동하여 할당 된 영역을 해제합니다.
  • 함수에 매개 변수가있는 경우 함수 호출 전에 스택에 푸시됩니다. 그런 다음 함수의 코드는 현재 스택 포인터에서 스택을 탐색하여 이러한 값을 찾을 수 있습니다.
  • 중첩 함수 호출은 매력처럼 작동합니다. 각각의 새 호출은 함수 매개 변수, 리턴 주소 및 로컬 변수에 대한 공간을 할당하며 이러한 활성화 레코드 는 중첩 된 호출에 대해 스택 될 수 있으며 함수가 리턴 될 때 올바른 방식으로 해제됩니다.
  • 스택은 제한된 메모리 블록이므로 너무 많은 중첩 함수를 호출하거나 로컬 변수에 너무 많은 공간을 할당 하여 스택 오버플 로를 일으킬 수 있습니다 . 스택에 사용되는 메모리 영역은 스택의 맨 아래 (가장 낮은 주소) 아래에 쓰면 CPU에서 트랩이나 예외가 발생하도록 설정되는 경우가 많습니다. 이 예외 조건은 런타임에 의해 포착되어 일종의 스택 오버플로 예외로 변환 될 수 있습니다.

스택

스택 대신 힙에 함수를 할당 할 수 있습니까?

아니요, 함수 (예 : 로컬 또는 자동 변수)의 활성화 레코드는 이러한 변수를 저장하고 중첩 된 함수 호출을 추적하는 데 사용되는 스택에 할당됩니다.

힙 관리 방법은 실제로 런타임 환경에 달려 있습니다. C는 사용 malloc하고 C ++는을 사용 new하지만 다른 많은 언어에는 가비지 수집이 있습니다.

그러나 스택은 프로세서 아키텍처와 밀접한 관련이있는보다 낮은 수준의 기능입니다. 충분한 공간이 없을 때 힙을 늘리는 것은 힙을 처리하는 라이브러리 호출에서 구현할 수 있으므로 너무 어렵지 않습니다. 그러나 스택 오버플로는 너무 늦을 때만 발견되므로 스택을 늘리는 것은 종종 불가능합니다. 실행 스레드를 종료하는 것이 가능한 유일한 옵션입니다.


35
@Martin-보다 추상적으로 받아 들여지는 답변보다 매우 좋은 답변 / 설명. vis 함수 호출에 사용되는 스택 포인터 / 레지스터를 보여주는 샘플 어셈블리 프로그램이 더 설명 적입니다.
Bikal Lem

3
모든 참조 유형은 값 유형 (int, string 등)으로 구성됩니다. 말했듯이, 값 유형은 참조 유형의 일부일 때 작동하는 방식보다 스택에 저장됩니다.
Nps

15
이 답변은 내 의견으로는 가장 좋았다. 왜냐하면 그것이 리턴 문이 실제로 무엇인지, 그리고 그것이 내가 항상 겪는이 "반환 주소"와 어떻게 관련되는지 이해하는 데 도움이 되었기 때문이다. 왜 함수가 스택에 푸시되는지. 좋은 답변입니다!
Alex

3
이것은 내 의견으로는 최고입니다. 즉, 힙 / 스택이 구현에 따라 매우 다르다는 것을 언급합니다 . 다른 답변 은 언어와 환경 / OS에 대해 많은 것을 가정합니다 . +1
Qix-MONICA가 MISTREATED

2
"함수의 코드는 현재 스택 포인터에서 스택을 탐색하여 이러한 값을 찾을 수 있습니다." ? 이것에 대해 자세히 설명해 주시겠습니까?
Koray Tugay

404

다음 C # 코드에서

public void Method1()
{
    int i = 4;
    int y = 2;
    class1 cls1 = new class1();
}

메모리 관리 방법은 다음과 같습니다.

스택의 변수 그림

Local Variables함수 호출이 스택에있는 한 지속되어야합니다. 힙은 수명이 실제로 모르지만 변수가 오래 지속되는 변수에 사용됩니다. 대부분의 언어에서 컴파일 타임에 변수를 스택에 저장하려면 변수가 얼마나 큰지를 아는 것이 중요합니다.

객체를 업데이트 할 때 크기가 다른 객체는 생성 시간에 얼마나 오래 지속 될지 알 수 없으므로 힙에 저장됩니다. 많은 언어에서 힙은 더 이상 참조가없는 객체 (예 : cls1 객체)를 찾기 위해 가비지 수집됩니다.

Java에서 대부분의 오브젝트는 힙으로 직접 이동합니다. C / C ++와 같은 언어에서는 포인터를 다루지 않을 때 구조체와 클래스가 스택에 남아있을 수 있습니다.

자세한 내용은 여기를 참조하십시오.

스택과 힙 메모리 할당의 차이점«timmurphy.org

그리고 여기:

스택 및 힙에 객체 생성

이 글은 위의 그림의 소스입니다. 6 가지 중요한 .NET 개념 : 스택, 힙, 값 유형, 참조 유형, 복싱 및 언 박싱-CodeProject

그러나 일부 부정확 한 부분이있을 수 있습니다.


15
이것은 올바르지 않습니다. i와 cls는 "정적"변수가 아닙니다. 이를 "로컬"또는 "자동"변수라고합니다. 매우 중요한 차이점입니다. [링크]를 참조하십시오 stackoverflow.com/a/13326916/1763801 해명
davec

9
나는 그들이 정적 변수 라고 말하지 않았다 . 나는 int와 cls1이 정적 아이템 이라고 말했다 . 그들의 메모리는 정적으로 할당되므로 스택으로 이동합니다. 이것은 동적 메모리 할당이 필요한 객체와 달리 힙에서 진행됩니다.
Snowcrash

12
나는 "정적 아이템들 ... 스택에 가라"고 인용한다. 이것은 단지 잘못되었습니다. 정적 항목은 데이터 세그먼트로 이동하고 자동 항목은 스택으로 이동합니다.
davec

14
또한 해당 코드 프로젝트 기사를 작성한 사람은 자신이 무엇을 이야기하는지 모릅니다. 예를 들어, 그는 "프리미티브 메모리는 정적 타입 메모리를 필요로한다"고 말하면서 완전히 사실이 아니다. 힙에 기본 요소를 동적으로 할당하는 것을 막을 수있는 것은 없습니다. "int array [] = new int [num]"및 .NET에서 동적으로 할당 된 기본 요소 인 voila와 같은 것을 작성하십시오. 그것은 몇 가지 부정확 한 것 중 하나 일뿐입니다.
davec

8
스택과 힙에 무엇이 들어가는 지에 대해 심각한 기술적 실수를했기 때문에 게시물을 편집했습니다.
Tom Leys

209

스택 함수를 호출하면 해당 함수에 대한 인수와 다른 오버 헤드가 스택에 추가됩니다. 일부 정보 (예 : 돌아가는 곳)도 저장됩니다. 함수 내에서 변수를 선언하면 해당 변수도 스택에 할당됩니다.

스택을 할당 해제하는 것은 할당 순서가 항상 역순이기 때문에 매우 간단합니다. 함수를 입력하면 스택 항목이 추가되고 해당 데이터를 종료하면 해당 데이터가 제거됩니다. 이것은 다른 많은 함수를 호출하는 많은 함수를 호출하거나 재귀 적 솔루션을 만들지 않는 한 스택의 작은 영역 내에 머무르는 경향이 있음을 의미합니다.

힙은 작성하는 데이터를 즉석에 배치하는 일반적인 이름입니다. 프로그램이 얼마나 많은 우주선을 만들지 모르는 경우 새로운 (또는 malloc 또는 이와 동등한) 연산자를 사용하여 각 우주선을 만들 수 있습니다. 이 할당은 잠시 동안 계속 진행될 것이므로 생성 한 순서와 다른 순서로 무료로 제공 할 수 있습니다.

따라서 힙은 훨씬 더 복잡합니다. 사용되지 않는 메모리 영역이 청크와 인터리브되어 메모리가 조각화되기 때문입니다. 필요한 크기의 여유 메모리를 찾는 것은 어려운 문제입니다. 이것이 힙을 피해야하는 이유입니다 (아직 자주 사용되지만).

구현 스택과 힙 모두의 구현은 일반적으로 런타임 / OS에 달려 있습니다. 종종 성능이 중요한 게임 및 기타 응용 프로그램은 자체 메모리 솔루션을 만들어 힙에서 많은 양의 메모리를 확보 한 다음 OS에 의존하여 메모리에 의존하지 않도록 내부적으로 제거합니다.

이것은 메모리 사용량이 표준과 매우 다른 경우에만 실용적입니다. 즉, 한 번의 큰 작업으로 레벨을로드하고 또 다른 큰 작업에서 전체 로트를 척킹 할 수있는 게임의 경우입니다.

메모리의 실제 위치 가상 메모리 라는 기술 로 인해 프로그램이 실제 데이터가 다른 곳 (하드 디스크에서도)에있는 특정 주소에 액세스 할 수 있다고 생각하게하므로 생각보다 덜 관련이 있습니다 . 콜 트리가 깊어 질수록 스택에 대한 주소가 증가합니다. 힙의 주소는 예측할 수 없으며 (즉, 암시 적 특정) 솔직히 중요하지 않습니다.


16
힙 사용을 피하는 것이 좋습니다. 최신 시스템에는 우수한 힙 관리자가 있으며 최신 동적 언어는 힙을 광범위하게 사용합니다 (프로그래머가 실제로 걱정하지 않고). 나는 힙을 사용한다고 말하지만 수동 할당 자와 함께 사용하는 것을 잊지 마십시오!
Greg Hewgill

2
스택 또는 힙을 사용할 수 있으면 스택을 사용하십시오. 스택을 사용할 수 없다면 실제로 선택의 여지가 없습니다. 나는 많이 사용하고 물론 std :: vector 또는 이와 유사한 히트를 사용하여 힙에 도달합니다. 초보자에게는 스택이 너무 간단하기 때문에 힙을 피하십시오!
Tom Leys

언어가 가비지 수집을 구현하지 않는 경우 스마트 포인터 (동적으로 할당 된 메모리 청크에 대한 참조 계산을 수행하는 포인터를 감싸는 할당 된 객체)는 가비지 수집과 밀접한 관련이 있으며 안전한 곳에서 힙을 관리하는 적절한 방법입니다 누출이없는 방식. 그것들은 다양한 프레임 워크로 구현되지만 자신의 프로그램에도 구현하기가 어렵지 않습니다.
BenPen

1
"이것은 힙을 피해야하는 이유입니다 (여전히 자주 사용되지만)." 메모리가 많은 고급 언어로 다르게 관리되기 때문에 이것이 실제로 무엇을 의미하는지 잘 모르겠습니다. 이 질문은 언어에 구애받지 않기 때문에이 특정 의견 / 줄이 잘못 배치되어 적용 할 수 없다고 말하고 싶습니다.
LintfordPickle

2
좋은 지적 @ JonnoHampson-당신이 유효한 지적을하는 동안, 당신이 GC와 함께 "고수준 언어"로 작업한다면 아마도 메모리 할당 메커니즘에 전혀 신경 쓰지 않을 것입니다. 스택과 힙이 무엇인지 신경 쓰십시오.
Tom Leys

194

명확하게하기 위해이 답변 에는 잘못된 정보가 있습니다 ( 토마스 는 의견에 따라 답변을 수정했습니다 : cool :)). 다른 답변은 정적 할당의 의미를 설명하지 않습니다. 따라서 세 가지 주요 할당 형태와 이들이 일반적으로 아래의 힙, 스택 및 데이터 세그먼트와 어떤 관련이 있는지 설명하겠습니다. 또한 사람들이 이해하도록 돕기 위해 C / C ++와 Python으로 예제를 보여줄 것입니다.

"정적"(일명 정적으로 할당 된) 변수는 스택에 할당되지 않습니다. "정적"소리가 "스택"처럼 들리기 때문에 많은 사람들이 그렇게 생각하지 마십시오. 실제로 스택이나 힙에 없습니다. 데이터 세그먼트 (data segment) 의 일부입니다 .

그러나 일반적으로 "스택"및 "힙"보다는 " 범위 "및 " 수명 " 을 고려 하는 것이 좋습니다.

범위는 코드에서 변수에 액세스 할 수있는 부분을 나타냅니다. 일반적으로 범위가 훨씬 복잡 해지더라도 로컬 범위 (현재 기능으로 만 액세스 할 수 있음)와 전역 범위 (어디서나 액세스 할 수 있음)를 생각합니다.

수명은 프로그램이 실행되는 동안 변수가 할당 및 할당 해제되는시기를 나타냅니다. 일반적으로 정적 할당 (변수는 프로그램의 전체 기간 동안 지속되므로 여러 함수 호출에서 동일한 정보를 저장하는 데 유용함) 대 자동 할당 (변수는 함수를 한 번 호출하는 동안에 만 지속되므로 유용합니다. 함수 중에 만 사용되며 일단 완료되면 버릴 수있는 정보 저장) 동적 할당 (정적 또는 자동과 같은 컴파일 시간 대신 런타임에 기간이 정의 된 변수 ) 대 동적 할당 .

대부분의 컴파일러와 인터프리터는 스택, 힙 등을 사용하여 이와 유사한 방식으로이 동작을 구현하지만 동작이 올바른 경우 컴파일러가 원하는 경우 이러한 규칙을 위반할 수 있습니다. 예를 들어, 최적화로 인해 로컬 변수는 대부분 스택에 존재하더라도 레지스터에만 존재하거나 완전히 제거 될 수 있습니다. 몇 가지 의견에서 지적했듯이 스택이나 힙을 사용하지 않고 다른 스토리지 메커니즘을 사용하는 컴파일러를 자유롭게 구현할 수 있습니다 (드물게 스택과 힙이 좋기 때문에 거의 수행되지 않음).

이 모든 것을 설명하기 위해 간단한 주석이 달린 C 코드를 제공 할 것입니다. 배우는 가장 좋은 방법은 디버거에서 프로그램을 실행하고 동작을 보는 것입니다. 파이썬을 읽는 것을 선호한다면 대답의 끝으로 건너 뛰십시오. :)

// Statically allocated in the data segment when the program/DLL is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed from anywhere in the code
int someGlobalVariable;

// Statically allocated in the data segment when the program is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed from anywhere in this particular code file
static int someStaticVariable;

// "someArgument" is allocated on the stack each time MyFunction is called
// "someArgument" is deallocated when MyFunction returns
// scope - can be accessed only within MyFunction()
void MyFunction(int someArgument) {

    // Statically allocated in the data segment when the program is first loaded
    // Deallocated when the program/DLL exits
    // scope - can be accessed only within MyFunction()
    static int someLocalStaticVariable;

    // Allocated on the stack each time MyFunction is called
    // Deallocated when MyFunction returns
    // scope - can be accessed only within MyFunction()
    int someLocalVariable;

    // A *pointer* is allocated on the stack each time MyFunction is called
    // This pointer is deallocated when MyFunction returns
    // scope - the pointer can be accessed only within MyFunction()
    int* someDynamicVariable;

    // This line causes space for an integer to be allocated in the heap
    // when this line is executed. Note this is not at the beginning of
    // the call to MyFunction(), like the automatic variables
    // scope - only code within MyFunction() can access this space
    // *through this particular variable*.
    // However, if you pass the address somewhere else, that code
    // can access it too
    someDynamicVariable = new int;


    // This line deallocates the space for the integer in the heap.
    // If we did not write it, the memory would be "leaked".
    // Note a fundamental difference between the stack and heap
    // the heap must be managed. The stack is managed for us.
    delete someDynamicVariable;

    // In other cases, instead of deallocating this heap space you
    // might store the address somewhere more permanent to use later.
    // Some languages even take care of deallocation for you... but
    // always it needs to be taken care of at runtime by some mechanism.

    // When the function returns, someArgument, someLocalVariable
    // and the pointer someDynamicVariable are deallocated.
    // The space pointed to by someDynamicVariable was already
    // deallocated prior to returning.
    return;
}

// Note that someGlobalVariable, someStaticVariable and
// someLocalStaticVariable continue to exist, and are not
// deallocated until the program exits.

수명과 범위를 구별하는 것이 중요한 이유에 대한 특별한 예는 변수가 로컬 범위를 가질 수 있지만 정적 수명을 가질 수 있다는 것입니다 (예 : 위의 코드 샘플에서 "someLocalStaticVariable"). 이러한 변수는 일반적이지만 비공식적 인 명명 습관을 매우 혼란스럽게 만듭니다. 예를 들어 " local " 이라고 말하면 일반적으로 " 로컬 범위가 자동 할당 된 변수 "를 의미하고 global을 말할 때는 일반적으로 " 전역 범위가 정적으로 할당 된 변수 "를 의미합니다. 불행히도 " 파일 범위가 정적으로 할당 된 변수 " 와 같은 것들에 관해서는 많은 사람들이 그냥 ... " huh ??? " 라고 말합니다 .

C / C ++의 일부 구문 선택은이 문제를 악화시킵니다. 예를 들어 많은 사람들이 아래 표시된 구문 때문에 전역 변수가 "정적"이 아니라고 생각합니다.

int var1; // Has global scope and static allocation
static int var2; // Has file scope and static allocation

int main() {return 0;}

위의 선언에 키워드 "static"을 넣으면 var2가 전역 범위를 갖지 못하게됩니다. 그럼에도 불구하고 전역 var1에는 정적 할당이 있습니다. 이것은 직관적이지 않습니다! 이러한 이유로, 범위를 설명 할 때 "정적"이라는 단어를 사용하지 말고 "file"또는 "file limited"범위와 같은 것을 말하려고합니다. 그러나 많은 사람들이 "정적"또는 "정적 범위"라는 문구를 사용하여 한 코드 파일에서만 액세스 할 수있는 변수를 설명합니다. 수명의 맥락에서 "정적"은 항상 프로그램 시작시 변수가 할당되고 프로그램이 종료 될 때 할당이 해제됨을 의미합니다.

어떤 사람들은 이러한 개념을 C / C ++에 특정한 것으로 생각합니다. 그들은 아닙니다. 예를 들어 아래의 Python 샘플은 세 가지 유형의 할당을 모두 보여줍니다 (여기서는 이해할 수없는 해석 언어에는 약간의 차이가있을 수 있음).

from datetime import datetime

class Animal:
    _FavoriteFood = 'Undefined' # _FavoriteFood is statically allocated

    def PetAnimal(self):
        curTime = datetime.time(datetime.now()) # curTime is automatically allocatedion
        print("Thank you for petting me. But it's " + str(curTime) + ", you should feed me. My favorite food is " + self._FavoriteFood)

class Cat(Animal):
    _FavoriteFood = 'tuna' # Note since we override, Cat class has its own statically allocated _FavoriteFood variable, different from Animal's

class Dog(Animal):
    _FavoriteFood = 'steak' # Likewise, the Dog class gets its own static variable. Important to note - this one static variable is shared among all instances of Dog, hence it is not dynamic!


if __name__ == "__main__":
    whiskers = Cat() # Dynamically allocated
    fido = Dog() # Dynamically allocated
    rinTinTin = Dog() # Dynamically allocated

    whiskers.PetAnimal()
    fido.PetAnimal()
    rinTinTin.PetAnimal()

    Dog._FavoriteFood = 'milkbones'
    whiskers.PetAnimal()
    fido.PetAnimal()
    rinTinTin.PetAnimal()

# Output is:
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is milkbones
# Thank you for petting me. But it's 13:05:02.256000, you should feed me. My favorite food is milkbones

함수 내에 선언 된 정적 변수를 로컬 접근성 만 가지고 있지만 일반적으로 "scope"라는 용어는 사용하지 않습니다. 또한 언어가 본질적으로 유연성이없는 하나의 스택 / 힙 측면에 주목할 필요가 있습니다. . 일부 언어 PostScript는 여러 개의 스택이 있지만 스택처럼 동작하는 "힙"이 있습니다.
supercat

@ supercat 그게 말이 되네요. 나는로 범위를 정의 "할 수있는 코드의 어떤 부분 에 액세스 우리가 동의 생각 때문에 (그리고 이것은 대부분의 표준 정의 느낌) : 변수를"
davec

변수 의 "범위"는 공간뿐만 아니라 시간에 의해 구속되는 것으로 간주합니다. 클래스 개체 범위의 변수는 개체가 존재하는 한 값을 유지해야합니다. 실행 컨텍스트 범위 내의 변수는 해당 컨텍스트에 실행이 유지되는 한 해당 값을 보유해야합니다. 정적 변수 선언은 범위가 현재 블록에 바인드 된 범위 의 식별자 를 작성하며 범위는 바인드 되지 않은 변수에 첨부됩니다 .
supercat

@supercat 이것이 제가 평생이라는 단어를 사용하는 이유입니다. 너무 많은 의미로 "scope"라는 단어를 오버로드 할 필요성을 줄입니다. 내가 알 수있는 한, 정식 소스조차도 정확한 정의에 대한 총 합의가없는 것 같습니다. 내 용어는 K & R에서 부분적으로 그리고 부분적으로 내가 공부하고 가르친 첫 번째 CS 부서의 일반적인 사용법에서 나온 것입니다. 항상 다른 정보가 제공되는 의견을 듣는 것이 좋습니다.
davec

1
농담해야 해요 함수 내에서 정적 변수를 실제로 정의 할 수 있습니까?
Zaeem Sattar

168

다른 사람들은 넓은 스트로크에 꽤 잘 대답했기 때문에 몇 가지 세부 사항을 설명하겠습니다.

  1. 스택과 힙은 단수 일 필요는 없습니다. 하나 이상의 스택이있는 일반적인 상황은 프로세스에 둘 이상의 스레드가있는 경우입니다. 이 경우 각 스레드에는 자체 스택이 있습니다. 또한 하나 이상의 힙을 가질 수 있습니다. 예를 들어 일부 DLL 구성으로 인해 서로 다른 힙에서 서로 다른 DLL이 할당 될 수 있으므로 일반적으로 다른 라이브러리에서 할당 한 메모리를 해제하는 것이 좋지 않습니다.

  2. C에서는 힙에 할당하는 alloc과 달리 스택에 할당하는 alloca를 사용하여 가변 길이 할당의 이점을 얻을 수 있습니다 . 이 메모리는 return 문에서 살아남지 않지만 스크래치 버퍼에 유용합니다.

  3. 많이 사용하지 않는 Windows에서 거대한 임시 버퍼를 만드는 것은 무료가 아닙니다. 이는 컴파일러가 스택이 존재하는지 확인하기 위해 함수가 입력 될 때마다 호출되는 스택 프로브 루프를 생성하기 때문입니다 (Windows는 스택의 끝에 단일 가드 페이지를 사용하여 스택을 확장해야하는시기를 감지하기 때문). 스택 끝에서 둘 이상의 메모리에 액세스하면 충돌이 발생합니다). 예:

void myfunction()
{
   char big[10000000];
   // Do something that only uses for first 1K of big 99% of the time.
}

"할당과 반대": "말록과는 반대"를 의미합니까?
피터 Mortensen

얼마나 휴대가 편리 alloca합니까?
피터 Mortensen

@PeterMortensen POSIX가 아니며 이식성이 보장되지 않습니다.
Don Neufeld

135

다른 사람들은 귀하의 질문에 직접 대답했지만 스택과 힙을 이해하려고 할 때 스레드 및 mmap()기반 할당자가 없는 전통적인 UNIX 프로세스의 메모리 레이아웃을 고려하는 것이 도움이된다고 생각합니다 . 메모리 관리 용어 웹 페이지는이 메모리 레이아웃의 다이어그램을 가지고있다.

스택과 힙은 일반적으로 프로세스 가상 주소 공간의 반대쪽 끝에 있습니다. 스택은 액세스 할 때 커널이 설정 한 크기 (으로 조정할 수 있음 setrlimit(RLIMIT_STACK, ...)) 까지 자동으로 커집니다 . 메모리 할당자가 brk()또는 sbrk()시스템 호출을 호출 할 때 힙이 증가하여 더 많은 물리적 메모리 페이지를 프로세스의 가상 주소 공간에 맵핑합니다.

일부 임베디드 시스템과 같은 가상 메모리가없는 시스템에서는 스택과 힙의 크기가 고정 된 것을 제외하고 동일한 기본 레이아웃이 적용되는 경우가 많습니다. 그러나 다른 임베디드 시스템 (예 : Microchip PIC 마이크로 컨트롤러 기반 시스템)에서 프로그램 스택은 데이터 이동 명령으로 처리 할 수없는 별도의 메모리 블록이며 프로그램 흐름 명령 (호출, 반품 등). Intel Itanium 프로세서와 같은 다른 아키텍처에는 여러 스택이 있습니다. 이런 의미에서 스택은 CPU 아키텍처의 요소입니다.


117

스택이란 무엇입니까?

스택은 일반적으로 깔끔하게 배열 된 객체의 더미입니다.

여기에 이미지 설명을 입력하십시오

컴퓨팅 아키텍처의 스택은 데이터가 후입 선출 방식으로 추가되거나 제거되는 메모리 영역입니다.
다중 스레드 응용 프로그램에서 각 스레드에는 자체 스택이 있습니다.

힙이란 무엇입니까?

힙은 우연히 쌓인 물건의 어수선한 모음입니다.

여기에 이미지 설명을 입력하십시오

컴퓨팅 아키텍처에서 힙은 운영 체제 또는 메모리 관리자 라이브러리에서 자동으로 관리하는 동적으로 할당 된 메모리 영역입니다.
힙의 메모리는 프로그램 실행 중에 정기적으로 할당, 할당 해제 및 크기 조정되므로 조각화라는 문제가 발생할 수 있습니다.
조각화는 메모리 개체에 추가 공간을 확보하기에 너무 작은 공간이 할당되어있을 때 발생합니다.
최종 결과는 추가 메모리 할당에 사용할 수없는 힙 공간의 백분율입니다.

양자

다중 스레드 응용 프로그램에서 각 스레드에는 자체 스택이 있습니다. 그러나 다른 모든 스레드는 힙을 공유합니다.
여러 스레드가 다중 스레드 응용 프로그램에서 힙을 공유하기 때문에 스레드간에 동일한 메모리 조각에 액세스하고 조작하지 않도록 스레드간에 조정이 필요하다는 의미이기도합니다. 동시.

스택이나 힙 중 어느 것이 더 빠릅니까? 그리고 왜?

스택은 힙보다 훨씬 빠릅니다.
이는 메모리가 스택에 할당되는 방식 때문입니다.
스택에 메모리를 할당하는 것은 스택 포인터를 위로 올리는 것만 큼 간단합니다.

프로그래밍에 익숙하지 않은 사람들에게는 스택을 사용하기가 더 쉬울 것입니다.
스택이 작기 때문에 데이터에 필요한 메모리 양을 정확히 알거나 데이터 크기가 매우 작은 경우 스택을 사용하려고합니다.
데이터에 많은 메모리가 필요하다는 것을 알거나 동적 배열과 같이 필요한 메모리 양이 확실하지 않은 경우 힙을 사용하는 것이 좋습니다.

자바 메모리 모델

여기에 이미지 설명을 입력하십시오

스택은 로컬 변수 (메소드 매개 변수 포함)가 저장되는 메모리 영역입니다. 객체 변수에 관해서는 힙의 실제 객체에 대한 참조 (포인터) 일뿐입니다.
객체가 인스턴스화 될 때마다 해당 객체의 데이터 (상태)를 유지하도록 힙 메모리 청크가 따로 설정됩니다. 객체는 다른 객체를 포함 할 수 있으므로이 데이터 중 일부는 실제로 중첩 된 객체에 대한 참조를 보유 할 수 있습니다.


115

스택은 'pop'(스택에서 값 제거 및 반환) 및 'push'(스택으로 값 푸시)와 같은 여러 주요 어셈블리 언어 명령어를 통해 조작 할 수있는 메모리의 일부입니다. 서브 루틴 호출-주소를 스택으로 리턴하고 리턴 (서브 루틴에서 리턴)-스택에서 주소를 팝하여 점프합니다. 스택 포인터 레지스터 아래의 메모리 영역이며 필요에 따라 설정할 수 있습니다. 스택은 서브 루틴에 인수를 전달하고 서브 루틴을 호출하기 전에 레지스터의 값을 보존하는 데에도 사용됩니다.

힙은 일반적으로 malloc과 같은 syscall을 통해 운영 체제에 의해 애플리케이션에 제공되는 메모리의 일부입니다. 최신 OS에서이 메모리는 호출 프로세스 만 액세스 할 수있는 일련의 페이지입니다.

스택의 크기는 런타임에 결정되며 일반적으로 프로그램이 시작된 후에는 커지지 않습니다. C 프로그램에서 스택은 각 함수 내에 선언 된 모든 변수를 보유 할 수있을만큼 커야합니다. 힙은 필요에 따라 동적으로 커지지 만 OS는 궁극적으로 전화를 걸고 있습니다 (종종 malloc이 요청한 값보다 힙을 많이 늘려서 적어도 일부 malloc은 커널로 돌아갈 필요가 없습니다) 더 많은 메모리를 확보하십시오.

프로그램을 시작하기 전에 스택을 할당 했으므로 스택을 사용하기 전에 malloc을 수행 할 필요가 없으므로 약간의 이점이 있습니다. 실제로, 페이지가 어떻게 구현되고 저장되는지는 구현 세부 사항이기 때문에 가상 메모리 서브 시스템이있는 최신 운영 체제에서 무엇이 빠를 지 느리게 예측할 수 없습니다.


2
또한 인텔은 스택 액세스, 특히 함수에서 돌아 오는 위치를 예측하는 것과 같은 스택 액세스를 크게 최적화한다는 점을 언급 할 가치가 있습니다.
Tom Leys

113

다른 많은 사람들이이 문제에 대해 대부분 정답을 주었다고 생각합니다.

그러나 놓친 한 가지 세부 사항은 "힙"이 실제로 "무료 저장소"라고해야한다는 것입니다. 이러한 차이점은 원래 무료 저장소가 "이항 힙"이라는 데이터 구조로 구현 되었기 때문입니다. 따라서 malloc () / free ()의 초기 구현에서 할당하는 것은 힙에서 할당되었습니다. 그러나 오늘날 현대의 대부분의 무료 상점은 이항 힙이 아닌 매우 정교한 데이터 구조로 구현됩니다.


8
또 다른 핵심적인 답변은 (약간) "스택"의 사용이 C언어에 필요하다는 것을 암시합니다 . C99 6.2.4 automatic storage duration objects(변수) 를 구현하는 데있어 패러다임이긴하지만 이것은 일반적인 오해 입니다. 실제로 "stack"이라는 단어는 C99언어 표준 에도 나타나지 않습니다 . open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf
johne

[@Heath] 귀하의 답변에 작은 의견이 있습니다. 이 질문에 허용되는 답변을 살펴보십시오 . 무료 저장소아마도 과 동일 하지만 반드시 그런 것은 아니라고 말합니다 .
OmarOthman

91

스택으로 몇 가지 흥미로운 일을 할 수 있습니다. 예를 들어, 메모리에 힙이 아닌 스택을 사용하는 malloc의 한 형태 인 alloca 와 같은 기능을 사용합니다 (사용에 관한 충분한 경고를 넘을 수 있다고 가정).

즉, 스택 기반 메모리 오류는 내가 경험 한 최악의 것 중 일부입니다. 힙 메모리를 사용하고 할당 된 블록의 경계를 초과하면 세그먼트 오류가 발생할 가능성이 높습니다. (100 % 아님 : 블록이 이전에 할당 한 다른 블록과 우연히 연속적 일 수 있습니다.) 그러나 스택에서 생성 된 변수는 항상 서로 인접하므로 경계를 쓰면 다른 변수의 값이 변경 될 수 있습니다. 내 프로그램이 논리 법칙을 준수하지 않는다고 느낄 때마다 버퍼 오버플로 일 수 있음을 알게되었습니다.


얼마나 휴대가 편리 alloca합니까? 예를 들어, Windows에서 작동합니까? 유닉스 계열 운영 체제에만 해당됩니까?
Peter Mortensen

89

간단히, 스택은 지역 변수가 생성되는 곳입니다. 또한 서브 루틴을 호출 할 때마다 프로그램 카운터 (다음 기계 명령어에 대한 포인터) 및 중요한 레지스터, 때로는 매개 변수가 스택에 푸시됩니다. 그런 다음 서브 루틴 내부의 모든 로컬 변수가 스택으로 푸시되고 여기에서 사용됩니다. 서브 루틴이 끝나면 그 모든 것이 스택에서 튀어 나옵니다. PC와 레지스터 데이터는 팝업 된 위치로 돌아가서 다시 가져 오므로 프로그램이 순조롭게 진행될 수 있습니다.

힙은 메모리 동적 메모리 할당 영역입니다 (명시 적 "새"또는 ​​"할당"호출). 다양한 크기의 메모리 블록과 할당 상태를 추적 할 수있는 특수한 데이터 구조입니다.

"클래식"시스템에서는 스택 포인터가 메모리 하단에서 시작되고 힙 포인터가 상단에서 시작되어 서로를 향해 성장하도록 RAM이 배치되었습니다. 겹치면 RAM이 부족한 것입니다. 그러나 최신 멀티 스레드 운영 체제에서는 작동하지 않습니다. 모든 스레드는 자체 스택을 가져야하며 동적으로 생성 될 수 있습니다.


[@TED] 왜 "때때로 매개 변수가 스택에 푸시됩니다"라고 말했습니까? 내가 아는 것은 그들이 항상 있다는 것 입니다. 좀 더 자세히 설명해 주시겠습니까?
OmarOthman

1
@OmarOthman-서브 루틴이 호출 될 때 발생하는 일은 컴파일러 / 통역가에게 전적으로 달려 있기 때문입니다. 클래식 포트란 동작은 스택을 전혀 사용하지 않는 것입니다. 일부 언어는 이름 별 통과와 같은 이국적인 언어를 지원하며 이는 사실상 텍스트 대체입니다.
TED

83

WikiAnwser에서.

스택

함수 또는 메소드가 다른 함수를 호출하여 다른 함수 등을 호출하는 경우, 가장 마지막 함수가 값을 리턴 할 때까지 모든 함수의 실행이 일시 중단됩니다.

스택의 요소 (함수 호출)가 서로 종속되기 때문에이 일시 중단 된 함수 호출 체인은 스택입니다.

스택은 예외 처리 및 스레드 실행에서 고려해야합니다.

더미

힙은 단순히 변수를 저장하기 위해 프로그램에서 사용하는 메모리입니다. 힙 요소 (변수)는 서로 종속성이 없으며 언제든지 무작위로 액세스 할 수 있습니다.


"허용 된 답변이 더 낮은 수준이므로 더 좋아합니다." 좋은 일이 아니라 나쁜 일입니다.
궤도에서 가벼움 레이스

54

스택

  • 매우 빠른 액세스
  • 변수를 명시 적으로 할당 해제 할 필요가 없습니다.
  • 공간은 CPU에 의해 효율적으로 관리되며 메모리는 조각화되지 않습니다
  • 지역 변수 만
  • 스택 크기 제한 (OS에 따라 다름)
  • 변수 크기를 조정할 수 없습니다

더미

  • 변수는 전 세계적으로 접근 가능
  • 메모리 크기 제한 없음
  • (상대적으로) 느린 액세스
  • 공간의 효율적인 사용을 보장하지 않으며, 메모리 블록이 할당 된 후 시간이 지남에 따라 메모리가 조각화 될 수 있습니다.
  • 메모리를 관리해야합니다 (변수 할당 및 해제를 담당 함)
  • realloc ()을 사용하여 변수 크기를 조정할 수 있습니다

50

OK, 간단하고 짧은 단어, 그들은 의미 주문주문하지 ...!

쌓기 : 쌓인 물건에서 물건이 서로 닿아 처리 속도가 빠르고 효율적입니다!

따라서 특정 항목을 가리키는 색인이 항상 있으며 처리 속도가 빨라지고 항목 사이에도 관계가 있습니다! ...

: 주문 없음, 처리 속도가 느려지고 값이 특정 주문이나 색인없이 함께 엉망이됩니다. 임의가 있고 그들 사이에 관계가 없습니다 ... 그래서 실행 및 사용 시간이 다를 수 있습니다 ...

또한 아래 이미지를 만들어 어떻게 생겼는지 보여줍니다.

여기에 이미지 설명을 입력하십시오


49

한마디로

스택은 정적 메모리 할당에 사용되며 동적 메모리 할당에 사용되는 힙은 모두 컴퓨터의 RAM에 저장됩니다.


상세히

스택

스택은 "LIFO"(last in, first out) 데이터 구조이며 CPU에 의해 관리되고 최적화됩니다. 함수가 새로운 변수를 선언 할 때마다 스택에 "푸시"됩니다. 그런 다음 함수가 종료 될 때마다 해당 함수에 의해 스택에 푸시 된 모든 변수가 해제됩니다 (즉, 삭제됨). 스택 변수가 해제되면 해당 메모리 영역을 다른 스택 변수에 사용할 수있게됩니다.

스택을 사용하여 변수를 저장하면 메모리를 관리 할 수 ​​있다는 이점이 있습니다. 손으로 메모리를 할당하거나 더 이상 필요하지 않은 메모리를 해제 할 필요가 없습니다. 또한 CPU가 스택 메모리를 효율적으로 구성하기 때문에 스택 변수를 읽고 쓰는 것이 매우 빠릅니다.

자세한 내용은 여기를 참조하십시오 .


힙은 컴퓨터 메모리에서 자동으로 관리되지 않으며 CPU에 의해 엄격하게 관리되지 않는 영역입니다. 메모리의 자유 부동 영역이며 더 큽니다. 힙에 메모리를 할당하려면 내장 C 함수 인 malloc () 또는 calloc ()을 사용해야합니다. 힙에 메모리를 할당 한 후에는 더 이상 필요하지 않은 free ()를 사용하여 해당 메모리를 할당 해제해야합니다.

이 작업을 수행하지 않으면 프로그램에 메모리 누수가 발생합니다. 즉, 힙의 메모리는 여전히 따로 설정되어 있으며 다른 프로세스에서는 사용할 수 없습니다. 디버깅 섹션에서 볼 수 있듯이 메모리 누수를 감지하는 데 도움이되는 Valgrind 라는 도구 가 있습니다.

스택과 달리 힙에는 컴퓨터의 명백한 물리적 제한을 제외하고 가변 크기에 대한 크기 제한이 없습니다. 힙 메모리는 포인터를 사용하여 힙의 메모리에 액세스해야하기 때문에 읽고 쓰는 데 약간 느립니다. 우리는 포인터에 대해 곧 이야기 할 것입니다.

스택과 달리 힙에서 생성 된 변수는 프로그램의 어느 곳에서나 모든 기능으로 액세스 할 수 있습니다. 힙 변수는 본질적으로 범위가 전역 적입니다.

자세한 내용은 여기를 참조하십시오 .


스택에 할당 된 변수는 메모리에 직접 저장되며이 메모리에 대한 액세스는 매우 빠르며 프로그램이 컴파일 될 때 할당이 처리됩니다. 함수 또는 메소드가 다른 함수를 호출하여 다른 함수 등을 호출하는 경우 마지막 함수가 값을 리턴 할 때까지 모든 함수의 실행이 일시 중단됩니다. 스택은 항상 LIFO 순서로 예약되며 가장 최근에 예약 된 블록은 항상 해제 할 다음 블록입니다. 이것은 스택을 추적하는 것을 정말로 간단하게 만듭니다. 스택에서 블록을 해제하는 것은 하나의 포인터를 조정하는 것 이상입니다.

힙에 할당 된 변수에는 런타임시 할당 된 메모리가 있으며이 메모리에 액세스하는 것이 약간 느리지 만 힙 크기는 가상 메모리의 크기에 의해서만 제한됩니다. 힙 요소는 서로 종속성이 없으며 언제든지 임의로 액세스 할 수 있습니다. 언제든지 블록을 할당하고 언제든지 해제 할 수 있습니다. 따라서 힙의 어느 부분이 언제 할당되거나 해제되는지 추적하는 것이 훨씬 더 복잡해집니다.

여기에 이미지 설명을 입력하십시오

컴파일 시간 전에 할당해야하는 데이터의 양을 정확히 알고 있고 너무 크지 않은 경우 스택을 사용할 수 있습니다. 런타임시 필요한 데이터 양을 정확히 모르거나 많은 데이터를 할당해야하는 경우 힙을 사용할 수 있습니다.

다중 스레드 상황에서 각 스레드는 완전히 독립적 인 스택을 갖지만 힙을 공유합니다. 스택은 스레드마다 다르며 힙은 응용 프로그램별로 다릅니다. 스택은 예외 처리 및 스레드 실행에서 고려해야합니다.

각 스레드는 스택을 가져 오지만 일반적으로 응용 프로그램에 대해 하나의 힙만 있습니다 (다양한 유형의 할당에 대해 여러 힙을 갖는 경우는 드물지만).

여기에 이미지 설명을 입력하십시오

런타임에 응용 프로그램에 더 많은 힙이 필요한 경우 사용 가능한 메모리에서 메모리를 할당 할 수 있고 스택에 메모리가 필요한 경우 응용 프로그램에 사용 가능한 메모리 할당 된 메모리에서 메모리를 할당 할 수 있습니다.

더 자세한 것은 여기여기에 있습니다 .


이제 귀하의 질문에 답변하십시오 .

OS 또는 언어 런타임에 의해 어느 정도까지 제어됩니까?

스레드가 작성 될 때 OS는 각 시스템 레벨 스레드에 스택을 할당합니다. 일반적으로 OS는 언어 런타임에 의해 호출되어 애플리케이션의 힙을 할당합니다.

자세한 내용은 여기를 참조하십시오 .

그들의 범위는 무엇입니까?

이미 위에 주어졌습니다.

"컴파일 시간 이전에 할당해야하는 데이터의 양을 정확히 알고 있고 너무 크지 않은 경우 스택을 사용할 수 있습니다. 런타임에 필요한 데이터의 양을 정확히 모르는 경우 힙을 사용할 수 있습니다. 많은 데이터를 할당해야합니다. "

자세한 내용은 여기를 참조하십시오 .

각각의 크기는 어떻게 결정됩니까?

스택의 크기 는 스레드가 생성 될 때 OS에 의해 설정됩니다 . 힙 크기는 응용 프로그램 시작시 설정되지만 공간이 필요할 때 늘어날 수 있습니다 (할당자는 운영 체제에서 더 많은 메모리를 요청 함).

무엇이 더 빨라 집니까?

실제로 스택 할당은 스택 포인터를 이동하기 때문에 스택 할당이 훨씬 빠릅니다. 메모리 풀을 사용하면 힙 할당에서 비슷한 성능을 얻을 수 있지만 약간의 복잡성과 자체 두통이 발생합니다.

또한 스택 대 힙은 성능 고려 사항 일뿐만 아니라; 또한 예상되는 개체 수명에 대해 많은 정보를 제공합니다.

자세한 내용은 여기 에서 확인할 수 있습니다 .


36

1980 년대에 UNIX는 대기업이 자체적으로 운영하는 토끼처럼 전파되었습니다. 엑손은 수십 개의 브랜드 이름을 잃어버린 것처럼 역사가 사라졌습니다. 많은 구현 자들의 재량에 따라 메모리가 어떻게 배치 되었는가.

일반적인 C 프로그램은 brk () 값을 변경하여 증가시킬 수있는 기회를 제공하면서 메모리에 평평하게 배치되었습니다. 일반적으로 HEAP는이 brk 값 바로 아래에 있었고 brk를 늘리면 사용 가능한 힙의 양이 증가했습니다.

단일 스택은 일반적으로 HEAP 아래 영역으로, 다음 고정 메모리 블록의 맨 위까지 값이없는 메모리 영역이었습니다. 이 다음 블록은 종종 시대의 유명한 핵 중 하나에서 스택 데이터로 덮어 쓸 수있는 CODE였습니다.

일반적인 메모리 블록 중 하나는 BSS (0 값 블록)였으며 실수로 한 제조업체의 제품에서 제로화되지 않았습니다. 다른 하나는 문자열과 숫자를 포함하여 초기화 된 값을 포함하는 DATA입니다. 세 번째는 CRT (C 런타임), 기본, 함수 및 라이브러리를 포함하는 CODE입니다.

UNIX에서 가상 메모리의 출현으로 많은 제약이 변경되었습니다. 이러한 블록이 연속적이거나 크기가 고정되어 있거나 특정 방식으로 주문되어야하는 객관적인 이유는 없습니다. 물론 유닉스 이전에는 이러한 제약으로 고통받지 않은 Multics였습니다. 다음은 그 시대의 메모리 레이아웃 중 하나를 보여주는 개략도입니다.

전형적인 1980 년대 스타일의 UNIX C 프로그램 메모리 레이아웃



26

몇 센트 : 메모리를 그래픽으로 그리고 더 간단하게 그리는 것이 좋습니다.

이것은 발생하는 과정을보다 쉽게 ​​이해할 수 있도록 단순화 된 프로세스 메모리 구성에 대한 나의 비전입니다.


화살표-스택 및 힙 증가, 프로세스 스택 크기에 제한이있는 위치, OS에 정의, 스레드 작성 API의 매개 변수에 의한 스레드 스택 크기 제한이 일반적으로 표시됩니다. 일반적으로 힙은 프로세스 최대 가상 메모리 크기 (예 : 32 비트 2-4GB)로 제한합니다.

간단한 방법 : 프로세스 힙은 프로세스와 내부의 모든 스레드에 일반적이며 malloc () 과 같은 일반적인 경우에 메모리 할당에 사용합니다. .

스택은 일반적인 경우 함수 반환 포인터 및 변수에 저장하기위한 빠른 메모리이며, 함수 호출의 매개 변수, 로컬 함수 변수로 처리됩니다.


23

일부 답변은 nitpicking 갔으므로 진드기에 기여할 것입니다.

놀랍게도, 아무도 (즉, 실행중인 OS 레벨 스레드의 수와 관련이 없음) 호출 스택이 이국적인 언어 (PostScript) 또는 플랫폼 (Intel Itanium)뿐만 아니라 섬유 , 녹색 스레드 에서도 발견되어야한다고 언급 한 사람은 없습니다. 코 루틴의 일부 구현 .

섬유, 녹색 실 및 코 루틴은 여러면에서 유사하여 많은 혼란을 초래합니다. 섬유와 녹색 스레드의 차이점은 전자는 협동 멀티 태스킹을 사용하지만 후자는 협동 또는 선점 (또는 둘 다)을 특징으로 할 수 있다는 점입니다. 섬유와 코 루틴의 차이점은 여기를 참조 하십시오 .

어쨌든 섬유, 녹색 실 및 코 루틴의 목적은 동시에 여러 기능을 동시에 실행하지만 병렬로 실행 하지 않는 것 입니다 ( 이 SO 질문 참조) 단일 OS 레벨 스레드 내 ) 제어를 서로주고받는 것입니다. 조직 된 방식으로.

섬유, 녹색 실 또는 코 루틴을 사용할 때 일반적으로 기능마다 별도의 스택이 있습니다. (기술적으로 스택뿐만 아니라 전체 실행 컨텍스트는 기능마다 다릅니다. 가장 중요한 것은 CPU 레지스터입니다.) 모든 스레드에 대해 동시에 실행중인 기능이있는만큼 많은 스택이 있으며 스레드는 각 기능 실행 사이를 전환합니다 프로그램의 논리에 따라. 함수가 끝까지 실행되면 스택이 파괴됩니다. 따라서 스택의 수와 수명 은 동적이며 OS 수준 스레드의 수에 의해 결정되지 않습니다!

" 일반적으로 기능마다 별도의 스택이 있습니다 "라고 말했음을 참고하십시오 . 모두가있어 stackful스택리스 couroutines의 구현. 가장 주목할만한 stackful C ++ 구현은 Boost.Coroutine마이크로 소프트 PPLasync/await. 그러나 C ++ 17에 제안 된 C ++의 재개 가능한 함수 (일명 " asyncawait")는 스택리스 코 루틴을 사용할 가능성이 높습니다.

C ++ 표준 라이브러리에 대한 광섬유 제안이 곧 나옵니다. 또한 타사 라이브러리도 있습니다. 녹색 스레드는 Python 및 Ruby와 같은 언어에서 매우 인기가 있습니다.


19

주요 사항이 이미 다루어 져 있지만 공유 할 내용이 있습니다.

스택

  • 매우 빠른 액세스.
  • RAM에 저장됩니다.
  • 함수 호출은 전달 된 로컬 변수 및 함수 매개 변수와 함께 여기에로드됩니다.
  • 프로그램이 범위를 벗어나면 공간이 자동으로 해제됩니다.
  • 순차 메모리에 저장됩니다.

더미

  • 스택에 비해 속도가 느립니다.
  • RAM에 저장됩니다.
  • 동적으로 생성 된 변수가 여기에 저장되므로 나중에 사용 후 할당 된 메모리를 해제해야합니다.
  • 메모리 할당이 완료 될 때마다 저장되며 항상 포인터로 액세스합니다.

재미있는 메모 :

  • 함수 호출이 힙에 저장되어 있으면 2 개의 지저분한 지점이 생겼을 것입니다.
    1. 스택의 순차적 스토리지로 인해 실행이 더 빠릅니다. 힙에 저장하면 시간이 많이 걸리므로 전체 프로그램 실행 속도가 느려집니다.
    2. 함수가 힙에 저장된 경우 (포인터가 가리키는 지저분한 저장 공간) 호출자 주소로 돌아갈 수있는 방법이 없었습니다 (메모리의 순차적 저장으로 인해 스택이 제공함).

1
간결하고 깨끗합니다. nice :)
ingconti

13

와! 너무 많은 답변과 나는 그들 중 하나가 옳지 않다고 생각합니다 ...

1) 어디에 그리고 무엇입니까 (실제 컴퓨터의 메모리에서)?

스택은 프로그램 이미지에 할당 된 가장 높은 메모리 주소로 시작한 메모리에서 시작하여 감소합니다. 호출 된 함수 매개 변수 및 함수에 사용 된 모든 임시 변수를 위해 예약되어 있습니다.

공개 및 개인의 두 가지 힙이 있습니다.

개인 힙은 프로그램의 마지막 코드 바이트 다음에 16 바이트 경계 (64 비트 프로그램의 경우) 또는 8 바이트 경계 (32 비트 프로그램의 경우)에서 시작하여 그 값이 증가합니다. 기본 힙이라고도합니다.

개인 힙이 너무 커지면 스택 영역과 겹치며 스택이 너무 커지면 스택과 겹치게됩니다. 스택은 더 높은 주소에서 시작하여 더 낮은 주소로 내려 가기 때문에 적절한 해킹으로 스택을 너무 크게 만들어 개인 힙 영역을 오버런하고 코드 영역과 겹칠 수 있습니다. 그러면 코드에 연결할 수있는 코드 영역이 겹치게됩니다. 약간 까다 롭고 프로그램 충돌의 위험이 있지만 쉽고 효과적입니다.

공용 힙은 프로그램 이미지 공간 외부의 자체 메모리 공간에 있습니다. 메모리 리소스가 부족한 경우 하드 디스크에 저장되는 것이이 메모리입니다.

2) OS 또는 언어 런타임에 의해 어느 정도까지 제어됩니까?

스택은 프로그래머에 의해 제어되고, 개인 힙은 OS에 의해 관리되며, 공용 힙은 OS 서비스이기 때문에 다른 사람에 의해 제어되지 않습니다. 요청을하고 요청이 승인되거나 거부됩니다.

2b) 그들의 범위는 무엇입니까?

그것들은 모두 프로그램에 대해 전 세계적이지만 그들의 내용은 개인, 공공 또는 전 세계 일 수 있습니다.

2c) 각각의 크기는 어떻게 결정됩니까?

스택 및 개인 힙의 크기는 컴파일러 런타임 옵션에 따라 결정됩니다. 공용 힙은 런타임에 크기 매개 변수를 사용하여 초기화됩니다.

2d) 무엇이 더 빨라 집니까?

그들은 빠르도록 설계되지 않았으며 유용하도록 설계되었습니다. 프로그래머가이를 사용하는 방법에 따라 "빠른"또는 "느린"여부가 결정됩니다.

REF :

https://norasandler.com/2019/02/18/Write-a-Compiler-10.html

https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-getprocessheap

https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-heapcreate


8

많은 답변이 개념으로 맞지만, 하드웨어 (예 : 마이크로 프로세서)는 서브 루틴 호출 (조립 언어에서 호출)을 허용하기 위해 스택이 필요하다는 점에 유의해야합니다. (OOP 사람들은 메소드 라고 부릅니다 )

스택에서 반송 주소를 저장하고 호출 → 푸시 / ret → 팝은 하드웨어에서 직접 관리됩니다.

스택을 사용하여 매개 변수를 전달할 수 있습니다. 레지스터를 사용하는 것보다 속도가 느리더라도 (마이크로 프로세서 전문가가 말하거나 1980 년대의 좋은 BIOS 책을 ...)

  • 스택이 없으면 마이크로 프로세서가 작동 하지 않습니다 . (우리는 서브 루틴 / 함수가없는 어셈블리 언어에서도 프로그램을 상상할 수 없습니다)
  • 힙이 없으면 가능합니다. (힙은 OS 개념이고 malloc은 OS / Lib 호출이므로 어셈블리 언어 프로그램은 작동하지 않습니다.

다음과 같이 스택 사용량이 더 빠릅니다.

  • 하드웨어이며 푸시 / 팝도 매우 효율적입니다.
  • malloc은 커널 모드로 들어가고 일부 코드를 실행하는 잠금 / 세마포어 (또는 다른 동기화 프리미티브)를 사용하고 할당을 추적하는 데 필요한 일부 구조를 관리해야합니다.

OPP 란 무엇입니까? OOP ( object-oriented_programming ) 를 의미 합니까?
Peter Mortensen

malloc이것이 커널 호출 이라고 말하는 것 입니까?
Peter Mortensen

1) 예, 죄송합니다. OOP ... 2) malloc : 짧게 작성합니다. 죄송합니다 ... malloc은 사용자 공간에 있습니다 .. 그러나 다른 호출을 트리거 할 수 있습니다 .... 요점은 힙 사용이 매우 느릴 수 있다는 것입니다 ...
ingconti

" 컨셉으로 많은 답변이 맞지만, 서브 루틴 호출 (조립 언어에서 호출)을 허용하려면 하드웨어 (예 : 마이크로 프로세서)에 스택이 필요합니다. " CPU 스택 (최신 CPU에 하나가있는 경우)과 언어 런타임 스택 (스레드 당 하나)을 혼동하고 있습니다. 프로그래머가 스택에 대해 이야기 할 때, 이것은 런타임의 스레드 실행 스택입니다 (예 : NET 스레드 스택). CPU 스택에 대해서는 이야기하지 않습니다.

1

스택은 본질적으로 쉽게 액세스 할 수있는 메모리로, 항목을 잘 스택으로 관리합니다. 크기가 미리 알려진 항목스택에 놓을 수 있습니다 . 숫자, 문자열, 부울의 경우입니다.

힙은 정확한 크기와 구조를 미리 결정할 수있는 항목에 대한 기억이다 . 객체와 배열은 런타임에 변경되고 변경 될 수 있으므로 힙으로 이동해야합니다.

출처 : Academind


0

정말 좋은 토론에 감사하지만 진짜 멍청한 놈으로서 지시 사항이 어디에 있는지 궁금합니다. BEGINNING에서 과학자들은 두 아키텍처 (모든 데이터가 DATA로 간주되는 폰 노이만과 명령을 위해 메모리 영역이 예약 된 HARVARD와 데이터를 위해 할당 된 HARVARD)를 결정했습니다. 궁극적으로, 우리는 폰 노이만 디자인과 함께 갔고 이제는 모든 것이 '동일한'것으로 간주됩니다. https://www.cs.virginia.edu/~evans/cs216/guides/x86.html 어셈블리를 배울 때 레지스터와 스택 포인터에 대해 이야기하기 때문에 어려웠습니다 .

위의 모든 것은 DATA에 대해 이야기합니다. 내 생각에 명령은 특정 메모리 공간을 가진 정의 된 것이므로 스택에 들어가므로 어셈블리에서 논의 된 모든 '레지스터'레지스터가 스택에 있습니다. 물론 명령과 데이터가 역동적 인 구조로 제공되는 객체 지향 프로그래밍이 왔으므로 이제 명령도 힙에 보관됩니까?

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