Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 6개 있습니다.)
(시리즈 글이 6개 있습니다.)
.NET Framework: 188. .NET 64비트 응용 프로그램에서 왜 (2GB) OutOfMemoryException 예외가 발생할까?
; https://www.sysnet.pe.kr/2/0/946

.NET Framework: 266. StringBuilder에서의 OutOfMemoryException 오류 원인 분석
; https://www.sysnet.pe.kr/2/0/1171

.NET Framework: 357. .NET 4.5의 2GB 힙 한계 극복
; https://www.sysnet.pe.kr/2/0/1403

.NET Framework: 367. LargeAddressAware 옵션이 적용된 닷넷 32비트 프로세스의 가용 메모리
; https://www.sysnet.pe.kr/2/0/1441

.NET Framework: 640. 닷넷 - 배열 크기의 한계
; https://www.sysnet.pe.kr/2/0/11142

.NET Framework: 2105. LargeAddressAware 옵션이 적용된 닷넷 32비트 프로세스의 가용 메모리 - 두 번째
; https://www.sysnet.pe.kr/2/0/13294




LargeAddressAware 옵션이 적용된 닷넷 32비트 프로세스의 가용 메모리

서버 측 개발 환경에서는 이제 거의 64비트가 대세를 이룬 것 같습니다. 그래도 여전히 가끔은 32비트 DLL과의 호환 때문에 프로세스까지도 32비트로 제한하는 경우가 종종 있는데요.

32비트 EXE의 경우, 32비트 운영체제에서는 기본적으로 2GB 상한값을 갖습니다. 한번 테스트 해볼까요? ^^ 다음의 간단한 프로그램을 실행하고,

using System;
using System.Runtime.InteropServices;

class Program
{
    static void Main(string[] args)
    {
        int gb4 = 1024 * 1024;

        for (int i = 0; i < gb4; i++)
        {
            Marshal.AllocCoTaskMem(4096); // 의도적으로 4KB 씩 메모리 누수
        }
    }
}

"Unhandled Exception: OutOfMemoryException." 예외가 발생하는 시점의 Task Manager를 확인해 보면 Commit Size == 1,978,052KB (약 1931MB)를 점유합니다. (물론, 구체적인 수치는 컴퓨터마다 달라질 수 있습니다.)

4GB까지 주소 지정이 가능한 32비트 응용 프로그램에게 이런 2GB 제한이 있는 것은 커널 주소 영역으로 2GB를 미리 예약하고 있기 때문입니다.





2GB를 넘어서 3GB로!

점점 더 응용 프로그램의 메모리 사용량이 커지면서 2GB는 너무나 작은 메모리가 되어 버렸습니다. 그래서 마이크로소프트는 EXE 프로세스가 2GB 상한을 넘어 사용할 수 있다는 LargeAddressAware 플래그를 추가했습니다.

하지만, Visual Studio는 닷넷 프로그램에 대해 기본적으로 LargeAddressAware 옵션을 적용하지 않는 것은 물론이고 아예 프로젝트 속성창에서 선택할 수 있는 옵션 자체가 없습니다. (Visual C++ 프로젝트에는 있습니다.)

LargeAddressAware 옵션이 적용된 EXE인가 하는 것은 닷넷 EXE 파일에 대해 dumpbin으로 확인해 보면 알 수 있는데, 보통은 다음과 같이 "characteristics"에 2개의 값만 나옵니다.

C:\ConsoleApplication2\bin\Debug>dumpbin /HEADERS ConsoleApplication2.exe
Microsoft (R) COFF/PE Dumper Version 11.00.60315.1
Copyright (C) Microsoft Corporation.  All rights reserved.

Dump of file ConsoleApplication2.exe

PE signature found

File Type: EXECUTABLE IMAGE

FILE HEADER VALUES
             14C machine (x86)
             ...[생략]...
             102 characteristics
                   Executable
                   32 bit word machine

닷넷 프로젝트에서 LargeAddressAware 옵션을 켜려면 editbin.exe를 이용해 빌드 이벤트 구성을 해야 합니다. 예전에 editbin.exe를 적용하는 방법에 대해서는 잠깐 소개해 드렸었지요. ^^

DEP 비호환 ActiveX 오류
; https://www.sysnet.pe.kr/2/0/773

그래서, "Post-build event command line"에 다음과 같이 옵션을 적용해 주면 됩니다.

