2011-11-17 10 views
8

Moja aplikacja wymaga dużej ilości pamięci i dużej struktury danych, aby wykonać swoją pracę. Często aplikacja wymaga więcej niż 1 GB pamięci, aw niektórych przypadkach moi klienci naprawdę muszą korzystać z 64-bitowej wersji aplikacji, ponieważ mają kilka gigabajtów pamięci.Zmuszanie systemu Windows do ładowania bibliotek DLL w miejscach, w których pamięć jest minimalnie pofragmentowana.

W przeszłości mogłem z łatwością wytłumaczyć użytkownikowi, że jeśli pamięć osiągnęła 1,6 do 1,7 GB pamięci, było to "za mało pamięci" lub naprawdę blisko sytuacji "braku pamięci" i było to konieczne zmniejszyć ich pamięć lub przejść do wersji 64-bitowej.

W zeszłym roku zauważyłem, że często aplikacja zużywa tylko około 1 GB, zanim zabraknie jej pamięci. Po pewnych badaniach wydawało się, że przyczyną tego problemu jest fragmentacja pamięci. Użyłem VMMAP (narzędzia SysInternals) do obejrzenia użycia pamięci w mojej aplikacji i zobaczyłem coś takiego: Address Space Fragmentation

Pomarańczowe obszary to pamięć przydzielona przez moją aplikację. Purpurowe obszary to kod wykonywalny.

Jak widać w dolnej połowie obrazu, purpurowe obszary (które są bibliotekami DLL) są ładowane pod wieloma różnymi adresami, co powoduje, że moja pamięć jest pofragmentowana. Nie stanowi to problemu, jeśli mój klient nie ma dużej ilości danych, ale jeśli mój klient ma zestawy danych, które zajmują więcej niż 1 GB, a część aplikacji potrzebuje dużego bloku pamięci (np. 50 MB), może to spowodować awarię alokacji pamięci, powodując awarię mojej aplikacji.

Większość moich struktur danych bazuje na STL i często nie wymaga dużych porcji ciągłej pamięci, ale w niektórych przypadkach (na przykład bardzo duże ciągi), naprawdę potrzebna jest przyległa blokada pamięci. Niestety nie zawsze jest możliwa zmiana kodu tak, aby nie potrzebował tak ciągłego bloku pamięci.

Pytania są:

  • W jaki sposób można wpływać na lokalizację, w której DLL są ładowane do pamięci, bez jawnie przy użyciu rebase na cały DLL na komputerze klienta, albo bez ładowania cały DLL wyraźnie.
  • Czy istnieje sposób określania adresów ładowania plików DLL we własnym pliku manifestu aplikacji?
  • Czy istnieje sposób, aby powiedzieć systemowi Windows (za pośrednictwem pliku manifestu?), Aby nie rozrzucać bibliotek DLL (myślę, że to rozproszenie nazywa się ASLR).

Oczywiście najlepsze rozwiązanie to takie, na które mogę wpływać z pliku manifestu mojej aplikacji, ponieważ polegam na automatycznym/dynamicznym ładowaniu bibliotek DLL przez system Windows.

Moja aplikacja to aplikacja mieszana (zarządzana i niezarządzana), chociaż główna część aplikacji jest niezarządzana.

Wszelkie sugestie?

+0

Czy to jest coś, co może ci pomóc? http://msdn.microsoft.com/en-us/library/f7f5138s.aspx – detunized

+1

mmm, czy naprawdę potrzebujesz tyle pamięci w tym samym czasie? Monitor procesu przechowuje logi w pamięci wirtualnej i w razie potrzeby przenosi dane tylko do przestrzeni adresowej pamięci procesu, sprawdź http://blogs.msdn.com/b/oldnewthing/archive/2004/08/10/211890.aspx, aby uzyskać kod przykład –

+1

Nie jestem pewien, czy wszyscy pozostawiający odpowiedź rozumieją konsekwencje ASLR: http://en.wikipedia.org/wiki/ASLR –

