문제

C와 C++가 스택에 큰 개체를 저장하는 방법을 알아내려고 합니다.일반적으로 스택은 정수 크기이므로 거기에 얼마나 큰 개체가 저장되는지 이해할 수 없습니다.단순히 여러 스택 "슬롯"을 차지합니까?

도움이 되었습니까?

해결책

스택은 메모리 조각입니다. 스택 포인터가 상단을 가리 킵니다. 스택에 값을 눌러 튀어 나와 튀어 나와 검색 할 수 있습니다.

예를 들어 두 매개 변수로 호출되는 함수가있는 경우 (1 바이트 크기와 다른 2 바이트 크기; 8 비트 PC가 있다고 가정).

둘 다 스택에 밀려 들어 스택 포인터를 위로 이동합니다.

03: par2 byte2
02: par2 byte1
01: par1

이제 기능이 호출되고 리턴 addres가 스택에 올려집니다.

05: ret byte2
04: ret byte1
03: par2 byte2
02: par2 byte1
01: par1

좋아, 함수 내에 2 개의 로컬 변수가 있습니다. 2 바이트 중 하나와 4 중 하나 중 하나가 스택에 예약되어 있지만 먼저 스택 포인터를 저장하므로 카운트 업으로 변수가 시작되는 위치를 알 수 있으며 매개 변수는 카운트 다운하여 찾을 수 있습니다.

11: var2 byte4
10: var2 byte3
09: var2 byte2
08: var2 byte1
07: var1 byte2
06: var1 byte1
    ---------
05: ret byte2
04: ret byte1
03: par2 byte2
02: par2 byte1
01: par1

보시다시피, 공간이 남아있는 한 스택에 아무것도 넣을 수 있습니다. 그렇지 않으면이 사이트에 이름을 부여하는 현상을 얻을 수 있습니다.

다른 팁


스택과 힙은 생각만큼 다르지 않습니다!


사실, 일부 운영 체제에는 스택 제한이 있습니다.(그 중 일부는 힙 제한도 심합니다!)

하지만 지금은 더 이상 1985년이 아닙니다.

요즘은 리눅스를 쓰고 있어요!

내 기본값 스택 크기 10MB로 제한됩니다.내 기본값 힙 크기 무제한입니다.스택 크기를 제한하는 것은 매우 간단합니다.(*기침* [tcsh] 스택 크기 무제한 *기침*.또는 설정한계().)

가장 큰 차이점은 스택 그리고 더미 이다:

  1. 스택 할당은 단지 포인터를 오프셋할 뿐입니다(스택이 충분히 커지면 새 메모리 페이지를 할당할 수도 있음). 더미 적합한 메모리 블록을 찾기 위해 데이터 구조를 검색해야 합니다.(그리고 새로운 메모리 페이지도 할당할 수도 있습니다.)
  2. 스택 현재 블록이 끝나면 범위를 벗어납니다. 더미 삭제/해제가 호출되면 범위를 벗어납니다.
  3. 더미 조각화될 수 있습니다. 스택 결코 단편화되지 않습니다.

리눅스에서는 둘 다 스택 그리고 더미 가상 메모리를 통해 관리됩니다.

할당 시간 측면에서 보면 심하게 조각난 메모리를 통한 힙 검색도 새 메모리 페이지 매핑에 도움이 되지 않습니다. 시간에 따른 차이는 무시할 수 있습니다!

OS에 따라 매핑된 새 메모리 페이지를 실제로 사용할 때만 발생하는 경우가 많습니다.(아니다malloc() 할당!) (이것은 게으른 평가 물건.)

(새로운 아마도 해당 메모리 페이지를 사용할 생성자를 호출할 것입니다...)


다음 중 하나에서 대형 객체를 생성하고 파괴하여 VM 시스템을 망칠 수 있습니다. 스택 아니면 그 더미.시스템에서 메모리를 회수할 수 있는지 여부는 OS/컴파일러에 따라 다릅니다.회수되지 않으면 힙에서 재사용할 수 있습니다.(다른 사람이 용도를 변경하지 않았다고 가정) malloc() 그 동안.) 마찬가지로 스택이 회수되지 않으면 재사용됩니다.

교체된 페이지는 다시 교체되어야 하며 이는 가장 큰 시간 적중이 될 것입니다.