call "$(DevEnvDir)..\tools\vsvars32.bat"
editbin.exe /largeaddressaware  "$(TargetPath)"

이렇게 적용하고 다시 한번 dumpbin으로 확인을 해보면 다음과 같이 새롭게 플래그가 적용된 것을 볼 수 있습니다.

122 characteristics
      Executable
      Application can handle large (>2GB) addresses
      32 bit word machine

그런데, 정말 효력이 있을까요? 4GB 메모리의 Windows Server 2008 x86에서 테스트 해보았습니다. 그런데, 별로 바뀐 점 없이 2GB 내에서 OOM 예외가 발생했습니다. ^^ 왜냐하면 사용자 메모리가 2GB를 넘어선다는 것을 운영체제도 알아야 하기 때문입니다. 그래서, Vista/2008 운영체제에서는 bcdedit.exe로 다음과 같은 명령어를 관리자 권한으로 실행하고 재부팅해야 합니다.

설정: bcdedit.exe /set IncreaseUserVA 3072
해제: bcdedit.exe /deletevalue IncreaseUserVA

이 경우, Windows Server 2008 x86에서 작업 관리자의 Commit Size가 3,142,268KB (약 3,068MB)까지 올라간 후 OOM 예외가 발생합니다.

하지만, 이것이 꼭 긍정적인 효과만 있는 것은 아닙니다. 왜냐하면, CPU 레지스터의 4GB 메모리 주소 지정에는 변함이 없는 상태에서 강제로 운영체제 차원에서 커널 1GB, 사용자 영역 3GB로 제한을 둔 것이기 때문에 자칫 커널 메모리가 많이 필요한 프로그램이 있다면 문제가 발생할 여지가 있습니다.





3GB를 넘어서 4GB로!

그렇다면, /largeaddressaware x86 응용 프로그램을 64비트 운영체제에서 실행하면 어떻게 될까요?

64비트 운영체제에 설치된 커널 디바이스 드라이버 프로그램들은 반드시 64비트여야 합니다. 즉, 64비트 윈도우는 사용자 프로그램에 대해서만 WOW64를 통해서 32비트/64비트 모두 실행해 주는 것일 뿐, 커널 측에는 오직 64비트만 허용됩니다.

따라서, 64비트 운영체제는 "bcdedit.exe /set IncreaseUserVA ..." 식의 설정이 필요없고 응용 프로그램만 /largeaddressaware 플래그만 적용되어 있다면 32비트로 허용된 4GB 영역 모두를 사용할 수 있습니다. 커널 드라이버가 모두 64비트로 작성되었으므로 하위 4GB에 배치시킬 필요가 없어 32비트 응용 프로그램에게 완전하게 4GB 주소 영역을 내줄 수 있는 것입니다.

실제로, "/largeaddressaware가 적용된 x86 응용 프로그램"을 "Windows Server 2008 R2"에서 실행시키면 Commit Size == 4,079,060KB (약 3,983MB)까지 올라갑니다.

edit_bin_test_1.png

따라서, 메모리 압박이 심한 32비트 응용 프로그램을 위해 운영체제를 64비트로 업그레이드하는 것은 어느 정도는 의미가 있습니다. ^^




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 12/14/2024]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2014-10-24 01시42분
How can I detect programmatically whether the /3GB switch is enabled?
; https://devblogs.microsoft.com/oldnewthing/20141023-00/?p=43783
정성태
2016-10-19 10시59분
[차가워] 안녕하세요
비주얼스튜디오2015, 윈도우10, 32비트 프로젝트
윗 명령줄을 넣어도 1.3기가를 초과할 경우 OOM 메시지를 출력합니다
[guest]
2016-10-19 11시05분
[차가워] 타켓을 anycpu로 하니 잘 작동하네요
[guest]
2016-10-19 11시34분
@차가워 님 윈도우 10은 64비트에서 테스트하신 건가요? 그런 경우라면 AnyCPU로 한 경우, 64비트 응용 프로그램으로 동작하기 때문에 OOM이 발생하지 않습니다. 32비트 운영체제를 대상으로 1.3GB에서 OOM이 발생했다면, .exe 파일을 다시 한번 명령행에서 dumpbin.exe를 이용해 "Application can handle large (>2GB) addresses" 표시가 있는지 확인해 보시고, 아울러 운영체제도 "bcdedit.exe /set IncreaseUserVA 3072" 옵션을 실행한 후 부팅해야 합니다.
정성태
2018-08-18 04시24분
[이범철] 명령줄 넣어도~ 메모리가 1.7기가 넘어가니까 OOM 발생하는데요~
물론 OS 62bit 입니다
[guest]
2018-08-18 06시31분
다음의 글을 참고하세요.

