Programming/Win32 API2008. 11. 19. 11:13

From Devpia

각 함수의 정의는 다음과 같다.
 
LPVOID HeapAlloc(HANDLE hHeap, DWORD dwFlags, DWORD dwBytes);
HLOCAL LocalAlloc(UINT uFlags, UINT uBytes);
HGLOBAL GlobalAlloc(UINT uFlags, DWORD dwBytes);

셋 다 힙에서 메모리 블록을 할당받는 동일한 기능을 한다.

LocalAlloc과 GalobalAlloc은 Win32에서는 완전히 동일하다. Win16에서는 어플리케이션마다 가지는 Local 힙과 힙 매니저가 가지는 Global 힙이 따로 존재했지만 Win32에서는 따로 존재하지 않는다. 따라서 Win32에서 Local 힙과 Global 힙은 동일하고 함수 자체는 하위 호환을 위해 남아있다.

LocalAlloc과 GlobalAlloc은 동일하므로 LocalAlloc만을 언급하겠다.

HeapAlloc과 LocalAlloc의 차이점을 살펴보자.
첫째로 메모리 블록을 할당하는 힙 자체가 다르다는 것이다. LocalAlloc은 항상 디폴트 힙에서 할당한다. 디폴트 힙은 하나의 어플리케이션이 하나씩 가지는 메인 힙이다. 이는 프로세스가 생성되고 가상 주소 공간이 생겨날 때 같이 생성되는 힙이다. 그 크기는 링크시 실행파일 헤더에 기록이 된다. 링크 옵션으로 그 크기를 변경할 수도 있다. 기본 값은 1MB이다. 반면에 HeapAlloc은 함수의 첫 번째 인자에서 보듯이 임의의 힙에서 할당한다. HeapCreate로 만든 힙일 수도 있고 GetProcessHeap으로 얻는 디폴트 힙일 수도 있다.

둘째로 리턴값을 살펴보면 HeapAlloc은 포인터를 리턴하지만 LocalAlloc은 핸들을 리턴한다. 따라서 LocalAlloc으로 할당받은 메모리 블록을 실제로 사용할 때는 LocalLock 함수로 포인터를 얻어와야 한다. LocalAlloc의 사용이 좀 더 불편하지만 핸들을 사용하므로써 동기화와 같은 기능을 제공한다. 그리고 포인터를 직접 사용하지 않고 포인터를 사용하는 또 다른 (결과적으로) 이유는 다음과 같다.
LocalAlloc의 첫번째 인자는 할당받을 메모리 블록의 특성을 지정하는데, GMEM_MOVEABLE 이라는 특성을 지정할 수 있다. 이는 힙 메모리 단편화 제거를 위해 또는 힙 공간이 부족할 때 힙 관리자가 메모리 블록을 이동할 수 있도록 하게 하는데 이렇게 되면 포인터로 리턴했을 경우 이동된 후 잘못된 참조를 하게 되므로 핸들로서 관리되는 것이다. 실제로 GMEM_MOVEABLE 이 아닌 GMEM_FIXED로 지정하게 되면 리턴값은 그 자체가 포인터이다.

※ 참고로 C run-time library인 malloc은 위의 API 함수들과 같은 기능을 하지만 디폴트 힙이 아닌 자신만의 힙을 사용하고 자신만의 방법으로 힙을 관리한다.

+ VirtualAlloc() 함수는 뭐냐. 이것도 생각해 봐야.

LocalAlloc()과 같은 heap function 은 memory exception 을 가져 올 수
있다는 것과 대용량의 memory 할당에는 적합하지 않다는 것입니다.



=======  From MSDN : "Heap Functions" ============================================
Windows NT 4.0: External factors may cause accesses to heap memory to generate access violations.
One possible cause of an access violation is very limited space in the paging file. Therefore, all
accesses to heap memory should be protected with structured exception handling. This is not
necessary on later versions of Windows.

Windows 95/98/Me: The heap managers are designed for memory blocks smaller than four megabytes. If
you expect your memory blocks to be larger than one or two megabytes, you can avoid significant
performance degradation by using the VirtualAlloc or VirtualAllocEx function instead.

======= From MSDN : "Comparing Memory Allocation Methods" =========================
This topic provides a brief comparison of the following memory allocation methods:

CoTaskMemAlloc
GlobalAlloc
HeapAlloc
LocalAlloc
malloc
new
VirtualAlloc

The following functions are equivalent: GlobalAlloc, LocalAlloc, and HeapAlloc with the handle
returned by the GetProcessHeap function.

The VirtualAlloc function allows you to specify additional options for memory allocation. However,
its allocations use a page granularity, so using VirtualAlloc can result in higher memory usage.