이 모든 것 중에서, 메모리 조각화가 가장 걱정됩니다!

수명(범위를 벗어나는 경우)은 항상 결정적인 요소입니다.

그러나 장기간 프로그램을 실행하면 조각화로 인해 메모리 사용량이 점차 증가합니다.끊임없는 교환으로 인해 결국 나를 죽입니다!




다음을 추가하도록 수정됨:


이봐, 나 버릇없었어!

뭔가 여기에 추가되지 않았습니다 ...나는 *나*가 전혀 기본에서 벗어난 사람이라고 생각했습니다.아니면 다른 사람들도 그랬습니다.아니면 둘 다일 가능성이 더 높습니다.아니면 어쩌면 둘 다일 수도 있습니다.

대답이 무엇이든, 나는 무슨 일이 일어나고 있는지 알아야 했습니다!

...긴 시간이 걸릴 것 같습니다.참아주세요...


나는 지난 12년의 대부분을 Linux에서 작업해왔습니다.그리고 약 10년 전 다양한 유닉스 환경에서 말이죠.컴퓨터에 대한 나의 관점은 다소 편향되어 있습니다.나는 버릇없었다!

나는 Windows에 대해 약간의 작업을 수행했지만 권위있게 말하기에는 충분하지 않습니다.비극적으로 Mac OS/Darwin에서도 마찬가지입니다...Mac OS/Darwin/BSD는 내 지식 중 일부가 전달될 만큼 충분히 가깝습니다.


32비트 포인터를 사용하면 4GB(2^32)의 주소 공간이 부족해집니다.

실질적으로 말하자면, 스택+더미 합쳐진 것은 일반적으로 다른 항목을 매핑해야 하기 때문에 2~4GB 사이로 제한됩니다.

(공유 메모리, 공유 라이브러리, 메모리 매핑 파일, 실행 중인 실행 이미지 등이 항상 좋습니다.)


Linux/Unix/MacOS/Darwin/BSD에서는 인위적으로 더미 아니면 그 스택 런타임에 원하는 임의의 값으로.그러나 궁극적으로 엄격한 시스템 제한이 있습니다.

이것은 (tcsh에서) "한계""제한 -h".또는 (bash에서) "ulimit -Sa""ulimit -Ha".또는 프로그래밍 방식으로 rlim_currlim_max ~에 구조체 제한.


이제 우리는 재미있는 부분에 도달합니다.에 대하여 마틴 요크의 코드.(감사합니다 남자 이름!좋은 예입니다.항상 시도해 보는 것이 좋습니다!.)

마틴의 아마도 Mac에서 실행 중일 것입니다.(아주 최근의 일입니다.그의 컴파일러 빌드는 내 것보다 최신입니다!)

물론 그의 코드는 기본적으로 Mac에서 실행되지 않습니다.하지만 그가 처음 호출하면 잘 실행될 것입니다. "스택 크기 무제한" (tcsh) 또는 "ulimit -Ss 무제한" (세게 때리다).


문제의 핵심:


고대(구식) Linux RH9 2.4.x 커널 상자에서 테스트하여 많은 양의 할당 스택   또는   더미, 둘 중 하나만 있어도 2GB에서 3GB 사이가 됩니다.(슬프게도 이 기기의 RAM+SWAP의 최대 용량은 3.5GB 미만입니다.32비트 OS입니다.그리고 이건 아니다 실행 중인 유일한 프로세스입니다.우리는 우리가 가진 것을 가지고 살아간다...)

그래서 실제로는 제한이 없습니다 스택 크기 대 더미 인공적인 것 이외의 Linux에서의 크기 ...


하지만:


Mac에는 다음과 같은 하드 스택 크기 제한이 있습니다. 65532킬로바이트.이는 사물이 메모리에 어떻게 배치되는지와 관련이 있습니다.


일반적으로 이상적인 시스템은 다음과 같이 생각됩니다. 스택 메모리 주소 공간의 한쪽 끝에, 더미 서로를 향해 구축됩니다.그들이 만나면 당신은 기억이 없어집니다.