.NET 4.5의 2GB 힙 한계 극복
; http://www.sysnet.pe.kr/2/0/1403

(이범철 님, 다음부터는 OOM이 발생한 상황에 대한 설명도 좀 추가해 주세요. 그냥 발생했다고 하면... ^^;;;)
정성태
2023-03-21 03시27분
[김훈] windows10 64bit 환경에서 32bit c# .net 프로그램을 작성합니다.
dumpbin 으로 실행파일에서 "Application can handle large (>2GB) addresses" 를 확인했지만
commit 메모리 2.6 ~ 2.7 GByte 정도에서 out of memory 오류가 발생합니다.

64bit windows 환경인데 4G 메모리 까지 사용하려면 또 어떤 설정이 필요할까요?
[guest]
2023-03-21 03시30분
[김훈] 아.. out of memory 테스트는 임의의 byte[1000] 을 List 에 계속 add 하는 방법으로 테스트 합니다.
[guest]
2023-03-21 03시46분
@김훈 이 글의 예제 코드로도 3GB 벽을 못 넘나요?
정성태
2023-03-22 09시06분
[김훈] 아.. 아래와 같이 거의 4G 못미쳐서 oom 이 발생하네요
2.6 에서의 oom 은 GC 에서의 total memory 값인데 , 아마도 managed heap 사이즈가 아닐까 합니다.
메모리가 알아볼려고 하면 알아볼수록 너무 어렵네요...

Alloc [ 3,933,588 MB ]
Alloc [ 3,933,592 MB ]

Unhandled Exception: OutOfMemoryException.
[guest]
2023-03-22 09시19분
[김훈] static void Main(string[] args)
        {
            long total = 0;
            List<byte[]> load = new List<byte[]>();

            int gb4 = 1024 * 1024;
            for (int i = 0; i < gb4; i++)
            {
                Marshal.AllocCoTaskMem(4096);

                total += 4096;

                // load 없음 -- 1번
                load.Add(new byte[1024]); // 2번
                load.Add(new byte[4096]); // 3번
                Console.WriteLine($"Alloc [ {(int)(total / 1024):n0} MB ], GC [ {(int)(GC.GetTotalMemory(false) / 1024):n0} MB]");
            }
        }

이와같이 테스트하여
[guest]
2023-03-22 09시20분
[김훈] 1번결과

Alloc [ 3,933,352 MB ], GC [ 4,447 MB]
Alloc [ 3,933,356 MB ], GC [ 4,447 MB]
Alloc [ 3,933,360 MB ], GC [ 4,447 MB]
Alloc [ 3,933,364 MB ], GC [ 4,447 MB]

Unhandled Exception: OutOfMemoryException.


2번결과

Alloc [ 2,911,084 MB ], GC [ 741,192 MB]
Alloc [ 2,911,088 MB ], GC [ 741,200 MB]
Alloc [ 2,911,092 MB ], GC [ 741,200 MB]
Alloc [ 2,911,096 MB ], GC [ 741,200 MB]
Alloc [ 2,911,100 MB ], GC [ 741,200 MB]
Alloc [ 2,911,104 MB ], GC [ 741,200 MB]

처리되지 않은 예외: OutOfMemoryException.



3번결과
Alloc [ 1,763,224 MB ], GC [ 1,771,058 MB]
Alloc [ 1,763,228 MB ], GC [ 1,771,066 MB]
Alloc [ 1,763,232 MB ], GC [ 1,771,066 MB]
Alloc [ 1,763,236 MB ], GC [ 1,771,074 MB]
Alloc [ 1,763,240 MB ], GC [ 1,771,074 MB]

처리되지 않은 예외: OutOfMemoryException.

이런 결과가 나오네요, 혹시 Marshal.AllocCoTaskMem(4096); 이 코딩은 어느 메모리를 Alloc 하는 걸까요?
[guest]
2023-03-22 09시31분
[김훈] 혹시 GC.GetTatalMemory() 이외의
메모리 사용량을 조회하는 함수가 있을까요?
(Marshal.AllocCoTaskMem() 이 alloc 하는 메모리의 현재 총 메모리 )
[guest]
2023-03-22 09시27분
@김훈 아래의 글을 읽어보시고, 그래도 궁금하신 것이 있다면 다시 질문해 주세요.

