성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] VT sequences to "CONOUT$" vs. STD_O...
[정성태] NetCoreDbg is a managed code debugg...
[정성태] Evaluating tail call elimination in...
[정성태] What’s new in System.Text.Json in ....
[정성태] What's new in .NET 9: Cryptography ...
[정성태] 아... 제시해 주신 "https://akrzemi1.wordp...
[정성태] 다시 질문을 정리할 필요가 있을 것 같습니다. 제가 본문에...
[이승준] 완전히 잘못 짚었습니다. 댓글 지우고 싶네요. 검색을 해보...
[정성태] 우선 답글 감사합니다. ^^ 그런데, 사실 저 예제는 (g...
[이승준] 수정이 안되어서... byteArray는 BYTE* 타입입니다...
글쓰기
제목
이름
암호
전자우편
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'>VirtualAlloc, HeapAlloc, GlobalAlloc, LocalAlloc, malloc, new의 차이점</h1> <p> 우선, 모든 할당 방식의 근간인 VirtualAlloc은 다음의 글을 읽어보시면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 작업 관리자에서의 "Commit size"가 가리키는 메모리의 의미 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/1850'>http://www.sysnet.pe.kr/2/0/1850</a> </pre> <br /> 말 그대로, '가상 메모리'를 확보하는 함수로 HeapAlloc, GlobalAlloc, LocalAlloc, malloc, new로 할당받은 모든 메모리는 결국 VirtualAlloc으로 이미 할당받았던 영역을 기반으로 합니다.<br /> <br /> 그다음, GlobalAlloc과 LocalAlloc을 볼까요?<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > HGLOBAL WINAPI GlobalAlloc(_In_ UINT uFlags, _In_ SIZE_T dwBytes) ; <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-globalalloc'>https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-globalalloc</a> HLOCAL WINAPI LocalAlloc(_In_ UINT uFlags,_In_ SIZE_T uBytes) ; <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-localalloc'>https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-localalloc</a> </pre> <br /> 예전 16비트 윈도우 시대에 나누어진 것으로 다음의 글에 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > What was the difference between LocalAlloc and GlobalAlloc? ; <a target='tab' href='https://devblogs.microsoft.com/oldnewthing/?p=37433'>https://devblogs.microsoft.com/oldnewthing/?p=37433</a> A look back at memory models in 16-bit MS-DOS ; <a target='tab' href='https://devblogs.microsoft.com/oldnewthing/?p=104012'>https://devblogs.microsoft.com/oldnewthing/?p=104012</a> </pre> <br /> CPU의 Real 모드 메모리 관리에 따라 Segment 레지스터로 64KB까지 메모리가 구별되던 시절 GlobalAlloc은 Far *, LocalAlloc은 Near * 포인터를 다뤘습니다.<br /> <br /> 하지만 32비트 윈도우로 오면서,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Comparing Memory Allocation Methods ; <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/memory/comparing-memory-allocation-methods'>https://learn.microsoft.com/en-us/windows/win32/memory/comparing-memory-allocation-methods</a> </pre> <br /> 프로세스마다 단일하게 할당된 기본 힙(process's default heap)을 대상으로 HeapAlloc을 사용하는 각자의 래퍼 함수로 바뀌었습니다. 이 때문에 별도로 HeapCreate/HeapAlloc을 호출해 사용하는 것보다 오버헤드가 더 크다고 합니다. (의미인즉, 단일하게 운영되고 있는 "process's default heap"에 대해 경쟁할 것이기 때문에.)<br /> <br /> 마이크로소프트는 현재 GlobalAlloc의 경우 DDE, 클립보드, OLE data object 영역을 제외하고는 Heap 함수(HeapAlloc 등)를 쓰라고 권장하고 있으며 LocalAlloc은 특별한 사용처가 없으므로 모든 경우에 Heap 함수를 대신 쓰면 됩니다. 참고로, 사용 중인 3rd-party DLL 중에 LocalAlloc으로 할당받은 인자를 요구하는 경우가 있다면 그에 맞춰주는 정도가 있으며, LocalFree의 경우에는 일부 Win32 함수(예: FORMAT_MESSAGE_ALLOCATE_BUFFER 옵션이 지정된 FormatMessage)의 메모리를 해제할 때 사용해야 합니다. (그나마도 <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessage'>Windows 10부터는 HeapFree를 쓰라</a>고 되어 있습니다.)<br /> <br /> 문서 상으로 GlobalAlloc과 LocalAlloc의 가장 큰 차이점은, GlobalAlloc의 경우 8바이트 정렬로 메모리 할당을 하기 때문에 실제 요구된 바이트보다 더 클 수 있다고 명시하는 반면 LocalAlloc은 8바이트 정렬이라는 내용은 없지만 마찬가지로 실제 요구된 바이트보다 더 클 수 있다는 식입니다. 결국, 같은 HeapAlloc을 사용하지만 약간의 내부적인 구현 상의 차이가 있다는 것인데 이 때문인지 마이크로소프트는 GlobalAlloc으로 할당받은 것은 반드시 GlobalFree로, LocalAlloc으로 할당받은 것은 LocalFree로 해제하라고 명시하고 있습니다.<br /> <br /> HeapAlloc은 VirtualAlloc으로 할당한 메모리를 기반으로 바로 위에서 동작하는 함수입니다. 또한 기본적으로 process's default heap이 하나 제공되어 그곳으로부터 HeapAlloc을 통해 메모리를 할당받을 수도 있지만, 별도로 HeapCreate를 사용해 별도의 heap 영역을 만드는 것도 가능합니다.<br /> <br /> 여기까지가, 운영체제 종속적인 메모리 할당 API입니다. 당연히 Linux로 가면 위의 함수들은 그에 대응하는 Linux 함수로 대체된다고 보시면 됩니다.<br /> <br /> <hr style='width: 50%' /> <a name='crt_alloc'></a> <br /> 이제 남은 것은 malloc과 new인데요.<br /> <br /> 우선, malloc은 C 런타임 라이브러리에서 구현한 메모리 할당 함수입니다. <br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > malloc() 작동 원리 ; <a target='tab' href='http://egloos.zum.com/minjang/v/1232908'>http://egloos.zum.com/minjang/v/1232908</a> </pre> <br /> 즉, 런타임 라이브러리가 어떻게 malloc을 구현했느냐에 따라 달라질 수 있습니다. 가령 같은 Visual C++ 컴파일러라도 버전에 따라 바뀌는 MSVCRT 라이브러리(예: msvcr100.dll과 msvcr120.dll)에서조차도 구현 방법이 다를 수 있고, 심지어 디버깅 정보로 인해 msvcr100.dll과 msvcr100d.dll에 따라서도 달라질 수 있습니다.<br /> <br /> 예를 들어, 여러분들이 test.dll 라이브러리를 만들었고 다음의 API를 export하고 있다고 가정해 보겠습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > __declspec(dllexport) int *AllocIntegerData(int len) { int *pData = (int *)malloc(len); return pData; } </pre> <br /> 그리고 위의 DLL을 링크한 test.exe 프로젝트에서 다음과 같이 사용할 수 있을 것입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > void main() { int *pData = AllocIntegerData(100); free(pData); } </pre> <br /> 이 상황에서, test.dll을 컴파일할 때 링크한 CRT 라이브러리가 msvcr100.dll이고, test.exe 프로젝트를 컴파일할 때 링크한 CRT 라이브러리가 msvcr120.dll이라면 어떻게 될까요?<br /> <br /> 이것은, 운에 따라 다릅니다. 만약 msvcr100.dll CRT 라이브러리에서 malloc으로 관리되는 내부 부가 데이터가 12바이트이고, msvcr120.dll에서는 16바이트라고 가정해 보겠습니다. 그럼, test.dll에서 AllocIntegerData는 +12바이트가 되는 영역을 할당받았는데 test.exe에서는 그 데이터를 +16바이트만큼 해제해버리는 결과를 낳게 됩니다. (사실, MSVCRT의 경우 이렇게까지 하위 호환성이 없지는 않습니다.)<br /> <br /> 이런 문제 때문에, DLL 내에서 할당받은 메모리는 반드시 그 DLL에서 해제를 하도록 권장하는 것입니다. (또는, DLL을 사용하는 측이 언제나 C/C++ 언어라고 가정할 수 없습니다.) 즉, 다음과 같이 test.dll에서 해제까지 담당하는 API를 제공하고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > __declspec(dllexport) void FreeIntegerData(int *pBuf) { free(pBuf); } </pre> <br /> DLL을 사용하는 다른 프로젝트에서는 이렇게 Alloc/Free를 맞춰서 사용해 주면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > void main() { int *pData = AllocIntegerData(100); FreeIntegerData(pData); } </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;' > "DLL의 경우, 메모리를 할당받는 API를 export시켰다면, 반드시 그 메모리를 해제하는 API까지 제공한다." </pre> <br /> 그렇다면 new 할당에 대한 의미도 풀립니다. new는 C++에서 제공하는 연산자로 대개의 경우 기본 할당은 내부적으로 malloc을 이용하지만 이는 결코 장담할 수 없습니다. 일례로 Visual C++의 경우 디버그 모드에서는 new가 malloc이 아닌 malloc_dbg를 사용하도록 하고 있으며 심지어 new 연산자 자체가 오버로딩이 가능하므로 사용자가 임의로 다른 메모리 할당 함수를 사용하는 것도 가능합니다.<br /> <br /> 따라서 말할 필요도 없이, new로 할당받은 메모리를 반환하는 DLL이 있다면 반드시 그것을 (타입에 맞는 delete로) 해제하는 함수도 제공해야 합니다.<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1633
(왼쪽의 숫자를 입력해야 합니다.)