Mac은 공유 시스템 라이브러리 양쪽을 제한하는 고정 오프셋 사이에 있습니다.아직 달릴 수 있어 마틴 요크의 코드 "무제한 스택 크기"를 사용하면 8MiB(< 64MiB) 정도의 데이터만 할당하기 때문입니다. 하지만 그 사람은 다 떨어질 거야 스택 그가 다 떨어지기 훨씬 전에 더미.

저는 리눅스를 사용하고 있습니다.나는하지 않을 것이다. 미안 꼬마야.여기 니켈이 있습니다.가서 더 나은 OS를 구입하세요.

Mac에 대한 해결 방법이 있습니다.그러나 보기 흉하고 지저분해지며 커널이나 링커 매개변수를 조정해야 합니다.

장기적으로 볼 때, Apple이 정말 어리석은 일을 하지 않는 한, 64비트 주소 공간은 이 전체 스택 제한 기능을 조만간 쓸모 없게 만들 것입니다.


조각화로 이동:


언제든지 뭔가를 밀면 스택 맨 마지막에 첨부되어 있습니다.그리고 현재 블록이 종료될 때마다 제거(롤백)됩니다.

결과적으로 구멍이 하나도 나지 않습니다. 스택.그것은 모두 사용된 메모리의 하나의 크고 견고한 블록입니다.맨 끝에 약간의 사용되지 않은 공간이 있으면 모두 재사용할 수 있습니다.

대조적으로, 더미 할당되고 해제되면 사용되지 않은 메모리 구멍이 생깁니다.이로 인해 시간이 지남에 따라 메모리 사용 공간이 점차 증가할 수 있습니다.코어 누출이 일반적으로 의미하는 것은 아니지만 결과는 비슷합니다.

메모리 조각화는 아니다 피해야 할 이유 더미 저장.코딩을 할 때 꼭 알아두어야 할 사항입니다.


어느 것이 스왑 스래싱:


  • 이미 많은 양의 힙이 할당/사용 중인 경우.
  • 파편화된 구멍이 많이 흩어져 있는 경우.
  • 그리고 작은 할당이 많은 경우.

그런 다음 코드의 작은 지역화된 영역 내에서 모두 활용되고 수많은 가상 메모리 페이지에 분산된 많은 수의 변수가 생길 수 있습니다.(이 2k 페이지에서 4바이트를 사용하고, 2k 페이지에서 8바이트를 사용하는 식으로 전체 페이지에 대해...)

이는 모두 프로그램을 실행하려면 많은 수의 페이지를 교체해야 함을 의미합니다.아니면 페이지를 계속해서 들어오고 나가게 될 것입니다.(우리는 이것을 스래싱이라고 부릅니다.)

반면에 이러한 작은 할당이 스택, 그것들은 모두 연속적인 메모리 영역에 위치하게 됩니다.로드해야 하는 VM 메모리 페이지 수가 더 적습니다.(4+8+...< 승리 시 2,000원.)

참고:이에 대해 주의를 환기시킨 이유는 모든 어레이가 HEAP에 할당되어야 한다고 주장한 특정 전기 엔지니어 때문이었습니다.우리는 그래픽을 위한 행렬 수학을 하고 있었습니다.3개 또는 4개 요소 배열의 *많은*.신규/삭제를 혼자 관리하는 것은 악몽이었습니다.심지어 수업 시간에 추상화되어도 슬픔을 야기했습니다!


다음 주제.스레딩:


예, 스레드는 기본적으로 매우 작은 스택으로 제한됩니다.

pthread_attr_setstacksize()를 사용하여 이를 변경할 수 있습니다.스레딩 구현에 따라 다르지만 여러 스레드가 동일한 32비트 주소 공간을 공유하는 경우 큰 개별 스레드당 스택은 문제가 될 것입니다! 공간이 그렇게 많지 않아요!다시 말하지만, 64비트 주소 공간(OS)으로 전환하는 것이 도움이 될 것입니다.

pthread_t       threadData;
pthread_attr_t  threadAttributes;

pthread_attr_init( & threadAttributes );
ASSERT_IS( 0, pthread_attr_setdetachstate( & threadAttributes,
                                             PTHREAD_CREATE_DETACHED ) );

ASSERT_IS( 0, pthread_attr_setstacksize  ( & threadAttributes,
                                             128 * 1024 * 1024 ) );

