it-swarm-ko.tech

스택과 힙은 어디에서 무엇입니까?

프로그래밍 언어 책은 값 유형이 stack 에 작성되고 참조 유형이 heap 에 작성되었으며이 두 가지가 무엇인지 설명하지 않는다고 설명합니다. 나는 이것에 대한 명확한 설명을 읽지 않았다. 스택이 무엇인지 이해합니다. 그러나,

  • 어디서 무엇입니까? (실제 컴퓨터의 메모리에 물리적으로)?
  • OS 또는 언어 런타임에 의해 어느 정도까지 제어됩니까?
  • 그들의 범위는 무엇입니까?
  • 각각의 크기를 결정하는 것은 무엇입니까?
  • 무엇이 더 빠른가?
7615
mattshane

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

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

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

직접 질문에 답변하려면 다음을 수행하십시오. 

OS 또는 언어 런타임에서 어느 정도까지 제어합니까?

OS는 스레드가 생성 될 때 각 시스템 수준 스레드에 대해 스택을 할당합니다. 일반적으로 OS는 응용 프로그램에 힙을 할당하기 위해 언어 런타임에 의해 호출됩니다.

범위는 무엇입니까?

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

각각의 크기를 결정하는 것은 무엇입니까? 

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

무엇이 더 빠른가?

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

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

5529
Jeff Hill

스택 :

  • 컴퓨터 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;
2199
Brian R. Bondy

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

  • 아이템들의 스택에서, 아이템들은 다른 아이템들 위에 놓여진 순서대로 하나의 아이템 위에 놓이고, 당신은 오직 하나만 제거 할 수 있습니다 (모든 것을 넘어 뜨리지 않고).

    Stack like a stack of papers

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

  • 힙에는 항목 배치 방식에 특별한 순서가 없습니다. 명확한 '상위'항목이 없으므로 순서에 관계없이 항목을 찾아서 제거 할 수 있습니다.

    Heap like a heap of licorice allsorts

    힙 할당은 조각난을 줄이기 위해 오버 헤드 유지 관리뿐 아니라 요청 된 크기에 맞게 충분히 큰 인접한 메모리 세그먼트를 찾는 등 메모리가 할당되고 무엇이 아닌지에 대한 전체 기록을 유지 관리해야합니다. 여유 공간을 남겨 두어 언제든지 메모리 할당을 해제 할 수 있습니다. 때로는 메모리 할당자가 할당 된 메모리를 이동하여 메모리 조각 모음 또는 가비지 수집과 같은 유지 관리 작업을 수행합니다. 런타임에 메모리가 더 이상 존재하지 않을 때이를 식별하고 할당을 해제합니다. 

이러한 이미지는 스택과 힙에서 메모리를 할당하고 해제하는 두 가지 방법을 설명하는 꽤 좋은 작업을 수행해야합니다. 얌!

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

    언급했듯이, 힙과 스택은 일반적인 용어이며 많은 방법으로 구현 될 수 있습니다. 컴퓨터 프로그램은 전형적으로 그것이 호출 된 함수와 어떤 로컬 변수에 대한 포인터와 같은 현재 함수와 관련된 정보를 저장하는 a call stack 이라고 불리는 스택을 가지고있다. 함수는 다른 함수를 호출하고 반환하기 때문에 스택이 커지고 축소되어 함수의 정보가 호출 스택 아래로 더 내려갑니다. 프로그램은 실제로 런타임 컨트롤을 가지고 있지 않습니다. 프로그래밍 언어, OS 및 시스템 아키텍처에 의해 결정됩니다.

    힙은 동적으로 및 임의적으로 할당되는 모든 메모리에 사용되는 일반적인 용어입니다. 즉 순서가 맞지 않는다. 메모리는 일반적으로 OS에 의해 할당되며 응용 프로그램은이 할당을 수행하는 API 함수를 호출합니다. 일반적으로 OS에서 처리하는 동적 할당 메모리를 관리하는 데 필요한 상당한 오버 헤드가 있습니다.

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

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

  • 각각의 크기를 결정하는 것은 무엇입니까?

    다시 말하지만 언어, 컴파일러, 운영 체제 및 아키텍처에 따라 다릅니다. 정의 상 연속 메모리 여야하기 때문에 스택은 일반적으로 미리 할당됩니다 (마지막 단락의 내용을 더 참조하십시오). 언어 컴파일러 또는 OS가 크기를 결정합니다. 스택에 거대한 데이터 덩어리를 저장하지 않으므로 원치 않는 무한 재귀 (따라서 "스택 오버플로") 또는 기타 비정상적인 프로그래밍 결정의 경우를 제외하고는 절대 완전히 사용해서는 안됩니다.

    힙은 동적으로 할당 할 수있는 모든 것에 대한 일반적인 용어입니다. 어떤 방식으로 보느냐에 따라 끊임없이 크기가 변합니다. 현대의 프로세서와 운영체제에서 작동하는 정확한 방식은 어쨌든 매우 추상화되어 있으므로 일반적으로 어떻게 작동하는지에 대해 크게 걱정할 필요가 없습니다. 단, (메모리가있는 언어에서는) 메모리를 사용하면 안됩니다. 당신은 아직 할당하지 않은 또는 당신이 해방 메모리.

  • 무엇이 더 빠른가?

    모든 여유 메모리가 항상 인접하기 때문에 스택이 빠릅니다. 여유 메모리의 모든 세그먼트에 대해 목록을 유지할 필요는 없으며 스택의 현재 맨 위의 포인터 하나만 유지해야합니다. 컴파일러는 대개이 포인터를 특수 목적의 fast register 에 저장합니다. 게다가 스택상의 후속 작업은 일반적으로 메모리의 매우 가까운 영역에 집중되어 있습니다.이 영역은 매우 낮은 수준에서 프로세서 온 - 다이 캐시의 최적화에 적합합니다.

