C/C++에서 메모리 부족 상황에 적절하게 대처하는 방법은 무엇입니까?
대용량 메모리를 사용하는 캐싱 앱을 쓰고 있습니다.
메모리 관리를 잘 할 수 있으면 좋겠지만, 메모리가 부족하면 어떻게 해야 할지 생각 중입니다.
단순한 오브젝트라도 할당하는 콜이 실패하면 syslog 콜도 실패할 가능성이 있습니까?
편집: 좋아요, 질문을 명확히 해야 할 것 같아요.malloc 또는 new가 NULL 또는 0L 값을 반환하는 경우 기본적으로 콜이 실패했음을 의미하며 어떤 이유로 메모리를 제공할 수 없습니다.그렇다면, 이 경우 어떻게 해야 할까요?
EDIT2: "new"에 대한 호출이 예외를 발생시킬 수 있다는 것을 방금 깨달았습니다.이것은 더 높은 수준에서 잡힐 수 있기 때문에 나는 아마도 우아하게 더 위로 나갈 수 있을 것이다.이 시점에서 빈 메모리의 양에 따라서는 회복이 가능할 수도 있습니다.적어도 그 시점까지는 뭔가를 기록할 수 있을 겁니다.그래서 새것 이후에 포인터의 값을 체크하는 코드를 봤지만, 그것은 불필요합니다.C에 있는 동안 malloc의 반환값을 확인해야 합니다.
이 질문은 오버 커밋된 메모리에 대한 추측을 하지 않나요?
즉, 메모리 부족 상황은 복구할 수 없을 수 있습니다.메모리가 남아 있지 않은 경우에도,malloc
및 다른 할당자는 프로그램이 메모리를 사용할 때까지 계속 성공할 수 있습니다.그러면 BAM! 일부 프로세스가 메모리 부하를 만족시키기 위해 커널에 의해 중지됩니다.
메모리 할당에 실패했을 경우,std::bad_alloc
예외.이 예외로 인해 프로그램 스택이 해제됩니다.대부분의 경우 애플리케이션 로직의 내부 루프는 메모리 부족 상태를 처리하지 않습니다.어플리케이션의 상위 레벨만이 처리할 수 있습니다.스택이 풀리고 있기 때문에, 중요한 메모리 청크가 해방됩니다.실제로 이 청크는 프로그램에서 사용되는 거의 모든 메모리입니다.
단, 만족할 수 없는 매우 큰 메모리(예를 들어 수백 MB)의 청크를 요구하는 경우는 예외입니다.다만, 이 경우는, 통상, 보다 작은 메모리 청크가 남아 있기 때문에, 정상적으로 장해를 처리할 수 있습니다.
스택 언와인딩은 당신의 친구입니다;)
편집: 질문에도 C 태그가 붙어 있는 것을 깨달았습니다.이 경우 메모리 부족 상태가 발견되었을 때 함수 내부 구조를 수동으로 해제해야 합니다.해당하지 않으면 메모리 누수가 발생합니다.
EDIT2: 예:
#include <iostream>
#include <vector>
void DoStuff()
{
std::vector<int> data;
//insert a whole crapload of stuff into data here.
//Assume std::vector::push_back does the actual throwing
//i.e. data.resize(SOME_LARGE_VALUE_HERE);
}
int main()
{
try
{
DoStuff();
return 0;
}
catch (const std::bad_alloc& ex)
{ //Observe that the local variable `data` no longer exists here.
std::cerr << "Oops. Looks like you need to use a 64 bit system (or "
"get a bigger hard disk) for that calculation!";
return -1;
}
}
EDIT3: 좋습니다.댓글에 의하면, 이 점에 관해서 기준을 따르지 않는 시스템이 있습니다.한편, 이러한 시스템에서는 어떠한 경우에도 SOL이 되기 때문에 논의의 가치가 없다고 생각합니다.하지만 만약 당신이 그런 플랫폼에 있다면, 그것은 명심해야 할 사항이다.
메모리 부족 상태에서 syslog에 대한 쓰기가 실패할 수 있습니다. 관련 기능의 소스를 살펴보지 않고는 모든 플랫폼에서 이를 알 수 없습니다.예를 들어 전달되는 문자열을 포맷하기 위해 동적 메모리가 필요할 수 있습니다.
그러나 메모리가 부족하기 훨씬 전에 디스크로 데이터를 페이징하기 시작합니다.이 경우 캐싱으로 인한 성능상의 이점을 무시할 수 있습니다.
개인적으로 저는 Varnish의 설계에 대해 확신합니다.운영체제는 많은 관련 문제를 해결하기 위한 서비스를 제공하고 있으며, 이러한 서비스(소규모 편집)를 사용하는 것이 합리적입니다.
그래서 Squid의 정교한 메모리 관리에서 어떤 일이 일어나느냐 하면 커널의 정교한 메모리 관리와의 싸움에 휘말리게 됩니다.
Squid는 RAM에 HTTP 개체를 생성하고 생성 후 몇 번이고 빠르게 사용됩니다.잠시 후 더 이상 히트하지 않고 커널이 이를 알아차립니다.그런 다음 누군가 커널에서 메모리를 가져오려고 하고 커널은 사용하지 않는 메모리 페이지를 밖으로 밀어내 공간을 교환하고 실제로 프로그램에 의해 사용되는 데이터를 위해 (캐시-RAM)을 더 현명하게 사용하기로 결정합니다.하지만, 이것은 Squid가 모르는 사이에 행해집니다.Squid는 여전히 이러한 http 객체가 RAM에 있다고 생각하고 있으며, 이 객체에 대한 접근을 시도하는 순간에도 RAM은 생산적인 용도로 사용됩니다.
시간이 지나면 Squid는 이러한 오브젝트가 사용되지 않는 것을 알아차리고 RAM을 더 많은 비지 데이터에 사용할 수 있도록 디스크로 이동하기로 결정합니다.Squid는 파일을 만들고 http 객체를 파일에 씁니다.
여기서 고속 카메라로 전환합니다: Squid는 write(2)를 호출하고, 주소가 "가상 주소"이며, 커널에는 "not at home"으로 표시됩니다.
커널은 빈 페이지를 찾으려고 합니다.빈 페이지가 없는 경우, 어딘가로부터 조금 사용된 페이지를 가져와, 아마도 다른 조금 사용된 Squid 오브젝트, 디스크의 페이징... 공간(스왑 영역)에 씁니다.이 기입이 완료되면, 페이징 풀내의 다른 장소에서, 사용되지 않는 RAM 페이지로 「페이징 아웃」한 데이터를 읽어, 복구합니다.페이징 테이블을 실행하고 실패한 명령을 다시 시도합니다. ...
이제 Squid는 RAM의 페이지에 오브젝트를 저장하고 디스크에 2개의 장소, 즉 운영 체제의 페이징 공간에 복사본이 하나, 파일 시스템에 복사본이 하나 있습니다.
Varnish의 방법은 다음과 같습니다.
가상 메모리를 할당하면 운영 체제에 디스크 파일의 공간을 사용하여 이 메모리를 백업하도록 지시합니다.오브젝트를 클라이언트에 송신할 필요가 있는 경우는, 그 가상 메모리의 일부를 참조해, 나머지를 커널에 맡깁니다.
커널이 RAM을 다른 용도로 사용해야 한다고 판단했을 경우, 페이지는 백업 파일에 쓰여지고 RAM 페이지는 다른 곳에서 재사용됩니다.
다음에 Vanish가 가상 메모리를 참조할 때 운영체제는 RAM 페이지를 검색하여 RAM 페이지를 해방하고 백업 파일에서 내용을 읽습니다.
그리고 이것이 마지막입니다.Varnish는 RAM에 캐시되어 있는 것과 캐시되어 있지 않은 것을 제어하려고 하지 않습니다.커널은 이를 수행하기 위한 코드와 하드웨어 지원을 가지고 있으며, 이 기능은 훌륭합니다.
캐싱 코드를 전혀 쓸 필요가 없을 수 있습니다.
애플리케이션이 대용량 메모리 블록을 할당할 가능성이 높고 프로세스별 또는 VM 제한에 도달할 위험이 있는 경우 할당이 실제로 실패할 때까지 기다리는 것은 복구하기 어려운 상황입니다.그때까지malloc
돌아온다NULL
또는new
던지다std::bad_alloc
안정적 회복을 하기에는 너무 오래 걸릴 수도 있습니다.복구 전략에 따라서는 많은 작업에 힙 할당 자체가 필요할 수 있으므로 신뢰할 수 있는 루틴에 매우 주의해야 합니다.
OS에 문의하여 사용 가능한 메모리를 감시하고 할당을 능동적으로 관리하는 것도 고려할 수 있습니다.이렇게 하면 큰 블록이 실패할 가능성이 높기 때문에 더 나은 복구 가능성이 있는 경우 큰 블록을 할당하지 않아도 됩니다.
또, 메모리 사용 패턴에 따라서는, 커스텀 할당기를 사용하는 것으로, 표준 내장형보다 뛰어난 결과를 얻을 수 있습니다.malloc
예를 들어 특정 할당 패턴은 시간이 지남에 따라 실제로 메모리 조각화를 일으킬 수 있습니다. 따라서 여유 메모리가 있더라도 히프 아레나에서 사용 가능한 블록은 적절한 크기의 블록을 가지고 있지 않을 수 있습니다.그 좋은 예로는 Firefox를 들 수 있습니다.dmalloc
메모리 효율이 크게 향상되었습니다.
대용량 메모리를 사용하는 캐싱 앱을 쓰고 있습니다.메모리 관리를 잘 할 수 있으면 좋겠지만, 메모리가 부족하면 어떻게 해야 할지 생각 중입니다.
365일 24시간 365일 실행되어야 하는 deamon을 쓰는 경우 동적 메모리 관리를 사용하지 마십시오.모든 메모리를 미리 할당하고 슬래브 할당기/메모리 풀 메커니즘을 사용하여 관리합니다.그러면 힙 플래그멘테이션도 다시 보호됩니다.
단순한 오브젝트라도 할당하는 콜이 실패하면 syslog 콜도 실패할 가능성이 있습니까?
그러면 안 돼요.이것이 부분적인 이유이기도 하다syslog
는 syscall로 존재합니다.응용 프로그램은 내부 상태와 무관하게 오류를 보고할 수 있습니다.
malloc 또는 new가 NULL 또는 0L 값을 반환하는 경우 기본적으로 콜이 실패했음을 의미하며 어떤 이유로 메모리를 제공할 수 없습니다.그렇다면, 이 경우 어떻게 해야 할까요?
일반적으로 오류 처리 규칙을 적용하여 오류 상태를 적절하게 처리하려고 합니다.초기화 중에 오류가 발생하면 오류와 함께 종료됩니다. 아마도 구성 오류일 수 있습니다.요청 처리 중 오류가 발생한 경우 메모리 부족 오류로 인해 요청이 실패합니다.
플레인 힙메모리의 경우malloc()
돌아오는0
일반적으로 다음과 같은 의미가 있습니다.
사용 중인 애플리케이션이 메모리를 확보하지 않는 한 힙을 모두 사용할 수 없습니다.
malloc()
는 성공하지 못합니다.잘못된 할당 크기: 블록 크기를 계산할 때 서명된 유형과 서명되지 않은 유형을 혼합하는 것은 매우 일반적인 코딩 오류입니다.사이즈가 잘못 음수인 경우는, 에 건네집니다.
malloc()
어디에size_t
예상대로 굉장히 많은 수가 됩니다.
그래서 어떤 의미에서는 하는 것도 나쁘지 않다.abort()
코어 파일을 작성하기 위해 나중에 분석할 수 있습니다.malloc()
반환했다0
(1) 에러 메세지에, 시도된 할당 사이즈를 포함해, (2) 한층 더 진행시켜 주세요.다른 메모리 문제(*)로 인해 응용 프로그램이 크래시 될 경우 코어 파일이 생성됩니다.
(*) 동적 메모리 관리 기능을 갖춘 소프트웨어를 복원하여malloc()
오류는 자주 볼 수 있습니다.malloc()
돌아온다0
신뢰할 수 없습니다.첫 번째 시도 반환0
성공이 뒤따른다.malloc()
유효한 포인터를 반환했습니다.단, 먼저 포인트메모리에 접속하면 어플리케이션이 크래시 됩니다.이것은 Linux와 HP-UX 양쪽에서 경험하고 있는 것입니다.Solaris 10에서도 같은 패턴을 볼 수 있었습니다.이 동작은 Linux에만 있는 것이 아닙니다.제가 알기로는 애플리케이션이 메모리 문제에 대해 100% 복원력을 갖도록 하는 유일한 방법은 모든 메모리를 미리 할당하는 것입니다.미션 크리티컬, 안전, 라이프 서포트 및 캐리어 그레이드 어플리케이션에서는 필수입니다.초기화 단계 이후의 다이내믹 메모리 관리는 허용되지 않습니다.
이미 말했듯이, 기억력이 고갈된다는 것은 모든 내기가 실패한다는 것을 의미한다.IMHO는 이 상황에 대처하는 최선의 방법은 (단순히 크래시 하는 것이 아니라) 정상적으로 실패하는 것입니다.인스턴스화 시 캐시에 적절한 양의 메모리가 할당될 수 있습니다.이 메모리의 사이즈는, 해방되면, 프로그램이 합리적으로 종료할 수 있는 용량과 같습니다.캐시가 메모리가 부족해지고 있는 것을 검출하면, 이 메모리를 해방해, 정상적인 셧다운을 개시합니다.
그 실패를 포착하는 것이malloc
또는new
당신의 처지에서 많은 것을 얻을 수 있을 것이다. linux
에 대규모 가상 페이지 청크를 할당합니다.malloc
을 이용하여mmap
이것에 의해, 사용하고 있는 가상 메모리보다 훨씬 더 많은 가상 메모리를 할당할 수 있는 상황에 처할 가능성이 있습니다(리얼+스왑).
그 후 스왑할 장소가 없는 첫 페이지에 쓸 때 SIGSEGV(segfault)와 함께 프로그램이 실패하게 됩니다.이론적으로는 신호 핸들러를 쓴 후 할당한 모든 페이지를 더티로 함으로써 이러한 상황을 테스트할 수 있습니다.
그러나, 그 훨씬 전에 애플리케이션의 상태가 매우 나빠지기 때문에, 통상은 이것 또한 큰 도움이 되지 않습니다.즉, 항상 하드 디스크와 교환하거나 기계적으로 컴퓨팅을 하거나 하는 것입니다.
Linux에 대한 특별한 경험은 없지만, 메모리 부족이 심한 게임 콘솔과 Windows 기반 도구에서 비디오 게임을 작업하는 데 많은 시간을 할애했습니다.
최신 OS에서는 주소 공간이 부족할 가능성이 높습니다.메모리 부족은 기본적으로 불가능합니다.따라서 기동시에 큰 버퍼(또는 버퍼)를 할당해, 필요한 모든 데이터를 보관 유지하는 것과 동시에, OS에는 소량의 버퍼를 남겨 둡니다.OS가 실제로 메모리를 프로세스에 할당하도록 하려면 이러한 영역에 랜덤으로 정크 데이터를 쓰는 것이 좋습니다.프로세스가 모든 바이트를 사용하려는 시도에서 살아남으면 이 모든 것을 위해 어떤 종류의 지원이 예약되어 있으므로 이제 안심입니다.
독자적인 메모리 매니저를 기입/스틸 해, 이러한 버퍼로부터 할당하도록 지시합니다.그런 다음 앱에서 이를 일관되게 사용하거나 gcc의 장점을 활용하십시오.--wrap
옵션, 콜 전송원malloc
그리고 친구들도 잘 사귀고마워.메모리 매니저를 호출하기 위해 지시할 수 없는 라이브러리를 사용하는 경우 방해가 되기 때문에 그것들을 폐기하십시오.덮어쓸 수 있는 메모리 관리 콜이 없는 것은 심각한 문제의 증거입니다.이 컴포넌트는 사용하지 않는 것이 좋습니다.(주의: 를 사용하고 있는 경우에도 마찬가지입니다).--wrap
날 믿어, 이건 여전히 문제의 증거야!메모리 관리에 과부하가 걸리지 않는 라이브러리를 사용하기에는 수명이 너무 짧습니다.)
메모리가 부족하면 문제가 발생하지만 이전에 사용 가능한 공간이 남아 있기 때문에 요청한 메모리 중 일부를 비우는 것이 너무 어렵다면 시스템 호출에 전화를 걸어 시스템 로그에 메시지를 쓴 후 종료하거나 할 수 있습니다.C 라이브러리로의 콜은 반드시 피하도록 해 주세요. 왜냐하면 C 라이브러리는 예기치 않은 타이밍에 메모리를 할당하려고 하기 때문입니다.가상화된 주소 공간을 사용하는 시스템을 사용하는 프로그래머는 이런 종류의 것으로 악명이 높기 때문입니다.그게 바로 문제의 원인입니다.
이 접근법은 골칫거리처럼 들릴지도 모른다.뭐...맞아요. 하지만 간단해요. 그러기 위해 조금 노력할 가치가 있어요.Kernighan and/또는 Ritche의 인용구가 있는 것 같습니다.
나는 왜 많은 합리적인 답변들이 부결되었는지 모르겠다.대부분의 서버 환경에서 메모리가 부족하다는 것은 어딘가에서 리크가 발생한다는 것을 의미하며, "메모리를 해방하고 계속하려고 한다"는 것은 의미가 없습니다.C++, 특히 표준 라이브러리의 특성은 항상 할당이 필요하다는 것입니다.운이 좋으면 일부 메모리를 해제하고 완전히 종료하거나 최소한 경고를 보낼 수 있습니다.
그러나 실패한 할당이 큰 것이 아니고, '정상적인' 것에 사용할 수 있는 메모리가 남아 있지 않는 한, 아무것도 할 수 없을 가능성이 훨씬 더 높습니다.
Dan Bernstein은 메모리가 제한된 상황에서 작동하는 서버 소프트웨어를 구현할 수 있는 몇 안 되는 사람 중 한 명입니다.
나머지 대부분의 경우 메모리 부족 오류로 인해 장애가 발생했을 때 유용한 상태로 유지되도록 소프트웨어를 설계해야 합니다.
네가 뇌외과 의사 아니면 할 일이 별로 없어
또한 대부분의 경우 std::bad_alloc 또는 그와 비슷한 것을 얻을 수 없습니다.malloc/new에 대한 답례로 포인터를 얻을 수 있습니다.그리고 그 모든 메모리를 실제로 만지려고 할 때만 사망합니다.이 문제는 운영 체제에서 오버커밋을 해제하여 방지할 수 있지만 여전히 마찬가지입니다.
커널이 원하지 않는 메모리를 만졌을 때 SIGSEGV를 처리할 수 있을 것으로 기대하지 마십시오.윈도 측에서 어떻게 동작하는지는 잘 모르겠지만 오버 커밋도 할 수 있을 것입니다.
대체로 이것은 C++의 강점 중 하나가 아닙니다.
언급URL : https://stackoverflow.com/questions/3596990/whats-the-graceful-way-of-handling-out-of-memory-situations-in-c-c
'programing' 카테고리의 다른 글
태그 열기/닫기 및 퍼포먼스 (0) | 2022.09.24 |
---|---|
루프나 조건문을 사용하지 않고1 ~ 1000 으로 인쇄하는 C 코드는 어떻게 동작합니까? (0) | 2022.09.24 |
python에서 두 datetime 객체의 시간 차이를 찾으려면 어떻게 해야 합니까? (0) | 2022.09.24 |
MySQL(MariaDB) 루프 중 (0) | 2022.09.22 |
file_get_contents()에는 타임아웃 설정이 있습니까? (0) | 2022.09.22 |