LargeAddressAware 옵션이 적용된 닷넷 32비트 프로세스의 가용 메모리 - 두 번째
; https://www.sysnet.pe.kr/2/0/13294
정성태

... 151  152  153  154  155  156  [157]  158  159  160  161  162  163  164  165  ...
NoWriterDateCnt.TitleFile(s)
1123정성태9/17/201165154Windows: 53. 2가지 모드의 Internet Explorer 10과 ActiveX [6]
1122정성태9/16/201132906Windows: 52. 새롭게 지원되는 WinRT 응용 프로그램 [7]
1121정성태9/12/201127617Java: 5. WTP 내에서 서블릿을 실행하는 환경
1120정성태9/11/201127529.NET Framework: 239. IHttpHandler.IsReusable 속성 이야기파일 다운로드1
1119정성태9/11/201126643Java: 4. 이클립스에 WTP SDK가 설치되지 않는다면? [2]
1118정성태9/11/201138307Java: 3. 이클립스에서 서블릿 디버깅하는 방법 [4]
1117정성태9/9/201125580제니퍼 .NET: 17. 제니퍼 닷넷 적용 사례 (2) - 웹 애플리케이션 hang의 원인을 알려주다.
1116정성태9/8/201156645Java: 2. 자바에서 "Microsoft SQL Server JDBC Driver" 사용하는 방법
1115정성태9/4/201130141Java: 1. 닷넷 개발자가 처음 실습해 본 서블릿
1114정성태9/4/201134655Math: 2. "Zhang Suen 알고리즘(세선화, Thinning/Skeletonization)"의 C# 버전 [4]파일 다운로드1
1113정성태9/2/201134231개발 환경 구성: 129. Hyper-V에 CentOS 설치하기
1112정성태9/2/201150967Linux: 1. 리눅스 <-> 윈도우 원격 접속 프로그램 사용 [3]
1111정성태8/29/201125420제니퍼 .NET: 16. 적용 사례 (1) - DB Connection Pooling을 사용하지 않았을 때의 성능 저하를 알려주다. [1]
1110정성태8/26/201126762오류 유형: 136. RDP 접속이 불연속적으로 끊기는 문제
1109정성태8/26/201129642오류 유형: 135. 어느 순간 Active Directory 접속이 안되는 문제
1108정성태8/22/201131173오류 유형: 134. OLE/COM Object Viewer - DllRegisterServer in IVIEWERS.DLL failed. [1]
1107정성태8/21/201128949디버깅 기술: 43. Windows Form의 Load 이벤트에서 발생하는 예외가 Visual Studio에서 잡히지 않는 문제
1106정성태8/20/201127263웹: 26. FailedRequestTracing 설정으로 인한 iisexpress.exe 비정상 종료 문제
1105정성태8/19/201127183.NET Framework: 238. Web Site Model 프로젝트에서 Trace.WriteLine 출력이 dbgview.exe에서 확인이 안 되는 문제파일 다운로드1
1104정성태8/19/201127349웹: 25. WebDev보다 IIS Express가 더 나은 점 - 다중 가상 디렉터리 매핑 [1]
1103정성태8/19/201133233오류 유형: 133. WCF 포트 바인딩 실패 오류 - TCP error(10013) [1]
1102정성태8/19/201130994Math: 1. 방탈출3 - Room 10의 '중복가능한 조합' 문제를 위한 C# 프로그래밍 [2]파일 다운로드1
1101정성태8/19/201129663.NET Framework: 237. WCF AJAX 서비스와 JavaScript 간의 DateTime 연동 [1]파일 다운로드1
1100정성태8/17/201128770.NET Framework: 236. SqlDbType - DateTime, DateTime2, DateTimeOffset의 차이점파일 다운로드1
1099정성태8/15/201128179오류 유형: 132. 어느 순간 갑자기 접속이 안 되는 TFS 서버
1098정성태8/15/201150197웹: 24. 네이버는 어떻게 로그인 처리를 할까요? [2]
... 151  152  153  154  155  156  [157]  158  159  160  161  162  163  164  165  ...