1305
thomasrutter

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

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

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

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

The heap

스택

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

The stack

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

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

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

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

702
Martin Liversage

다음 C # 코드

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

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

Picture of variables on the stack

Local Variables는 함수 호출이 스택에있는 한 오래 걸릴 필요가 있습니다. 힙 (heap)은 수명을 우리가 실제로 알지 못하는 변수에 사용되지만, 그 수명은 오래 지속될 것으로 기대합니다. 대부분의 언어에서 우리가 스택에 저장하기를 원한다면 변수의 크기가 얼마나 큰지 컴파일 시간에 알아야합니다. 

개체 (업데이트 할 때 크기가 달라짐)는 생성 시간에 얼마나 오랫동안 지속될 것인지 모르기 때문에 힙을 사용합니다. 많은 언어에서 힙은 더 이상 참조가없는 객체 (예 : cls1 객체)를 찾기 위해 가비지 수집됩니다. 

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

더 많은 정보는 여기에서 찾을 수 있습니다 :

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

그리고 여기: 

스택 및 힙에 객체 생성

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

그러나 일부 부정확성을 포함 할 수 있음을 알고 있어야합니다. 

379
Snowcrash

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

스택을 할당 해제하는 것은 할당하는 역순으로 항상 할당을 해제하기 때문에 매우 간단합니다. 함수를 입력 할 때 스택 스터가 추가되고 종료 할 때 해당 데이터가 제거됩니다. 즉, 다른 함수를 많이 호출하거나 재귀 적 솔루션을 만드는 많은 함수를 호출하지 않는 한 스택의 작은 영역에 머물러있는 경향이 있습니다.

힙은 작성한 데이터를 저장하는 곳의 일반적인 이름입니다. 프로그램이 얼마나 많은 우주선을 만들지 모른다면, 새로운 (또는 malloc 또는 동급의) 연산자를 사용하여 각 우주선을 만들 가능성이 높습니다. 이 할당은 잠시 머물러 있기 때문에 우리가 만든 순서와 다른 순서로 물건을 해제 할 가능성이 있습니다. 

따라서 힙은 훨씬 복잡합니다. 왜냐하면 메모리가 조각난 채로 덩어리가 끼어 들지 않는 메모리 영역이되기 때문입니다. 필요한 크기의 여유 메모리를 찾는 것은 어려운 문제입니다. 이런 이유로 힙을 피해야합니다 (그래도 여전히 자주 사용되지만).

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

이는 메모리 사용량이 표준과 상당히 다른 경우, 즉 거대한 한 번의 작업에서 레벨을로드하고 다른 거대한 작업에서 전체를 멀리 치울 수있는 게임의 경우에만 실용적입니다.