Odpowiedz

5

Po pierwsze, fragmentacja wirtualnej przestrzeni adresowej nie musi koniecznie powodować stanu braku pamięci. Tak by było, gdyby Twoja aplikacja musiała przydzielić sąsiednich bloków pamięci o odpowiednim rozmiarze. W przeciwnym razie wpływ fragmentacji powinien być niewielki.

Mówisz, że większość twoich danych to "oparte na STL", ale jeśli na przykład przeznaczysz ogromny std::vector będziesz potrzebował sąsiedniego bloku pamięci.

AFAIK nie ma możliwości określenia preferowanego adresu mapowania biblioteki DLL podczas jej ładowania. Tak więc istnieją tylko dwie opcje: albo zmień ją ponownie (plik DLL), albo zaimplementuj bibliotekę DLL ładującą się (co oczywiście nie jest banalne).

Zwykle nie ma potrzeby ponownego podstawiania standardowych bibliotek DLL interfejsu API systemu Windows, ponieważ są one ładowane w przestrzeni adresowej bardzo ściśle. Fragmentacja może nadejść z niektórych bibliotek DLL innych firm (np. Haki do okien, iniekcje antywirusowe itp.).

3

Nie można tego zrobić z manifestem, musi to być zrobione przez opcję linkera/BASE. Linker + Advanced + Podstawowy adres w IDE. Najbardziej elastycznym sposobem jest użycie/BASE: @ filename, składnia klawiszy tak, aby linker odczytał adres bazowy z pliku tekstowego.

Najlepszym sposobem na wypełnienie pliku tekstowego jest okno Debug + Windows + Modules. Pobierz kompilację Release swojego programu załadowanego do debuggera i załaduj cały plik. Debuguj + Przerwij wszystko, wywołaj okno i skopiuj i wklej je do pliku tekstowego. Edytuj go, aby dopasować wymagany format, obliczając adresy ładowania z kolumny Adres. Pozostaw wystarczająco dużo miejsca między bibliotekami DLL, aby nie musieć ciągle modyfikować pliku tekstowego.

+0

Oczywiście to nie pomaga w ogóle z wszystkimi haczykami DLL znalezionymi w typowym systemie . Sterownik graficzny. Anti Virus. Sterownik myszy. Itd. –

+0

Hmm, nie, nawet te mogą zostać zhackowane za pomocą narzędzia rebase.exe. Nie jestem pewien, czy bym to zalecił, strata czasu, chyba że planujesz wysłać swoją maszynę razem z produktem. –

1

Jeśli jesteś w stanie wykonać swój własny kod przed załadowaniem bibliotek, możesz zarezerwować sobie spory fragment przestrzeni adresowej przed czasem, aby go przydzielić.

W przeciwnym razie należy zidentyfikować odpowiedzialne biblioteki DLL, aby określić, dlaczego są one ładowane. Na przykład czy są częścią .NET, biblioteki uruchomieniowej języka, własnego kodu lub bibliotek stron trzecich, z których korzystasz?

Dla własnego kodu najrozsądniejszym rozwiązaniem jest prawdopodobnie stosowanie statycznego zamiast dynamicznego łączenia. To powinno być również możliwe dla środowiska uruchomieniowego języka i może być możliwe dla bibliotek stron trzecich.

W przypadku bibliotek innych firm można zmienić tryb ładowania niejawnego na jawne, tak aby ładowanie odbywało się dopiero po zarezerwowaniu fragmentu przestrzeni adresowej.

Nie wiem, czy jest coś, co można zrobić z bibliotekami .NET; ale ponieważ większość kodu jest niezarządzana, możliwe jest wyeliminowanie zarządzanych komponentów w celu pozbycia się .NET. A może możesz podzielić części .NET na osobny proces.

+0

... ale znacznie bardziej sensownym byłoby, gdyby program był niezbędny do wyeliminowania wymogu dużego, ciągłego bloku pamięci. –

Powiązane problemy