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
정성태

... 61  62  63  64  [65]  66  67  68  69  70  71  72  73  74  75  ...
NoWriterDateCnt.TitleFile(s)
12314정성태9/7/202019958오류 유형: 645. IIS HTTPERR - Timer_MinBytesPerSecond, Timer_ConnectionIdle 로그
12313정성태9/6/202019447개발 환경 구성: 509. Logstash - 사용자 정의 grok 패턴 추가를 이용한 IIS 로그 처리
12312정성태9/5/202026415개발 환경 구성: 508. Logstash 기본 사용법 [2]
12311정성태9/4/202018959.NET Framework: 937. C# - 간단하게 만들어 보는 리눅스의 nc(netcat), json_pp 프로그램 [1]
12310정성태9/3/202018270오류 유형: 644. Windows could not start the Elasticsearch 7.9.0 (elasticsearch-service-x64) service on Local Computer.
12309정성태9/3/202017490개발 환경 구성: 507. Elasticsearch 6.6부터 기본 추가된 한글 형태소 분석기 노리(nori) 사용법
12308정성태9/2/202019353개발 환경 구성: 506. Windows - 단일 머신에서 단일 바이너리로 여러 개의 ElasticSearch 노드를 실행하는 방법
12307정성태9/2/202020227오류 유형: 643. curl - json_parse_exception / Invalid UTF-8 start byte
12306정성태9/1/202017484오류 유형: 642. SQL Server 시작 오류 - error code 10013
12305정성태9/1/202019330Windows: 172. "Administered port exclusions"이 아닌 포트 범위 항목을 삭제하는 방법
12304정성태8/31/202017744개발 환경 구성: 505. 윈도우 - (네트워크 어댑터의 우선순위로 인한) 열거되는 IP 주소 순서를 조정하는 방법
12303정성태8/30/202018049개발 환경 구성: 504. ETW - 닷넷 프레임워크 기반의 응용 프로그램을 위한 명령행 도구 etrace 소개
12302정성태8/30/202018229.NET Framework: 936. C# - ETW 관련 Win32 API 사용 예제 코드 (5) - Private Logger파일 다운로드1
12301정성태8/30/202017811오류 유형: 641. error MSB4044: The "Fody.WeavingTask" task was not given a value for the required parameter "IntermediateDir".
12300정성태8/29/202018104.NET Framework: 935. C# - ETW 관련 Win32 API 사용 예제 코드 (4) CLR ETW Consumer파일 다운로드1
12299정성태8/27/202018832.NET Framework: 934. C# - ETW 관련 Win32 API 사용 예제 코드 (3) ETW Consumer 구현파일 다운로드1
12298정성태8/27/202018344오류 유형: 640. livekd - Could not resolve symbols for ntoskrnl.exe: MmPfnDatabase
12297정성태8/25/202017824개발 환경 구성: 503. SHA256 테스트 인증서 생성 방법
12296정성태8/24/202018921.NET Framework: 933. C# - ETW 관련 Win32 API 사용 예제 코드 (2) NT Kernel Logger파일 다운로드1
12295정성태8/24/202017810오류 유형: 639. Bitvise - Address is already in use; bind() in ListeningSocket::StartListening() failed: Windows error 10013: An attempt was made to access a socket ,,,
12293정성태8/24/202018771Windows: 171. "Administered port exclusions" 설명
12292정성태8/20/202021766.NET Framework: 932. C# - ETW 관련 Win32 API 사용 예제 코드 (1)파일 다운로드2
12291정성태8/15/202018721오류 유형: 638. error 1297: Device driver does not install on any devices, use primitive driver if this is intended.
12290정성태8/11/202019912.NET Framework: 931. C# - IP 주소에 따른 국가별 위치 확인 [8]파일 다운로드1
12289정성태8/6/202016876개발 환경 구성: 502. Portainer에 윈도우 컨테이너를 등록하는 방법
12288정성태8/5/202016010오류 유형: 637. WCF - The protocol 'net.tcp' does not have an implementation of HostedTransportConfiguration type registered.
... 61  62  63  64  [65]  66  67  68  69  70  71  72  73  74  75  ...