1. 가상 메모리 (Virtual Memory) 컨트롤

* Reserve, Commit 그리고 Free

: 페이지의 상태의 Reserve는 예약, Commit은 할당, Free는 할당되지 않았음을 의미한다.

: <페이지의 개수 = 가상 메모리의 크기 / 페이지 하나당 크기> 이므로, 페이지 개수는 가상 메모리의 크기에 비례하며, 모든 페이지는 Reserved, Commit, Free 이 세가지 상태 중 하나를 지닌다.

: 여기서 물리 메모리란, 램과 하드디스크를 모두 포함하는 말이다. 즉, 해당 페이지가 물리 메모리에 할당된 상태를 가리켜 Commit 이라고 한다. 

: 할당되지 않은 상태를 Free라고 한다. 

: Windows는 Reserve라는 상태를 하나 더 두어 메모리 사용의 효율성을 높일 기회를 제공한다.

: 아주 큰 배열을 선언했을 때, 모두 Commit으로 둔다면 해당 페이지는 모두 물리 메모리에 할당되어져 버린다. 이 떄 Reserve를 통해 메모리 사용되어져 메모리의 효율성이 높아지게 된다.

: 일부 페이지를 Reserve상태로 둠으로써 다른 메모리 할당 함수에 의해 해당 번지가 할당되지 못하도록 선언할 수 있다. 동시에 메모리 소비는 발생하지 않는다.

* 메모리 할당의 시작점과 단위 확인하기

: 메모리를 할당하기 전에 기본적으로 생각해야 하는 것은 두가지이다. 메모리 할당의 시작 주소, 할당할 메모리의 크기

: 가상 메모리 시스템은 페이지 단위로 관리되기 때문에, 페이지의 중간 위치에서부터 할당을 시작할 수 없으며, 페이지 크기의 배수 단위로 할당을 해야만 한다.

: Windows 는 메모리가 지나치게 조각나는 것을 막기 위해서, 또 관리의 효율성을 위해 조금 더 넓은 범위의 값을 할당의 경계로 정의하고 있다.

: 메모리 할당의 시작 주소가 될 수 있는 기본 단위를 가리켜 Allocation Granularity Boundary 라고 한다. 

: Allocation Granularity Boundary와 페이지의 크기는 GetSystemInfo 를 통해 알 수 있다. 

* VirtualAlloc & VirtualFree 함수

: VirtualAlloc 함수는 페이지 상태를 Reserve와 Commit으로 만드는 역할을 한다. 

: VirtualFree 함수는 VirtualAlloc 함수가 정해놓은 상태를 되돌린다.

* Dynamic Array Design

: 배열의 크기만큼 물리 메모리가 할당되는 것이 아니라, 사용량의 증가에 따라 물리 메모리에 할당되는 동적 배열 디자인이다.

-시나리오

1. 페이즈 사이즈와 Allocation Granularity Boundary 를 얻어온다.

2. 예상되는 최대 크기로 메모리를 예약한다

3. 필요한 만큼 Commit한다. 이 후 필요에 따라 점진적으로 할당한다.

4. 할당했던 메모리를 반환한다.

2. 힙(Heap) 컨트롤

: 힙을 컨트롤하는 것은 가상 메모리를 컨트롤하는 것보다 최소 몇배 이상 유용ㅎ안 일이다.

* 힙(Heap) 컨트롤에 대한 필자의 기억

: 리스트 자료구조의 특성상 리스트 정보는 동적할당되어 힙에 저장된다.

: 이를 반환하려면 두가지 문제가 등장한다.

1. 메모리 유출(삭제되지 않는 위험), 메모리 누수가 누적되면 프로그램이 종료된다

2. 성능, 일일이 돌아다니면서 삭제하기위해서는 리스트의 길이에 따라 성능 저하 요인이 된다.

: Windows에서는 이 둘의 문제를 해결할 수 있다..

* 디폴트 힙(Default Heap) & Windows 시스템에서의 힙

: C언어의 경우 malloc 함수와 free 함수를, C++을 사용할 경우 new와 delete 연산자를 사용해서 힙 영역에 메모리를 할당한다.

: 이 경우 1M바이트 크기의 디폴트 힙 영역에 메모리를 할당하게 된다.

: 디폴트 힙은 프로세스에 기본적으로 할당되는 힙이라 하여 프로세스 힙(Process Heap)이라고도 부른다.    

: Windows 함수를 이용하면 2번째 사진과 같은 힙 메모리 형태를 만들 수 있다.

: 홍길동이 탈퇴한다면, 해당 힙 메모리를 제거함으로써 메모리 유출과 해제에 대한 성능 저하 문제를 해결할 수 있다.

* 디폴트 힙 컨트롤

