2009-11-05 3 views
9

내 프로그램에서 메모리 조각화에 문제가있어 잠시 후 매우 큰 메모리 블록을 할당 할 수 없습니다. 이 포럼에서 관련 게시물을 읽었습니다. 주로 this입니다. 그리고 나는 아직도 몇 가지 질문을 가지고있다.힙 조각화 및 창 메모리 관리자

나는 메모리 공간을 얻기 위해 profiler 메모리 공간을 사용 해왔다. 나는 cin >> var를 포함하는 1 라인 프로그램을 썼다. - 녹색 빈 공간 할당 노란색, 빨간색 최선을 다하고을 나타냅니다

alt text http://img22.imageshack.us/img22/6808/memoryk.gif 상단 아크에 : 메모리의 사진을했다. 내 질문은 오른쪽에 할당 된 메모리 무엇입니까? 메인 쓰레드를위한 스택인가? 이 메모리는 해제되지 않고 필요한 연속 메모리를 분할합니다. 이 간단한 1 라인 프로그램에서는 분할이 나쁘지 않습니다. 내 실제 프로그램에는 주소 공간의 중간에 더 많은 자료가 할당되어 있으며 어디에서 왔는지 알지 못합니다. 나는 그 기억을 아직 할당하지 않고있다.

  1. 어떻게 해결할 수 있습니까? 나는 nedmalloc이나 dlmalloc 같은 것으로 전환 할 생각을하고있었습니다. 그러나 그것은 내가 명시 적으로 할당 한 객체에만 적용되는 반면, 그림에 표시된 분할은 사라지지 않을 것입니다. 아니면 CRT 할당을 다른 메모리 관리자로 대체 할 수있는 방법이 있습니까?

  2. 오브젝트에 관해서는 nedmalloc for C++에 대한 래퍼가 있습니까? 그래서 새 오브젝트와 delete 오브젝트를 할당 할 수 있습니까?

감사합니다.

+1

Microsoft Security Essentials는 원본 질문에 링크 된 "프로파일 러"응용 프로그램에 Win32.Bisar! rts 트로이 목마가 포함되어 있다고 생각합니다. –

답변

12

먼저 내 도구를 이용해 주셔서 감사합니다. 유용하다고 생각하고 기능 요청이나 기고 물을 제출해 주시기 바랍니다.

일반적으로 주소 공간의 고정 된 지점에있는 얇은 슬라이스는 연결된 주소가 선호하는 주소로로드되어 발생합니다. 주소 공간에서로드가 높은 것은 Microsoft 운영 체제 DLL이되는 경향이 있습니다. dll의 읽기 전용 부분이 모두 프로세스간에 공유 될 수 있기 때문에 운영 체제가 선호 주소로로드 될 수 있다면 운영 체제에서 더 효율적입니다.

볼 수있는 슬라이스는 걱정할 필요가 없으며 주소 공간에서 거의 아무것도 잘라 내지 않습니다. 앞서 언급했듯이 주소 공간의 다른 지점에로드되는 dll이 있습니다. IIRC shlwapi.dll은 사용 가능한 주소 공간의 큰 부분을 2 개의 작은 조각으로 분할하는 약 0x2000000 (다시 IIRC)에서로드되는 특히 나쁜 예입니다. 이 문제는 일단 DLL이로드되면이 공간을 이동시키기 위해 할 수있는 일이 없다는 것입니다.

직접 또는 다른 DLL을 통해 DLL에 연결하면 수행 할 수있는 작업이 없습니다. LoadLibrary을 사용하는 경우, 비열하고 기본 주소를 예약하여 예약 된 메모리를 해제하기 전에 주소 공간의 어딘가에서 더 자주 옮길 수 있습니다. 그래도 항상 작동하지는 않습니다.

주소 공간 모니터에서는 VirtualQueryEx을 사용하여 프로세스의 주소 공간을 검사하지만 다른 도구 (예 : Process Explorer)가 psapi 라이브러리에서 사용하는 다른 파일 (DLL을 포함하여)이 매핑 된 것을 보여줄 수 있습니다 주소 공간의 어느 부분에

