C #의 스택 크기가 정확히 1MB 인 이유는 무엇입니까?


102

오늘날의 PC에는 많은 양의 물리적 RAM이 있지만 여전히 C #의 스택 크기는 32 비트 프로세스의 경우 1MB, 64 비트 프로세스의 경우 4MB입니다 ( C #의 스택 용량 ).

CLR의 스택 크기가 여전히 제한적인 이유는 무엇입니까?

2MB 또는 512KB가 아닌 정확히 1MB (4MB) 인 이유는 무엇입니까? 이 금액을 사용하기로 결정한 이유는 무엇입니까?

나는 그 결정이면의 고려 사항과 이유에 관심이 있습니다.


6
64 비트 프로세스의 기본 스택 크기는 4MB이고 32 비트 프로세스의 경우 1MB입니다. PE 헤더의 값을 변경하여 메인 스레드 스택 크기를 수정할 수 있습니다 . Thread생성자 의 올바른 오버로드를 사용하여 스택 크기를 지정할 수도 있습니다 . 그러나 이것은 왜 더 큰 스택이 필요합니까?
Yuval Itzchakov 2015

2
감사합니다. :) 문제는 더 큰 스택 크기를 사용하는 방법이 아니라 스택 크기가 1MB (4MB)로 결정된 이유 입니다.
Nikolay Kostov 2015

8
각 스레드는 기본적으로이 스택 크기를 가지며 대부분의 스레드는 그다지 많이 필요하지 않기 때문입니다. 방금 내 PC를 부팅했고 시스템은 현재 1200 스레드를 실행합니다. 이제 수학을하세요;)
Lucas Trzesniewski

2
@LucasTrzesniewski 그뿐만 아니라 기억에 전염성이 있어야합니다 . 스택 크기가 클수록 프로세스가 가상 주소 공간에 생성 할 수있는 스레드 수가 적습니다.
Yuval Itzchakov 2015

"정확히"1MB에 대해 확실하지 않음 : 내 Windows 8.1에서 .NET Core 3.1 콘솔 응용 프로그램에는 1572864바이트 기본 스택 크기가 있습니다 (GetCurrentThreadStackLimits Win32 API를 사용하여 검색 됨). StackOverflowException없이 stackalloc대략 1500000바이트 수 있습니다.
George Chakhidze

답변:


210

여기에 이미지 설명 입력

당신은 그 선택을 한 사람을보고 있습니다. David Cutler와 그의 팀은 기본 스택 크기로 1MB를 선택했습니다. .NET 또는 C #과는 관련이 없습니다. 이것은 그들이 Windows NT를 만들 때 정해졌습니다. 1 메가 바이트는 프로그램의 EXE 헤더 또는 CreateThread () winapi 호출이 스택 크기를 명시 적으로 지정하지 않을 때 선택하는 것입니다. 이것이 일반적인 방법이며 거의 모든 프로그래머가 OS를두고 크기를 선택합니다.

그 선택은 아마도 Windows NT 디자인보다 이전 일 것입니다. 역사는 이것에 대해 너무 모호합니다. Cutler가 그것에 관한 책을 쓰면 좋을 것입니다. 그러나 그는 작가가 된 적이 없습니다. 그는 컴퓨터 작동 방식에 엄청난 영향을 미쳤습니다. 그의 첫 번째 OS 디자인은 DEC 컴퓨터 용 16 비트 운영 체제 인 RSX-11M (Digital Equipment Corporation)이었습니다. 이는 8 비트 마이크로 프로세서를위한 최초의 괜찮은 OS 인 Gary Kildall의 CP / M에 큰 영향을 미쳤습니다. MS-DOS에 큰 영향을 미쳤습니다.

그의 다음 디자인은 가상 메모리를 지원하는 32 비트 프로세서 용 운영 체제 인 VMS였습니다. 매우 성공적입니다. 그의 다음 것은 회사가 해체되기 시작할 무렵 DEC에 의해 취소되어 값싼 PC 하드웨어와 경쟁 할 수 없었습니다. Cue Microsoft, 그들은 그에게 거절 할 수없는 제안을했습니다. 그의 동료들도 많이 합류했습니다. 그들은 Windows NT로 더 잘 알려진 VMS v2에서 작업했습니다. DEC는 그것에 대해 화를 냈고 돈은 그것을 해결하기 위해 손을 바 꾸었습니다. VMS가 이미 1 메가 바이트를 선택했는지 여부는 알 수 없지만 RSX-11 만 잘 알고 있습니다. 그럴 것 같지 않습니다.

