IT TIP

"메모리 부족"이 복구 가능한 오류입니까?

itqueen 2020. 10. 19. 13:44
반응형

"메모리 부족"이 복구 가능한 오류입니까?


나는 오랫동안 프로그래밍을 해왔고 내가 본 프로그램은 메모리가 부족할 때 정리 및 종료를 시도합니다. 즉, 정상적으로 실패합니다. 마지막으로 한 사람이 실제로 복구를 시도하고 정상적으로 작동하는 것을 본 적이 없습니다.

특히 가비지 수집 된 언어에서 메모리를 성공적으로 할당 할 수 있어야 많은 처리가 이루어집니다. 메모리 부족 오류는 복구 불가능으로 분류되어야합니다. (복구 불가능한 오류에는 스택 오버플로와 같은 것이 포함됩니다.)

오류를 복구 할 수있는 오류로 만드는 이유는 무엇입니까?


그것은 정말로 당신이 무엇을 만들고 있는지에 달려 있습니다.

웹 서버가 하나의 요청 / 응답 쌍을 실패한 다음 추가 요청을 계속하는 것은 전적으로 불합리한 것은 아닙니다. 그러나 단일 실패가 전역 상태에 해로운 영향을 미치지 않았는지 확인해야합니다. 이는 까다로운 부분입니다. 실패로 인해 대부분의 관리 환경 (예 : .NET 및 Java)에서 예외가 발생한다는 점을 감안할 때 예외가 "사용자 코드"에서 처리되면 향후 요청에 대해 복구 할 수있을 것입니다. 예를 들어 한 요청이 10GB의 메모리를 할당하려고 시도한 경우 실패하면 나머지 시스템에 해를 끼치 지 않아야합니다. 그러나 사용자 코드에 요청을 전달하는 동안 시스템의 메모리가 부족하면 그런 종류의 문제가 더 심해질 수 있습니다.


라이브러리에서 파일을 효율적으로 복사하려고합니다. 그렇게 할 때 일반적으로 적은 수의 큰 청크를 사용하여 복사하는 것이 많은 작은 청크를 복사하는 것보다 훨씬 효과적이라는 것을 알게 될 것입니다 (예를 들어 15,000 개를 복사하는 것보다 15 개의 1MB 청크를 복사하여 15MB 파일을 복사하는 것이 더 빠릅니다. 1K 청크).

그러나 코드는 모든 청크 크기에서 작동합니다. 따라서 1MB 청크로 더 빠를 수 있지만 많은 파일이 복사되는 시스템을 설계하는 경우 OutOfMemoryError를 포착하고 성공할 때까지 청크 크기를 줄이는 것이 현명 할 수 있습니다.

또 다른 장소는 데이터베이스에 저장된 객체에 대한 캐시입니다. 가능한 한 많은 객체를 캐시에 보관하고 싶지만 나머지 응용 프로그램을 방해하고 싶지는 않습니다. 이러한 개체는 다시 만들 수 있으므로 메모리를 절약하여 메모리 부족 처리기에 캐시를 연결하여 앱의 나머지 부분이 다시 숨을 쉴 수있는 충분한 공간이 생길 때까지 항목을 삭제하는 것이 현명한 방법입니다.

마지막으로 이미지 조작을 위해 가능한 한 많은 이미지를 메모리에로드하려고합니다. 다시 말하지만, OOM 핸들러를 사용하면 사용자 또는 OS가 코드를 부여 할 메모리 양을 미리 알지 않고도이를 구현할 수 있습니다.

[편집] 응용 프로그램에 고정 된 양의 메모리를 제공했으며이 양은 스왑 공간을 제외한 사용 가능한 총 메모리보다 작다는 가정하에 작업합니다. 메모리의 일부를 교체해야 할만큼 많은 메모리를 할당 할 수 있다면 내 의견 중 일부는 더 이상 의미가 없습니다.


MATLAB 사용자는 큰 배열로 산술을 수행 할 때 항상 메모리가 부족합니다. 예를 들어 변수 x가 메모리에 맞고 "x + 1"을 실행하면 MATLAB은 결과를위한 공간을 할당 한 다음 채 웁니다. 할당이 MATLAB 오류에 실패하고 사용자는 다른 작업을 시도 할 수 있습니다. 이 사용 사례가 나올 때마다 MATLAB이 종료되면 재앙이 될 것입니다.


종료가 OOM에서 복구하는 유일한 전략이 아니므로 OOM을 복구 할 수 있어야합니다.