: 디폴트 힙의 기본 크기는 1M바이트이지만, 링커(Linker) 옵션을 통해서 변경이 가능하다.

: 하지만, 그 이상의 크기를 사용할 때에 Windows가 알아서 그 크기를 늘려주므로 따로 재정의해줄 필요는 없다.

: 물론, 디폴트 힙 크기를 정해줌으로써 프로세스가 실행중에 새로운 메모리를 할당해서 시간이 소요되는 것을 방지할 수 있다.

* 힙(Dynamic Heap) 생성이 가져다 주는 또 다른 이점

1. 메모리 단편화의 최소화에 따른 성능 향상

: 힙을 미리 A,B,C 처럼 할당하여 둔다면, 연속성이 무너지지 않을 것이다.

: 하지만, 디폴트 힙에서 처리한다면, 단편화가 되어 프로그램의 로컬리티의 특성이 낮아질 우려가 크다. 

: 이는 성능에 많은 영향을 끼치므로 힙 생성을 적절하게 사용한다면 성능향상을 기대할 수 있다.

: 페이지 단위로 힙을 불러오기 때문에, 로컬리티 특성이 매우 좋아지며, 섞여 있을 경우 여러 페이지를 모두 불러와야 하기 때문에 로컬리티 특성이 매우 낮아진다.

2. 동기화 문제에서 자유로워짐으로 인한 성능 향상

: 쓰레드 마다 다른 힙을 사용할 경우 동기화를 하지 않아도 되기 때문에 이로 인한 성능향상을 기대할 수 있다.

: 힙자체를 반환해 버릴 수 있다는 장점도 존재한다.

* 힙의 생성과 소멸 그리고 할당

: 리스트 자료구조에 동적 힙의 개념을 도입하면 메모리 유출의 방지와 프로그램 구현의 용이성이라는 두가지 장점을 얻게 된다.

3. MMF(Memory Mapped File)

* MMF의 이해

: MMF는 File을 Memory에 Mapping(연결) 시킨다는 의미를 지니고 있다. 파일의 일부 영역을 가상 메모리 일부에 연결시키는 매커니즘을 가리켜 MMF라고 한다. 

: 위의 그림을 보면, 가상메모리와 파일의 데이터가 연결되어있다. 이는 실제로도 메모리에만 데이터가 저장되는 것이 아니라, 메모리에 연결된 파일에 실제 데이터가 저장된다.

장점 1. 프로그래밍하기 편하다

: 파일안에 저장되어 있는 데이터를 조작하려면 일단 메모리로 읽어 들여야 하기 때문에, 너무 번거롭다. MMF를 사용하면 메모리 상에 저장된 데이터를 조작하는 방식으로 파일 내 데이터를 조작할 수 있어 편리하다.

장점 2. 성능이 향상된다

: 성능이 저하될 것 같지만, 일반적으로는 향상된다.

: 메모리는 중간의 캐쉬 역할만 한다고 생각하여 로컬리티 특성만 충족시킬 경우 아주 높은 성능 향상이 이루어진다. 하지만, 메모리 상의 데이터가 변경되었을 때 바로 반영한다면, 이는 구현의 용이성만 얻게되고 성능 향상은 어렵다.

* MMF의 구현과정

1 단계 : 파일개방

: 반드시 CreateFile 함수 호출을 통해서 파일을 열고 해당 파일의 핸들을 얻어야 한다.

: 핸들은 파일을 읽고 쓰는데 필요하다.

2 단계 : 파일 연결 오브젝트 생성

: 메모리에 연결할 파일 정보를 담고 있는 커널 오브젝트를 생성하는 것이다.

: CreateFileMapping 함수 호출을 통해 만들어진다.

: 파일 연결 오브젝트는 메모리에 매핑시키는데 필요하다.

3 단계 : 가상 메모리에 파일 연결

: 가상메모리에 파일을 연결한다.

: MapViewOfFile 함수를 통해 완성되며, 반환되는 포인터를 가지고 메모리에 접근하여 데이터를 변경하면, 파일에 반영된다.

* Copy-On-Write(COW)

: 데이터를 쓸 때 복사하라.

: 이는 MMF처럼 시스템 함수 수준에서 제공되거나, OS와 같은 고급 소프트웨어 를 구현할 떄 내부적으로 적용하는 최적화(Optimization) 기술이다.

: 일반적으로 쓰레드는 항상 테이블을 복제해서 사용하였다.

: 하지만, 쓰레드가 생성될 때마다 기본 테이블을 참조하며, 특정 테이블을 변경했을 때에만 테이블을 복사한다면 더 효율적일것이다. 이것이 COW이다.


뇌를 자극하는 윈도우즈 시스템 프로그래밍
국내도서
저자 : 윤성우
출판 : 한빛미디어 2007.03.30
상세보기


+ Recent posts