etc./StackOverFlow

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

청렴결백한 만능 재주꾼 2021. 9. 25. 02:25
반응형

질문자 :mattshane


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

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


답변자 : Jeff Hill


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

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

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

질문에 직접 답변하려면:

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

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

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

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

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

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

무엇이 더 빨라지나요?

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

명확한 시연:
이미지 출처: vikashazrati.wordpress.com



답변자 : Brian R. Bondy


스택:

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


답변자 : thomasrutter


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

  • 항목 더미에서 항목은 배치된 순서대로 다른 항목 위에 하나씩 놓이며 맨 위에 있는 항목만 제거할 수 있습니다(전체를 넘어뜨리지 않고).

    종이 더미처럼 쌓기

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

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

    온갖 감초 더미 같은 더미

    힙 할당에는 할당된 메모리와 할당되지 않은 메모리에 대한 전체 레코드를 유지 관리해야 하며 단편화를 줄이고 요청된 크기에 맞도록 충분히 큰 연속 메모리 세그먼트를 찾는 등의 약간의 오버헤드 유지 관리가 필요합니다. 메모리는 여유 공간을 남겨두고 언제든지 할당을 취소할 수 있습니다. 때때로 메모리 할당자는 할당된 메모리를 이동하여 메모리 조각 모음 또는 가비지 수집과 같은 유지 관리 작업을 수행합니다.

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

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

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

    힙은 동적으로 무작위로 할당되는 모든 메모리에 사용되는 일반적인 용어입니다. 즉, 고장. 메모리는 일반적으로 OS에 의해 할당되며 애플리케이션은 이 할당을 수행하기 위해 API 함수를 호출합니다. 동적으로 할당된 메모리를 관리하는 데 상당한 오버헤드가 필요하며, 이는 일반적으로 사용되는 프로그래밍 언어 또는 환경의 런타임 코드에서 처리됩니다.

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

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

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

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

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

  • 무엇이 더 빨라지나요?

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



답변자 : Martin Liversage


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

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

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

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

힙

스택

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

스택

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

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

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

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



답변자 : Snowcrash


다음 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

그러나 일부 부정확한 내용이 포함될 수 있습니다.



답변자 : Tom Leys


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

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

힙은 생성한 데이터를 즉석에서 저장하는 일반적인 이름입니다. 프로그램에서 몇 개의 우주선을 만들지 모르는 경우 새(또는 malloc 또는 이와 동등한) 연산자를 사용하여 각 우주선을 만들 수 있습니다. 이 할당은 한동안 유지될 것이므로 생성한 것과 다른 순서로 항목을 해제할 가능성이 높습니다.

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

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

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

메모리의 물리적 위치 이것은 가상 메모리 라는 기술로 인해 프로그램이 물리적 데이터가 다른 곳에 있는 특정 주소에 액세스할 수 있다고 생각하게 하는 기술 때문에 생각하는 것보다 관련성이 낮습니다(하드 디스크에도 있음). 스택에 대해 얻는 주소는 호출 트리가 깊어질수록 오름차순입니다. 힙의 주소는 예측할 수 없으며(즉, 특정 구현) 솔직히 중요하지 않습니다.



답변자 : Community Wiki


다른 답변은 정적 할당이 무엇을 의미하는지 설명하지 않습니다. 그래서 나는 할당의 세 가지 주요 형태와 그것들이 일반적으로 아래에서 힙, 스택 및 데이터 세그먼트와 어떻게 관련되는지 설명할 것입니다. 또한 사람들이 이해하는 데 도움이 되도록 C/C++ 및 Python으로 몇 가지 예를 보여 드리겠습니다.

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

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

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

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

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

이 모든 것을 설명하기 위해 주석이 달린 간단한 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"). 이러한 변수는 일반적이지만 비공식적인 명명 습관을 매우 혼란스럽게 만들 수 있습니다. 예를 들어 " 로컬 " 이라고 하면 일반적으로 " 로컬 범위에서 자동으로 할당된 변수 " 를 의미하고 전역 이라고 하면 일반적으로 " 전역 범위에서 정적으로 할당된 변수 " 를 의미합니다 . 불행히도 "파일 범위 정적으로 할당된 변수 "와 같은 문제에 관해서는 많은 사람들이 " 응??? "이라고 말합니다.

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