메모리의 물리적 위치 이것은 Virtual Memory 라는 기술로 인해 당신이 생각하는 것보다 덜 관련이 있습니다. 프로그램에서 물리적 데이터가있는 특정 주소에 액세스 할 수 있다고 생각하게 만듭니다 어딘가에 있습니다 (심지어 하드 디스크에도!). 호출 트리가 깊어 질수록 스택에 대한 주소가 늘어납니다. 힙의 주소는 예측할 수 없으므로 (즉, 구현에 국한 됨) 솔직하게 중요하지 않습니다.

194
Tom Leys

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

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

그러나 일반적으로 "stack"및 "heap"보다는 "scope"및 "lifetime"을 고려하는 것이 좋습니다.

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

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

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

이 모든 것을 설명하기 위해 간단한 주석이 달린 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 "이라고하면 일반적으로 "로 범위가 지정된 자동 할당 변수 "를 의미하며 전역을 말할 때 일반적으로 의미합니다 " 전역 범위 정적 할당 변수 ". 불행히도 " 파일 범위 정적 할당 변수 "와 같은 것들에 관해서는 많은 사람들이 그냥 말합니다 ... " 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 __== "__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
181
davec

다른 사람들은 폭 넓은 스트로크에 꽤 잘 답변 했으므로 몇 가지 세부 사항을 설명하겠습니다.

  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.
}
158
Don Neufeld

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

스택과 힙은 프로세스의 가상 주소 공간의 반대쪽 끝에 위치합니다. 스택은 액세스 할 때 커널에 의해 설정된 크기 (setrlimit(RLIMIT_STACK, ...)로 조정 가능)까지 자동으로 커집니다. 힙은 메모리 할당자가 brk() 또는 sbrk() 시스템 호출을 호출하여 실제 메모리의 더 많은 페이지를 프로세스의 가상 주소 공간에 매핑 할 때 커집니다. 

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

127
bk1e

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

힙은 일반적으로 malloc과 같은 시스템 호출을 통해 운영 체제에서 응용 프로그램에 제공하는 메모리의 일부입니다. 최신 OS에서는이 메모리가 호출 프로세스 만 액세스 할 수있는 페이지 집합입니다.

스택의 크기는 런타임에 결정되며 일반적으로 프로그램이 시작된 후에는 증가하지 않습니다. C 프로그램에서 스택은 각 함수 내에서 선언 된 모든 변수를 담을 수있을 정도로 커야합니다. 힙은 필요에 따라 동적으로 커지지 만 OS가 궁극적으로 호출합니다 (malloc에서 요청한 값 이상으로 힙이 커질 수 있으므로 적어도 일부 malloc은 커널로 돌아갈 필요가 없습니다). 더 많은 메모리를 얻으십시오.이 동작은 종종 사용자 정의 가능합니다)

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

108
Daniel Papasian

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

그러나 놓친 한 가지 세부 사항은 "힙"을 실제로 "무료 저장소"라고해야한다는 것입니다. 이 구별의 이유는 원래의 무료 저장소가 "이항 힙"으로 알려진 데이터 구조로 구현 되었기 때문입니다. 이러한 이유로 malloc ()/free ()의 초기 구현에서 할당하는 것은 힙에서 할당하는 것이 었습니다. 그러나이 현대 시대에 대부분의 무료 스토어는 이항 힙이 아닌 매우 정교한 데이터 구조로 구현됩니다.

107
Heath

스택이란 무엇입니까?

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

 Enter image description here

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

힙이란 무엇입니까?

힙은 우연히 쌓여있는 깔끔한 컬렉션입니다.

 Enter image description here

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

둘 다 함께

다중 스레드 응용 프로그램에서 각 스레드는 자체 스택을 갖습니다. 그러나 모든 다른 스레드가 힙을 공유합니다. 
다른 스레드가 멀티 스레드 응용 프로그램에서 힙을 공유하기 때문에 스레드의 동일한 조정 (들)을 액세스하고 조작하려고 시도하지 않도록 스레드 간의 조정이 있어야 함을 의미합니다. 같은 시간에 힙의 메모리.

더 빠른 - 스택 또는 힙? 그리고 왜?

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

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

Java 메모리 모델

 Enter image description here

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

101
Shreyos Adikari

스택으로 흥미로운 것을 할 수 있습니다. 예를 들어, 당신은 alloca (사용에 관한 많은 경고를 지나칠 수 있다고 가정 할 때)와 같은 함수를 가지고있다. 이것은 메모리를 위해 힙이 아니라 스택을 사용하는 malloc의 한 형태이다.