실제로 응용 프로그램 수준에서 OOM 문제에 대한 꽤 표준적인 해결책이 있습니다. 애플리케이션 설계의 일부로 메모리 부족 상태에서 복구하는 데 필요한 안전한 최소 메모리 양을 결정합니다. (예 : 문서 자동 저장, 경고 대화 상자 표시, 로그 종료 데이터에 필요한 메모리).

애플리케이션 시작 또는 중요 블록 시작시 해당 메모리 양을 미리 할당하십시오. 메모리 부족 상태를 감지하면 가드 메모리를 해제하고 복구를 수행하십시오. 이 전략은 여전히 ​​실패 할 수 있지만 전체적으로 큰 이익을 가져다줍니다.

응용 프로그램을 종료 할 필요는 없습니다. OOM 조건이 해결 될 때까지 모달 대화 상자를 표시 할 수 있습니다.

100 % 확신 할 수는 없지만 ' 코드 완성 '(존경하는 소프트웨어 엔지니어에게 필요함)이이를 다룰 것이라고 확신 합니다.

추신 :이 전략에 도움이되도록 애플리케이션 프레임 워크를 확장 할 수 있지만 라이브러리에서 그러한 정책을 구현하지 마십시오 (좋은 라이브러리는 애플리케이션 동의없이 글로벌 결정을 내릴 수 없음).


많은 것들과 마찬가지로 비용 / 이익 분석이라고 생각합니다. 당신은 할 수 의 malloc () 실패에서 시도 회복 프로그램 - 그것은 어려울 수 있지만 (핸들러 더 나은이 처리하는 의미 것 같은 메모리 부족 파울 빠지지했다).

가장 일반적인 경우는 정리하고 정상적으로 실패하는 것임을 이미 알고 있습니다. 이 경우 정상적으로 중단하는 비용이 복구시 개발 비용과 성능 비용을 합친 것보다 낮다고 결정되었습니다.

프로그램을 종료하는 것이 매우 비싼 옵션 (생명 유지 장치, 우주선 제어, 장기 실행 및 시간 결정적인 재무 계산 등) 인 상황에 대한 자신의 예를 생각할 수 있다고 확신합니다. 물론 프로그램에 예측 가능한 메모리 사용량이 있고 환경이이를 제공 할 수 있는지 확인합니다.


성능 향상을 위해 IO 캐시에 메모리를 할당하는 시스템에서 작업 중입니다. 그런 다음 OOM을 감지하면 일부를 다시 가져 와서 IO 캐시가 적고 쓰기 성능이 약간 낮아 지더라도 비즈니스 로직이 진행될 수 있습니다.

또한 가비지 수집을 강제하고 선택적으로 미리 가져 오거나 캐시 된 데이터와 같은 중요하지 않은 일부 개체를 해제하여 OOM을 관리하려는 임베디드 Java 애플리케이션으로 작업했습니다.

OOM 처리의 주요 문제점은 다음과 같습니다.

1) 발생한 위치에서 재 시도 할 수 있거나 더 높은 지점에서 롤백하고 재 시도 할 수 있습니다. 대부분의 최신 프로그램은 언어에 너무 많이 의존하여 종료 위치와 작업 재시도 방법을 관리하지 않습니다. 보존하도록 설계되지 않은 경우 일반적으로 작업의 컨텍스트가 손실됩니다.

2) 실제로 일부 메모리를 해제 할 수 있습니다. 이것은 어떤 객체가 중요하고 무엇이 중요하지 않은지를 아는 일종의 리소스 관리자를 의미하며, 시스템은 릴리스 된 객체가 나중에 중요해질 때 해제 된 객체를 다시 요청할 수 있습니다.

또 다른 중요한 문제는 또 다른 OOM 상황을 트리거하지 않고 롤백 할 수 있다는 것입니다. 이것은 높은 수준의 언어에서 제어하기 어려운 것입니다.

또한 기본 OS는 OOM과 관련하여 예상대로 작동해야합니다. 예를 들어 Linux는 메모리 오버 커밋이 활성화 된 경우 그렇지 않습니다. 많은 스왑 가능 시스템은 OOM을 문제가되는 응용 프로그램에보고하는 것보다 빨리 죽습니다.

그리고 상황을 만든 프로세스가 아닌 경우가 있으므로 문제가되는 프로세스가 계속 누출되면 메모리를 해제해도 도움이되지 않습니다.

이 모든 이유로이 기술을 사용하는 것은 대형 임베디드 시스템입니다. OS와 메모리를 제어 할 수 있고이를 구현할 규율 / 동기도 있기 때문입니다.


그것을 잡고 올바르게 처리해야만 복구 할 수 있습니다.