답변자 : Don Neufeld


다른 사람들은 광범위한 스트로크에 꽤 잘 대답했으므로 몇 가지 세부 사항을 던질 것입니다.

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

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

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

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


답변자 : bk1e


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

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

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



답변자 : Daniel Papasian


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

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

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

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



답변자 : Shreyos Adikari


스택이란 무엇입니까?

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

여기에 이미지 설명 입력

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

힙이란?

무더기는 아무렇게나 쌓여 있는 어수선한 물건들의 집합체이다.

여기에 이미지 설명 입력

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

양자

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

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

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

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

자바 메모리 모델

여기에 이미지 설명 입력

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



답변자 : Heath


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

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



답변자 : Peter


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

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



답변자 : T.E.D.


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

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

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



답변자 : devXen


WikiAnwser에서.

스택

함수나 메서드가 다른 함수를 호출하여 차례로 다른 함수를 호출하는 경우 등 모든 함수의 실행은 맨 마지막 함수가 값을 반환할 때까지 일시 중단된 상태로 유지됩니다.

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

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

더미

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



답변자 : unknown


스택

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

더미

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


답변자 : Abrar Jahin


간단히 말해서

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


상세히

스택

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

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

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


더 힙

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

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

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

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

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


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

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

여기에 이미지 설명 입력

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

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

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

여기에 이미지 설명 입력

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

더 자세한 내용은 여기여기 에 나와 있습니다.


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

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

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

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

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

이미 상단에 주어졌습니다.

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

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

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

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

무엇이 더 빨라지나요?

스택 할당은 실제로 스택 포인터를 이동하는 것뿐이므로 훨씬 빠릅니다. 메모리 풀을 사용하면 힙 할당에서 비슷한 성능을 얻을 수 있지만 약간의 복잡성이 추가되고 문제가 발생합니다.

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

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



답변자 : Alireza


좋아, 간단하고 간단히 말해서 주문 하고 주문하지 않은 것을 의미합니다 ...!

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

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

: 순서가 없고 처리 속도가 느려지고 특정 순서나 인덱스가 없으면 값이 엉망이 됩니다... 랜덤이 있고 그들 사이에 관계가 없습니다... 그래서 실행 및 사용 시간이 다를 수 있습니다...

또한 어떻게 보이는지 보여주기 위해 아래 이미지를 만듭니다.

여기에 이미지 설명 입력



답변자 : Yousha Aleayoub


가상 메모리에 있는 각 프로세스의 스택 , 데이터:

스택, 힙 및 정적 데이터



답변자 : jlettvin


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

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

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

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

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

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



답변자 : Maxim Akristiniy


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

이것은 일어나는 일을 더 쉽게 이해할 수 있도록 단순화한 프로세스 메모리 구성에 대한 나의 비전입니다.


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

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

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



답변자 : pkthapa


주요 요점은 이미 다루었지만 공유할 것이 있습니다.

스택

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

더미

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

흥미로운 참고 사항:

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


답변자 : shakurov


일부 답변이 엉뚱한 데로 갔기 때문에 나는 내 진드기에 기여할 것입니다.

놀랍게도, 아무도 다중(즉, 실행 중인 OS 수준 스레드의 수와 관련이 없는) 호출 스택이 이국적인 언어(PostScript) 또는 플랫폼(Intel Itanium)뿐만 아니라 fiber , 녹색 스레드에서도 발견될 것이라고 언급하지 않았습니다. 코루틴의 일부 구현.

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

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

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

" 일반적으로 기능당 별도의 스택이 있습니다"라고 말했습니다. 쿠루틴의 스택형 구현과 스택리스 구현이 모두 있습니다. 가장 주목할만한 스택형 C++ 구현은 Boost.CoroutineMicrosoft PPLasync/await 입니다. (그러나 C++17에 제안된 C++의 재개 가능한 함수 (일명 " async and await ")는 스택리스 코루틴을 사용할 가능성이 높습니다.)

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



답변자 : ar18


우와! 답변이 너무 많은데 그 중 하나가 정답이 아닌 것 같아요...

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



답변자 : ingconti


많은 답변이 개념으로서 정확하지만, 서브루틴 호출(어셈블리 언어의 CALL..)을 허용하려면 하드웨어(예: 마이크로프로세서)에서 스택이 필요하다는 점에 유의해야 합니다. (OOP 사람들은 그것을 메소드 라고 부를 것입니다)

