Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 6개 있습니다.)

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/3/2022]

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

1  2  3  4  5  6  7  8  9  [10]  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13373정성태6/19/20234398오류 유형: 865. 파이썬 - pymssql 설치 관련 오류 정리
13372정성태6/15/20233111개발 환경 구성: 682. SQL Server TLS 통신을 위해 사용되는 키 길이 확인 방법
13371정성태6/15/20233132개발 환경 구성: 681. openssl - 인증서 버전(V1 / V3)
13370정성태6/14/20233295개발 환경 구성: 680. C# - Ubuntu + Microsoft.Data.SqlClient + SQL Server 2008 R2 연결 방법 - TLS 1.2 지원
13369정성태6/13/20233094개발 환경 구성: 679. PyCharm(을 비롯해 JetBrains에 속한 여타) IDE에서 내부 Window들의 탭이 없어진 경우
13368정성태6/13/20233225개발 환경 구성: 678. openssl로 생성한 인증서를 SQL Server의 암호화 인증서로 설정하는 방법
13367정성태6/10/20233333오류 유형: 864. openssl로 만든 pfx 인증서를 Windows Server 2016 이하에서 등록 시 "The password you entered is incorrect" 오류 발생
13366정성태6/10/20233130.NET Framework: 2128. C# - 윈도우 시스템에서 지원하는 암호화 목록(Cipher Suites) 나열파일 다운로드1
13365정성태6/8/20232896오류 유형: 863. MODIFY FILE encountered operating system error 112(failed to retrieve text for this error. Reason: 15105)
13364정성태6/8/20233678.NET Framework: 2127. C# - Ubuntu + Microsoft.Data.SqlClient + SQL Server 2008 R2 연결 방법 [1]
13363정성태6/7/20233242스크립트: 49. 파이썬 - "Transformers (신경망 언어모델 라이브러리) 강좌" - 1장 2절 코드 실행 결과
13362정성태6/1/20233164.NET Framework: 2126. C# - 서버 측의 요청 제어 (Microsoft.AspNetCore.RateLimiting)파일 다운로드1
13361정성태5/31/20233638오류 유형: 862. Facebook - ASP.NET/WebClient 사용 시 graph.facebook.com/me 호출에 대해 403 Forbidden 오류
13360정성태5/31/20233036오류 유형: 861. WSL/docker - failed to start shim: start failed: io.containerd.runc.v2: create new shim socket
13359정성태5/19/20233354오류 유형: 860. Docker Desktop - k8s 초기화 무한 반복한다면?
13358정성태5/17/20233660.NET Framework: 2125. C# - Semantic Kernel의 Semantic Memory 사용 예제 [1]파일 다운로드1
13357정성태5/16/20233464.NET Framework: 2124. C# - Semantic Kernel의 Planner 사용 예제파일 다운로드1
13356정성태5/15/20233769DDK: 10. Device Driver 테스트 설치 관련 오류 (Code 37, Code 31) 및 인증서 관련 정리
13355정성태5/12/20233685.NET Framework: 2123. C# - Semantic Kernel의 ChatGPT 대화 구현 [1]파일 다운로드1
13354정성태5/12/20233957.NET Framework: 2122. C# - "Use Unicode UTF-8 for worldwide language support" 설정을 한 경우, 한글 입력이 '\0' 문자로 처리
13352정성태5/12/20233569.NET Framework: 2121. C# - Semantic Kernel의 대화 문맥 유지파일 다운로드1
13351정성태5/11/20234072VS.NET IDE: 185. Visual Studio - 원격 Docker container 내에 실행 중인 응용 프로그램에 대한 디버깅 [1]
13350정성태5/11/20233323오류 유형: 859. Windows Date and Time - Unable to continue. You do not have permission to perform this task
13349정성태5/11/20233661.NET Framework: 2120. C# - Semantic Kernel의 Skill과 Function 사용 예제파일 다운로드1
13348정성태5/10/20233570.NET Framework: 2119. C# - Semantic Kernel의 "Basic Loading of the Kernel" 예제
13347정성태5/10/20233933.NET Framework: 2118. C# - Semantic Kernel의 Prompt chaining 예제파일 다운로드1
1  2  3  4  5  6  7  8  9  [10]  11  12  13  14  15  ...