성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Roll A Lisp In C - Reading ; https...
[정성태] Java - How to use the Foreign Funct...
[정성태] 제가 큰 실수를 했군요. ^^; Delegate를 통한 Bein...
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
글쓰기
제목
이름
암호
전자우편
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'>닷넷 - 배열 크기의 한계</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;' > 8GB 이상의 byte 배열 생성 ; <a target='tab' href='https://social.msdn.microsoft.com/Forums/ko-KR/fd578b7f-d3c8-4f00-9708-2407a03653f2/8gb-byte-?forum=visualcsharpko'>https://social.msdn.microsoft.com/Forums/ko-KR/fd578b7f-d3c8-4f00-9708-2407a03653f2/8gb-byte-?forum=visualcsharpko</a> </pre> <br /> 위의 글에 대한 답변으로 예전에 썼던 .NET GC Heap을 2GB 넘게 사용하는 방법을 무심코 써먹었는데요. ^^;<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > .NET 4.5의 2GB 힙 한계 극복 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/1403'>http://www.sysnet.pe.kr/2/0/1403</a> </pre> <br /> 아쉽게도 이것은 다른 문제입니다. 정리하면, GC Heap을 2GB 넘게 사용하는 것은 gcAllowVeryLargeObjects 옵션을 이용해 가능하지만 그렇다고 해서 배열의 요소 수가 2<sup>32</sup>을 넘지는 못합니다. 이에 대해서는 gcAllowVeryLargeObjects 문서에 자세하게 나와 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <gcAllowVeryLargeObjects> Element ; <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/framework/configure-apps/file-schema/runtime/gcallowverylargeobjects-element'>https://learn.microsoft.com/en-us/dotnet/framework/configure-apps/file-schema/runtime/gcallowverylargeobjects-element</a> </pre> <br /> <ul> <li>The maximum number of elements in an array is UInt32.MaxValue.</li> <li>The maximum index in any single dimension is 2,147,483,591 (0x7FFFFFC7) for byte arrays and arrays of single-byte structures, and 2,146,435,071 (0X7FEFFFFF) for other types.</li> <li>The maximum size for strings and other non-array objects is unchanged.</li> </ul> <br /> 위의 의미를 해석해 보면!<br /> <br /> 우선, 단일 차원의 크기는 Int32.MaxValue에서 56을 뺀 2,147,483,591(0x7FFFFFC7)이 최대 값입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > byte[] t1 = new byte[Int32.MaxValue - 56]; // Int32.MaxValue - 56 == 2,147,483,591 </pre> <br /> 또한, 다중 차원으로 하는 경우 전체 요소의 수는 UInt32.MaxValue까지 가능합니다. 따라서, 다음과 같이 배열 할당은 가능하지만,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > byte[,] t = new byte[Int32.MaxValue - 56, <span style='color: blue; font-weight: bold'>2</span>]; // 2,147,483,591 * 2 <= UInt32.MaxValue (4,294,967,295) byte[,] t = new byte[<span style='color: blue; font-weight: bold'>65537</span>, <span style='color: blue; font-weight: bold'>65535</span>]; // 65,537 * 65,535 <= UInt32.MaxValue (4294967295) </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;' > byte[,] t = new byte[<span style='color: blue; font-weight: bold'>Int32.MaxValue</span>, 1]; byte[,] t = new byte[Int32.MaxValue - 56, <span style='color: blue; font-weight: bold'>3</span>]; // 2,147,483,591 * 3 > UInt32.MaxValue (4294967295) byte[,] t = new byte[65537, <span style='color: blue; font-weight: bold'>65536</span>]; // 65,537 * 65,536 > UInt32.MaxValue (4294967295) </pre> <br /> 마지막으로 단일 문자열의 크기는 2GB를 넘을 수 없습니다. 즉, 문자 하나의 크기가 2바이트이므로 Int32.MaxValue / 2 == 1,073,741,823개의 글자만 담을 수 있는데 실제로 테스트해 보면 32를 뺀 1,073,741,791까지만 가능합니다. 즉, 다음은 허용 가능하지만,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > int len = 1073741823 - 32; string s1 = new string('c', len); </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;' > int len = 1073741823 - 31; string s1 = new string('c', len); </pre> <br /> OOM(System.OutOfMemoryException) 예외가 발생합니다.<br /> <br /> ("other non-array objects"라는 것은 단일 클래스로 내부의 primitive 멤버 만으로 2GB를 넘는 경우일 텐데 이런 상황은 거의 없을 것이므로 넘어갑니다. ^^)<br /> <br /> <hr style='width: 50%' /> <br /> 참고로, byte 타입의 경우 Marshal.AllocCoTaskMem / AllocHGlobal을 사용하면 Int32.MaxValue의 모든 범위를 접근 가능한 배열을 얻을 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > int len = Int32.MaxValue; IntPtr pBuf = Marshal.AllocCoTaskMem(len); byte* ptr = (byte*)pBuf.ToPointer(); int i = 0; for (i = 0; i < len; i++) { *(ptr + i) = 10; } Console.WriteLine(*(ptr + len - 1)); Console.WriteLine(); Marshal.FreeCoTaskMem(pBuf); </pre> <br /> AllocCoTaskMem 메서드가 받아들이는 인자의 타입이 int이므로 어쩔 수 없이 2GB 한계는 갖게 되지만 CLR 오버헤드로 인한 56바이트의 제약은 벗어날 수 있습니다.<br /> <br /> <hr style='width: 50%' /> <br /> 자... 그럼 재미있는 거 하나를 더 해볼까요? ^^<br /> <br /> Marshal.AllocCoTaskMem 메서드는 사실 내부적으로 CoTaskMemAlloc Win32 API를 호출하는 것뿐입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > CoTaskMemAlloc function ; <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-cotaskmemalloc'>https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-cotaskmemalloc</a> </pre> <br /> 그리고, 이 함수의 입력 인자 타입은 SIZE_T인데 64비트에서는 __int64로 정의된 64비트 타입입니다. 오호~~~ 그렇다면 닷넷에서 직접 CoTaskMemAlloc을 불러 배열의 한계를 넘는 것이 가능할 수 있습니다. 실제로 아래와 같이 UInt32.MaxValue의 크기를 갖는 메모리를 할당 후 바이트 배열로 취급하는 것이 가능합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <span style='color: blue; font-weight: bold'>[DllImport("ole32.dll")] static extern IntPtr CoTaskMemAlloc(long len); [DllImport("ole32.dll")] static extern void CoTaskMemFree(IntPtr pBuf);</span> private unsafe static void Alloc2Test() { long len = <span style='color: blue; font-weight: bold'>UInt32.MaxValue</span>; IntPtr pBuf = CoTaskMemAlloc(len); byte* ptr = (byte*)pBuf.ToPointer(); long i = 0; for (i = 0; i < len; i++) { *(ptr + i) = 10; } Console.WriteLine(*(ptr + len - 1)); Console.WriteLine(); CoTaskMemFree(pBuf); } </pre> <br /> 그럼, 이제 답이 나왔군요. "8GB 이상의 byte 배열 생성"은 Win32 API의 힘을 빌린다면 닷넷에서도 가능합니다. ^^<br /> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1102&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1221
(왼쪽의 숫자를 입력해야 합니다.)