즉, 스택 기반 메모리 오류는 제가 경험 한 최악의 경우입니다. 힙 메모리를 사용하고 할당 된 블록의 경계를 넘어 서면 세그먼트 오류가 발생합니다. (100 %가 아닌 : 블록이 이전에 할당 한 다른 블록과 부수적으로 연속적 일 수 있습니다.) 그러나 스택에서 생성 된 변수는 항상 서로 연속되어 있으므로 경계를 작성하면 다른 변수의 값을 변경할 수 있습니다. 나는 나의 프로그램이 논리의 법칙에 순종하지 않는다고 느낄 때마다 아마 버퍼 오버 플로우라는 것을 배웠다.

87
Peter

간단히 말해 스택은 로컬 변수가 생성되는 곳입니다. 또한 서브 루틴을 호출 할 때마다 프로그램 카운터 (다음 머신 명령어에 대한 포인터)와 중요한 레지스터가 호출되며 때로는 매개 변수가 스택에 푸시됩니다. 그런 다음 서브 루틴 내부의 모든 로컬 변수가 스택에 푸시되고 거기에서 사용됩니다. 서브 루틴이 완료되면, 그 모든 것들이 스택에서 다시 터진다. PC 및 레지스터 데이터는 가져 왔을 때의 위치로 가져오고 되돌려 놓으면 프로그램이 즐겁게 돌아갈 수 있습니다.

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

"클래식"시스템에서는 스택 포인터가 메모리의 맨 아래에서 시작되고 힙 포인터가 맨 위에서 시작하여 서로를 향하여 자라도록 RAM 배열되었습니다. 중복되면 RAM이 부족합니다. 그것은 현대의 멀티 스레드 OS에서는 작동하지 않습니다. 모든 스레드는 자체 스택을 가져야하며 동적으로 생성 될 수 있습니다.

84
T.E.D.

WikiAnwser에서.

스택

함수 또는 메소드가 다른 함수를 호출 할 때 다른 함수를 호출하면 마지막 함수가 그 값을 반환 할 때까지 모든 함수의 실행이 일시 중단됩니다.

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

예외 처리 및 스레드 실행시 고려해야 할 스택이 중요합니다.

더미

힙은 단순히 변수를 저장하는 프로그램에서 사용되는 메모리입니다. 힙 (변수)의 요소는 서로 종속되지 않으며 언제든지 임의로 액세스 할 수 있습니다.

78
devXen

스택

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

  • 변수는 전역 적으로 액세스 할 수 있습니다.
  • 메모리 크기 제한 없음
  • (상대적으로) 느린 액세스
  • 효율적인 공간 사용을 보장하지 못하면 메모리 블록이 할당 된 후 시간이 지남에 따라 메모리가 조각화되어 해제 될 수 있습니다.
  • 메모리를 관리해야합니다 (변수를 할당하고 해제하는 책임이 있습니다)
  • 변수는 realloc ()을 사용하여 크기를 조정할 수 있습니다.
50
unknown

OK, 간단히 요약하면 orderedordered not...를 의미합니다.

Stack : 스택 항목에서 모든 것들이 서로의 상단에 위치하며, 처리가 더 빠르고 효율적이라는 것을 의미합니다! ... 

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

: 순서가 없으므로 처리 속도가 느려지고 특정 순서 또는 색인없이 값이 엉망이됩니다 ... 임의적이며 서로간에 관계가 없기 때문에 실행 및 사용 시간이 길어질 수 있습니다 다르다...

아래 이미지가 어떻게 생겼는지 보여줍니다.

 enter image description here

38
Alireza

짧게

스택은 정적 메모리 할당과 동적 메모리 할당을위한 힙 (둘 다 컴퓨터의 RAM에 저장 됨)에 사용됩니다.


상세히

스택

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

스택을 사용하여 변수를 저장하면 메모리가 관리된다는 이점이 있습니다. 메모리를 직접 할당하거나 더 이상 필요하지 않으면 메모리를 확보 할 필요가 없습니다. 게다가 CPU가 스택 메모리를 효율적으로 구성하기 때문에 스택 변수를 읽고 쓰는 속도가 매우 빠릅니다.

더 많은 것을 찾을 수 있습니다 여기.


The Heap

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

이렇게하지 않으면 프로그램에 메모리 누수가 발생합니다. 즉, 힙의 메모리는 여전히 따로 설정됩니다 (다른 프로세스에서는 사용할 수 없음). 디버깅 섹션에서 볼 수 있듯이 Valgrind 는 메모리 누수를 감지하는 데 도움이되는 도구입니다.

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

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

