성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] 그냥 RSS Reader 기능과 약간의 UI 편의성 때문에 사용...
[이종효] 오래된 소프트웨어는 보안 위협이 되기도 합니다. 혹시 어떤 기능...
[정성태] @Keystroke IEEE의 문서를 소개해 주시다니... +_...
[손민수 (Keystroke)] 괜히 듀얼채널 구성할 때 한번에 같은 제품 사라고 하는 것이 아...
[정성태] 전각(Full-width)/반각(Half-width) 기능을 토...
[정성태] Vector에 대한 내용은 없습니다. Vector가 닷넷 BCL...
[orion] 글 읽고 찾아보니 디자인 타임에는 InitializeCompon...
[orion] 연휴 전에 재현 프로젝트 올리자 생각해 놓고 여의치 않아서 못 ...
[정성태] 아래의 글에 정리했으니 참고하세요. C# - Typed D...
[정성태] 간단한 재현 프로젝트라도 있을까요? 저런 식으로 설명만 해...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>C# - Java의 Xmx와 유사한 힙 메모리 최댓값 제어 옵션 HeapHardLimit</h1> <p> 재미있는 글이 있군요. ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Running with Server GC in a Small Container Scenario Part 1 – Hard Limit for the GC Heap ; <a target='tab' href='https://devblogs.microsoft.com/dotnet/running-with-server-gc-in-a-small-container-scenario-part-1-hard-limit-for-the-gc-heap/'>https://devblogs.microsoft.com/dotnet/running-with-server-gc-in-a-small-container-scenario-part-1-hard-limit-for-the-gc-heap/</a> </pre> <br /> 위의 글에 보면 COMPlus_GCHeapHardLimit 옵션이 나오는데요, 이게 Java의 Xmx와 유사한 역할을 합니다. 가볍게 테스트를 해볼까요? ^^<br /> <br /> .NET Core 3.1 + Debug 빌드로 다음의 코드를 마련하고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; using System.Runtime.InteropServices; namespace ConsoleApp2 { internal class Program { static void Main(string[] args) { CallGC(); ShowGCInfo(); Console.WriteLine(Environment.NewLine); } static void CallGC() { GC.Collect(2, GCCollectionMode.Forced, true); } static void ShowGCInfo() { var info = GC.GetGCMemoryInfo(); Console.WriteLine($"HeapSizeBytes: {info.HeapSizeBytes}"); Console.WriteLine($"TotalAvailableMemoryBytes: {info.TotalAvailableMemoryBytes}"); Console.WriteLine($"FragmentedBytes: {info.FragmentedBytes}"); Console.WriteLine($"HighMemoryLoadThresholdBytes: {info.HighMemoryLoadThresholdBytes}"); Console.WriteLine($"MemoryLoadBytes: {info.MemoryLoadBytes}"); } } } </pre> <br /> 이렇게 max 값을 주면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > c:\temp> <span style='color: blue; font-weight: bold'>set COMPlus_GCHeapHardLimit=33000</span> c:\temp> <span style='color: blue; font-weight: bold'>ConsoleApp2.exe</span> Failed to create CoreCLR, HRESULT: 0x8007000E </pre> <br /> 0x8007000E (Not enough memory resources are available to complete this) 오류가 발생합니다. 위에서 COMPlus_GCHeapHardLimit은 16진수 값을 받아들이는데, 따라서 0x33000 == 208,986 바이트로는 메모리 부족이 발생한 것입니다.<br /> <br /> 여기서 조금 늘려 실행하면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > c:\temp> <span style='color: blue; font-weight: bold'>set COMPlus_GCHeapHardLimit=33100</span> c:\temp> <span style='color: blue; font-weight: bold'>ConsoleApp2.exe</span> HeapSizeBytes: 57096 TotalAvailableMemoryBytes: 209152 FragmentedBytes: 120 HighMemoryLoadThresholdBytes: 61603382476 MemoryLoadBytes: 29432727183 </pre> <br /> 잘 실행이 됩니다. 위의 결과를 보면 COMPlus_GCHeapHardLimit의 설정값과 TotalAvailableMemoryBytes의 값이 동일한데요, 실제로 공식 문서에는,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > GCMemoryInfo.TotalAvailableMemoryBytes Property ; <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/api/system.gcmemoryinfo.totalavailablememorybytes'>https://learn.microsoft.com/en-us/dotnet/api/system.gcmemoryinfo.totalavailablememorybytes</a> </pre> <br /> TotalAvailableMemoryBytes가 GC Heap의 "Free"를 나타내진 않고 현재 프로세스에서 최대 사용할 수 있는 메모리의 크기를 나타낸다고 쓰여있습니다.<br /> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> This property value will be the value of the COMPlus_GCHeapHardLimit environment variable, or the Server.GC.HeapHardLimit value in runtimeconfig.json, if either is set.<br /> </div><br /> <br /> 그건 그렇고, <a target='tab' href='https://devblogs.microsoft.com/dotnet/running-with-server-gc-in-a-small-container-scenario-part-1-hard-limit-for-the-gc-heap/'>Running with Server GC in a Small Container Scenario Part 1 – Hard Limit for the GC Heap</a> 글에 보면, 닷넷 프로세스에서 사용하는 메모리를 크게 3분류로 나누는데,<br /> <br /> <ol> <li>이미 할당된 GC Heap 메모리와, 향후 GC가 사용할 목적으로 할당된 native memory</li> <li>닷넷 런타임에 의해 사용되는 native memory, 예를 들어 IL 코드를 번역한 기계어가 위치하는 jitted code heap</li> <li>닷넷 런타임 이외의 코드에서 할당된 메모리, 예를 들어 Pinvoke로 호출한 C/C++ DLL에서 할당한 메모리</li> </ol> <br /> TotalAvailableMemoryBytes는 과연 어떤 메모리에 대한 상한을 제한하는 것일까요? 일단 위의 메모리 중 당연히 3번은 제외했을 것이고, 그렇다면 1번만 해당할까요? 1, 2번 모두 해당할까요? 문서에 보면, "Specifies the maximum commit size, in bytes, for the GC heap and GC bookkeeping"라고 나오는 걸로 봐서는 1번만 포함하는 것이 맞습니다.<br /> <br /> 여기서 유의할 것은 "GC heap"과 함께 "GC가 예약하는 크기"가 포함된다는 점입니다. 이런 점은 간단하게 테스트로 확인할 수 있는데요, 위의 출력 결과를 보면, TotalAvailableMemoryBytes == 209152 값에서 현재 HeapSizeBytes가 57096이니까, 대충 152,056 바이트 여유 공간이 남습니다. 그런데, 다음과 같이 그에 한참 못 미치는 15,000 바이트 정도를 소비하는 byte 버퍼를 생성해 테스트하면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > static byte[] buffer = null; static void Main(string[] args) { <span style='color: blue; font-weight: bold'>buffer = new byte[15_000];</span> CallGC(); ShowGCInfo(); Console.WriteLine(Environment.NewLine); } /* 실행 결과 HeapSizeBytes: 72144 TotalAvailableMemoryBytes: 209152 FragmentedBytes: 120 HighMemoryLoadThresholdBytes: 61603382476 MemoryLoadBytes: 29432727183 <span style='color: blue; font-weight: bold'>Out of memory.</span> */ </pre> <br /> HeapSizeBytes는 15,048 바이트 정도 늘어났지만 저렇게 "Out of memory"가 프로그램의 말미에 발생하고 있습니다. 아마도 저 순간에 예약을 늘리는 코드가 동작했기 때문이 아닌가... 추측을 해봅니다.<br /> <br /> 당연하겠지만, native 메모리를 사용하는 것은 제약에 포함이 안 되므로 아래의 코드는 잘 실행됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // 아래의 제약에서도 정상 실행 // set COMPlus_GCHeapHardLimit=33100 static void Main(string[] args) { <span style='color: blue; font-weight: bold'>Marshal.AllocCoTaskMem(1024 * 1024 * 512);</span> CallGC(); ShowGCInfo(); Console.WriteLine(Environment.NewLine); } </pre> <br /> <hr style='width: 50%' /><br /> <br /> 해당 글에 보면, HeapHardLimit을 runtimeconfig.json에도 설정할 수 있다고 나오는데요, 그러면서 값을 "Server.GC.HeapHardLimit"라고 소개합니다. 하지만, 실제로 문서를 보면 "System.GC.HeapHardLimit"가 맞습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Runtime configuration options for garbage collection - Heap limit ; <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/core/runtime-config/garbage-collector#heap-limit'>https://learn.microsoft.com/en-us/dotnet/core/runtime-config/garbage-collector#heap-limit</a> </pre> <br /> 그래서 이렇게 설정할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // <a target='tab' href='https://www.sysnet.pe.kr/2/0/12983'>runtimeconfig.template.json</a> // <a target='tab' href='https://github.com/steveharter/dotnet_coreclr/blob/master/Documentation/project-docs/clr-configuration-knobs.md'>Host Configuration Knobs</a> { "configProperties": { "System.GC.HeapHardLimit": 209152 } } </pre> <br /> 유의할 점은, 환경 변수에서는 16진수로 값을 설정했지만 위의 json에서는 10진수로 설정한다는 것입니다.<br /> <br /> 또한, HeapHardLimit와 함께 "Heap limit percent"도 제공하고 있으니 관심 있으신 분은 문서를 읽어보시길. ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Heap limit percent ; <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/core/runtime-config/garbage-collector#heap-limit-percent'>https://learn.microsoft.com/en-us/dotnet/core/runtime-config/garbage-collector#heap-limit-percent</a> </pre> <br /> 참고로, <a target='tab' href='https://devblogs.microsoft.com/dotnet/running-with-server-gc-in-a-small-container-scenario-part-1-hard-limit-for-the-gc-heap/'>Running with Server GC in a Small Container Scenario Part 1 – Hard Limit for the GC Heap</a> 글에서 container가 자주 언급되는데, 아마도 이런 설정들은 근래의 container 환경이 대두됨에 따라 추가된 듯합니다.<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1082
(왼쪽의 숫자를 입력해야 합니다.)