Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 5개 있습니다.)
(시리즈 글이 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/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
정성태

... 46  [47]  48  49  50  51  52  53  54  55  56  57  58  59  60  ...
NoWriterDateCnt.TitleFile(s)
12639정성태5/12/202112387오류 유형: 715. Arduino: Open Serial Monitor - The module '...\detection.node' was compiled against a different Node.js version using NODE_MODULE_VERSION
12638정성태5/12/202113523사물인터넷: 63. NodeMCU v1 ESP8266 - 펌웨어 내 파일 시스템(SPIFFS, LittleFS) 및 EEPROM 활용
12637정성태5/10/202113027사물인터넷: 62. NodeMCU v1 ESP8266 보드의 A0 핀에 다중 아날로그 센서 연결 [1]
12636정성태5/10/202113221사물인터넷: 61. NodeMCU v1 ESP8266 보드의 A0 핀 사용법 - FSR-402 아날로그 압력 센서 연동파일 다운로드1
12635정성태5/9/202112260기타: 81. OpenTabletDriver를 (관리자 권한으로 실행하지 않고도) 관리자 권한의 프로그램에서 동작하게 만드는 방법
12634정성태5/9/202111051개발 환경 구성: 572. .NET에서의 신뢰도 등급 조정 - 외부 Manifest 파일을 두는 방법파일 다운로드1
12633정성태5/7/202113150개발 환경 구성: 571. UAC - 관리자 권한 없이 UIPI 제약을 없애는 방법
12632정성태5/7/202114117기타: 80. (WACOM도 지원하는) Tablet 공통 디바이스 드라이버 - OpenTabletDriver
12631정성태5/5/202113242사물인터넷: 60. ThingSpeak 사물인터넷 플랫폼에 ESP8266 NodeMCU v1 + 조도 센서 장비 연동파일 다운로드1
12630정성태5/5/202113631사물인터넷: 59. NodeMCU v1 ESP8266 보드의 A0 핀 사용법 - CdS Cell(GL3526) 조도 센서 연동파일 다운로드1
12629정성태5/5/202115469.NET Framework: 1057. C# - CoAP 서버 및 클라이언트 제작 (UDP 소켓 통신) [1]파일 다운로드1
12628정성태5/4/202113289Linux: 39. Eclipse 원격 디버깅 - Cannot run program "gdb": Launching failed
12627정성태5/4/202113914Linux: 38. 라즈베리 파이 제로 용 프로그램 개발을 위한 Eclipse C/C++ 윈도우 환경 설정
12626정성태5/3/202113937.NET Framework: 1056. C# - Thread.Suspend 호출 시 응용 프로그램 hang 현상 (2)파일 다운로드1
12625정성태5/3/202112733오류 유형: 714. error CS5001: Program does not contain a static 'Main' method suitable for an entry point
12624정성태5/2/202117030.NET Framework: 1055. C# - struct/class가 스택/힙에 할당되는 사례 정리 [10]파일 다운로드1
12623정성태5/2/202113239.NET Framework: 1054. C# 9 최상위 문에 STAThread 사용 [1]파일 다운로드1
12622정성태5/2/20219572오류 유형: 713. XSD 파일을 포함한 프로젝트 - The type or namespace name 'TypedTableBase<>' does not exist in the namespace 'System.Data'
12621정성태5/1/202114177.NET Framework: 1053. C# - 특정 레지스트리 변경 시 알림을 받는 방법 [1]파일 다운로드1
12620정성태4/29/202116333.NET Framework: 1052. C# - 왜 구조체는 16 바이트의 크기가 적합한가? [1]파일 다운로드1
12619정성태4/28/202116366.NET Framework: 1051. C# - 구조체의 크기가 16바이트가 넘어가면 힙에 할당된다? [2]파일 다운로드1
12618정성태4/27/202114856사물인터넷: 58. NodeMCU v1 ESP8266 CP2102 Module을 이용한 WiFi UDP 통신 [1]파일 다운로드1
12617정성태4/26/202112663.NET Framework: 1050. C# - ETW EventListener의 Keywords별 EventId에 따른 필터링 방법파일 다운로드1
12616정성태4/26/202112506.NET Framework: 1049. C# - ETW EventListener를 상속받았을 때 초기화 순서파일 다운로드1
12615정성태4/26/20219951오류 유형: 712. Microsoft Live 로그인 - 계정을 선택하는(Pick an account) 화면에서 진행이 안 되는 문제
12614정성태4/24/202113408개발 환경 구성: 570. C# - Azure AD 인증을 지원하는 ASP.NET Core/5+ 웹 애플리케이션 예제 구성 [4]파일 다운로드1
... 46  [47]  48  49  50  51  52  53  54  55  56  57  58  59  60  ...