Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 

VirtualAlloc, HeapAlloc, GlobalAlloc, LocalAlloc, malloc, new의 차이점

우선, 모든 할당 방식의 근간인 VirtualAlloc은 다음의 글을 읽어보시면 됩니다.

작업 관리자에서의 "Commit size"가 가리키는 메모리의 의미
; https://www.sysnet.pe.kr/2/0/1850

말 그대로, '가상 메모리'를 확보하는 함수로 HeapAlloc, GlobalAlloc, LocalAlloc, malloc, new로 할당받은 모든 메모리는 결국 VirtualAlloc으로 이미 할당받았던 영역을 기반으로 합니다.

그다음, GlobalAlloc과 LocalAlloc을 볼까요?

HGLOBAL WINAPI GlobalAlloc(_In_ UINT uFlags, _In_ SIZE_T dwBytes)
; https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-globalalloc

HLOCAL WINAPI LocalAlloc(_In_ UINT uFlags,_In_ SIZE_T uBytes)
; https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-localalloc

예전 16비트 윈도우 시대에 나누어진 것으로 다음의 글에 보면,

What was the difference between LocalAlloc and GlobalAlloc?
; https://blogs.msdn.microsoft.com/oldnewthing/20041101-00/?p=37433/

CPU의 Real 모드 메모리 관리에 따라 Segment 레지스터로 64KB까지 메모리가 구별되던 시절 GlobalAlloc은 Far *, LocalAlloc은 Near * 포인터를 다뤘습니다.

하지만 32비트 윈도우로 오면서,

Comparing Memory Allocation Methods
; https://docs.microsoft.com/en-us/windows/win32/memory/comparing-memory-allocation-methods