ASSERT_IS( 0, pthread_create ( & threadData,
                               & threadAttributes,
                               & runthread,
                               NULL ) );

에 대하여 마틴 요크의 스택 프레임:


어쩌면 당신과 나는 서로 다른 생각을 하고 있는 게 아닐까요?

내가 생각할 때 스택 프레임, 호출 스택이 생각납니다.각 함수나 메서드에는 고유한 속성이 있습니다. 스택 프레임 반환 주소, 인수 및 로컬 데이터로 구성됩니다.

나는 크기에 대한 제한을 본 적이 없습니다. 스택 프레임.에는 제한이 있습니다. 스택 전체적으로는 그렇지만 그게 전부야 스택 프레임 결합.

좋은 다이어그램과 토론이 있습니다. 스택 프레임 위키에서요.


마지막 참고 사항:


Linux/Unix/MacOS/Darwin/BSD에서는 최대값을 변경할 수 있습니다. 스택 프로그래밍 방식으로 크기 제한뿐만 아니라 한계(tcsh) 또는 한도(세게 때리다):

struct rlimit  limits;
limits.rlim_cur = RLIM_INFINITY;
limits.rlim_max = RLIM_INFINITY;
ASSERT_IS( 0, setrlimit( RLIMIT_STACK, & limits ) );

Mac에서는 INFINITY로 설정하지 마세요...그리고 사용하기 전에 바꿔보세요.;-)


추가 자료:



Push 그리고 pop 일반적으로 지침은 로컬 스택 프레임 변수를 저장하는 데 사용되지 않습니다. 함수의 시작 부분에서 스택 프레임은 함수의 로컬 변수에 필요한 바이트 수 (단어 크기에 정렬 된) 횟수만큼 스택 포인터를 줄임으로써 설정됩니다. 이 값에 대해 필수 공간의 "스택에"공간이 할당됩니다. 그런 다음 모든 로컬 변수는이 스택 프레임에 대한 포인터를 통해 액세스됩니다 (ebp x86).

스택은 로컬 변수, 기능 호출로부터의 반환을위한 정보 등을 저장하는 큰 메모리 블록입니다. 스택의 실제 크기는 OS에서 크게 다릅니다. 예를 들어, Windows에서 새 스레드를 만들 때 기본 크기는 1MB입니다.

스택에서 현재 사용 가능한 것보다 더 많은 메모리가 필요한 스택 객체를 만들려고하면 스택 오버플로가 발생하고 나쁜 일이 발생합니다. 대량의 악용 코드는 의도적으로 이러한 또는 유사한 조건을 만들려고합니다.

스택은 정수 크기의 덩어리로 나뉘 지 않습니다. 그것은 단지 평평한 바이트 배열 일뿐입니다. 유형 size_t (int가 아님)의 "정수"로 색인됩니다. 현재 사용할 수있는 공간에 맞는 큰 스택 객체를 만드는 경우 스택 포인터를 위로 올리거나 아래로 향하게하여 해당 공간을 사용합니다.

다른 사람들이 지적했듯이 스택이 아닌 큰 물체에 힙을 사용하는 것이 가장 좋습니다. 이것은 스택 오버플로 문제를 피합니다.

편집하다: 64 비트 애플리케이션을 사용하고 OS 및 런타임 라이브러리가 유용한 경우 (MRREE의 게시물 참조) 스택에 큰 임시 객체를 할당하는 것이 좋습니다. 응용 프로그램이 32 비트이고 / 또는 OS / 런타임 라이브러리가 좋지 않은 경우 힙에 이러한 객체를 할당해야 할 것입니다.

함수를 입력 할 때마다 스택은 해당 함수의 로컬 변수에 맞게 커집니다. 주어진 a largeObject 400 바이트를 사용하는 클래스 :

void MyFunc(int p1, largeObject p2, largeObject *p3)
{
   int s1;
   largeObject s2;
   largeObject *s3;
}

이 기능을 호출 할 때 스택은 이와 같은 것으로 보입니다 (세부 사항은 통화 및 아키텍처에 따라 다릅니다).

   [... rest of stack ...]
   [4 bytes for p1] 
   [400 bytes for p2]
   [4 bytes for p3]
   [return address]
   [old frame pointer]
   [4 bytes for s1]
   [400 bytes for s2]
   [4 bytes for s3]