발견 한대로 2GB 사용자 주소 공간에서 공간이 부족할 수 있습니다. 근본적으로, 당신은 메모리 단편화에 대한 최선의 방책은 단순히 큰 연속적인 메모리 블록을 필요로하지 않는 것입니다. 역 호환이 어렵긴하지만 응용 프로그램을 '중간 크기'청크로 작업하도록 설계하면 대개 주소 공간을 훨씬 효율적으로 사용할 수 있습니다.

마찬가지로 메모리 매핑 파일 또는 Address Windowing Extensions을 사용하여 페이징 전략을 사용할 수 있습니다.

+0

멋진 도구를 사용해 주신 Process Explorer의 기능을 알려 주셔서 감사합니다. – Budric

+0

@Charles Bailey : Re static linking - DLL을 리베이스 할 수 없습니까? – rpg

+0

예, DLL을 리베이스 할 수 있습니다. 그러나 * 주소 공간 분할 및로드 시간을 최적화하는 데 도움이 될 수 있습니다 ... 일반적으로 사용자가 소유 한 DLL로만 수행해야하며 너무 많은 마이크로 최적화를 수행하면 한 시점에서 특정 exe에서 제대로 작동하는 DLL 집합이지만 다른 exe에는 최적화 된로드 전략이 없습니다. DLL이 다시 빌드되고 크기가 변경되면 프로세스를 다시 수행해야합니다. 그래서 어느 정도는 효과가 있습니다. 그러나 리조트에 의지해야만한다면 유지 보수가 높은 악순환이 생길 수 있습니다. –

1

실행 파일 일 수 있습니까? 그것은 어딘가에 주소 공간에로드해야합니다 ....

2에 관해서는 전역 새롭고 삭제 기능을 무시하기 쉽습니다 ... 그냥 정의하십시오.

2

다양한 크기의 개체를 자주 할당하고 할당을 해제한다고 가정하고 이로 인해 메모리 조각화 문제가 발생합니다.

이러한 문제를 해결하기위한 다양한 전략이 있습니다. 당신이 언급 한 다른 메모리 관리자가 당신을 위해 분열 문제를 해결할 수 있다면 도움이 될지도 모르지만 그것은 분열의 근본 원인을 좀 더 분석해야 할 것입니다. 예를 들어, 세 가지 또는 네 가지 유형의 객체를 자주 할당하고 메모리 조각화 문제를 악화시키는 경향이있는 경우이를 올바른 크기의 메모리 블록을 다시 사용할 수 있도록 자체 메모리 풀에 넣을 수 있습니다. 그런 식으로,이 특정 객체에 맞는 일련의 메모리 블록을 사용할 수 있어야하고 객체 X의 할당이 갑자기 Y를 할당 할 수없는 방식으로 Y를 담을만큼 큰 메모리 블록을 분할하는 일반적인 시나리오를 방지해야합니다 더 이상.

(2)와 마찬가지로 nedmalloc (솔직히 nedmalloc에 ​​익숙하지 않습니다.)에 대한 래퍼는 알지 못합니다.하지만 클래스 별 연산자를 만들면 매우 쉽게 자신의 래퍼를 만들 수 있습니다 신규 및 삭제 또는 과부하/대체 글로벌 연산자 신규 및 삭제. 후자를 크게 좋아하지는 않지만 배정 된 "핫스팟"이 소수의 클래스로 구성되어 있다면 일반적으로 새롭고 삭제 된 클래스 별 연산자를 사용하여 클래스를 쉽게 다시 작성할 수 있습니다.

즉, nedmalloc은 표준 malloc/free 및 적어도 MS 컴파일러 대신에 C++ 런타임 라이브러리가 malloc/free에 새로운/delete를 전달할 것이라고 생각합니다. nedmalloc로 실행 파일을 빌드하십시오.

1

프로그램에서 메모리가 할당되는 위치를 확인하는 가장 좋은 방법은 디버거를 사용하는 것입니다. 로드 된 모든 DLL과 실행 파일 자체에 대한 할당이 있으며 모두 가상 메모리 조각입니다. 또한 C/C++ 라이브러리 및 Windows API를 사용하면 응용 프로그램에 힙이 만들어져 최소한의 가상 메모리가 예약됩니다.