프로세스마다 단일하게 할당된 기본 힙(process's default heap)을 대상으로 HeapAlloc을 사용하는 각자의 래퍼 함수로 바뀌었습니다. 이 때문에 별도로 HeapCreate/HeapAlloc을 호출해 사용하는 것보다 오버헤드가 더 크다고 합니다. (의미인즉, 단일하게 운영되고 있는 "process's default heap"에 대해 경쟁할 것이기 때문에.)

마이크로소프트는 현재 GlobalAlloc의 경우 DDE, 클립보드, OLE data object 영역을 제외하고는 Heap 함수(HeapAlloc 등)를 쓰라고 권장하고 있으며 LocalAlloc은 특별한 사용처가 없으므로 모든 경우에 Heap 함수를 대신 쓰면 됩니다. 참고로, 사용 중인 3rd-party DLL 중에 LocalAlloc으로 할당받은 인자를 요구하는 경우가 있다면 그에 맞춰주는 정도가 있으며, LocalFree의 경우에는 일부 Win32 함수(예: FORMAT_MESSAGE_ALLOCATE_BUFFER 옵션이 지정된 FormatMessage)의 메모리를 해제할 때 사용해야 합니다. (그나마도 Windows 10부터는 HeapFree를 쓰라고 되어 있습니다.)

문서 상으로 GlobalAlloc과 LocalAlloc의 가장 큰 차이점은, GlobalAlloc의 경우 8바이트 정렬로 메모리 할당을 하기 때문에 실제 요구된 바이트보다 더 클 수 있다고 명시하는 반면 LocalAlloc은 8바이트 정렬이라는 내용은 없지만 마찬가지로 실제 요구된 바이트보다 더 클 수 있다는 식입니다. 결국, 같은 HeapAlloc을 사용하지만 약간의 내부적인 구현 상의 차이가 있다는 것인데 이 때문인지 마이크로소프트는 GlobalAlloc으로 할당받은 것은 반드시 GlobalFree로, LocalAlloc으로 할당받은 것은 LocalFree로 해제하라고 명시하고 있습니다.

HeapAlloc은 VirtualAlloc으로 할당한 메모리를 기반으로 바로 위에서 동작하는 함수입니다. 또한 기본적으로 process's default heap이 하나 제공되어 그곳으로부터 HeapAlloc을 통해 메모리를 할당받을 수도 있지만, 별도로 HeapCreate를 사용해 별도의 heap 영역을 만드는 것도 가능합니다.

여기까지가, 운영체제 종속적인 메모리 할당 API입니다. 당연히 Linux로 가면 위의 함수들은 그에 대응하는 Linux 함수로 대체된다고 보시면 됩니다.



이제 남은 것은 malloc과 new인데요.

우선, malloc은 C 런타임 라이브러리에서 구현한 메모리 할당 함수입니다.

malloc() 작동 원리
; http://egloos.zum.com/minjang/v/1232908

즉, 런타임 라이브러리가 어떻게 malloc을 구현했느냐에 따라 달라질 수 있습니다. 가령 같은 Visual C++ 컴파일러라도 버전에 따라 바뀌는 MSVCRT 라이브러리(예: msvcr100.dll과 msvcr120.dll)에서조차도 구현 방법이 다를 수 있고, 심지어 디버깅 정보로 인해 msvcr100.dll과 msvcr100d.dll에 따라서도 달라질 수 있습니다.

예를 들어, 여러분들이 test.dll 라이브러리를 만들었고 다음의 API를 export하고 있다고 가정해 보겠습니다.

__declspec(dllexport) int *AllocIntegerData(int len)
{
    int *pData = (int *)malloc(len);
    return pData;
}

그리고 위의 DLL을 링크한 test.exe 프로젝트에서 다음과 같이 사용할 수 있을 것입니다.

void main()
{
    int *pData = AllocIntegerData(100);
    free(pData);
}

이 상황에서, test.dll을 컴파일할 때 링크한 CRT 라이브러리가 msvcr100.dll이고, test.exe 프로젝트를 컴파일할 때 링크한 CRT 라이브러리가 msvcr120.dll이라면 어떻게 될까요?

이것은, 운에 따라 다릅니다. 만약 msvcr100.dll CRT 라이브러리에서 malloc으로 관리되는 내부 부가 데이터가 12바이트이고, msvcr120.dll에서는 16바이트라고 가정해 보겠습니다. 그럼, test.dll에서 AllocIntegerData는 +12바이트가 되는 영역을 할당받았는데 test.exe에서는 그 데이터를 +16바이트만큼 해제해버리는 결과를 낳게 됩니다. (사실, MSVCRT의 경우 이렇게까지 하위 호환성이 없지는 않습니다.)

이런 문제 때문에, DLL 내에서 할당받은 메모리는 반드시 그 DLL에서 해제를 하도록 권장하는 것입니다. (또는, DLL을 사용하는 측이 언제나 C/C++ 언어라고 가정할 수 없습니다.) 즉, 다음과 같이 test.dll에서 해제까지 담당하는 API를 제공하고,

__declspec(dllexport) void FreeIntegerData(int *pBuf)
{
    free(pBuf);
}

DLL을 사용하는 다른 프로젝트에서는 이렇게 Alloc/Free를 맞춰서 사용해 주면 됩니다.

void main()
{
    int *pData = AllocIntegerData(100);
    FreeIntegerData(pData);
}

결코 잊지 말아야 할 것!

"DLL의 경우, 메모리를 할당받는 API를 export시켰다면, 반드시 그 메모리를 해제하는 API까지 제공한다."

그렇다면 new 할당에 대한 의미도 풀립니다. new는 C++에서 제공하는 연산자로 대개의 경우 기본 할당은 내부적으로 malloc을 이용하지만 이는 결코 장담할 수 없습니다. 일례로 Visual C++의 경우 디버그 모드에서는 new가 malloc이 아닌 malloc_dbg를 사용하도록 하고 있으며 심지어 new 연산자 자체가 오버로딩이 가능하므로 사용자가 임의로 다른 메모리 할당 함수를 사용하는 것도 가능합니다.

따라서 말할 필요도 없이, new로 할당받은 메모리를 반환하는 DLL이 있다면 반드시 그것을 (타입에 맞는 delete로) 해제하는 함수도 제공해야 합니다.




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]