충분한 역사. 1 메가 바이트는 많고 실제 스레드는 몇 킬로바이트 이상을 거의 사용하지 않습니다. 따라서 메가 바이트는 실제로 다소 낭비입니다. 그러나 수요 페이징 가상 메모리 운영 체제에서 감당할 수있는 낭비 유형입니다. 메가 바이트는 가상 메모리 입니다. 프로세서에는 4096 바이트마다 하나씩 숫자 만 표시됩니다. 실제로 주소를 지정할 때까지 실제 메모리, 컴퓨터의 RAM을 실제로 사용하지 않습니다.

원래 기본 프로그램을 수용하기 위해 1MB 크기가 선택 되었기 때문에 .NET 프로그램에서는 과도합니다. 큰 스택 프레임을 생성하고 스택에 문자열과 버퍼 (배열)를 저장하는 경향이 있습니다. 멀웨어 공격 벡터로 악명 높은 버퍼 오버플로는 데이터로 프로그램을 조작 할 수 있습니다. .NET 프로그램이 작동하는 방식이 아니라 문자열과 배열이 GC 힙에 할당되고 인덱싱이 확인됩니다. C #을 사용하여 스택에 공간을 할당하는 유일한 방법은 안전하지 않은 stackalloc 키워드를 사용하는 것입니다.

.NET에서 스택의 중요한 용도는 지터뿐입니다. 스레드 스택을 사용하여 MSIL을 기계 코드로 적시에 컴파일합니다. 필요한 공간을 확인하거나 확인한 적이 없습니다. 코드의 특성과 최적화 프로그램의 활성화 여부에 따라 다르지만 수십 킬로바이트는 대략적인 추측입니다. 그렇지 않으면이 웹 사이트가 이름을 얻은 방법입니다. .NET 프로그램의 스택 오버플로는 매우 치명적입니다. 예외를 포착하려는 코드를 안정적으로 JIT하기에 충분한 공간 (3KB 미만)이 남아 있지 않습니다. Kaboom to desktop이 유일한 옵션입니다.

마지막으로, .NET 프로그램은 스택에서 매우 비생산적인 작업을 수행합니다. CLR은 스레드 스택을 커밋 합니다. 이는 스택의 크기를 예약하는 것이 아니라 필요할 때 스택을 항상 교체 할 수 있도록 운영 체제의 페이징 파일에 공간이 예약되어 있는지 확인하는 값 비싼 단어입니다. 커밋에 실패하면 치명적인 오류가 발생하며 프로그램이 무조건 종료됩니다. 이는 완전히 너무 많은 프로세스를 실행하는 RAM이 매우 적은 기계에서만 발생하며, 그러한 기계는 프로그램이 죽기 전에 당밀로 변했을 것입니다. 15 년 이상 전, 오늘이 아닌 가능한 문제. F1 경주 용 자동차처럼 작동하도록 프로그램을 조정하는 프로그래머는<disableCommitThreadStack> .config 파일 요소를 합니다.

Fwiw, Cutler는 운영 체제 설계를 중단하지 않았습니다. 그 사진은 그가 Azure에서 작업하는 동안 만들어졌습니다.


업데이트, .NET이 더 이상 스택을 커밋하지 않는다는 것을 알았습니다. 언제, 왜 이런 일이 발생했는지 정확히 모르겠지만 확인한 지 너무 오래되었습니다. 이 디자인 변경은 .NET 4.5 주변에서 발생했다고 생각합니다. 꽤 현명한 변화.


3
귀하의 의견에 wrt- The only way to allocate space on the stack with C# is with the unsafe stackalloc keyword.지역 변수가 예를 들어 int스택에 저장되지 않은 메서드 내에서 선언 되었습니까? 나는 그들이 생각한다.
RBT

2
확인. 이제 스택 프레임이 함수의 로컬 변수를위한 저장소의 유일한 선택이 아니라는 것을 알았습니다. 그것은 할 수 있습니다 귀하의 글 머리 기호 중 하나를 제안하지만 스택 프레임에 저장됩니다. 매우 깨달은 한스. 이렇게 통찰력있는 글을 써 주셔서 감사합니다. 솔직히 스택은 불필요한 복잡성을 피하기 위해 일반적으로 프로그래밍에 대한 큰 추상화입니다.
RBT

매우 상세한 설명 @Hans. maxStackSize스레드의 가능한 최소값이 무엇인지 궁금 합니다. [MSDN] ( msdn.microsoft.com/en-us/library/5cykbwz4(v=vs.110).aspx ) 에서 찾을 수 없습니다 . 귀하의 의견에 따르면 스택 사용량은 절대 최소이며 가능한 최대 스레드를 수용하기 위해 가장 작은 값을 사용할 수 있습니다. 감사.
MKR

1
@KFL : 질문을 시도해 보면 쉽게 대답 할 수 있습니다!
Eric Lippert 2018

1
기본 동작에 경우 더 이상 스택을 커밋,이 가격 인하 파일을 편집 할 필요가 없다 github.com/dotnet/docs/blob/master/docs/framework/...
존 Stewien