예를 들어 같은 경우에 요청이 많은 메모리를 할당하려고했습니다. 매우 예측 가능하며 매우 잘 처리 할 수 ​​있습니다.

그러나 다중 스레드 응용 프로그램의 대부분의 경우 OOE는 백그라운드 스레드에서도 발생할 수 있습니다 (시스템 / 타사 라이브러리에 의해 생성됨). 예측하는 것은 거의 불가능하며 모든 스레드의 상태를 복구하지 못할 수도 있습니다.


아니요. GC의 메모리 부족 오류는 일반적으로 현재 스레드 내에서 복구 할 수 없어야합니다. (복구 가능한 스레드 (사용자 또는 커널) 생성 및 종료는 지원되어야 함)

카운터 예와 관련하여 : 저는 현재 GPU 컴퓨팅을 위해 NVIDIA의 CUDA 플랫폼을 사용하는 D 프로그래밍 언어 프로젝트를 진행하고 있습니다. GPU 메모리를 수동으로 관리하는 대신 D의 GC를 활용하기 위해 프록시 개체를 만들었습니다. 따라서 GPU가 메모리 부족 오류를 반환하면 전체 수집을 실행하고 두 번째 실패한 경우에만 예외를 발생시킵니다. 그러나 이것은 실제로 메모리 부족 복구의 예가 아니라 GC 통합 중 하나입니다. 복구의 다른 예 (캐시, 자유 목록, 자동 축소가없는 스택 / 해시 등)는 GC와 분리되어 할당에 국한되지 않는 경향이있는 자체 메모리 수집 / 압축 방법이있는 모든 구조입니다. 함수. 따라서 사람들은 다음과 같은 것을 구현할 수 있습니다.

T new2(T)( lazy T old_new ) {
    T obj;
    try{
        obj = old_new;
    }catch(OutOfMemoryException oome) {
        foreach(compact; Global_List_Of_Delegates_From_Compatible_Objects)
            compact();
        obj = old_new;
    }
    return obj;
}

일반적으로 가비지 수집기에 자체 수집 / 압축 개체 등록 / 등록 해제에 대한 지원을 추가하기위한 적절한 주장입니다.


일반적으로 복구 할 수 없습니다.

그러나 시스템에 어떤 형태의 동적 캐싱이 포함 된 경우 메모리 부족 처리기는 종종 캐시 (또는 전체 캐시)에서 가장 오래된 요소를 덤프 할 수 있습니다.

물론, "덤핑"프로세스에 새로운 메모리 할당이 필요하지 않은지 확인해야합니다. :) 또한 할당 자에서 직접 캐시 덤프 코드를 연결할 수 없다면 실패한 특정 할당을 복구하는 것이 까다로울 수 있습니다. 실패가 호출자에게 전파되지 않도록합니다.


메모리 부족으로 의미하는 바에 따라 다릅니다.

malloc()대부분의 시스템에서 실패하면 주소 공간이 부족했기 때문입니다.

해당 메모리의 대부분이 캐시 또는 mmap'd 영역에서 사용되는 경우 캐시를 해제하거나 mmap을 해제하여 일부 메모리를 회수 할 수 있습니다. 그러나 이것은 실제로 당신이 그 메모리를 무엇에 사용하고 있는지 알고 있어야하며 대부분의 프로그램이 그렇지 않거나 차이를 만들지 않는다는 것을 알았습니다.

당신이 사용하는 경우 setrlimit()자신에, 당신은 당신의 오류 처리기의 제한을 완화 할 수 있습니다 (아마도, 아니면 루트가 당신에게 그것을했다, 예상치 못한 공격으로부터 보호하기 위해). 가능한 경우 사용자에게 메시지를 표시하고 이벤트를 기록한 후이 작업을 자주 수행합니다.

반면에 스택 오버플로를 잡는 것은 조금 더 어렵고 이식성이 없습니다. 나는 ECL에 대한 posixish 솔루션을 작성 하고이 길을 가고 있다면 Windows 구현을 설명했습니다. 몇 달 전에 ECL에 확인되었지만 관심이 있으시면 원래 패치를 파헤칠 수 있습니다.


특히 가비지 수집 된 환경에서 높은 수준의 애플리케이션에서 OutOfMemory 오류를 포착하면 많은 항목이 범위를 벗어나 메모리를 되찾기 위해 회수 될 수 있습니다.

단일 과도한 할당의 경우 앱이 완벽하게 계속 작동 할 수 있습니다. 물론 점진적인 메모리 누수가 발생하면 문제가 다시 발생하게되지만 (나중보다 더 빨리) 앱이 정상적으로 종료되고 저장되지 않은 변경 사항을 저장할 수있는 기회를주는 것이 좋습니다. GUI 앱의 경우 등


