문제

프로그래밍 언어 책에서는 값 유형이 스택, 참조 유형은 다음에 생성됩니다. 더미, 이 두 가지가 무엇인지 설명하지 않고.나는 이에 대한 명확한 설명을 읽지 않았습니다.나는 무엇을 이해한다 스택 이다.하지만,

  • 그것들은 어디에 있고 무엇입니까(물리적으로 실제 컴퓨터의 메모리에 있습니까?)
  • OS 또는 언어 런타임에 의해 어느 정도 제어됩니까?
  • 그들의 범위는 무엇입니까?
  • 각각의 크기는 어떻게 결정됩니까?
  • 무엇이 더 빨라지나요?
도움이 되었습니까?

해결책

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

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

각 스레드는 스택을 가져오지만 일반적으로 애플리케이션에는 하나의 힙만 있습니다(다양한 할당 유형에 대해 여러 힙을 갖는 것은 드문 일이 아니지만).

질문에 직접 답변하려면:

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

OS는 스레드가 생성될 때 각 시스템 수준 스레드에 대한 스택을 할당합니다.일반적으로 OS는 애플리케이션에 대한 힙을 할당하기 위해 언어 런타임에 의해 호출됩니다.

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

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

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

스택의 크기는 스레드가 생성될 때 설정됩니다.힙 크기는 애플리케이션 시작 시 설정되지만 공간이 필요하면 커질 수 있습니다(할당자는 운영 체제에 더 많은 메모리를 요청합니다).

무엇이 더 빨라지나요?

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

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

다른 팁

스택:

  • 힙과 마찬가지로 컴퓨터 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;

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

  • 항목 더미에서는 항목이 배치된 순서대로 다른 항목 위에 쌓여 있으며, 맨 위에 있는 항목만 제거할 수 있습니다(전체 항목을 쓰러뜨리지 않고).

    Stack like a stack of papers

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

  • 힙에서는 항목을 배치하는 방식에 특별한 순서가 없습니다.명확한 '상위' 항목이 없기 때문에 어떤 순서로든 항목에 접근하여 제거할 수 있습니다.

    Heap like a heap of licorice allsorts

    힙 할당에는 할당된 메모리와 할당되지 않은 메모리에 대한 전체 기록을 유지 관리하는 것뿐만 아니라 조각화를 줄이기 위한 일부 오버헤드 유지 관리, 요청된 크기에 맞을 만큼 큰 연속 메모리 세그먼트 찾기 등이 필요합니다.여유 공간을 남기고 언제든지 메모리 할당을 취소할 수 있습니다.때때로 메모리 할당자는 할당된 메모리를 이동하여 메모리 조각 모음 또는 가비지 수집과 같은 유지 관리 작업을 수행합니다. 즉, 메모리가 더 이상 범위를 벗어나는 시기를 런타임에 식별하고 할당을 취소합니다.

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

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

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

    힙은 동적으로 무작위로 할당되는 메모리에 사용되는 일반적인 용어입니다.즉.고장난.메모리는 일반적으로 OS에 의해 할당되며, 애플리케이션은 이 할당을 수행하기 위해 API 함수를 호출합니다.일반적으로 OS에서 처리하는 동적으로 할당된 메모리를 관리하려면 상당한 오버헤드가 필요합니다.

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

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

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

    다시 말하지만 언어, 컴파일러, 운영 체제 및 아키텍처에 따라 다릅니다.스택은 일반적으로 사전 할당됩니다. 정의에 따라 연속 메모리여야 하기 때문입니다(자세한 내용은 마지막 단락에서 설명).언어 컴파일러나 OS에 따라 크기가 결정됩니다.스택에는 엄청난 양의 데이터를 저장하지 않으므로 원치 않는 끝없는 재귀(따라서 "스택 오버플로") 또는 기타 비정상적인 프로그래밍 결정의 경우를 제외하고는 완전히 사용되어서는 안 될 만큼 충분히 큽니다.

    힙은 동적으로 할당될 수 있는 모든 것을 가리키는 일반적인 용어입니다.보는 각도에 따라 크기가 계속해서 변합니다.최신 프로세서와 운영 체제에서는 작동 방식이 매우 추상화되어 있으므로 일반적으로 작동 방식에 대해 크게 걱정할 필요가 없습니다. 단, 허용되는 언어에서는 메모리를 사용해서는 안 됩니다. 아직 할당하지 않았거나 해제한 메모리입니다.

  • 무엇이 더 빨라지나요?

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

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

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

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

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