The malloc function has the disadvantage of being run-time dependent. The new operator has the
disadvantage of being compiler dependent and language dependent.

The CoTaskMemAlloc function has the advantage of working well in either C, C++, or Visual Basic.
It is also the only way to share memory in a COM-based application, since MIDL uses CoTaskMemAlloc
and CoTaskMemFree to marshal memory.

GlobalAlloc, LocalAlloc, HeapAlloc 함수들은 궁극적으로 동일한 힙에 메모리를 할당하지만, 약간의 차이를 가지고 있다. HeapAlloc은 메모리가 할당될 수 없으면 예외를 발생시킬 수 있으며, LocalAlloc은 이런 기능이 없다. LocalAlloc은 핸들 할당을 지원함으로써 핸들값을 바꾸지않고 재할당을 통해 기초메모리 (underlying memory) 를 이동시킬 수 있으며, HeapAlloc은 이런 기능이 없다.

각각의 힙 할당자는 서로 다른 기법을 사용해 구현되었기에, 각 함수에 맞게 메모리를 해제해야 한다. 예를 들어, HeapAlloc으로 할당된 메모리는 LocalFree / GlobalFree이 아닌 HeapFree으로 해제되어야 한다.

VirtualAlloc 함수는 메모리 할당에 추가 옵션을 허용한다. 하지만 이 함수는 페이지 입도를 사용하므로, 더 많은 메모리가 소비될 수 있다.

malloc함수는 런타임 의존적, new연산자는 컴파일러/언어 의존적이라는 약점을 가지고 있다.

 

CoTaskMemAlloc함수는 C/C++/Visual Basic 모두에서 잘 작동하는 이점을 가지고 있다. 또한 MIDL (Microsoft Interface Definition Language) 이 메모리를 마샬링*하는데 CoTaskMemAlloc / CoTaskMemFree를 사용하므로, COM 기반 애플리케이션에서 메모리를 공유하는 유일한 방법이다.

 

*마샬링(marshalling) : 하나 이상의 프로그램 또는 연속적이지 않은 저장공간으로부터 데이터를 모은 다음, 이들을 메시지 버퍼에 집어넣고 특정 수신기나 프로그래밍 인터페이스에 맞도록 그 데이터를 조직화하거나, 미래 정해진 다른 형식으로 변환하는 과정.

주로 한 언어로 작성된 프로그램의 출력 매개변수를 다른 언어로 작성된 프로그램의 입력으로 전달해야 하는 경우 필요하다.

 

 VirtualAlloc은 힙을 사용하지는 않습니다.
그리고 힙처럼 연속된 메모리로 제한되지는 않으며
64KB의 조각들로 할당합니다...
그리고 또 하나...
새로운 사실인데...
new가 메모리 할당에 실패했는데 VirtualAlloc함수가 성공하는 경우가 있다는 군요...

VirtualAlloc이라는 함수가 하드에 잡아준다 이건 절대 아닙니다.
사람들이 가상메모리하면 바로 하드 스와핑을 떠올리시는데 그건 아닙니다.
Virtual* 메모리 API는 Win32에서 가장 원시적인(?) 함수라고 보시면됩니다.
(내가 쓸 메모리는 항상 램에 있어야한다 등등은 AWE라는 메모리 기술로
조절할 수 있습니다)

VirtualAlloc은 큰 메모리를 잡을 때 많이 씁니다.
왜냐면 얘들은 allocation granularity와 page size에 맞춰서
까다롭게 commit, decommit을 해줘야하기 때문이죠.

반면 HeapAlloc함수들은 이런 메모리 바운더리 및 얼라인먼트에대한
부담없이 언제든지 원하는 메모리를 뽑아 쓸 수 있도록 설계된 것이죠.
(결국 HeapAlloc도 VirtualAlloc으로 commit된 메모리에서 나오는 것이죠)
new, malloc 등 모든 함수들은 결국 HeapAlloc으로 귀결된답니다.

아무래도 메모리의 경계를 잘못 맞추어서 겪으신 어려움인것 같군요.
priority낮은 쓰레드가 뒤에서 돌면서 가비지 콜렉터 역할을 하며
페이지크기에 맞게 남은 페이지를 decommit해야하는걸로 알고 있습니다.
여튼, 속도는 잘나오겠지만 쓰는 법이 약간 까다롭죠.

결론은 작은 메모리를 빈번하게 할당, 해지해야할 땐
Heap메모리를 써야한다는 것, 큰 메모리를 비교적 적은 횟수에
쓰실 때엔 Virtual메모리를 쓰는게 정석인 듯 하옵니다..

윈도우 메모리 구조는 Programming Apps for Windows란
책에 가장 자세히 설명되어 있습니다^^

Posted by skensita