더 많은 것을 찾을 수 있습니다 여기.


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

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

 Enter image description here

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

다중 스레드 상황에서 각 스레드는 자체적으로 완전히 독립적 인 스택을 갖지만 힙을 공유합니다. 스택은 스레드에 고유하며 힙은 응용 프로그램에 따라 다릅니다. 예외 처리 및 스레드 실행시 고려해야 할 스택이 중요합니다.

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

 Enter image description here

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

더 자세한 내용은 herehere 로 제공됩니다.


이제 귀하의 질문에 대한 답변으로 오십시오.

OS 또는 언어 런타임에서 어느 정도까지 제어합니까?

OS는 스레드가 생성 될 때 각 시스템 수준 스레드에 대해 스택을 할당합니다. 일반적으로 OS는 응용 프로그램에 힙을 할당하기 위해 언어 런타임에 의해 호출됩니다.

더 많은 것을 찾을 수 있습니다 여기.

범위는 무엇입니까?

이미 위에 주어진.

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

더 많은 것을 here 에서 찾을 수 있습니다.

각각의 크기를 결정하는 것은 무엇입니까?

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

무엇이 더 빠른가?

스택 할당은 스택 포인터를 이동시키는 것보다 훨씬 빠릅니다. 메모리 풀을 사용하면 힙 할당에서 비교할 수없는 성능을 얻을 수 있지만 약간의 복잡성과 자체적 인 골치 거리가 있습니다.

또한 스택 대 힙 (heap)은 성능 고려 사항 일뿐만 아니라, 또한 객체의 예상 수명에 대해 많이 알려줍니다.

세부 정보는 여기에서 찾을 수 있습니다.

35
Abrar Jahin

1980 년대 UNIX는 대기업과 함께 토끼처럼 번식했습니다. Exxon은 수십 개의 브랜드 이름을 역사상 잃어 버렸습니다. 기억이 어떻게 배치되었는지는 많은 사람들의 재량에 달렸습니다. 구현 자.

일반적인 C 프로그램은 brk () 값을 변경하여 증가시킬 수있는 기회를 평평하게 메모리에 배치했습니다. 일반적으로 HEAP는이 brk 값 보다 약간 낮았으며 brk은 사용 가능한 힙의 양을 늘 렸습니다.

단일 스택은 전형적으로 HEAP 아래의 영역으로 다음 고정 된 메모리 블록의 최상부까지 가치가없는 메모리 영역 이었습니다. 이 블록은 종종 CODE 였고 덮어 쓸 수있었습니다 그 시대의 유명한 해킹 중 하나에 스택 데이터 .

하나의 전형적인 메모리 블록은 BSS (제로 값의 블록) 로 한 제조업 자의 제안에서 실수로 제로화되지 않았습니다. 또 다른 하나는 문자열과 숫자를 포함한 초기화 된 값을 포함하는 DATA 입니다. 세 번째는 CODE가 포함 된 CRT (C 런타임), main, 함수 및 라이브러리입니다.

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

A typical 1980s style UNIX C program memory layout

35
jlettvin

stack , heap data 가상 메모리의 각 프로세스 :

 stack, heap and static data

29
Yousha Aleayoub

몇 센트 : 저는 그래픽과 더 단순한 메모리를 그리는 것이 좋을 것이라고 생각합니다.

 This is my vision of process memory construction with simplification for more easy understanding wht happening


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

간단한 방법 : 프로세스 힙은 일반적으로 프로세스와 모든 스레드 내부에서 사용되며, 일반적으로 malloc () 같은 메모리 할당을 사용합니다.

스택은 함수 호출에서 매개 변수로 처리되는 일반적인 경우 함수 리턴 포인터 및 변수의 저장을위한 빠른 메모리이며 로컬 함수 변수입니다.

24
Maxim Akristiniy

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

놀랍게도, 아무도 (즉, 실행중인 OS 레벨 스레드의 수와 관련이 없음) 호출 스택이 이국적인 언어 (PostScript) 또는 플랫폼 (Intel Itanium)뿐만 아니라 fibers , green threads 및 일부 코 루틴 .

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

어쨌든, 섬유, 녹색 실 및 코 루틴의 목적은 동시에 여러 기능을 실행하지만 not 병렬로 수행하는 것입니다 ( this 참조) 단일 OS 수준 스레드 내에서 SO question 구별) : 체계적인 방식으로 서로 제어를주고 받음.

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