5

기본 예약 스택 크기는 링커에 의해 지정되며 개발자가 링크시 PE 값을 변경하거나 WinAPI 함수에 dwStackSize대한 매개 변수를 지정하여 개별 스레드에 대해 재정의 할 수 있습니다 CreateThread.

초기 스택 크기가 기본 스택 크기보다 크거나 같은 스레드를 생성하면 가장 가까운 1MB의 배수로 반올림됩니다.

값이 32 비트 프로세스의 경우 1MB이고 64 비트의 경우 4MB 인 이유는 무엇입니까? Windows를 설계 한 개발자에게 물어 보거나 누군가가 질문에 답할 때까지 기다려야한다고 생각합니다.

아마도 Mark Russinovich는 그것을 알고 당신은 그에게 연락 할 수 있습니다 . 이 정보는 그의 기사 보다는 스택에 대한 정보가 적은 6 판 이전의 Windows 내부 책에서 찾을 수 있습니다 . 아니면 Raymond Chen이 Windows 내부와 그 역사에 대해 흥미로운 내용을 작성했기 때문에 이유를 알고있을 수도 있습니다. 그는 귀하의 질문에 답할 수 있지만 제안 상자에 제안을 게시해야합니다 .

그러나 지금은 Microsoft가 MSDN, Mark 및 Raymond의 블로그를 사용하여 이러한 값을 선택한 몇 가지 가능한 이유를 설명하려고합니다.

초기에는 PC가 느리고 스택에 메모리를 할당하는 것이 힙에 메모리를 할당하는 것보다 훨씬 빠르기 때문에 기본값이 이러한 값을 가질 수 있습니다. 그리고 스택 할당이 훨씬 저렴했기 때문에 사용되었지만 더 큰 스택 크기가 필요했습니다.

따라서 값은 대부분의 애플리케이션에 대해 최적의 예약 된 스택 크기였습니다. 많은 중첩 호출을 수행하고 스택에 메모리를 할당하여 구조를 호출 함수에 전달할 수 있기 때문에 최적입니다. 동시에 많은 스레드를 생성 할 수 있습니다.

요즘에는 WinAPI 함수에 매개 변수로 전달되는 구조가 여전히 스택에 할당되기 때문에 이러한 값은 대부분 이전 버전과의 호환성을 위해 사용됩니다. 그러나 스택 할당을 사용하지 않는 경우 스레드의 스택 사용량은 기본 1MB보다 훨씬 적으며 Hans Passant가 언급했듯이 낭비입니다. 그리고이를 방지하기 위해 OS는 애플리케이션의 PE 헤더에 다른 항목이 지정되지 않은 경우 스택의 첫 페이지 (4KB) 만 커밋합니다. 다른 페이지는 요청시 할당됩니다.

일부 응용 프로그램은 예약 된 주소 공간을 무시하고 처음에는 메모리 사용을 최적화하기 위해 커밋됩니다. 예를 들어 IIS 네이티브 프로세스 스레드의 최대 스택 크기는 256KB ( KB932909 )입니다. Microsoft에서는 다음 과 같이 기본값을 줄이는 것이 좋습니다 .

가능한 한 작은 스택 크기를 선택하고 스레드 또는 파이버가 안정적으로 실행되는 데 필요한 스택을 커밋하는 것이 가장 좋습니다. 스택 용으로 예약 된 모든 페이지는 다른 용도로 사용할 수 없습니다.

출처 :

  1. 스레드 스택 크기 (Microsoft Docs)
  2. Windows의 한계 극복 : 프로세스 및 스레드 (Mark Russinovich)
  3. 기본적으로 기본 IIS 프로세스에서 생성되는 스레드의 최대 스택 크기는 256KB (KB932909)입니다.

더 큰 스택 크기를 원하는 경우 설정할 수 있습니다 ( atalasoft.com/cs/blogs/rickm/archive/2008/04/22/… ). 그 결정의 고려 사항과 이유를 알고 싶습니다.
Nikolay Kostov 2015

2
괜찮아. 이제 이해합니다 :) 기본 스택 크기는 최적이어야하며 (@Lucas Trzesniewski 주석 참조) 할당 단위의 가장 가까운 배수로 반올림되어야합니다. 지정된 스택 크기가 기본 스택 크기보다 크면 가장 가까운 1MB의 배수로 반올림됩니다. 따라서 Microsoft는이 크기를 모든 사용자 모드 응용 프로그램의 기본 스택 크기로 선택했습니다. 그리고 다른 이유는 없습니다.
Yoh Deadfall 2015

출처가 있습니까? 문서가 있습니까? :)
Nikolay Kostov 2015

@Yoh 흥미로운 링크. 당신은 그것을 당신의 대답에 요약해야합니다.
Lucas Trzesniewski
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.