이번에 다루어볼 C# 예외는 Out Of Memory입니다.
아웃 오브 메모리(Out Of Memory: 이하 OOM이라 하겠습니다)는 개발 당시에는 발생하지 않다가 상용화 과정에서 발생하는 오류 중 하나입니다.
보통 디버깅 시간이 길지 않다 보니 메모리 예외가 발생하기 전에 새로운 디버깅으로 메모리가 초기화되기 때문일 것입니다.
Out Of Memory Exception
의외로 많은 개발자가 OOM을 컴퓨터 부품 중 하나인 기억장치(RAM : random access memory, 보통 메모리라 부릅니다.)와 관련이 있을 것이라 생각합니다. 하지만 마이크로소프트의 공식 답변에 의하면 램은 OOM과 전혀 관련이 없습니다.
실제 RAM 크기와 상관 없이 32비트 운영체제는 4기가바이트의 가상주소공간을 할당합니다. 이 중에 2기가는 운영체제를 위해 예약되어 있으며, 나머지 2기가는 사용자가 사용할 수 있도록 할당합니다. 각각, Kernel-mode memory와 User-mode memory라고 하며 커널은 운영체제 핵심 프로그램을 뜻합니다. 커널모드 메모리 2기가는 모든 프로세스가 공유하고, 유저모드 메모리 2기가는 각 프로세스마다 가상주소공간을 갖습니다.
가비지 콜렉터의 매니지드 힙 관리 방법
- 85KB 미만 스몰 오브젝트 힙 => 64MB 메모리 할당
- 라지 오브젝트 힙 => 16MB 메모리 할당, 2기가 바이트 내의 인접한 블록에서 메모리 확장이 가능하여야 합니다. 이때 메모리가 초과하는 경우 Out Of Memory Exception이 발생합니다.
운영체제가 32비트인가, 64비트인가에 따라 메모리 크기가 달라집니다.
다음은 64비트 운영체제에서 32비트 프로세스와 64비트 프로세스가 가동할 때입니다.
[64비트 운영체제] | 32비트 프로세스 실행 시 | 64비트 프로세스 실행 시 |
유저 모드 메모리 | 4기가바이트 | 8테라바이트 |
64비트에서 64비트 프로세스는 8테라나 할당이 되기 때문에 웬만하면 OOM이 발생하지 않습니다. 빌드 이벤트에서 32비트 기본 사용을 체크 해제하여 64비트로 구동하도록 하는 방법도 하나의 해결방법이 될 수 있습니다.
문자열 연결 변경하기
문자열에 새로운 값이 할당되면 현재 값의 복사본이 메모리 상에 계속 남아있습니다. 과도한 문자열 연결은 메모리 문제와 관련이 있으니, 문자열 연결이 너무 많은 경우에는 string 대신에 StringBuilder와 같은 클래스 사용을 검토해 주세요.
데이터베이스 대용량 데이터 조회 확인
반환되는 값을 제한하도록 하세요. 데이터베이스의 전체 데이터를 캐싱하는 것은 OOM의 발생의 원인이 될 수 있습니다. 페이징 처리 또는 입력값을 검증하며 최소한의 데이터를 활용하도록 합니다. SELECT * FROM ~ 모든 데이터 조회를 조심하세요.
SELECT * FROM EMP;
트레이싱 중단해 보기(ASP.NET)
사용 환경에서 트레이싱이 켜져 있는 경우, 시간이 지나면서 트레이싱 데이터가 OOM을 발생시킬 수 있습니다. 실제로 사용할 때에는 TRACE 속성을 FALSE로 해보세요. <deploy retail="true" /> 설정을 고려합니다.
네이티브 리소스 누수 확인
IDisposable 인터페이스를 상속하는 객체는 반드시 Dispose 메서드로 네이티브 리소스를 지워서 누수가 일어나지 않도록 합니다.
메모리 단편화(매니지드 힙, 가상주소 공간)
메모리 단편화 관련은 아마 가능성이 낮아보이기는 하는데 마이크로소프트에서는 서술하고 있네요.
1. 객체를 메모리에 고정(pin)시키는 방법은 권유하지 않습니다.
2. 필요한 어셈블리를 여러 애플리케이션 도메인에 올리는 것을 권유하지 않습니다. 이때에는 GAC(Global Assembly Cache)에 올리도록 권장합니다.