[최초 등록일: ]
[최종 수정일: 10/19/2019 ]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer@outlook.com

비밀번호

댓글 쓴 사람
 




... [46]  47  48  49  50  51  52  53  54  55  56  57  58  59  60  ...
NoWriterDateCnt.TitleFile(s)
11211정성태5/26/20174924오류 유형: 391. BeginInvoke에 전달한 람다 함수에 CS1660 에러가 발생하는 경우
11210정성태5/25/20174904기타: 65. ActiveX 없는 전자 메일에 사용된 "개인정보 보호를 위해 암호화된 보안메일"의 암호화 방법
11209정성태5/25/201717476Windows: 143. Windows 10의 Recovery 파티션을 삭제 및 새로 생성하는 방법 [6]
11208정성태5/25/20177774오류 유형: 390. diskpart의 set id 명령어에서 "The specified type is not in the correct format." 오류 발생
11207정성태5/24/20178372Windows: 142. Windows 10의 복구 콘솔로 부팅하는 방법
11206정성태5/24/20175996오류 유형: 389. DISM.exe - The specified image in the specified wim is already mounted for read/write access.
11205정성태5/24/20174902.NET Framework: 658. C#의 tail call 구현은?
11204정성태3/8/202013102개발 환경 구성: 316. 간단하게 살펴보는 Docker for Windows [5]
11203정성태5/19/20176698오류 유형: 388. docker - Host does not exist: "default"
11202정성태5/19/20174869오류 유형: 387. WPF - There is no registered CultureInfo with the IetfLanguageTag 'ug'.
11201정성태5/16/20177268오류 유형: 386. WPF - .NET 3.5 이하에서 TextBox에 한글 입력 시 TextChanged 이벤트의 비정상 종료 문제 [1]파일 다운로드1
11200정성태5/16/20174826오류 유형: 385. WPF - 폰트가 없어 System.IO.FileNotFoundException 예외가 발생하는 경우
11199정성태5/16/20175794.NET Framework: 657. CultureInfo.GetCultures가 반환하는 값
11198정성태5/10/20176926.NET Framework: 656. Windows Forms의 오류(Exception) 처리 방법에 대한 차이점 설명
11197정성태5/8/20175051개발 환경 구성: 315. VHD 파일의 최소 크기파일 다운로드1
11196정성태5/4/20176195오류 유형: 384. Msvm_ImageManagementService WMI 객체를 사용할 때 오류 상황 정리 [1]
11195정성태8/19/20176295.NET Framework: 655. .NET Framework 4.7 릴리스
11194정성태5/3/20177576오류 유형: 383. net use 명령어로 네트워크 드라이브 연결 시 "System error 67 has occurred." 오류 발생
11193정성태5/3/20176339Windows: 141. 설치된 Windows로부터 설치 이미지를 만드는 방법
11192정성태5/2/20177226Windows: 140. unattended.xml/autounattend.xml 파일을 마련하는 방법
11191정성태5/2/20177391Windows: 139. Dell Venue 8 Pro 태블릿에 USB를 이용한 윈도우 운영체제 설치 방법
11190정성태5/2/20179179Windows: 138. Windows 운영체제의 ISO 설치 파일에 미리 Device driver를 준비하는 방법
11189정성태5/2/20175501Windows: 137. Windows 7 USB/DVD DOWNLOAD TOOL로 98%에서 실패하는 경우
11188정성태4/27/20176186VC++: 118. Win32 HANDLE 자료형의 이모저모
11187정성태4/26/20177749개발 환경 구성: 314. C# - PowerPoint 확장 Add-in 만드는 방법 [1]파일 다운로드1
11186정성태4/24/20176200VS.NET IDE: 117. Visual Studio 확장(VSIX)을 이용해 사용자 매크로를 추가하는 방법 [1]파일 다운로드1
... [46]  47  48  49  50  51  52  53  54  55  56  57  58  59  60  ...