" usually 함수마다 별도의 스택이 있습니다"라고 언급했습니다. 쿠 루틴의 stackful stackless 구현이 모두 있습니다. 가장 주목할만한 스택 C++ 구현은 Boost.CoroutineMicrosoft PPL 's async/await입니다. 그러나 C++ 17에 제안 된 C++의 다시 시작 가능한 함수 (즉, "asyncawait")는 스택리스 코 루틴을 사용할 가능성이 높습니다.)

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

21
shakurov

주요 포인트가 이미 다루어졌지만 공유 할 것이 있습니다.

스택  

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

  • 스택에 비해 상대적으로 느립니다.
  • RAM에 저장됩니다.
  • 동적으로 생성 된 변수는 여기에 저장되며 나중에 사용 후 할당 된 메모리를 해제해야합니다.
  • 메모리 할당이 수행되는 모든 위치에 저장되며 항상 포인터로 액세스됩니다.

재미있는 메모 :

  • 함수 호출이 힙에 저장 되었다면 2 가지 지저분한 결과가 발생했습니다.
    1. 스택에 순차적으로 저장되므로 실행이 더 빠릅니다. 힙에 저장하면 엄청난 시간 소비가 발생하므로 전체 프로그램이 느리게 실행됩니다.
    2. 함수가 힙 (포인터가 가리키는 지저분한 저장소)에 저장되면 호출자 주소로 돌아갈 방법이 없었을 것입니다 (스택은 메모리에 순차적 저장으로 인해 제공됨).
12
Pankaj Kumar Thapa

많은 답이 개념으로 옳지 만, 서브 루틴 호출 (어셈블리 언어에서는 CALL)을 허용하기 위해 하드웨어 (예 : 마이크로 프로세서)가 스택을 필요로한다는 것을 알아야합니다. (OOP 사람들은 그것을 methods 라고 부를 것입니다)

스택에서 반송 주소를 저장하고 → Push/Ret → pop을 직접 하드웨어에서 관리합니다.

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

  • 스택이 없으면 no 마이크로 프로세서가 작동 할 수 있습니다. (어셈블리 언어로도 서브 루틴/함수없이 프로그램을 상상할 수는 없습니다)
  • 힙이 없으면 그렇게 할 수 있습니다. (어셈블리 언어 프로그램은 힙이 OS 개념이기 때문에 malloc, 즉 OS/Lib 호출없이 작동 할 수 있습니다.

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

  • 하드웨어, 푸쉬/팝조차도 매우 효율적입니다.
  • malloc은 커널 모드로 들어가야하며, 일부 코드를 실행하는 lock/semaphore (또는 다른 동기화 프리미티브)를 사용하고 할당을 추적하는 데 필요한 몇 가지 구조를 관리해야합니다.
8
ingconti

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

1) 실제 컴퓨터의 메모리에있는 위치와 그 위치는 무엇입니까?

스택은 프로그램 이미지에 할당 된 가장 높은 메모리 주소로 시작하는 메모리이며 그 다음에는 값이 감소합니다. 호출 된 함수 매개 변수와 함수에 사용 된 모든 임시 변수에 예약되어 있습니다.

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

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

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

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

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

스택은 프로그래머에 의해 제어되고, 개인 힙은 OS에 의해 관리되며 공개 힙은 OS 서비스이기 때문에 아무도 제어하지 않습니다. 요청을하고 허가 또는 거부합니다.

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

그것들은 모두 프로그램의 글로벌 프로그램이지만 내용은 개인, 공개 또는 글로벌 일 수 있습니다.

2c) 각각의 크기를 결정하는 것은 무엇입니까?

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

2d) 무엇이 더 빠를까요?

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

참고 :

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

3
ar18

프로그래밍 언어 별 스토리지 할당 전략 

C의 메모리 - 스택, 힙, 정적

 enter image description here  MEMORY IN C – THE STACK, THE HEAP, AND STATIC

  1. OP : OS 또는 언어 런타임에서 어느 정도까지 제어합니까?

이 중요한 질문에 대해 몇 가지 추가하고 싶습니다. 

OS 및 공용 언어 런타임 

.NET Framework의 주요 구성 요소  enter image description here Net Framework 4.5 아키텍처  enter image description here

CLR 구성 요소  Components of CLR  enter image description here  enter image description here  enter image description here

0
leonidaa