The heap

스택

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

The stack

스택 대신 힙에 함수를 할당할 수 있나요?

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

힙 관리 방법은 실제로 런타임 환경에 따라 다릅니다.C에서는 다음을 사용합니다. malloc 그리고 C++에서는 new, 그러나 다른 많은 언어에는 가비지 수집 기능이 있습니다.

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

다음 C# 코드에서

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

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

Picture of variables on the stack

Local Variables 함수 호출이 스택에 있는 동안에만 지속되어야 합니다.힙은 수명을 실제로 알 수는 없지만 한동안 지속될 것으로 예상되는 변수에 사용됩니다.대부분의 언어에서는 변수를 스택에 저장하려는 경우 컴파일 타임에 변수의 크기를 아는 것이 중요합니다.

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

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

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

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

그리고 여기:

스택과 힙에 객체 생성

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

그러나 일부 부정확한 내용이 포함될 수 있다는 점에 유의하세요.

스택함수를 호출하면 해당 함수에 대한 인수와 기타 오버헤드가 스택에 저장됩니다.일부 정보(예: 돌아올 때 어디로 가야 하는지 등)도 여기에 저장됩니다.함수 내에서 변수를 선언하면 해당 변수도 스택에 할당됩니다.

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

힙은 즉시 생성하는 데이터를 저장하는 위치에 대한 일반적인 이름입니다.프로그램에서 생성할 우주선 수를 모르는 경우 new(또는 malloc 또는 이에 상응하는) 연산자를 사용하여 각 우주선을 생성할 가능성이 높습니다.이 할당은 한동안 지속될 예정이므로 생성한 순서와 다른 순서로 항목을 해제할 가능성이 높습니다.

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

구현스택과 힙의 구현은 일반적으로 런타임/OS에 따라 결정됩니다.성능이 중요한 게임 및 기타 애플리케이션은 힙에서 많은 양의 메모리를 가져온 다음 메모리를 OS에 의존하지 않기 위해 내부적으로 정리하는 자체 메모리 솔루션을 만드는 경우가 많습니다.

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

메모리의 물리적 위치이것은 당신이 생각하는 것보다 관련성이 적습니다. 왜냐하면 다음과 같은 기술 때문입니다. 가상 메모리 이는 실제 데이터가 다른 곳에 있는 특정 주소(심지어 하드 디스크에도!)에 액세스할 수 있다고 프로그램이 생각하게 만듭니다.스택에 대해 얻는 주소는 호출 트리가 깊어질수록 증가하는 순서로 표시됩니다.힙의 주소는 예측할 수 없으며(즉, 특정 구현에 따라) 솔직히 중요하지 않습니다.

명확히 하기 위해, 이 답변 잘못된 정보가 있습니다(도마 댓글 후 답변을 수정했습니다. 멋지네요 :) ).다른 답변은 정적 할당의 의미를 설명하지 않습니다.그래서 나는 할당의 세 가지 주요 형태와 그것이 일반적으로 힙, 스택 및 데이터 세그먼트와 어떻게 관련되는지 아래에서 설명할 것입니다.또한 사람들의 이해를 돕기 위해 C/C++와 Python으로 몇 가지 예를 보여 드리겠습니다.