스택에서 반환 주소를 저장하고 → push / ret → pop을 호출하면 하드웨어에서 직접 관리됩니다.

레지스터를 사용하는 것보다 느리더라도 스택을 사용하여 매개변수를 전달할 수 있습니다.

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

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

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


답변자 : nCardot


스택은 기본적으로 항목을 스택으로 관리하는 액세스하기 쉬운 메모리입니다. 크기가 미리 알려진 항목 만 스택에 들어갈 수 있습니다. 이것은 숫자, 문자열, 부울의 경우입니다.

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

출처: 아카데미마인드



답변자 : user12031933


CPU 스택과 힙은 물리적으로 CPU와 레지스터가 메모리에서 작동하는 방식, 기계 어셈블리 언어가 작동하는 방식과 관련이 있으며 이러한 언어가 작은 것을 결정할 수 있다고 해도 고급 언어 자체가 아닙니다.

모든 최신 CPU는 "동일한" 마이크로프로세서 이론으로 작동합니다. 모두 "레지스터"라고 하는 것을 기반으로 하고 일부는 "스택"이 성능을 얻기 위한 것입니다. 모든 CPU는 처음부터 스택 레지스터를 가지고 있으며 내가 아는 한 항상 여기에 있었습니다. 어셈블리 언어는 변형에도 불구하고 처음부터 동일합니다. OO 가상 머신 어셈블리 언어를 갖도록 패러다임을 변경한 Microsoft와 IL(Intermediate Language)까지. 따라서 우리는 미래에 일부 CLI/CIL CPU를 가질 수 있을 것입니다(MS의 한 프로젝트).

CPU에는 메모리 액세스 속도를 높이기 위한 스택 레지스터가 있지만 프로세서에 사용 가능한 모든 메모리에 대한 전체 액세스를 얻기 위해 다른 레지스터를 사용하는 것에 비해 제한적입니다. 이것이 스택 및 힙 할당에 대해 이야기한 이유입니다.

요약하면, 일반적으로 스택은 작고 빠르며 "로컬" 변수 및 참조(관리하는 것을 잊기 위한 숨겨진 포인터)에 대한 힙은 느리고 느리며 "전역" 인스턴스 및 개체 콘텐츠를 위한 것입니다.

따라서 메서드에서 new 키워드를 사용할 때 참조(int)는 스택에 생성되지만 객체와 모든 콘텐츠(객체뿐만 아니라 값 유형)는 힙에 생성됩니다. 그러나 로컬 기본 값 유형과 배열은 스택에 생성됩니다.

메모리 액세스의 차이점은 셀 참조 수준에 있습니다. 프로세스의 전체 메모리인 힙에 주소를 지정하는 것은 CPU 스택 때문에 주소 지정 측면에서 로컬로 "더 많은" 스택보다 CPU 레지스터 처리 측면에서 더 복잡합니다. 내가 기억한다면 레지스터는 기본 주소로 사용됩니다.

이것이 우리가 매우 길거나 무한한 재귀 호출이나 루프를 가질 때 현대 컴퓨터에서 시스템을 정지시키지 않고 스택 오버플로를 빠르게 얻는 이유입니다...

.NET의 C# 힙(ing) 대 스택(ing)

스택 대 힙: 차이점 알기

C#이 저장된 정적 클래스 메모리 할당

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

https://en.wikipedia.org/wiki/Memory_management

https://en.wikipedia.org/wiki/Stack_register

어셈블리 언어 리소스:

어셈블리 프로그래밍 튜토리얼

인텔® 64 및 IA-32 아키텍처 소프트웨어 개발자 매뉴얼



답변자 : aquagremlin


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

위의 모든 내용은 DATA에 대해 설명합니다. 내 생각에 명령어는 특정 메모리 풋프린트로 정의된 것이므로 스택으로 이동하므로 어셈블리에서 논의된 모든 '해당' 레지스터가 스택에 있습니다. 물론 명령과 데이터가 동적인 구조로 결합된 객체 지향 프로그래밍이 등장하여 이제 명령도 힙에 보관됩니까?



출처 : Here


출처 : http:www.stackoverflow.com/questions/79923/what-and-where-are-the-stack-and-heap">

반응형