예, OOM을 복구 할 수 있습니다. 극단적 인 예로서, Unix 및 Windows 운영 체제는 대부분의 경우 OOM 조건에서 상당히 잘 복구됩니다. 응용 프로그램은 실패하지만 OS는 살아남습니다 (처음부터 OS가 제대로 시작되기에 충분한 메모리가 있다고 가정).

나는 이것이 가능하다는 것을 보여주기 위해이 예를 인용 할 뿐이다.

OOM을 다루는 문제는 실제로 프로그램과 환경에 따라 다릅니다.

예를 들어, 대부분의 경우 OOM이 발생할 가능성이 가장 높은 곳은 OOM 상태에서 실제로 복구하기에 가장 좋은 곳이 아닙니다.

이제 사용자 지정 할당자는 OOM을 처리 할 수있는 코드 내에서 중앙 지점으로 작동 할 수 있습니다. Java 할당자는 실제로 OOM 예외가 발생하기 전에 전체 GC를 수행합니다.

할당자가 "애플리케이션을 더 잘 인식"할수록 OOM에 대한 중앙 처리기 및 복구 에이전트로 더 적합합니다. Java를 다시 사용하면 할당자가 특히 응용 프로그램을 인식하지 않습니다.

이것은 Java와 같은 것이 쉽게 좌절되는 곳입니다. 할당자를 재정의 할 수 없습니다. 따라서 자신의 코드에서 OOM 예외를 트래핑 할 수는 있지만 사용중인 일부 라이브러리가 제대로 트래핑하거나 OOM 예외를 제대로 던진다는 의미는 없습니다. 일부 개체가 null로 설정되고 "발생하지 않음"으로 설정되어 복구 할 수 없기 때문에 OOM 예외로 인해 영원히 망가진 클래스를 만드는 것은 간단합니다.

예, OOM은 복구 가능하지만 특히 Java와 같은 최신 환경에서는 매우 어려울 수 있으며 다양한 품질의 타사 라이브러리가 과도합니다.