"정적"(정적으로 할당이라고도 함) 변수는 스택에 할당되지 않습니다.그렇게 가정하지 마십시오. 많은 사람들이 그렇게 생각하는 이유는 "정적"이 "스택"과 매우 유사하기 때문입니다.실제로는 스택이나 힙에 존재하지 않습니다.이것은 소위 말하는 것의 일부입니다. 데이터 세그먼트.

그러나 일반적으로 "를 고려하는 것이 좋습니다.범위" 그리고 "일생"스택"과 "힙"이 아닌 "스택"을 사용합니다.

범위는 코드의 어느 부분이 변수에 액세스할 수 있는지를 나타냅니다.일반적으로 우리가 생각하는 로컬 범위 (현재 함수로만 액세스할 수 있음) 대 전역 범위 (어디서나 액세스 가능) 범위는 훨씬 더 복잡해질 수 있습니다.

수명은 프로그램 실행 중에 변수가 할당되고 할당 해제되는 시기를 나타냅니다.보통 우리가 생각하는 정적 할당 (변수는 프로그램 전체 기간 동안 지속되므로 여러 함수 호출에 걸쳐 동일한 정보를 저장하는 데 유용합니다.) 자동 할당 (변수는 함수를 한 번 호출하는 동안에만 지속되므로 함수 중에만 사용되고 작업이 끝나면 삭제할 수 있는 정보를 저장하는 데 유용합니다.) 동적 할당 (정적 또는 자동과 같은 컴파일 시간 대신 런타임에 기간이 정의되는 변수)

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

이 모든 것을 설명하기 위해 간단한 주석이 달린 C 코드를 제공하겠습니다.학습하는 가장 좋은 방법은 디버거에서 프로그램을 실행하고 동작을 관찰하는 것입니다.Python을 읽고 싶다면 답변의 끝 부분으로 건너뛰세요 :)

// 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").이러한 변수는 우리의 일반적이지만 비공식적인 명명 습관을 매우 혼란스럽게 만들 수 있습니다.예를 들어 우리가 "현지의"우리는 보통 "을 의미합니다.로컬 범위의 자동 할당 변수" 그리고 우리가 글로벌이라고 말할 때 일반적으로 "를 의미합니다.전역 범위 정적으로 할당된 변수".불행하게도 "파일 범위의 정적으로 할당된 변수"많은 사람들이 그냥 말하는데..."뭐???".

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에는 정적 할당이 있습니다.이것은 직관적이지 않습니다!이러한 이유로 나는 범위를 설명할 때 "정적"이라는 단어를 사용하지 않고 대신 "파일" 또는 "파일 제한" 범위와 같은 표현을 사용하려고 합니다.그러나 많은 사람들은 하나의 코드 파일에서만 액세스할 수 있는 변수를 설명하기 위해 "정적" 또는 "정적 범위"라는 문구를 사용합니다.수명의 맥락에서 "정적" 언제나 변수가 프로그램 시작 시 할당되고 프로그램 종료 시 할당 해제됨을 의미합니다.

어떤 사람들은 이러한 개념이 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

다른 사람들이 대략적인 내용에 대해 꽤 잘 답변해 주었으므로 몇 가지 세부 사항을 설명하겠습니다.

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

  2. C에서는 다음을 사용하여 가변 길이 할당의 이점을 얻을 수 있습니다. 알로카, 힙에 할당하는 alloc과 달리 스택에 할당합니다.이 메모리는 return 문 이후에는 유지되지 않지만 스크래치 버퍼에는 유용합니다.

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

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

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

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

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

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

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

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

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

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

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

스택이란 무엇입니까?

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

Enter image description here

컴퓨팅 아키텍처의 스택은 후입선출 방식으로 데이터가 추가되거나 제거되는 메모리 영역입니다.
다중 스레드 애플리케이션에서 각 스레드는 자체 스택을 갖습니다.

힙이란 무엇입니까?

힙(heap)은 아무렇게나 쌓아둔 어수선한 것들을 모아 놓은 것입니다.

