DLL - dynamiczne ładowanie i usuwanie z RAM

0

Piszę w C++ Builder 2009. Stworzyłem czystą bibliotekę bez żadnych funkcji

#include <vcl.h>
#include <windows.h>
#pragma hdrstop

#pragma argsused
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, LPVOID lpvReserved)
{
   return 1;
}

Skompilowałem, wrzuciłem do katalogu z aplikacją, kliknięcie buttona powoduje tylko wczytanie i zwolnienie biblioteki, nic poza tym, kliknięcie buttona to tylko te dwie poniższe linie

 HINSTANCE DLLHandle = LoadLibrary("DL0001.dll");
 FreeLibrary(DLLHandle);

Ładuję bibliotekę, potem ją zwalniam. Po zwolnieniu biblioteki za każdym razem w RAM pozostają jakieś śmieci w wielkości około 300kB. Każde kliknięcie buttona powoduje zwiększenie zużycia RAM-u przez aplikację o kolejne 300 kB.

Teraz gdy z katalogu systemowego "system32" skopiuję dowolną bibliotekę, zmieniam jej nazwę (by aplikacja korzystała z tej która jest w katalogu aplikacji a nie z katalogu systemowego) i ładuję jak poprzednią "DL0001.dll" potem usuwam poprzez polecenie FreeLibrary, biblioteka jest w 100 % usuwana z RAM-u.

Śmieci w RAM-ie pozostają tylko po bibliotekach stworzonych przeze mnie poprzez Buildera.

Biblioteka nie powinna pozostawiać w RAM żadnych śmieci po sobie, niestety jednak jest inaczej. Tym bardziej czysta biblioteka, która nie zawiera żadnych funkcji i nie tworzy żadnych zmiennych.

Czy ktoś może mi pomóc w rozwiązaniu tego problemu ?

0

A co zwraca FreeLibrary? Sprawdź wynik, może licznik referencji nie doszedł do zera i system nie zwolnił zasobów DLLki, np. biblioteka tematów UXTHEME.dll mimo, że samemu się ją ładuje tylko raz, ma licznik referencji większy niż 1 (w międzyczasie jakieś inne komponenty z nią związane mogą używać LoadLibrary zamiast GetModuleHandle do pobrania jej bazy) i też jej zwolnienie 1 "strzałem" przez FreeLibrary nie zwalnia jej zasobów (tylko zmniejsza licznik), co można zaobserwować śledząć aplikację np. BoundsCheckerem.

Możesz też sprawdzić czy po FreeLibrary nadal coś siedzi pod adresem hModule czy pamięc została zwolniona, chociaż byłoby to dziwne, zwłaszcza, że jak mówisz masz czystą DLL-ke.

W ostatecznym wypadku możesz też prześledzić czy do DLL-ki lecą wszystkie notyfikacje DLL_PROCESS_ATTACH i DLL_PROCESS_DETACH.

0

Zrobiłem coś takiego:

 HINSTANCE DLLHandle = LoadLibrary("DL0001.dll");
 FreeLibrary(DLLHandle);

 if (DLLHandle == NULL)
 {
	ShowMessage ("Biblioteka wolna");
 }
 else
 {
	 ShowMessage ("Biblioteka zajęta");
 } 

Wygląda na to że FreeLibrary nie zwalnia biblioteki ponieważ za każdym razem wyświetla mi monit "Biblioteka zajęta".

Fragment kodu

if (FreeLibrary(DLLHandle))
ShowMessage("Usunięcie biblioteki z pamięci udało się");
else
ShowMessage("Usunięcie biblioteki z pamięci nie powiodło się");;

Zwraca również "Usunięcie biblioteki z pamięci nie powiodło się"

Macie jakieś pomysły jak to rozwiązać ?

0

dla testów, sprawdz ile razy masz załadowaną bibliotekę:

BOOL bResult = FALSE;
DWORD dwCount = 0;

do
{
 bResult = FreeLibrary(hLibrary);

 if (bResult == TRUE) dwCount++;

} while (bResult == TRUE)

printf("%u", dwCount);

a takie głupie pytanie, nie masz jej czasem też statycznie podlinkowanej?

W ogóle wystaw swoje binarki, rzucę sobie okiem jak to masz zrobione.

0
Bartosz Wójcik napisał(a)

W ogóle wystaw swoje binarki, rzucę sobie okiem jak to masz zrobione.

Proszę, oto mój najprostszy projekt.

W jednym katalogu projekt aplikacji (Aplikacja) oraz DLL-ki (DLL_projekt)

0

OK, teraz same binarki.

System na którym pracuję to WIN7, nie mam możliwości przekompilowania aplikacji na innym systemie, tylko mogę ją uruchomić. Testowałem na WIN XP - jest to samo. Antywirus to AVAST 5.

Tu na czystej bibliotece śmieci pozostające w RAM są mniejszego rozmiaru niż pisałem w pierwszym poście, ale dalej zostają.

Uruchomienie aplikacji, biblioteka jeszcze nie była ładowana - wielkość w RAM 916 KB
Jednokrotne załadowanie / rozładowanie biblioteki - wielkość w RAM 1140 kB
Pięciokrotne załadowanie / rozładowanie biblioteki - wielkość w RAM 1216 kB
Dwudziestokrotne załadowanie / rozładowanie biblioteki - wielkość w RAM 1472 kB

Jak widać każdorazowe wywołanie i zwolnienie biblioteki zwiększa zużycie RAM-u przez aplikację.

W przypadku gdy w bibliotece są funkcje i formy jednokrotne załadowanie / wyładowanie biblioteki zwiększa zużycie RAM-u w moim konkretnym przypadku o ok 300 kB.

0

