Jestem programistą C ++ na platformie Windows. Używam Visual Studio 2008.
Zazwyczaj kończę w kodzie z przeciekami pamięci.
Zwykle stwierdzam wyciek pamięci poprzez sprawdzenie kodu, ale jest on uciążliwy i nie zawsze jest dobrym podejściem.
Ponieważ nie stać mnie na płatne narzędzie do wykrywania wycieków pamięci, chciałem, abyście zaproponowali najlepsze możliwe sposoby uniknięcia wycieków pamięci.
- Chcę wiedzieć, jak programista może wykryć wycieki pamięci.
- Czy istnieje jakiś standard lub procedura, którą należy zastosować, aby upewnić się, że nie ma wycieku pamięci w programie?
c++
memory-leaks
Chris_vr
źródło
źródło
Odpowiedzi:
Instrukcje
Rzeczy, których będziesz potrzebować
1
Zrozumienie podstaw operatora. Operator C ++
new
przydziela pamięć sterty. Przezdelete
pamięć Operatora uwalnia sterty. Dla każdegonew
należy użyćdelete
tak, aby zwolnić tę samą pamięć, którą przydzielono:2)
Przydziel ponownie pamięć tylko po usunięciu. W poniższym kodzie
str
uzyskuje nowy adres z drugim przydziałem. Pierwszy adres zostaje bezpowrotnie utracony, podobnie jak 30 bajtów, które wskazał. Teraz nie można ich uwolnić, a masz przeciek pamięci:3)
Obejrzyj te przypisania wskaźnika. Każda zmienna dynamiczna (przydzielona pamięć na stercie) musi być powiązana ze wskaźnikiem. Kiedy zmienna dynamiczna zostaje odłączona od swojego wskaźnika (wskaźników), niemożliwe jest usunięcie. Ponownie powoduje to wyciek pamięci:
4
Uważaj na lokalne wskaźniki. Wskaźnik zadeklarowany w funkcji jest alokowany na stosie, ale zmienna dynamiczna, na którą wskazuje, jest alokowana na stercie. Jeśli go nie usuniesz, będzie się utrzymywać po wyjściu programu z funkcji:
5
Zwróć uwagę na kwadratowe nawiasy klamrowe po „usuń”. Skorzystaj
delete
sam z siebie, aby uwolnić pojedynczy obiekt. Użyjdelete []
z nawiasami kwadratowymi, aby zwolnić tablicę sterty. Nie rób czegoś takiego:6
Jeśli przeciek jest jeszcze dozwolony - zwykle szukam go w deleaker (sprawdź tutaj: http://deleaker.com ).
źródło
someFunction("some parameter")
czy muszę usunąć"some parameter"
wsomeFunction
, po wywołaniu funkcji, czy też są one automatycznie usuwane?Możesz użyć niektórych technik w kodzie, aby wykryć wyciek pamięci. Najczęstszym i najłatwiejszym sposobem na wykrycie jest zdefiniowanie makra powiedz DEBUG_NEW i użycie go wraz ze wstępnie zdefiniowanymi makrami, takimi jak
__FILE__
i__LINE__
zlokalizowanie wycieku pamięci w kodzie. Te predefiniowane makra informują o wycieku pliku i linii.DEBUG_NEW to po prostu MAKRO, które zwykle definiuje się jako:
Dzięki temu, gdziekolwiek użyjesz
new
, może również śledzić numer pliku i linii, które mogą być użyte do zlokalizowania wycieku pamięci w twoim programie.I
__FILE__
,__LINE__
są predefiniowanymi makrami, które oceniają odpowiednio nazwę pliku i numer wiersza, gdzie ich używasz!Przeczytaj następujący artykuł, który wyjaśnia technikę używania DEBUG_NEW z innymi interesującymi makrami, bardzo pięknie:
Wieloplatformowy wykrywacz wycieków pamięci
Z Wikpedii ,
źródło
#define
spowoduje to bałagan z przeciążeniemoperator new
i wygeneruje błędy kompilatora. Nawet jeśli uda ci się to pokonać, nadal przeciążone funkcje nie zostaną rozwiązane. Chociaż technika jest dobra, czasem wymaga wielu zmian w kodzie.auto_ptr
nie będzie działać z standardowych pojemnikach, takich jakstd::vector
,std::list
itd. Zobacz to: stackoverflow.com/questions/111478/...operator new
i jakich wersji używasz?Istnieje kilka dobrze znanych technik programowania, które pomogą zminimalizować ryzyko wycieków pamięci z pierwszej ręki:
new
idelete
zawsze paruj, i upewnij się, że kod alokacji / dezalokacji nazywa się paramivector<T> t
zamiast tego gdziekolwiek to możliweT* t = new T[size]
źródło
Valgrind http://valgrind.org/
i
GDB http://www.gnu.org/software/gdb/
źródło
gflags
narzędzia, aby włączyć ślady stosu w trybie użytkownika.UMDH
do robienia wielu migawek pamięci programu. Zrób migawkę, zanim pamięć zostanie przydzielona, i zrób drugą migawkę po punkcie, w którym uważasz, że program wyciekł z pamięci. Możesz dodać przerwy lub monity w swoim programie, aby dać Ci szansę na uruchomienieUMDH
i zrobienie migawek.UMDH
ponownie, tym razem w trybie, który różni się między dwoma migawkami. Następnie wygeneruje raport zawierający stosy wywołań podejrzanych wycieków pamięci.gflags
ustawienia.UMDH
dostarczy ci więcej informacji niż sterty debugowania CRT, ponieważ obserwuje alokacje pamięci w całym procesie; może nawet powiedzieć, czy przeciekają komponenty innych firm.źródło
Uruchomienie „Valgrind” może:
1) Pomóż zidentyfikować wycieki pamięci - pokaż, ile masz wycieków pamięci, i wskaż wiersze w kodzie, w którym przydzielono wyciek pamięci.
2) Wskaż niewłaściwe próby zwolnienia pamięci (np. Niewłaściwe wywołanie
delete
)Instrukcje korzystania z „Valgrind”
1) Zdobądź valgrind tutaj .
2) Skompiluj swój kod z
-g
flagą3) W biegu powłoki:
Gdzie „myprog” to skompilowany program i
arg1
,arg2
argumenty za swój program.4) Wynikiem jest lista połączeń do
malloc
/,new
które nie miały kolejnych połączeń do darmowego usunięcia.Na przykład:
Mówi, w której linii
malloc
została wywołana (która nie została uwolniona).Jak wskazali inni, upewnij się, że dla każdego
new
/malloc
połączenia masz kolejnedelete
/free
połączenie.źródło
Jeśli używasz gcc, dostępny jest gprof.
Niektóre używają narzędzi, inne robią to, co robisz, mogą również przeglądać kod równorzędny
Dla mnie: za każdym razem, gdy tworzę dynamicznie przydzielane obiekty, zawsze umieszczam kod zwalniający po, a następnie wypełniam kod pomiędzy. Byłoby to OK, jeśli jesteś pewien, że między kodem nie będzie wyjątków. W przeciwnym razie korzystam z try-wreszcie (nie używam często C ++).
źródło
W studiu wizualnym jest wbudowany detektor wycieku pamięci o nazwie C Runtime Library. Kiedy program zakończy działanie po powrocie funkcji głównej, CRT sprawdzi stertę debugowania aplikacji. jeśli nadal masz przydzielone bloki na stercie debugowania, oznacza to przeciek pamięci.
To forum omawia kilka sposobów uniknięcia wycieku pamięci w C / C ++ ..
źródło
Wyszukaj w kodzie wystąpienia
new
i upewnij się, że wszystkie występują w konstruktorze z pasującym usunięciem w destruktorze. Upewnij się, że jest to jedyna możliwa operacja wyrzucania w tym konstruktorze. Prostym sposobem na to jest zawinięcie wszystkich wskaźnikówstd::auto_ptr
lubboost::scoped_ptr
(w zależności od tego, czy potrzebujesz przesunąć semantykę). Dla całego przyszłego kodu po prostu upewnij się, że każdy zasób jest własnością obiektu, który czyści zasób w swoim destruktorze. Jeśli potrzebujesz semantyki przenoszenia, możesz uaktualnić do kompilatora, który obsługuje odwołania r-wartość (wierzę VS2010) i tworzyć konstruktory przenoszenia. Jeśli nie chcesz tego robić, możesz użyć różnych podchwytliwych technik polegających na sumiennym użyciu wymiany lub wypróbować bibliotekę Boost.Move.źródło
scope_ptr
s i każdy jest inicjowany indywidualnie, wszystkie te, które zostały pomyślnie skonstruowane, usuną swoje wskaźniki, a pozostałe i tak nie będą jeszcze utrzymywać wskaźników w przydzielonej pamięci. Podam przykład za kilka godzin, kiedy wrócę z pracy do domu.Możesz użyć narzędzia Valgrind do wykrywania wycieków pamięci.
Ponadto, aby znaleźć wyciek w określonej funkcji, użyj wyjścia (0) na końcu funkcji, a następnie uruchom ją za pomocą Valgrind
źródło
Przegląd automatycznych kontrolerów wycieków pamięci
W tej odpowiedzi porównuję kilka różnych kontrolerów wycieków pamięci w prostym, łatwym do zrozumienia przykładzie wycieku pamięci.
Przede wszystkim zobacz tę ogromną tabelę na wiki ASan, która porównuje wszystkie narzędzia znane człowiekowi: https://github.com/google/sanitizers/wiki/AddressSanitizerComparisonOfMemoryTools/d06210f759fec97066888e5f27c7e722832b0924
Analizowany przykład to:
main.c
GitHub w górę .
Postaramy się zobaczyć, jak wyraźnie różne narzędzia wskazują nam na nieszczelne połączenia.
tcmalloc z gperftools od Google
https://github.com/gperftools/gperftools
Zastosowanie na Ubuntu 19.04:
Dane wyjściowe uruchomienia programu zawierają analizę wycieków pamięci:
a wynik
google-pprof
zawiera analizę użycia sterty:Dane wyjściowe wskazują nam dwa z trzech wycieków:
Nie jestem pewien, dlaczego trzeci się nie pojawił
W każdym razie, gdy zwykle, gdy coś wycieka, zdarza się to wiele razy, a kiedy użyłem go w prawdziwym projekcie, po prostu bardzo łatwo zauważyłem funkcję przecieku.
Jak wspomniano na samym wyjściu, powoduje to znaczne spowolnienie wykonania.
Dalsza dokumentacja na:
Zobacz także: Jak korzystać z TCMalloc?
Testowany w Ubuntu 19.04, google-perftools 2.5-2.
Adres Sanitizer (ASan) również przez Google
https://github.com/google/sanitizers
Wcześniej wspomniano w: Jak znaleźć wyciek pamięci w kodzie / projekcie C ++? TODO vs tcmalloc.
Jest to już zintegrowane z GCC, więc możesz po prostu:
i wyniki wykonania:
który wyraźnie identyfikuje wszystkie wycieki. Miły!
ASan może również wykonywać inne fajne kontrole, takie jak zapisywanie poza granicami: Wykryto rozbicie stosu
Testowane w Ubuntu 19.04, GCC 8.3.0.
Valgrind
http://www.valgrind.org/
Wcześniej wspomniano na stronie : https://stackoverflow.com/a/37661630/895245
Stosowanie:
Wynik:
Po raz kolejny wszystkie wycieki zostały wykryte.
Zobacz także: Jak używać valgrind do wykrywania wycieków pamięci?
Testowane w Ubuntu 19.04, valgrind 3.14.0.
źródło
Visual Leak Detector (VLD) to darmowy, solidny system wykrywania wycieków pamięci typu open source dla Visual C ++.
Jeśli masz tylko zrzuty awaryjne, możesz użyć
!heap -l
polecenia Windbg , wykryje ono wycieki bloków sterty. Lepiej otwórz opcję gflags: „Utwórz bazę danych śledzenia stosu trybu użytkownika”, wtedy zobaczysz stos wywołań alokacji pamięci.źródło
MTuner to bezpłatne narzędzie do profilowania pamięci, wykrywania i analizy wycieków z wielu platform, obsługujące kompilatory MSVC, GCC i Clang. Dodatki zawarte:
Użytkownicy mogą profilować dowolne oprogramowanie atakujące platformy za pomocą kompilatorów krzyżowych GCC lub Clang. MTuner ma wbudowaną obsługę platform Windows, PlayStation 4 i PlayStation 3.
źródło
W systemie Windows można użyć sterty debugowania CRT .
Tak, nie używaj ręcznego zarządzania pamięcią (jeśli kiedykolwiek zadzwonisz
delete
lubdelete[]
ręcznie, to robisz to źle). Używaj wskaźników RAII i inteligentnych, ogranicz alokacje sterty do absolutnego minimum (przez większość czasu wystarczą zmienne automatyczne).źródło
Odpowiadając na drugą część pytania,
Tak jest. Jest to jedna z kluczowych różnic między C i C ++.
W języku C ++ nigdy nie należy wywoływać
new
anidelete
wprowadzać kodu użytkownika. RAII jest bardzo często stosowaną techniką, która właściwie rozwiązuje problem zarządzania zasobami. Każdy zasób w twoim programie (zasób to wszystko, co należy nabyć, a następnie uwolnić: uchwyty plików, gniazda sieciowe, połączenia z bazą danych, ale także zwykłe przydziały pamięci, a w niektórych przypadkach pary wywołań API (BeginX ( ) / EndX (), LockY (), UnlockY ()), powinny być opakowane w klasę, w której:new
jeśli zasób jest alokacją memroy)Ta klasa jest następnie tworzona lokalnie, na stosie lub jako członek klasy, a nie przez wywoływanie
new
i przechowywanie wskaźnika.Często nie musisz samodzielnie definiować tych klas. Standardowe kontenery biblioteczne zachowują się również w ten sposób, dzięki czemu każdy obiekt przechowywany w pliku
std::vector
zostanie zwolniony po zniszczeniu wektora. Więc jeszcze raz, nie przechowywać wskaźnik do pojemnika (który musiałby pan zadzwonićnew
idelete
), ale raczej obiekt sam (co daje zarządzanie pamięcią za darmo ). Podobnie, inteligentne klasy wskaźników mogą być używane do łatwego zawijania obiektów, które muszą zostać przydzielonenew
i kontrolowania ich żywotności.Oznacza to, że gdy obiekt wykracza poza zakres, zostaje automatycznie zniszczony, a jego zasoby zwolnione i oczyszczone.
Jeśli robisz to konsekwentnie w całym kodzie, po prostu nie będziesz mieć przecieków pamięci. Wszystko co może wyciec, jest powiązane z destruktorem, który z pewnością zostanie wywołany, gdy kontrola opuści zakres, w którym obiekt został zadeklarowany.
źródło
AddressSanitizer (ASan) to szybki wykrywacz błędów pamięci. Znajduje błędy przepełnienia bufora „use-after-free” i {heap, stack, global} w programach C / C ++. Znajduje:
To narzędzie jest bardzo szybkie. Średnie spowolnienie instrumentowanego programu wynosi ~ 2x.
źródło
Poza narzędziami i metodami opisanymi w innych odpowiedziach, do wykrywania wycieków pamięci (i innych problemów) można także użyć narzędzi do analizy kodu statycznego. Bezpłatne, niezawodne narzędzie to Cppcheck. Istnieje jednak wiele innych dostępnych narzędzi. Wikipedia ma listę narzędzi do analizy kodu statycznego.
źródło
Upewnij się, że cała pamięć sterty została pomyślnie zwolniona. Nie ma potrzeby, jeśli nigdy nie przydzielisz pamięci na stercie. Jeśli to zrobisz, policz, ile razy pamięć malloc została zliczona, i policz ilość wolnego czasu pamięci.
źródło
W kodzie aplikacji nie należy nigdy używać „nowego” ani „skasować”. Zamiast tego utwórz nowy typ korzystający z idiomu menedżer / pracownik, w którym klasa menedżera przydziela i zwalnia pamięć oraz przekazuje wszystkie inne operacje do obiektu roboczego.
Niestety jest to więcej pracy, niż powinno być, ponieważ C ++ nie ma przeciążenia „operatora”. To jeszcze więcej pracy w obecności polimorfizmu.
Ale to jest warte wysiłku, ponieważ nigdy nie musisz się martwić o wycieki pamięci, co oznacza, że nawet nie musisz ich szukać.
źródło