Czy kiedykolwiek dopuszczalne jest wycieki pamięci w aplikacji C lub C ++?
Co się stanie, jeśli przydzielisz trochę pamięci i wykorzystasz ją do ostatniego wiersza kodu w aplikacji (na przykład destruktora obiektu globalnego)? O ile zużycie pamięci nie rośnie z czasem, czy można ufać systemowi operacyjnemu, który zwolni twoją pamięć po zakończeniu aplikacji (w systemach Windows, Mac i Linux)? Czy uznałbyś to za prawdziwy wyciek pamięci, gdyby pamięć była używana nieprzerwanie, dopóki nie zostanie zwolniona przez system operacyjny.
Co jeśli biblioteka innej firmy wymusi na tobie taką sytuację? Czy odmówiłby korzystania z tej biblioteki strony trzeciej, bez względu na to, jak świetna byłaby w przeciwnym razie?
Widzę tylko jedną praktyczną wadę, a mianowicie to, że te łagodne wycieki pojawią się wraz z narzędziami do wykrywania wycieków pamięci jako fałszywie pozytywne.
źródło
Odpowiedzi:
Nie.
Jako profesjonaliści pytanie, którego nie powinniśmy sobie zadawać, brzmi: „Czy to jest w porządku?” ale raczej „Czy kiedykolwiek jest ku temu dobry powód?” A „polowanie na wyciek pamięci to ból” nie jest dobrym powodem.
Lubię upraszczać sprawy. I prosta zasada jest taka, że mój program nie powinien mieć wycieków pamięci.
To też sprawia, że moje życie jest proste. Jeśli wykryję wyciek pamięci, eliminuję go, zamiast przeglądać skomplikowaną strukturę drzewa decyzyjnego w celu ustalenia, czy jest to „akceptowalny” wyciek pamięci.
Jest podobny do ostrzeżeń kompilatora - czy ostrzeżenie będzie śmiertelne dla mojej konkretnej aplikacji? Może nie.
Ale ostatecznie jest to kwestia dyscypliny zawodowej. Tolerowanie ostrzeżeń kompilatora i tolerowanie wycieków pamięci to zły nawyk, który ostatecznie ugryzie mnie w tył.
Mówiąc ekstremalnie, czy chirurg byłby w stanie pozostawić pacjentowi jakiś sprzęt operacyjny?
Chociaż możliwe jest, że zaistnieje sytuacja, w której koszt / ryzyko usunięcia tego sprzętu przewyższy koszt / ryzyko pozostawienia go, i mogą zaistnieć sytuacje, w których byłby nieszkodliwy, gdybym zobaczył to pytanie opublikowane na SurgeonOverflow.com i zobaczył jakąkolwiek odpowiedź inną niż „nie”, poważnie podważyłoby to moje zaufanie do zawodu lekarza.
-
Gdyby biblioteka trzeciej strony wymusiła na mnie tę sytuację, doprowadziłbym do poważnego podejrzenia ogólnej jakości danej biblioteki. Byłoby tak, jakbym jechał samochodem testowym i znalazł kilka luźnych podkładek i nakrętek w jednym z uchwytów na kubki - sam w sobie może to nie być wielka sprawa, ale obrazuje brak przywiązania do jakości, więc rozważę alternatywy.
źródło
Nie uważam tego za wyciek pamięci, chyba że ilość „używanej” pamięci ciągle rośnie. Niewielka pamięć, choć nie idealna, nie stanowi dużego problemu, chyba że ilość wymaganej pamięci ciągle rośnie.
źródło
Najpierw poprawmy nasze definicje. Wyciek pamięci występuje, gdy pamięć jest dynamicznie przydzielana, np. Za pomocą
malloc()
, i wszystkie odniesienia do pamięci są tracone bez odpowiedniego wolnego. Łatwy sposób na zrobienie tego jest taki:Zauważ, że za każdym razem w pętli while (1) przydzielane są 1024 bajty (+ narzut), a nowy adres jest przypisywany do vp; nie ma żadnego wskaźnika do poprzednich bloków Malloc'ed. Ten program będzie działał do momentu wyczerpania stosu i nie ma możliwości odzyskania żadnej pamięci malloc'ed. Pamięć „wycieka” ze sterty, nigdy więcej nie będzie widoczna.
Jednak to, co opisujesz, brzmi
Przydzielasz pamięć i pracujesz z nią do momentu zakończenia programu. To nie jest wyciek pamięci; nie wpływa to negatywnie na program, a cała pamięć zostanie automatycznie oczyszczona po zakończeniu programu.
Zasadniczo należy unikać wycieków pamięci. Po pierwsze, ponieważ podobnie jak wysokość nad tobą i zapasy paliwa w hangarze, pamięć, która wyciekła i nie można jej odzyskać, jest bezużyteczna; po drugie, o wiele łatwiej jest poprawnie zakodować, nie przeciekając pamięci na początku, niż później znaleźć przeciek pamięci.
źródło
malloc
za dużo pamięci, jest to Zła Rzecz. To wciąż nie jest wyciek . To nie jest przeciek, dopókimalloc
pamięć nie zostanie utracona.Teoretycznie nie, w praktyce to zależy .
To naprawdę zależy od tego, na ile danych pracuje program, jak często program jest uruchamiany i czy działa stale.
Jeśli mam szybki program, który odczytuje niewielką ilość danych, dokonuje obliczeń i kończy pracę, niewielki wyciek pamięci nigdy nie zostanie zauważony. Ponieważ program nie działa bardzo długo i zużywa tylko niewielką ilość pamięci, wyciek będzie niewielki i uwolniony, gdy program będzie istniał.
Z drugiej strony, jeśli mam program, który przetwarza miliony rekordów i działa przez długi czas, niewielki wyciek pamięci może sprowadzić maszynę na wystarczająco długi czas.
Jeśli chodzi o biblioteki stron trzecich, które mają wycieki, jeśli powodują problem, napraw bibliotekę lub znajdź lepszą alternatywę. Jeśli to nie powoduje problemu, czy to naprawdę ma znaczenie?
źródło
Wydaje się, że wiele osób ma wrażenie, że po zwolnieniu pamięci natychmiast wraca do systemu operacyjnego i może być używana przez inne programy.
To nie jest prawda. Systemy operacyjne zwykle zarządzają pamięcią na stronach 4KiB.
malloc
a inne rodzaje zarządzania pamięcią pobierają strony z systemu operacyjnego i zarządzają nimi podrzędnie według własnego uznania. Jest całkiem prawdopodobne, żefree()
będzie nie wrócić strony do systemu operacyjnego, przy założeniu, że program będzie więcej pamięci malloc później.Nie twierdzę, że
free()
nigdy nie zwraca pamięci do systemu operacyjnego. Może się to zdarzyć, szczególnie jeśli uwalniasz duże obszary pamięci. Ale nie ma gwarancji.Ważny fakt: jeśli nie zwolnisz pamięci, której już nie potrzebujesz, kolejne centra handlowe będą zużywać jeszcze więcej pamięci. Ale jeśli najpierw zwolnisz, malloc może ponownie użyć zwolnionej pamięci.
Co to oznacza w praktyce? Oznacza to, że jeśli wiesz, że twój program nie będzie odtąd wymagał więcej pamięci (na przykład jest w fazie czyszczenia), zwolnienie pamięci nie jest tak ważne. Jeśli jednak program może później przydzielić więcej pamięci, należy unikać wycieków pamięci - szczególnie takich, które mogą się powtarzać.
Zobacz także ten komentarz, aby uzyskać więcej informacji na temat tego, dlaczego zwalnianie pamięci tuż przed zakończeniem jest złe.
Komentator wydawał się nie rozumieć, że wywołanie
free()
nie pozwala automatycznie innym programom na korzystanie z uwolnionej pamięci. Ale o to chodzi w tej odpowiedzi!Tak więc, aby przekonać ludzi, pokażę przykład, w którym free () robi niewiele dobrego. Aby matematyka była łatwa do naśladowania, będę udawać, że system operacyjny zarządza pamięcią na 4000 bajtowych stronach.
Załóżmy, że alokujesz dziesięć tysięcy 100-bajtowych bloków (dla uproszczenia zignoruję dodatkową pamięć, która byłaby wymagana do zarządzania tymi alokacjami). To zużywa 1 MB lub 250 stron. Jeśli następnie uwolnisz 9000 tych bloków losowo, pozostanie tylko 1000 bloków - ale są one rozproszone po całym miejscu. Statystycznie około 5 stron będzie pustych. Pozostałe 245 będą miały co najmniej jeden przydzielony blok. Daje to 980 KB pamięci, której system operacyjny nie może odzyskać - mimo że teraz masz przydzielone tylko 100 KB!
Z drugiej strony, możesz teraz malloc () 9000 kolejnych bloków bez zwiększania ilości pamięci, którą program wiąże.
Nawet jeśli technicznie
free()
może zwrócić pamięć do systemu operacyjnego, może tego nie zrobić. musi osiągnąć równowagę między szybkim działaniem a oszczędnością pamięci. Poza tym program, który już przydzielił dużo pamięci, a następnie zwolnił, prawdopodobnie zrobi to ponownie. Serwer WWW musi obsłużyć żądanie za żądaniem po żądaniu - sensowne jest utrzymywanie dostępnej „luźnej” pamięci, aby nie trzeba było cały czas pytać systemu operacyjnego o pamięć.free()
źródło
Nie ma nic koncepcyjnie złego w czyszczeniu systemu operacyjnego po uruchomieniu aplikacji.
To zależy od aplikacji i sposobu jej uruchomienia. Należy dbać o stale występujące wycieki w aplikacji, która musi działać przez kilka tygodni, ale małe narzędzie, które oblicza wynik bez zbyt dużej pamięci, nie powinno stanowić problemu.
Istnieje powód, dla którego wiele języków skryptowych nie śmieci cyklicznych odwołań… ze względu na wzorce użycia nie jest to rzeczywisty problem, a zatem marnowanie zasobów byłoby tak samo marnotrawstwem pamięci.
źródło
Uważam, że odpowiedź brzmi „nie”, nigdy nie dopuść do wycieku pamięci, i mam kilka powodów, których nie widziałem wyraźnie wyrażonych. Są tutaj świetne odpowiedzi techniczne, ale myślę, że prawdziwa odpowiedź zależy od bardziej społecznych / ludzkich powodów.
(Po pierwsze, zauważ, że jak wspomniano inni, prawdziwy wyciek ma miejsce, gdy twój program w dowolnym momencie traci ślad przydzielonych zasobów pamięci. W C dzieje się tak, gdy
malloc()
wskaźnik do wskaźnika pozwala na opuszczenie zakresu bez wykonywania poleceniafree()
pierwszy.)Ważnym sednem twojej decyzji jest przyzwyczajenie. Kiedy piszesz w języku używającym wskaźników, będziesz często używać wskaźników . Wskaźniki są niebezpieczne; to najprostszy sposób na dodanie wszelkiego rodzaju poważnych problemów do kodu.
Kiedy kodujesz, czasami będziesz na piłce, a czasem będziesz zmęczony, wściekły lub zmartwiony. W tych nieco rozproszonych czasach kodujesz więcej na autopilocie. Efekt autopilota nie rozróżnia jednorazowego kodu od modułu w większym projekcie. W tych czasach nawyki, które ustanawiasz, znajdą się w twojej bazie kodu.
Więc nie, nigdy nie dopuszczaj do wycieków pamięci z tego samego powodu, dla którego powinieneś nadal sprawdzać martwe pola podczas zmiany pasów, nawet jeśli jesteś jedynym samochodem w tej chwili na drodze. W czasie, gdy twój aktywny mózg jest rozproszony, dobre nawyki są w stanie uratować cię przed katastrofalnymi błędami.
Oprócz kwestii „przyzwyczajenia” wskaźniki są złożone i często wymagają dużej mocy mózgu do śledzenia mentalnego. Najlepiej jest nie „moczyć wody”, jeśli chodzi o korzystanie ze wskaźników, szczególnie gdy dopiero zaczynasz programować.
Jest też aspekt społeczny. Dzięki właściwemu użyciu
malloc()
ifree()
, każdy, kto spojrzy na twój kod, będzie miał swobodę; zarządzasz swoimi zasobami. Jeśli tego nie zrobisz, natychmiast podejrzewają problem.Być może zdałeś sobie sprawę, że wyciek pamięci niczego nie zaszkodzi w tym kontekście, ale każdy opiekun twojego kodu będzie musiał to sobie wyobrazić, gdy przeczyta ten fragment kodu. Korzystając z niej
free()
, usuniesz potrzebę nawet rozważenia problemu.Wreszcie, programowanie pisze mentalny model procesu w jednoznacznym języku, aby osoba i komputer mogli doskonale zrozumieć ten proces. Istotną częścią dobrej praktyki programowania nigdy nie jest wprowadzanie niepotrzebnej dwuznaczności.
Inteligentne programowanie jest elastyczne i ogólne. Złe programowanie jest niejednoznaczne.
źródło
new
, dzięki czemu natychmiast eliminuje większość wycieków pamięci. Tylko jeśli absolutnie musisz tego użyćnew
. Wynik tegonew
należy natychmiast umieścić w inteligentnym wskaźniku. Jeśli zastosujesz się do tych dwóch zasad, po prostu nigdy nie wyciekasz pamięci (z wyjątkiem błędu w bibliotece). Jedynym przypadkiem pozostałyshared_ptr
cykle, w takim przypadku musisz wiedzieć, aby użyćweak_ptr
.Myślę, że w twojej sytuacji odpowiedź może być dobra. Ale zdecydowanie musisz udokumentować, że wyciek pamięci jest świadomą decyzją. Nie chcesz, aby pojawił się programista konserwacji, uderzył kod wewnątrz funkcji i wywołał go milion razy. Jeśli więc podejmiesz decyzję, że wyciek jest w porządku, musisz go udokumentować (DUŻYMI LITERAMI) dla każdego, kto będzie musiał pracować nad programem w przyszłości.
Jeśli jest to biblioteka strony trzeciej, możesz zostać uwięziony. Ale zdecydowanie udokumentuj, że ten wyciek występuje.
Ale w zasadzie, jeśli wyciek pamięci jest znaną wielkością, taką jak bufor 512 KB lub coś takiego, to nie jest to problem. Jeśli wyciek pamięci rośnie, jak za każdym razem, gdy wywołujesz wywołanie biblioteczne, pamięć zwiększa się o 512 KB i nie jest zwalniana, możesz mieć problem. Jeśli go udokumentujesz i kontrolujesz liczbę wykonanych połączeń, możesz nim zarządzać. Ale wtedy naprawdę potrzebujesz dokumentacji, ponieważ choć 512 to niewiele, 512 ponad milion połączeń to dużo.
Musisz także sprawdzić dokumentację systemu operacyjnego. Jeśli było to urządzenie wbudowane, mogą istnieć systemy operacyjne, które nie zwalniają całej pamięci z zamykanego programu. Nie jestem pewien, może to nie jest prawda. Ale warto się temu przyjrzeć.
źródło
Dam niepopularną, ale praktyczną odpowiedź, że zwalnianie pamięci zawsze jest złe, chyba że spowoduje to zmniejszenie zużycia pamięci przez program . Na przykład program, który dokonuje pojedynczej alokacji lub serii alokacji w celu załadowania zestawu danych, którego będzie używał przez cały okres istnienia, nie musi niczego zwalniać. W bardziej powszechnym przypadku dużego programu z bardzo dynamicznymi wymaganiami dotyczącymi pamięci (pomyśl o przeglądarce internetowej), powinieneś oczywiście zwolnić pamięć, której już nie używasz, tak szybko jak to możliwe (na przykład zamykając kartę / dokument / itp.) , ale nie ma powodu, aby cokolwiek zwalniać, gdy użytkownik wybierze kliknięcie „wyjdź”, a takie działanie jest w rzeczywistości szkodliwe dla wygody użytkownika.
Czemu? Zwolnienie pamięci wymaga dotknięcia pamięci. Nawet jeśli implementacja malloc w twoim systemie nie przechowuje metadanych w sąsiedztwie przydzielonych bloków pamięci, prawdopodobnie będziesz chodził strukturami rekurencyjnymi tylko po to, aby znaleźć wszystkie wskazówki, które musisz uwolnić.
Załóżmy teraz, że twój program działał z dużą ilością danych, ale od dłuższego czasu nie dotykał większości z nich (znowu przeglądarka internetowa jest świetnym przykładem). Jeśli użytkownik uruchamia wiele aplikacji, duża część tych danych prawdopodobnie została zamieniona na dysk. Jeśli po prostu wyjdziesz (0) lub wrócisz z głównego, wyjście natychmiast. Świetne wrażenia użytkownika. Jeśli próbujesz uwolnić wszystko, możesz poświęcić 5 sekund lub więcej na zamianę wszystkich danych z powrotem, tylko po to, aby natychmiast je wyrzucić. Strata czasu użytkownika. Marnowanie czasu pracy baterii laptopa. Odpady zużycia na dysku twardym.
To nie tylko teoretyczne. Ilekroć znajduję się z załadowaną zbyt dużą liczbą aplikacji, a dysk zaczyna się bić, nawet nie zastanawiam się nad kliknięciem „zamknij”. Dojeżdżam do terminala tak szybko, jak to możliwe i wpisuję killall -9 ... ponieważ wiem, że „wyjście” tylko pogorszy sytuację.
źródło
Jestem pewien, że ktoś może wymyślić powód, aby powiedzieć Tak, ale to nie będę ja. Zamiast powiedzieć nie, powiem, że to nie powinno być pytanie tak / nie. Istnieją sposoby zarządzania lub ograniczania wycieków pamięci, a wiele systemów ma je.
Istnieją systemy NASA na urządzeniach, które opuszczają Ziemię, które to planują. Systemy będą automatycznie uruchamiać się ponownie tak często, aby przecieki pamięci nie były śmiertelne dla całej operacji. Tylko przykład ograniczania.
źródło
Jeśli przydzielisz pamięć i wykorzystasz ją do ostatniej linii programu, nie będzie to przeciek. Jeśli przydzielisz pamięć i zapomnisz o niej, nawet jeśli ilość pamięci nie rośnie, to jest problem. Ta przydzielona, ale nieużywana pamięć może powodować spowolnienie działania innych programów lub ich wcale.
źródło
Mogę liczyć z jednej strony liczbę „łagodnych” wycieków, które widziałem w czasie.
Tak więc odpowiedź jest bardzo wykwalifikowana tak.
Przykład. Jeśli masz zasób singletonu, który potrzebuje bufora do przechowywania kolejki cyklicznej lub deque, ale nie wie, jak duży będzie ten bufor, i nie może sobie pozwolić na obciążenie związane z blokowaniem lub każdym czytnikiem, wówczas alokuje się wykładniczo podwójny bufor, ale nie zwolnienie starych spowoduje wyciek ograniczonej ilości pamięci na kolejkę / deque. Ich zaletą jest to, że znacznie przyspieszają każdy dostęp i mogą zmienić asymptotykę rozwiązań wieloprocesorowych, nigdy nie ryzykując rywalizacji o zamek.
Zauważyłem, że takie podejście przyniosło wiele korzyści w przypadku rzeczy o bardzo wyraźnie ustalonych liczbach, takich jak deques kradnące pracę na procesor, oraz w znacznie mniejszym stopniu w buforze używanym do przechowywania singletonu
/proc/self/maps
stanu w konserwatywnym śmieciarzu Hansa Boehm'a dla C / C ++, który służy do wykrywania zestawów głównych itp.Chociaż technicznie jest to wyciek, oba te przypadki są ograniczone pod względem wielkości, aw rosnącej krętej pracy kradnącej skrzynkę deque istnieje ogromna wygrana wydajności w zamian za czynnik związany z 2 wzrostem wykorzystania pamięci dla kolejek.
źródło
Jeśli na początku programu przydzielisz sporo sterty i nie zwolnisz go po wyjściu, nie będzie to samo w sobie wycieku pamięci. Wyciek pamięci występuje, gdy program zapętla sekcję kodu, a ten kod przydziela stertę, a następnie „traci ją” bez zwalniania.
W rzeczywistości nie ma potrzeby wykonywania połączeń z free () lub usuwania bezpośrednio przed wyjściem. Kiedy proces kończy się, cała jego pamięć jest odzyskiwana przez system operacyjny (z pewnością ma to miejsce w przypadku POSIX. W innych systemach operacyjnych - szczególnie osadzonych - YMMV).
Jedyną ostrożnością, którą chciałbym zachować, aby nie zwolnić pamięci w momencie wyjścia, jest to, że jeśli kiedykolwiek zmienisz program tak, aby na przykład stał się usługą, która czeka na dane wejściowe, robi wszystko, co robi program, a następnie zapętla się, czekając na kolejne zgłoszenie serwisowe, a następnie to, co zakodowałeś, może zmienić się w wyciek pamięci.
źródło
jest to tak specyficzne dla domeny, że nie warto na nie odpowiadać. użyj swojej cholernej głowy.
i istnieje szereg sytuacji pośrednich.
koszt alternatywny ($$$) opóźnienia wydania produktu w celu naprawienia wszystkich oprócz najgorszych wycieków pamięci zwykle przyćmiewa wszelkie poczucie bycia „niechlujnym lub nieprofesjonalnym”. Twój szef płaci ci za zarabianie pieniędzy, a nie za ciepłe, niewyraźne uczucia.
źródło
Musisz najpierw uświadomić sobie, że istnieje duża różnica między postrzeganym wyciekiem pamięci a rzeczywistym wyciekiem pamięci. Bardzo często narzędzia analityczne zgłaszają wiele czerwonych śledzi i opisują coś jako wyciek (pamięć lub zasoby, takie jak uchwyty itp.) Tam, gdzie tak naprawdę nie jest. Często wynika to z architektury narzędzia analizy. Na przykład niektóre narzędzia analityczne zgłaszają obiekty wykonawcze jako wycieki pamięci, ponieważ nigdy nie widzą uwolnienia tych obiektów. Ale zwolnienie następuje w kodzie zamykania środowiska wykonawczego, którego narzędzie analizy może nie widzieć.
Powiedziawszy to, nadal będą chwile, w których rzeczywiste wycieki pamięci będą albo bardzo trudne do znalezienia, albo bardzo trudne do naprawienia. Teraz pojawia się pytanie, czy pozostawienie ich w kodzie jest w porządku?
Idealna odpowiedź brzmi: „nie, nigdy”. Bardziej pragmatyczną odpowiedzią może być „nie, prawie nigdy”. Bardzo często w prawdziwym życiu masz ograniczoną liczbę zasobów i czasu do rozwiązania oraz nieskończoną listę zadań. Kiedy jednym z zadań jest eliminowanie wycieków pamięci, bardzo często pojawia się zasada malejących zwrotów. Można wyeliminować np. 98% wszystkich wycieków pamięci w aplikacji w ciągu tygodnia, ale pozostałe 2% może potrwać miesiące. W niektórych przypadkach wyeliminowanie pewnych przecieków może być niemożliwe ze względu na architekturę aplikacji bez znacznej zmiany kodu. Musisz rozważyć koszty i korzyści wynikające z wyeliminowania pozostałych 2%.
źródło
W tego rodzaju pytaniach kontekst jest wszystkim. Osobiście nie mogę znieść przecieków, aw swoim kodzie staram się je naprawić, jeśli się pojawią, ale nie zawsze warto naprawić wyciek, a kiedy ludzie płacą mi za godzinę, mam czasem powiedziałem im, że nie warto płacić za naprawienie wycieku w kodzie. Dam ci przykład:
Pracowałem nad projektem, wykonałem perfekcyjną pracę i naprawiłem wiele błędów. Podczas inicjacji aplikacji wystąpił wyciek, który wyśledziłem i w pełni zrozumiałem. Prawidłowe jego naprawienie wymagałoby około dnia refaktoryzacji fragmentu funkcjonalnego kodu. Mógłbym zrobić coś hackującego (np. Wpychanie wartości do globalnej i chwytanie jej w pewnym momencie, wiem, że nie było już używane do uwolnienia), ale to spowodowałoby więcej zamieszania u następnego faceta, który musiał dotknąć kodu.
Osobiście nie napisałbym kodu w ten sposób, ale większość z nas nie zawsze musi pracować nad nieskazitelnie dobrze zaprojektowanymi bazami kodów, a czasami trzeba patrzeć na te rzeczy pragmatycznie. Czas, który zajęłoby mi naprawienie tego, że wyciek 150 bajtów mógłby zamiast tego zostać wykorzystany na ulepszenia algorytmu, które zmniejszyłyby megabajty pamięci RAM.
Ostatecznie zdecydowałem, że wyciek 150 bajtów dla aplikacji, która używała około gigabajta pamięci RAM i działał na dedykowanej maszynie, nie był wart naprawy, więc napisałem komentarz, że wyciekł, co należy zmienić, aby naprawić i dlaczego nie było warto w tym czasie.
źródło
Podczas gdy większość odpowiedzi koncentruje się na prawdziwych przeciekach pamięci (które nigdy nie są OK, ponieważ są oznaką niechlujnego kodowania), ta część pytania wydaje mi się bardziej interesująca:
Jeśli używana jest powiązana pamięć, nie można jej zwolnić przed zakończeniem programu. To, czy uwolnienie odbywa się przez wyjście z programu, czy przez system operacyjny, nie ma znaczenia. Tak długo, jak jest to udokumentowane, aby zmiana nie powodowała prawdziwych wycieków pamięci, i dopóki w obrazie nie występuje żadna funkcja C ++ destructor lub funkcja czyszczenia C. Niezamknięty plik może zostać ujawniony przez wyciekający
FILE
obiekt, ale brakująca funkcja fclose () może również spowodować, że bufor nie zostanie opróżniony.Tak więc, wracając do oryginalnej skrzynki, samo w sobie jest całkowicie OK IMHO, tak bardzo, że Valgrind, jeden z najsilniejszych wykrywaczy wycieków, leczy takie wycieki tylko na żądanie. W przypadku Valgrind, gdy nadpisujesz wskaźnik bez wcześniejszego jego zwolnienia, jest on uważany za wyciek pamięci, ponieważ jest bardziej prawdopodobne, że powtórzy się i spowoduje nieskończony wzrost stosu.
Następnie nie ma dostępnych bloków pamięci, które są nadal dostępne. Można się upewnić, że wszystkie zostaną uwolnione przy wyjściu, ale to tylko strata czasu sama w sobie. Chodzi o to, czy mogliby być wcześniej uwolnieni . W każdym przypadku przydatne jest zmniejszenie zużycia pamięci.
źródło
Zgadzam się z vfilby - to zależy. W Windows traktujemy wycieki pamięci jako stosunkowo poważne błędy. Ale to bardzo zależy od komponentu.
Na przykład wycieki pamięci nie są bardzo poważne w przypadku komponentów, które działają rzadko i przez ograniczony czas. Te komponenty działają, wykonują pracę, a następnie wychodzą. Kiedy wychodzą, cała pamięć zostaje uwolniona niejawnie.
Jednak wycieki pamięci w usługach lub innych komponentach długo działających (takich jak powłoka) są bardzo poważne. Powodem jest to, że te błędy „kradną” pamięć z czasem. Jedynym sposobem na odzyskanie tego jest ponowne uruchomienie komponentów. Większość ludzi nie wie, jak zrestartować usługę lub powłokę - więc jeśli spadnie wydajność ich systemu, po prostu uruchomią się ponownie.
Więc jeśli masz wyciek - oceń jego wpływ na dwa sposoby
Foredecker
źródło
Nawet jeśli masz pewność, że „znany” wyciek pamięci nie spowoduje spustoszenia, nie rób tego. W najlepszym razie utoruje ci drogę do popełnienia podobnej i prawdopodobnie bardziej krytycznej pomyłki w innym czasie i miejscu.
Dla mnie zadawanie tego jest jak pytanie: „Czy mogę rozbić czerwone światło o 3 nad ranem, kiedy nikogo nie ma w pobliżu?”. Oczywiście nie może to powodować żadnych problemów, ale zapewni ci dźwignię, abyś zrobił to samo w godzinach szczytu!
źródło
Nie, nie powinieneś mieć przecieków, które system operacyjny wyczyści dla Ciebie. Powodem (o którym nie wspomniano w odpowiedziach powyżej, o ile mogłem to sprawdzić) jest to, że nigdy nie wiesz, kiedy twoja funkcja main () zostanie ponownie użyta jako funkcja / moduł w innym programie . Jeśli twoja funkcja main () stanie się często wywoływaną funkcją w oprogramowaniu innej osoby - w tym oprogramowaniu wystąpi wyciek pamięci, który z czasem zjada pamięć.
KIV
źródło
Myślę, że to w porządku, jeśli piszesz program przeznaczony do wycieku pamięci (tj. Do testowania wpływu wycieków pamięci na wydajność systemu).
źródło
Jestem zaskoczony, widząc tak wiele niepoprawnych definicji wycieku pamięci. Bez konkretnej definicji dyskusja na temat tego, czy jest to zła rzecz, czy nie, nic nie da.
Jak słusznie zauważyli niektórzy komentatorzy, wyciek pamięci występuje tylko wtedy, gdy pamięć przydzielona przez proces wykracza poza zakres w zakresie, w jakim proces nie jest już w stanie go odwoływać ani usuwać.
Proces, który chwyta coraz więcej pamięci niekoniecznie przecieka. Tak długo, jak jest w stanie odwoływać się do tej pamięci i ją zwalniać, pozostaje ona pod wyraźną kontrolą procesu i nie wyciekła. Proces ten może być źle zaprojektowany, szczególnie w kontekście systemu, w którym pamięć jest ograniczona, ale to nie to samo, co przeciek. I odwrotnie, utrata zakresu, powiedzmy, 32-bajtowego bufora jest nadal przeciekaniem, nawet jeśli ilość wycieku pamięci jest niewielka. Jeśli uważasz, że jest to nieistotne, poczekaj, aż ktoś obejmie algorytm wokół twojego wywołania w bibliotece i zadzwoni do niego 10 000 razy.
Nie widzę powodu, aby zezwalać na wycieki w twoim własnym kodzie, nawet najmniejszym. Współczesne języki programowania, takie jak C i C ++, bardzo się starają, aby pomóc programistom zapobiegać takim wyciekom, a rzadko jest dobry argument, aby nie stosować dobrych technik programowania - szczególnie w połączeniu z określonymi udogodnieniami językowymi - aby zapobiec wyciekom.
W odniesieniu do istniejącego kodu lub kodu strony trzeciej, gdzie kontrola nad jakością lub zdolność do zmiany może być bardzo ograniczona, w zależności od powagi wycieku, możesz zostać zmuszony do zaakceptowania lub podjęcia działań łagodzących, takich jak regularne ponowne uruchamianie procesu w celu ograniczenia efekt wycieku.
Zmiana lub wymiana istniejącego (nieszczelnego) kodu może być niemożliwa, dlatego możesz zostać zobowiązany do jego zaakceptowania. Nie jest to jednak to samo, co deklarowanie, że jest OK.
źródło
To naprawdę nie jest wyciek, jeśli jest celowy i nie stanowi problemu, chyba że ma znaczną ilość pamięci lub może wzrosnąć do znacznej ilości pamięci. Dość powszechne jest to, że nie usuwają globalnych przydziałów w trakcie trwania programu. Jeśli wyciek występuje w serwerze lub długo działającej aplikacji, rośnie z czasem, to jest problem.
źródło
Myślę, że odpowiedziałeś na własne pytanie. Największą wadą jest to, że kolidują one z narzędziami do wykrywania wycieków pamięci, ale myślę, że wadą jest OGROMNA wada dla niektórych rodzajów aplikacji.
Pracuję ze starszymi aplikacjami serwerowymi, które powinny być solidne, ale mają przecieki, a globale przeszkadzają w wykrywaniu pamięci. To ważna sprawa.
W książce „Upadek” Jareda Diamonda autor zastanawia się, o czym myślał facet, który ściął ostatnie drzewo na Wyspie Wielkanocnej, drzewo, którego potrzebowałby, aby zbudować kajak, aby wydostać się z wyspy. Zastanawiam się, jak wiele lat temu ten pierwszy globalny został dodany do naszej bazy kodów. Tego dnia powinien był zostać złapany.
źródło
Widzę ten sam problem, co wszystkie takie pytania scenariusza: Co dzieje się, gdy program się zmienia i nagle ten mały wyciek pamięci jest wywoływany dziesięć milionów razy, a koniec twojego programu jest w innym miejscu, więc to ma znaczenie? Jeśli jest w bibliotece, zaloguj się do opiekuna biblioteki, nie wyciekaj do własnego kodu.
źródło
Odpowiem nie.
Teoretycznie system operacyjny posprząta po tobie, jeśli zostawisz bałagan (teraz jest to po prostu niegrzeczne, ale ponieważ komputery nie mają uczuć, może to być dopuszczalne). Ale nie można przewidzieć każdej możliwej sytuacji, która może wystąpić podczas uruchamiania programu. Dlatego (chyba że jesteś w stanie przeprowadzić formalny dowód niektórych zachowań), tworzenie wycieków pamięci jest po prostu nieodpowiedzialne i niechlujne z profesjonalnego punktu widzenia.
Jeśli komponent innej firmy wycieka z pamięci, jest to bardzo silny argument przeciwko jej użyciu, nie tylko z powodu bezpośredniego efektu, ale także dlatego, że pokazuje, że programiści działają niechętnie i że może to również wpływać na inne parametry. Teraz, gdy rozważamy starsze systemy, jest to trudne (rozważ komponenty do przeglądania stron internetowych: według mojej wiedzy wszystkie one przeciekają pamięć), ale powinna to być norma.
źródło
Historycznie miało to znaczenie w niektórych systemach operacyjnych w niektórych przypadkach skrajnych. Te przypadki skrajne mogą istnieć w przyszłości.
Oto przykład: w systemie SunOS w erze Sun 3 wystąpił problem, jeśli proces używał exec (lub bardziej tradycyjnie rozwidla, a następnie exec), późniejszy nowy proces odziedziczyłby taki sam ślad pamięci jak rodzic i nie mógł zostać skurczony . Jeśli proces nadrzędny przydzieli 1/2 gigabajta pamięci i nie zwolni go przed wywołaniem exec, proces potomny zacznie używać tej samej 1/2 gigabajta (nawet jeśli nie został przydzielony). To zachowanie najlepiej pokazał SunTools (ich domyślny system okienkowy), który był świnią pamięci. Każda aplikacja, którą stworzyła, została utworzona za pomocą fork / exec i odziedziczyła ślad SunTools, szybko wypełniając przestrzeń wymiany.
źródło
Zostało to już omówione ad nauseam . Najważniejsze jest to, że wyciek pamięci jest błędem i musi zostać naprawiony. Jeśli biblioteka innej firmy wycieka z pamięci, to zastanawia się, co jeszcze jest z nią nie tak, nie? Gdybyś budował samochód, czy użyłbyś silnika, który czasami wycieka olej? W końcu ktoś zrobił silnik, więc to nie twoja wina i nie możesz tego naprawić, prawda?
źródło
Zasadniczo wyciek pamięci w samodzielnej aplikacji nie jest śmiertelny, ponieważ jest usuwany po wyjściu z programu.
Co robisz dla programów serwera zaprojektowanych tak, aby się nie zamykały?
Jeśli jesteś programistą, który nie projektuje i nie implementuje kodu, w którym zasoby są przydzielane i zwalniane poprawnie, nie chcę mieć nic wspólnego z tobą ani twoim kodem. Jeśli nie chcesz wyczyścić wyciekającej pamięci, co z twoimi zamkami? Czy zostawiasz je tam również? Czy zostawiasz małe pliki tymczasowych plików leżące w różnych katalogach?
Wyciekła pamięć i pozwolił programowi ją wyczyścić? Nie, absolutnie nie. To zły nawyk, który prowadzi do błędów, błędów i innych błędów.
Posprzątaj po sobie. Wasza mamo, już tu nie pracuje.
źródło
Zasadniczo, jeśli masz wycieki pamięci, których nie możesz uniknąć, musisz się bardziej zastanowić nad własnością obiektu.
Ale na twoje pytanie moja odpowiedź w skrócie brzmi: W kodzie produkcyjnym, tak. Podczas rozwoju nie . Może się to wydawać wstecz, ale oto moje rozumowanie:
W opisywanej sytuacji, w której pamięć jest przechowywana do końca programu, nie można jej zwolnić. Po zakończeniu procesu system operacyjny i tak wyczyści system. W rzeczywistości może to poprawić wrażenia użytkownika: w grze, nad którą pracowałem, programiści sądzili, że zwolnienie całej pamięci przed wyjściem byłoby czystsze, powodując zamknięcie programu nawet do pół minuty! Szybka zmiana, która właśnie wywołała exit (), sprawiła, że proces natychmiast zniknął, i umieścił użytkownika z powrotem na pulpicie, gdzie chciał być.
Jednak masz rację co do narzędzi do debugowania: sprawdzą się, a wszystkie fałszywe alarmy mogą sprawić, że znalezienie prawdziwej pamięci wycieka z bólu. Z tego powodu zawsze pisz kod debugujący, który zwalnia pamięć, i wyłączaj ją podczas wysyłania.
źródło