Wycieki są w bibliotekach z RAD Studio, w DLLce masz wyciek (za każdym razem jak ją ładujesz), na niektóre tego typu rzeczy nie masz wpływu (biblioteki CRT z Visual C++ też posiadają liczne wycieki, choćby zasobów), deadlisting pokazuje Ci, gdzie następuje alokacja:

.text:004104D0 ; Attributes: library function bp-based frame
.text:004104D0
.text:004104D0 ; __fastcall Classes::MakeObjectInstance(void __fastcall (__closure *)(Messages::TMessage &))
.text:004104D0 @Classes@MakeObjectInstance$qqrynpqqrr17Messages@TMessage$v proc near
.text:004104D0                                         ; CODE XREF: Classes::AllocateHWnd(void (*)(Messages::TMessage &))+96p
.text:004104D0                                         ; Forms::_16480+9p
.text:004104D0                                         ; Dialogs::TCommonDialog::TCommonDialog(Classes::TComponent *)+25p
.text:004104D0                                         ; Controls::TWinControl::TWinControl(Classes::TComponent *)+3Bp
.text:004104D0
.text:004104D0 arg_0           = dword ptr  8
.text:004104D0 arg_4           = dword ptr  0Ch
.text:004104D0
.text:004104D0                 push    ebp
.text:004104D1                 mov     ebp, esp
.text:004104D3                 push    ebx
.text:004104D4                 push    esi
.text:004104D5                 push    edi
.text:004104D6                 mov     edi, offset dword_46C57C
.text:004104DB                 cmp     dword ptr [edi], 0
.text:004104DE                 jnz     short loc_41054C
.text:004104E0                 push    40h             ; flProtect
.text:004104E2                 push    1000h           ; flAllocationType
.text:004104E7                 push    1000h           ; dwSize
.text:004104EC                 push    0               ; lpAddress
.text:004104EE                 call    VirtualAlloc

Ja to testowałem na XP, sprawa z Windows 7 może być inna, być może inne rzeczy zostają inicjalizowane (dla tego konkretnego systemu) za każdym razem jak ładujesz bibliotekę, pamięć przez procedury biblioteczne w DLLce jest alokowana, ty robisz FreeLibrary, biblioteka jest poprawnie zwalniana, ale pamięć nadal pozostaje zaalokowana, dopóki nie zakończysz procesu, stąd wycieki.

Sama aplikacja też ma wycieki pamięci, konkretnie 1.2 MB:

.text:00425A80 ; Attributes: library function
.text:00425A80
.text:00425A80 ; System::_16512
.text:00425A80 @System@_16512  proc near               ; CODE XREF: System::SysGetMem(int)+1CEp
.text:00425A80                                         ; System::SysGetMem(int)+2F6p
.text:00425A80                 push    ebx
.text:00425A81                 mov     ebx, eax
.text:00425A83                 call    @System@_16511  ; System::_16511
.text:00425A88                 push    4               ; flProtect
.text:00425A8A                 push    1000h           ; flAllocationType
.text:00425A8F                 push    13FFF0h         ; dwSize <----------------
.text:00425A94                 push    0               ; lpAddress
.text:00425A96                 call    VirtualAlloc
 

W załączniku efekt 3 krotnego kliknięcia na button, widać jak 3 razy jest alokowane 0x1000 bajtów z biblioteki.

0

W takim razie przyznam że mocno niedorobiony jest ten produkt borlanda - C++ Builder 2009.

Aplikacja zawierająca tylko jeden button i jedną funkcję (obsługa buttona) nie tworząca żadnych zmiennych już ma tak duże wycieki ?
Nawet czysta (nie zawierająca żadnych funkcji) DLL-ka też ma wycieki pamięci?

W tym wypadku nie jestem w stanie napisać nawet najprostszej i poprawnie działającej aplikacji korzystającej z funkcji zawartych w DLL-kach. Chciałem do dll-ek wrzucić moduły z innego projektu, które będą w aplikacji najrzadziej używane. W tym przypadku nie ma to najmniejszego sensu.

Powoli tracę przekonanie do tego środowiska programistycznego.

U siebie też coś takiego macie ???

0

Mało kto na takie rzeczy zwraca uwagę, ale tu leci 10kB tam jakieś uchwyty nie są zwalniane no i robi się wyciek, jak chcesz mieć w pełni kontrolę nad kodem zacznij pisać w WinAPI czysty Windowsowy kod (poszukaj przykładów dla DllMain), bez używania bibliotek pomocniczych (albo w assemblerze co nie jest takie trudne), z tym 1 buttonem to się tak wydaje, że nic nie ma, ale jakby faktycznie tam nic wielkiego nie było to wynikowy kod by nie zajmował ~600 kB, a tam masz napchaną całą masę kodu bibliotek pomocniczych (od form, które nie są natywne dla Window) i innego badziewia. Co do środowiska to sam wiedziałeś w co się pakujesz, w końcu nazywa się to Rapid Application Development :P

0

W takim przypadku nie opłaca się przerzucać funkcji i modułów do DLL-ek. Lepiej wrzucić całą gotową formę z już istniejącego projektu do nowo tworzonego i wkompilować na stałe do pliku exe. Teoretycznie po przerzuceniu do DLL mało używanego modułu zmniejsza się rozmiar zużycia RAM przez aplikację ale tylko do momentu korzystania z modułu umieszczonego w DLL.

0

To zależy jak to robisz, przy własnym kodzie możesz zadbać o każdą alokację pamięci, poza tym zwykle DLL-ki ładuje się tylko raz podczas działania aplikacji, więc nawet przy tej 300 kB stracie, którą masz, byłby to niski koszt.

1 użytkowników online, w tym zalogowanych: 0, gości: 1