힙에 메모리를 할당 할 때 유일한 제한은 여유 RAM (또는 가상 메모리)입니다. 그것은 Gb의 메모리를 만듭니다.
그렇다면 스택 크기가 왜 그렇게 제한되어 있습니까 (약 1Mb)? 스택에 정말 큰 개체를 만들지 못하게하는 기술적 이유는 무엇입니까?
업데이트 : 내 의도 힘이 명확하지 않을, 내가 원하지 않는 스택에 큰 개체를 할당하고, 내가 필요하지 않습니다 더 큰 스택을. 이 질문은 순수한 호기심입니다.
힙에 메모리를 할당 할 때 유일한 제한은 여유 RAM (또는 가상 메모리)입니다. 그것은 Gb의 메모리를 만듭니다.
그렇다면 스택 크기가 왜 그렇게 제한되어 있습니까 (약 1Mb)? 스택에 정말 큰 개체를 만들지 못하게하는 기술적 이유는 무엇입니까?
업데이트 : 내 의도 힘이 명확하지 않을, 내가 원하지 않는 스택에 큰 개체를 할당하고, 내가 필요하지 않습니다 더 큰 스택을. 이 질문은 순수한 호기심입니다.
답변:
내 직감은 다음과 같습니다. 스택은 힙만큼 관리하기 쉽지 않습니다. 스택은 연속적인 메모리 위치에 저장되어야합니다. 즉, 필요에 따라 스택을 무작위로 할당 할 수 없지만 최소한 해당 목적을 위해 가상 주소를 예약해야합니다. 예약 된 가상 주소 공간의 크기가 클수록 만들 수있는 스레드 수가 줄어 듭니다.
예를 들어, 32 비트 애플리케이션은 일반적으로 2GB의 가상 주소 공간을 갖습니다. 즉, 스택 크기가 2MB (pthreads의 기본값)이면 최대 1024 개의 스레드를 만들 수 있습니다. 이것은 웹 서버와 같은 애플리케이션의 경우 작을 수 있습니다. 예를 들어 스택 크기를 100MB로 늘리면 (즉, 100MB를 예약하지만 반드시 스택에 100MB를 즉시 할당 할 필요는 없음) 스레드 수가 약 20 개로 제한되어 단순한 GUI 응용 프로그램에서도 제한 될 수 있습니다.
흥미로운 질문은 왜 우리가 64 비트 플랫폼에서 여전히 이러한 제한을 가지고 있는지입니다. 답은 모르겠지만 사람들이 이미 몇 가지 "스택 모범 사례"에 익숙하다고 가정합니다. 힙에 거대한 개체를 할당하도록주의하고 필요한 경우 스택 크기를 수동으로 늘립니다. 따라서 아무도 64 비트 플랫폼에서 "대용량"스택 지원을 추가하는 것이 유용하지 않다고 생각했습니다.
아직 아무도 언급하지 않은 한 가지 측면 :
제한된 스택 크기는 오류 감지 및 억제 메커니즘입니다.
일반적으로 C 및 C ++에서 스택의 주요 작업은 호출 스택 및 로컬 변수를 추적하는 것이며 스택이 범위를 벗어나면 거의 항상 설계 및 / 또는 응용 프로그램 동작의 오류입니다. .
스택이 임의로 커질 수있는 경우 이러한 오류 (예 : 무한 재귀)는 운영 체제 리소스가 고갈 된 후에 만 매우 늦게 포착됩니다. 이는 스택 크기에 임의의 제한을 설정함으로써 방지됩니다. 실제 크기는 시스템 성능 저하를 방지 할 수있을만큼 작다는 점을 제외하면 그다지 중요하지 않습니다.
std::unique_ptr
소멸자를 작성하기 위해 (장난감) 목록 구현에 대한 언급의 수에 유의하십시오 ( 스마트 포인터에 의존하지 않음)).
기본 크기입니다. 더 많이 필요하면 링커에 추가 스택 공간을 할당하도록 지시하여 더 많이 얻을 수 있습니다.
큰 스택의 단점은 스레드를 여러 개 만들면 각각 하나의 스택이 필요하다는 것입니다. 모든 스택이 다중 MB를 할당하지만 사용하지 않는 경우 공간이 낭비됩니다.
프로그램에 적합한 균형을 찾아야합니다.
@BJovke와 같은 일부 사람들은 가상 메모리가 본질적으로 무료라고 믿습니다. 모든 가상 메모리를 지원하는 물리적 메모리가 필요하지 않다는 것은 사실입니다. 적어도 가상 메모리에 주소를 제공 할 수 있어야합니다.
그러나 일반적인 32 비트 PC에서 가상 메모리의 크기는 실제 메모리의 크기와 동일합니다. 가상이든 아니든 모든 주소에 대해 32 비트 만 있기 때문입니다.
프로세스의 모든 스레드가 동일한 주소 공간을 공유하기 때문에 스레드간에 분할해야합니다. 그리고 운영 체제가 그 역할을 맡은 후에는 응용 프로그램에 "만"2-3GB가 남습니다. 그리고 그 크기는 더 이상 주소가 없기 때문에 물리적 메모리 와 가상 메모리 모두에 대한 제한입니다 .
우선 스택은 연속적이므로 12MB를 할당하면 생성 한 항목보다 아래로 이동하려면 12MB를 제거해야합니다. 또한 움직이는 물체가 훨씬 더 어려워집니다. 다음은 이해하기 쉽게 만드는 실제 사례입니다.
방 주위에 상자를 쌓고 있다고 가정 해 보겠습니다. 관리가 더 쉬운 방법 :
이 두 가지 예는 전체적인 일반화이며 비유에서 노골적으로 잘못된 몇 가지 점이 있지만 두 경우 모두에서 이점을 보는 데 도움이되기를 바랍니다.
근거리에서 원거리 순으로 스택을 생각하십시오. 레지스터는 CPU에 가깝고 (빠름) 스택은 조금 더 멀고 (하지만 상대적으로 가까움) 힙은 멀리 떨어져 있습니다 (느린 액세스).
스택은 물론 힙에 있지만 계속 사용되기 때문에 CPU 캐시를 떠나지 않아 평균 힙 액세스보다 빠릅니다. 이것이 스택 크기를 합리적으로 유지해야하는 이유입니다. 가능한 한 많이 캐시 된 상태로 유지합니다. 큰 스택 객체 할당 (오버플로가 발생하면 스택 크기 자동 조정)은이 원칙에 위배됩니다.
따라서 예전에서 남은 것이 아니라 성능에 대한 좋은 패러다임입니다.
큰 스택이 필요하다고 생각하는 많은 작업은 다른 방법으로 수행 할 수 있습니다.
Sedgewick의 "Algorithms"에는 재귀를 반복으로 대체하여 QuickSort와 같은 재귀 알고리즘에서 재귀를 "제거"하는 몇 가지 좋은 예가 있습니다. 실제로 알고리즘은 여전히 재귀 적이며 여전히 스택으로 존재하지만 런타임 스택을 사용하는 대신 힙에 정렬 스택을 할당합니다.
(파스칼로 제공된 알고리즘을 사용하는 두 번째 버전을 선호합니다. 8 달러에 사용할 수 있습니다.)
그것을 보는 또 다른 방법은 큰 스택이 필요하다고 생각하면 코드가 비효율적이라는 것입니다. 더 적은 스택을 사용하는 더 좋은 방법이 있습니다.
기술적 인 이유가 없다고 생각하지만 스택에 하나의 거대한 슈퍼 오브젝트 만 생성 한 이상한 앱일 것입니다. 스택 객체는 크기가 증가함에 따라 더 문제가되는 유연성이 부족합니다. 객체를 파괴하지 않고는 반환 할 수 없으며 다른 스레드에 대기열에 넣을 수 없습니다.
int main() { char buffer[1048576]; }
매우 흔한 초보자 문제입니다. 물론 쉬운 해결 방법이 있지만 스택 크기를 해결해야하는 이유는 무엇입니까?