스택 사용량이 너무 많습니까?


22

최근에 C 또는 C ++를 작성할 때 Java와 달리 옵션이기 때문에 스택에 모든 변수를 선언합니다.

그러나 스택에 큰 것을 선언하는 것은 나쁜 생각이라고 들었습니다.

  1. 왜 이럴까요? 스택 오버플로가 관련되어 있다고 생각하지만 그 이유가 명확하지 않습니다.
  2. 스택의 물건이 너무 많습니까?

100MB 파일을 스택에 넣지 않고 문자열 버퍼 또는 기타로 사용할 수십 킬로바이트 배열을 사용하려고합니다. 스택 사용량이 너무 많습니까?

(중복 된 경우 스택을 검색하면 스택 오버플로에 대한 참조가 계속 발생합니다. 호출 스택 태그조차 없으며 추상 태그 만 사용했습니다.)


1
"스택에 100MB 파일을 넣는 방법"은 무엇입니까? 버퍼 및 컨테이너 구현 (및 std :: string과 유사)은 일반적으로 힙을 사용하여 페이로드를 저장합니다.
머피

2
재귀가 포함될 때까지 함수 / 방법 당 상당한 양의 스택 사용을 피할 수 있습니다. 그런 다음 재귀 깊이에 대한 기능을 심각하게 제한 할 수 있으므로 재귀 함수 내에서 작은 로컬을 사용하고 싶습니다. 가능한 가변 / 스택 공간.
Erik Eidt

3
C & C ++는 다릅니다. 로컬 std::vector<int>변수는 많은 스택 공간을 차지하지 않으며 대부분의 데이터는 힙에 있습니다.
Basile Starynkevitch

답변:


18

운영 체제에 따라 다릅니다. Windows에서 스택의 일반적인 최대 크기는 1MB이고 일반적인 현대 Linux에서는 8MB이지만 다양한 값으로 조정할 수 있습니다. 전체 호출 스택에서 스택 변수 (반환 주소, 스택 기반 인수, 반환 값 자리 표시 자 및 정렬 바이트와 같은 하위 수준 오버 헤드 포함)의 합계가 해당 제한을 초과하면 스택 오버플로가 발생하여 일반적으로 복구 기회없이 프로그램.

몇 킬로바이트는 보통 괜찮습니다. 수십 킬로바이트는 요약하기 때문에 위험합니다. 수백 킬로바이트는 매우 나쁜 생각입니다.


1
2016 년에 전형적인 스택이 오늘날 메가 바이트 (즉, 보통 1 개 이상, 아마도 12 개 미만)를 제한하지 않습니까? 리눅스 데스크탑에서는 기본적으로
8MB입니다

"에 [...] 리눅스는 스택의 일반적인 최대 크기는 1MB입니다은" $ ulimit -a다른 사람의 사이에서 내 시스템 수익률 stack size (kbytes, -s) 8192.
머피

9

유일하게 유효한 답변은 "스택이 넘칠 때"라는 모호한 것입니다.

프로그램의 진입 점과 해당 함수 사이의 모든 코드 줄 구현을 완전히 제어하지 않으면 사용 가능한 스택 양에 대한 가정을 할 수 없습니다. 예를 들어이 함수를 호출해도 스택 오버플로가 발생하지 않는다고 보장 할 수는 없습니다.

void break_the_camels_back()
{
    int straw;
    ...
}

현대 유닉스의 기본 8 MiB 스택은 스택이 갈 때 상당히 많은 공간을 차지합니다. 특히 8 비트 스택 포인터가있는 CPU를 기억할 수있는 괴짜가있는 사람에게는 특히 그렇습니다. 실질적인 현실은 시도하지 않고 그것을 날려 버릴 가능성이 없다는 것입니다. 만약 그렇다면, 스택 제한을 초과하는 것은 일반적으로 세그먼트 위반으로 간주되며, 메모리 관리가 충분한 시스템은이를 감지하여 SIGSEGV발생시이를 전송 합니다.

몇 가지 옵션이 있습니다. 먼저 사용 가능한 스택 양을 추측하지 말고 시스템에 문의하십시오. POSIX를 준수하는 모든 getrlimit(2)것에는 상한을 알려주 는 기능이 있습니다. RLIMIT_STACK원하는 특정 제한입니다. 두 번째는 프로그램이 얼마나 많은 스택을 사용하는지 모니터링하고이를 기반으로 자동 변수 대 동적 메모리 할당을 결정하는 것입니다. 내가 아는 한, 얼마나 많은 스택이 사용되는지를 결정하는 표준 함수는 없지만, 같은 프로그램이 스택 valgrind을 분석 할 수 있습니다.


4

스택에 10,000 바이트의 배열을 할당하면 해당 배열의 크기가 제한됩니다. 10,000은 많을 수 있지만 10,001 바이트가 필요한 경우 프로그램이 충돌하거나 더 악화 될 수 있습니다. 따라서이 상황에서는 필요한 크기에 맞는 무언가를 원하고 무언가가 쌓이지 않아야합니다.

스택의 문자열 버퍼에 대한 고정 크기 배열은 스택에 메모리를 유지하기 때문에 문제가되지 않습니다. 고정 크기 버퍼는 발생하기를 기다리는 치명적인 문제이기 때문에 문제가됩니다.

그러나 C ++을 사용하고 스택에 std :: string 또는 std :: vec를 선언하면 스택의 내용은 실제로 고정되고 작은 크기입니다. 실제 데이터는 힙에 저장됩니다. std :: string 인스턴스에 백만 개의 문자를 저장할 수 있으며 스택에는 매우 적은 양의 데이터 (일반적으로 구현에 따라 8-24 바이트)와 힙에는 백만 바이트가 필요합니다.


2

* nix의 경우 1MB가 적당합니다. 재귀는 스택 할당과 함께 스택 오버플로의 주요 원인 일 수 있습니다. 그러나 대부분의 경우 스택에 놓기에는 너무 큰 것처럼 보이는 신 객체는 힙에서 내부 메모리를 관리하고 스택이 튀어 나올 때 자동으로 소멸되는 방법으로 만 스택을 사용하도록 설계되었습니다. 소멸자는 내부적으로 관리되는 엄청난 양의 메모리를 해제합니다. std 컨테이너는 그렇게 설계되었으며 공유 / 고유 포인터도 그렇게 설계되었습니다.

중요한 것은 char [1024 * 1024]와 같이 스택에 큰 원시 mem 덩어리를 할당하지 않고 힙 할당을 래핑하고 소멸자를 자동으로 호출하는 편의를 위해서만 스택을 사용하도록 클래스를 설계하는 것입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.