Enter image description here

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

양자

다중 스레드 애플리케이션에서 각 스레드는 자체 스택을 갖습니다.그러나 모든 다른 스레드는 힙을 공유합니다.
다중 스레드 애플리케이션에서는 서로 다른 스레드가 힙을 공유하므로 이는 스레드가 힙에 있는 동일한 메모리 조각에 액세스하고 조작하려고 시도하지 않도록 스레드 간에 어느 정도 조정이 필요함을 의미하기도 합니다. 동시.

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

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

프로그래밍을 처음 접하는 사람들에게는 스택이 더 쉽기 때문에 스택을 사용하는 것이 아마도 좋은 생각일 것입니다.
스택은 작기 때문에 데이터에 필요한 메모리 양을 정확히 알고 있거나 데이터 크기가 매우 작다는 것을 알고 있는 경우 사용하고 싶을 것입니다.
데이터에 많은 메모리가 필요하다는 것을 알고 있거나 (동적 배열의 경우와 같이) 얼마나 많은 메모리가 필요할지 확실하지 않은 경우 힙을 사용하는 것이 좋습니다.

자바 메모리 모델

Enter image description here

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

스택을 사용하여 몇 가지 흥미로운 작업을 수행할 수 있습니다.예를 들어 다음과 같은 기능이 있습니다. 알로카 (사용에 관한 많은 경고를 무시할 수 있다고 가정) 이는 메모리에 힙이 아닌 스택을 특별히 사용하는 malloc 형식입니다.

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

간단히 말해서 스택은 지역 변수가 생성되는 곳입니다.또한 서브루틴을 호출할 때마다 프로그램 카운터(다음 기계 명령어에 대한 포인터)와 중요한 레지스터, 때로는 매개변수가 스택에 푸시됩니다.그런 다음 서브루틴 내부의 모든 지역 변수가 스택으로 푸시됩니다(그리고 거기에서 사용됩니다).서브루틴이 끝나면 해당 항목은 모두 스택에서 다시 꺼내집니다.PC 및 레지스터 데이터는 팝된 대로 가져오고 원래 위치로 되돌리므로 프로그램이 즐거운 방식으로 진행될 수 있습니다.

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

"클래식" 시스템에서 RAM은 스택 포인터가 메모리 맨 아래에서 시작하고 힙 포인터가 맨 위에서 시작하여 서로를 향해 커지도록 배치되었습니다.겹치는 경우 RAM이 부족한 것입니다.하지만 최신 멀티스레드 OS에서는 작동하지 않습니다.모든 스레드에는 자체 스택이 있어야 하며 이러한 스택은 동적으로 생성될 수 있습니다.

WikiAnwser에서.

스택

함수나 메서드가 다른 함수를 호출하고 다른 함수 등을 호출하는 경우, 마지막 함수가 해당 값을 반환할 때까지 모든 함수의 실행이 일시 중지됩니다.

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

스택은 예외 처리 및 스레드 실행에서 고려하는 것이 중요합니다.

더미

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

스택

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

더미

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

좋아요, 간단하고 짧게 말하면, 주문하다 그리고 주문하지 않음...!

스택:스택 항목에서 항목이 서로 겹친다는 것은 처리가 더 빠르고 효율적이라는 것을 의미합니다!...

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

더미:순서가 없습니다. 처리 속도가 느려지고 특정 순서나 색인 없이 값이 엉망이 됩니다...무작위가 있고 그들 사이에는 아무런 관계가 없습니다 ...따라서 실행 및 사용 시간은 다를 수 있습니다.

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

enter image description here

짧게

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


상세히

스택

스택은 CPU에 의해 매우 밀접하게 관리되고 최적화되는 "LIFO"(후입선출) 데이터 구조입니다.함수가 새 변수를 선언할 때마다 스택에 "푸시"됩니다.그런 다음 함수가 종료될 때마다 해당 함수에 의해 스택에 푸시된 모든 변수가 해제됩니다(즉, 삭제됩니다).스택 변수가 해제되면 해당 메모리 영역을 다른 스택 변수에 사용할 수 있게 됩니다.

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

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


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