질문은 "언어 불가지론 적"태그가 지정되어 있지만 언어 및 / 또는 기본 시스템을 고려하지 않고 대답하기가 어렵습니다. (나는 여러 개의하든을 본다

If memory allocation is implicit, with no mechanism to detect whether a given allocation succeeded or not, then recovering from an out-of-memory condition may be difficult or impossible.

For example, if you call a function that attempts to allocate a huge array, most languages just don't define the behavior if the array can't be allocated. (In Ada this raises a Storage_Error exception, at least in principle, and it should be possible to handle that.)

On the other hand, if you have a mechanism that attempts to allocate memory and is able to report a failure to do so (like C's malloc() or C++'s new), then yes, it's certainly possible to recover from that failure. In at least the cases of malloc() and new, a failed allocation doesn't do anything other than report failure (it doesn't corrupt any internal data structures, for example).

Whether it makes sense to try to recover depends on the application. If the application just can't succeed after an allocation failure, then it should do whatever cleanup it can and terminate. But if the allocation failure merely means that one particular task cannot be performed, or if the task can still be performed more slowly with less memory, then it makes sense to continue operating.

A concrete example: Suppose I'm using a text editor. If I try to perform some operation within the editor that requires a lot of memory, and that operation can't be performed, I want the editor to tell me it can't do what I asked and let me keep editing. Terminating without saving my work would be an unacceptable response. Saving my work and terminating would be better, but is still unnecessarily user-hostile.


This is a difficult question. On first sight it seems having no more memory means "out of luck" but, you must also see that one can get rid of many memory related stuff if one really insist. Let's just take the in other ways broken function strtok which on one hand has no problems with memory stuff. Then take as counterpart g_string_split from the Glib library, which heavily depends on allocation of memory as nearly everything in glib or GObject based programs. One can definitly say in more dynamic languages memory allocation is much more used as in more inflexible languages, especially C. But let us see the alternatives. If you just end the program if you run out of memory, even careful developed code may stop working. But if you have a recoverable error, you can do something about it. So the argument, making it recoverable means that one can choose to "handle" that situation differently (e.g putting aside a memory block for emergencies, or degradation to a less memory extensive program).

So the most compelling reason is. If you provide a way of recovering one can try the recoverying, if you do not have the choice all depends on always getting enough memory...

Regards


It's just puzzling me now.

At work, we have a bundle of applications working together, and memory is running low. While the problem is either make the application bundle go 64-bit (and so, be able to work beyond the 2 Go limits we have on a normal Win32 OS), and/or reduce our use of memory, this problem of "How to recover from a OOM" won't quit my head.

Of course, I have no solution, but still play at searching for one for C++ (because of RAII and exceptions, mainly).

Perhaps a process supposed to recover gracefully should break down its processing in atomic/rollback-able tasks (i.e. using only functions/methods giving strong/nothrow exception guarantee), with a "buffer/pool of memory" reserved for recovering purposes.

Should one of the task fails, the C++ bad_alloc would unwind the stack, free some stack/heap memory through RAII. The recovering feature would then salvage as much as possible (saving the initial data of the task on the disk, to use on a later try), and perhaps register the task data for later try.

I do believe the use of C++ strong/nothrow guanrantees can help a process to survive in low-available-memory conditions, even if it would be akin memory swapping (i.e. slow, somewhat unresponding, etc.), but of course, this is only theory. I just need to get smarter on the subject before trying to simulate this (i.e. creating a C++ program, with a custom new/delete allocator with limited memory, and then try to do some work under those stressful condition).

Well...


Out of memory normally means you have to quit whatever you were doing. If you are careful about cleanup, though, it can leave the program itself operational and able to respond to other requests. It's better to have a program say "Sorry, not enough memory to do " than say "Sorry, out of memory, shutting down."


Out of memory can be caused either by free memory depletion or by trying to allocate an unreasonably big block (like one gig). In "depletion" cases memory shortage is global to the system and usually affects other applications and system services and the whole system might become unstable so it's wise to forget and reboot. In "unreasonably big block" cases no shortage actually occurs and it's safe to continue. The problem is you can't automatically detect which case you're in. So it's safer to make the error non-recoverable and find a workaround for each case you encounter this error - make your program use less memory or in some cases just fix bugs in code that invokes memory allocation.


There are already many good answers here. But I'd like to contribute with another perspective.

Depletion of just about any reusable resource should be recoverable in general. The reasoning is that each and every part of a program is basically a sub program. Just because one sub cannot complete to it's end at this very point in time, does not mean that the entire state of the program is garbage. Just because the parking lot is full of cars does not mean that you trash your car. Either you wait a while for a booth to be free, or you drive to a store further away to buy your cookies.

In most cases there is an alternative way. Making an out of error unrecoverable, effectively removes a lot of options, and none of us like to have anyone decide for us what we can and cannot do.

The same applies to disk space. It's really the same reasoning. And contrary to your insinuation about stack overflow is unrecoverable, i would say that it's and arbitrary limitation. There is no good reason that you should not be able to throw an exception (popping a lot of frames) and then use another less efficient approach to get the job done.

My two cents :-)


If you are really out of memory you are doomed, since you can not free anything anymore.

If you are out of memory, but something like a garbage collector can kick in and free up some memory you are non dead yet.

The other problem is fragmentation. Although you might not be out of memory (fragmented), you might still not be able to allocate the huge chunk you wanna have.


I know you asked for arguments for, but I can only see arguments against.

I don't see anyway to achieve this in a multi-threaded application. How do you know which thread is actually responsible for the out-of-memory error? One thread could allocating new memory constantly and have gc-roots to 99% of the heap, but the first allocation that fails occurs in another thread.

A practical example: whenever I have occurred an OutOfMemoryError in our Java application (running on a JBoss server), it's not like one thread dies and the rest of the server continues to run: no, there are several OOMEs, killing several threads (some of which are JBoss' internal threads). I don't see what I as a programmer could do to recover from that - or even what JBoss could do to recover from it. In fact, I am not even sure you CAN: the javadoc for VirtualMachineError suggests that the JVM may be "broken" after such an error is thrown. But maybe the question was more targeted at language design.


uClibc has an internal static buffer of 8 bytes or so for file I/O when there is no more memory to be allocated dynamically.


What is the compelling argument for making it a recoverable error?

In Java, a compelling argument for not making it a recoverable error is because Java allows OOM to be signalled at any time, including at times where the result could be your program entering an inconsistent state. Reliable recoery from an OOM is therefore impossible; if you catch the OOM exception, you can not rely on any of your program state. See No-throw VirtualMachineError guarantees


I have this:

void *smalloc(size_t size) {
  void *mem = null; 
  for(;;) {
   mem = malloc(size);
   if(mem == NULL) {
    sleep(1);
   } else 
     break;
  }
  return mem;
}

Which has saved a system a few times already. Just because you're out of memory now, doesn't mean some other part of the system or other processes running on the system have some memory they'll give back soon. You better be very very careful before attempting such tricks, and have all control over every memory you do allocate in your program though.

참고URL : https://stackoverflow.com/questions/333736/is-out-of-memory-a-recoverable-error

반응형