보다 X86 전화 컨벤션 스택 작동 방식에 대한 정보. MSDN은 또한 몇 가지 다른 통화 대류를위한 멋진 다이어그램이 있습니다. 샘플 코드 그리고 결과 스택 다이어그램.

다른 사람들이 말했듯이, "큰 물건"이 의미하는 바는 확실하지 않습니다.

그들은 단순히 여러 개의 스택 "슬롯"을 차지합니까?

나는 당신이 단순히 정수보다 큰 것을 의미한다고 가정 할 것입니다. 그러나 다른 사람이 언급했듯이 스택에는 정수 크기의 "슬롯"이 없습니다. 메모리의 한 부분 일뿐 아니라 모든 바이트에는 자체 주소가 있습니다. 컴파일러는 주소로 모든 변수를 추적합니다. 첫 번째 해당 변수의 바이트-주소 연산자를 사용하는 경우 얻을 수있는 값입니다 ( &var ), 그리고 포인터의 값은 다른 변수의 경우이 주소 일뿐입니다. 컴파일러는 또한 모든 변수가 어떤 유형인지 알고 있으며 (변수를 선언했을 때 알려 주었음) 각 유형이 얼마나 큰지 알고 있습니다. 프로그램을 컴파일 할 때는 수학이 얼마나 많은 공간을 파악하는 데 필요한 모든 수학을 수행합니다. 함수가 호출 될 때 변수가 필요하며 함수 엔트리 포인트 코드 (PDADDY가 언급 한 스택 프레임)의 결과를 포함합니다.

C 및 C ++에서는 스택이 제한되어 있기 때문에 큰 물체를 스택에 저장해서는 안됩니다 (추측 한대로). 각 스레드의 스택은 일반적으로 몇 메가 바이트 이하입니다 (스레드를 만들 때 지정할 수 있음). 객체를 만들기 위해 "새"라고 부르면 스택에 놓지 않습니다. 대신 힙에 끼워집니다.

스택 크기는 제한되어 있습니다. 일반적으로 스택 크기는 프로세스가 생성 될 때 설정됩니다. 해당 프로세스의 각 스레드는 CreateTheRdread () 호출에 달리 지정되지 않으면 기본 스택 크기를 자동으로 가져옵니다. 예 : 여러 스택 '슬롯'이있을 수 있지만 각 스레드에는 하나만 있습니다. 그리고 그들은 스레드 사이에서 공유 할 수 없습니다.

나머지 스택 크기보다 큰 개체를 스택에 넣으면 스택 오버플로가 발생하고 응용 프로그램이 충돌합니다.

따라서 객체가 매우 큰 경우 스택이 아닌 힙에 할당하십시오. 힙은 가상 메모리의 양 (스택보다 큰 크기)에 의해서만 제한됩니다.

스택에 올려 놓는 것이 합리적이지 않을 정도로 거대하거나 충분히 많은 물건을 가질 수 있습니다. 이 경우 힙에 물체를 넣고 스택에 포인터를 넣을 수 있습니다. 이것은 통과 별 가치와 참조 별 패스의 차이입니다.

큰 물체를 어떻게 정의합니까? 우리는 할당 된 스택 공간의 크기보다 더 크거나 작게 이야기하고 있습니까?

예를 들어 다음과 같은 것이있는 경우 :

void main() {
    int reallyreallybigobjectonthestack[1000000000];
}

시스템에 따라 객체를 저장할 공간이 충분하지 않기 때문에 Segfault가 발생할 수 있습니다. 그렇지 않으면 다른 개체와 같이 저장됩니다. 실제 물리적 메모리에서 말하는 경우 운영 체제 수준의 가상 메모리가 처리를 처리하기 때문에 이에 대해 걱정할 필요가 없습니다.

또한 스택의 크기는 정수의 크기 일 가능성이 없을 것입니다. 운영 체제와 응용 프로그램의 레이아웃에 전적으로 의존합니다. 가상 주소 공간.

"스택은 정수의 크기"에 의해 "스택 포인터는 정수의 크기"를 의미합니다. 그것은 스택의 상단을 가리키며, 이는 거대한 메모리 영역입니다. 글쎄, 정수보다 큽니다.

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