이 작업을 수행하지 못하면 프로그램에 메모리 누수라는 현상이 발생합니다.즉, 힙의 메모리는 여전히 따로 보관됩니다(다른 프로세스에서는 사용할 수 없습니다).디버깅 섹션에서 볼 수 있듯이 다음과 같은 도구가 있습니다. 발그린드 이는 메모리 누수를 감지하는 데 도움이 될 수 있습니다.

스택과 달리 힙에는 가변 크기에 대한 크기 제한이 없습니다(컴퓨터의 명백한 물리적 제한은 제외).힙 메모리는 힙의 메모리에 액세스하려면 포인터를 사용해야 하기 때문에 읽고 쓰는 속도가 약간 느립니다.곧 포인터에 대해 이야기하겠습니다.

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

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


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

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

Enter image description here

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

다중 스레드 상황에서 각 스레드는 완전히 독립적인 자체 스택을 가지지만 힙을 공유합니다.스택은 스레드에 따라 다르며 힙은 애플리케이션에 따라 다릅니다.스택은 예외 처리 및 스레드 실행에서 고려하는 것이 중요합니다.

각 스레드는 스택을 가져오지만 일반적으로 애플리케이션에는 하나의 힙만 있습니다(다양한 할당 유형에 대해 여러 힙을 갖는 것은 드문 일이 아니지만).

Enter image description here

런타임 시 애플리케이션에 더 많은 힙이 필요한 경우 여유 메모리에서 메모리를 할당할 수 있고, 스택에 메모리가 필요한 경우 애플리케이션에 할당된 여유 메모리에서 메모리를 할당할 수 있습니다.

심지어 자세한 내용도 나와있습니다 여기 그리고 여기.


이제 와서 귀하의 질문에 대한 답변.

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

OS는 스레드가 생성될 때 각 시스템 수준 스레드에 대한 스택을 할당합니다.일반적으로 OS는 애플리케이션에 대한 힙을 할당하기 위해 언어 런타임에 의해 호출됩니다.

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

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

이미 상단에 제공되었습니다.

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

더 많은 내용은 다음에서 확인할 수 있습니다. 여기.

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

스택의 크기는 다음과 같이 설정됩니다. 운영체제 스레드가 생성될 때.힙 크기는 애플리케이션 시작 시 설정되지만 공간이 필요할 때 커질 수 있습니다(할당자는 운영 체제에 더 많은 메모리를 요청합니다).

무엇이 더 빨라지나요?

스택 할당은 실제로 스택 포인터를 이동하는 것뿐이므로 훨씬 빠릅니다.메모리 풀을 사용하면 힙 할당에서 비슷한 성능을 얻을 수 있지만 약간의 복잡성과 그 자체로 골칫거리가 추가됩니다.

또한 스택 대힙은 성능만을 고려하는 것이 아닙니다.또한 개체의 예상 수명에 대해 많은 정보를 제공합니다.

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

1980년대에 UNIX는 대기업이 자체적으로 운영하는 토끼처럼 전파되었습니다.엑슨은 역사 속으로 사라진 수십 개의 브랜드 이름과 마찬가지로 하나를 가지고 있었습니다.메모리 배치 방법은 많은 구현자의 재량에 달려 있습니다.

BRK () 값을 변경하여 전형적인 C 프로그램이 메모리에서 평평하게 배치되었습니다.일반적으로, 힙은이 BRK 값 바로 아래에 있었고 BRK가 사용 가능한 힙의 양이 증가했습니다.

단일 스택은 일반적으로 힙 아래의 영역으로, 다음 고정 된 메모리 블록의 상단까지 가치가없는 메모리의 트랙입니다.이 다음 블록은 종종 시대의 유명한 해킹 중 하나에서 스택 데이터로 덮어 쓰일 수있는 코드였습니다.