예를 들어 VirtualAlloc을 사용하여 비교적 작은 프로그램에서 가상 메모리를 대량 확보 할 수 있으며 VirtualAlloc이 실패하거나 나중에 새 DLL (등)을로드하려고 할 때 응용 프로그램이 실패하는 경우에만 찾을 수 있습니다. 로드 할 DLL과 위치를 항상 제어 할 수는 없습니다. 많은 A/V 및 기타 제품이 시작되는 동안 실행중인 모든 프로세스에 DLL을 주입합니다. 이런 일이 발생하면, 해당 DLL은 종종로드 주소에서 첫 번째 선택을합니다. 즉, 기본적으로 컴파일/링크 된 것입니다. 일반적인 32 비트 Windows 응용 프로그램의 사용 가능한 2GB 가상 주소 공간 중 DLL이 해당 주소 공간의 중간에 큰 소리로로드되면 획득 할 수있는 최대 할당/예약 크기는 1GB 미만입니다.

windbg를 사용하면 어떤 메모리 영역이 사용되는지, 예약되어 있는지 확인할 수 있습니다. lm 명령은 모든 DLL과 EXE 및 해당 범위의로드 주소를 표시합니다. ! vadump 명령은 프로세스와 페이지 보호에 사용되는 모든 가상 메모리를 보여줍니다. 페이지 보호는 거기에 무엇이 있는지에 대한 큰 힌트입니다. 예를 들어, 64 비트 calc.exe 프로세스의 다음 (부분)! 대충 률에서 첫 번째 영역은 단순히 액세스로부터 보호되는 가상 메모리의 범위입니다. (다른 것들 중에서도 이것은 주소 0에 메모리를 할당하지 못하게합니다.) MEM_COMMIT은 메모리가 RAM 또는 페이징 파일에 의해 백업됨을 의미합니다. PAGE_READWRITE는 힙 메모리이거나로드 된 모듈의 데이터 세그먼트 일 수 있습니다. PAGE_READEXECUTE는 일반적으로로드되어 lm이 생성 한 목록에 표시되는 코드입니다. MEM_RESERVE는

0:004> !vadump 
BaseAddress:  0000000000000000 
RegionSize:  0000000000010000 
State:    00010000 MEM_FREE 
Protect:   00000001 PAGE_NOACCESS 

BaseAddress:  0000000000010000 
RegionSize:  0000000000010000 
State:    00001000 MEM_COMMIT 
Protect:   00000004 PAGE_READWRITE 
Type:    00040000 MEM_MAPPED 

BaseAddress:  0000000000020000 
RegionSize:  0000000000003000 
State:    00001000 MEM_COMMIT 
Protect:   00000002 PAGE_READONLY 
Type:    00040000 MEM_MAPPED 

내가 그 일을 설명 도움이되기를 바랍니다 ... 뭔가 메모리 영역을 예약에서 VirtualAlloc를 호출 한 의미하지만, 그것은 등 가상 메모리 관리자에 의해 매핑되지 않고있다. Windbg은 훌륭한 도구이며 메모리가 사용되는 위치를 찾는 데 도움이되는 많은 확장 기능을 제공합니다.

정말로 힙에 대해 신경 쓰면, 힙을보십시오.

+0

감사합니다. windbg를 사용해 보겠습니다. – Budric

2

메모리 조각화를 줄이려면 Windows Low-Fragmentation Heap을 활용할 수 있습니다. 우리는이 제품을 사용하여 좋은 효과를 얻었으며 그렇게해온 이후로 거의 많은 메모리 관련 문제를 겪지 않았습니다.

+0

이 기능을 보았습니다. 그러나이를 사용하려면 HeapSetInformation()을 실행해야합니다. 메모리의 스냅 샷은 main()의 첫 번째 라인에서 바로 잡았으며, 이미 1.3GB의 주소 공간이 채워진 후에는 메모리가 조각난 상태입니다. 프로세스 탐색기에서 DLL과 다른 것들을 살펴 본다. 그래서 LFH 도움이 될 수도 있지만 이미 DLL로드로 인해 발생하는 조각화를 방지하지 않습니다. – Budric