하나의 일반적인 메모리 블록은 BSS (제로 값의 블록)로, 우연히 하나의 제조업체의 오퍼링에서 제로화되지 않았습니다.또 다른 하나는 문자열과 숫자를 포함하여 초기화된 값을 포함하는 DATA였습니다.세 번째는 CRT(C 런타임), 메인, 함수, 라이브러리를 포함하는 CODE였습니다.

UNIX에 가상 메모리가 등장하면서 많은 제약이 바뀌었습니다.이 블록이 인접하거나 크기가 고정되어야하는 객관적인 이유는 없습니다.물론 UNIX 이전에는 이러한 제약을 겪지 않은 Multics가 있었습니다.다음은 그 시대의 메모리 레이아웃 중 하나를 보여주는 개략도입니다.

A typical 1980s style UNIX C program memory layout

스택, 더미 그리고 데이터 가상 메모리의 각 프로세스:

stack, heap and static data

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

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


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

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

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

일부 답변이 까다로워서 제 진드기에 기여하겠습니다.

놀랍게도 아무도 그 배수(즉,실행 중인 OS 수준 스레드 수와 관련 없음) 호출 스택은 이국적인 언어(PostScript)나 플랫폼(Intel Itanium)뿐만 아니라 다른 언어에서도 발견됩니다. 섬유, 녹색 실 그리고 일부 구현 코루틴.

파이버, 녹색 스레드 및 코루틴은 여러 면에서 유사하므로 많은 혼란을 야기합니다.파이버 스레드와 그린 스레드의 차이점은 전자는 협력적인 멀티태스킹을 사용하는 반면 후자는 협력적이거나 선점적인 스레드(또는 둘 다)를 특징으로 할 수 있다는 것입니다.파이버와 코루틴의 차이점은 다음을 참조하세요. 여기.

어쨌든 Fiber, Green Thread, Coroutine의 목적은 여러 기능을 동시에 실행하는 것입니다. ~ 아니다 병렬로 (참조 이 SO 질문 구별을 위해) 단일 OS 수준 스레드 내에서 조직적인 방식으로 서로 제어를 주고받습니다.

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

내가 "라고 말한 것에 주목하세요.대개 기능마다 별도의 스택이 있습니다."둘 다 있어요 쌓인 그리고 스택리스 쿠루틴 구현.가장 주목할만한 스택형 C++ 구현은 다음과 같습니다. 부스트.코루틴 그리고 마이크로소프트 PPL'에스 async/await.(단, C++에서는 재개 가능한 기능 (일명"async 그리고 await")는 C++17에 제안된 스택리스 코루틴을 사용할 가능성이 높습니다.)

C++ 표준 라이브러리에 대한 Fibers 제안이 곧 나올 예정입니다.또한 일부 타사도 있습니다. 도서관.녹색 스레드는 Python 및 Ruby와 같은 언어에서 매우 인기가 있습니다.

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

스택

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

더미

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

흥미로운 참고사항:

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

많은 답변이 개념적으로는 정확하지만 하드웨어에는 스택이 필요하다는 점에 유의해야 합니다(예:마이크로프로세서)를 사용하여 서브루틴 호출(어셈블리 언어의 CALL..)을 허용합니다.(OOP 사람들이 부를 거야 행동 양식)

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

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

  • 스택 없음 아니요 마이크로프로세서가 작동할 수 있습니다.(어셈블리 언어에서도 서브루틴/함수가 없는 프로그램은 상상할 수 없습니다)
  • 힙이 없어도 가능합니다.(힙은 OS 개념이므로 어셈블리 언어 프로그램은 OS/Lib 호출인 malloc 없이도 작동할 수 있습니다.

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

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

우와!답변이 너무 많아서 그중 하나도 정답을 맞추지 못한 것 같습니다...

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

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top