Czy po wyjściu z aplikacji C pamięć malloc-ed jest automatycznie zwalniana?

94

Powiedzmy, że mam następujący kod w C:

int main () {
  int *p = malloc(10 * sizeof *p);
  *p = 42;
  return 0;  //Exiting without freeing the allocated memory
}

Kiedy kompiluję i wykonuję ten program w C, tj. Po przydzieleniu pewnej ilości miejsca w pamięci, czy ta przydzielona mi pamięć będzie nadal przydzielana (tj. W zasadzie zajmuje miejsce) po wyjściu z aplikacji i zakończeniu procesu?

Andreas Grech
źródło
9
porządkowanie pamięci to „dobry styl”, nie dlatego, że możesz działać na systemie operacyjnym, który nie ma pamięci chronionej (co jest główną sugestią poniżej), ale dlatego, że zwiększa prawdopodobieństwo wystąpienia wycieków pamięci i zachowuje Twój kod jest szczupły i poprawny ...
Matt Joiner,

Odpowiedzi:

114

To zależy od systemu operacyjnego. Większość nowoczesnych (i wszystkie główne) systemy operacyjne zwalniają pamięć, która nie jest zwalniana przez program po jego zakończeniu.

Poleganie na tym jest złą praktyką i lepiej jest to wyraźnie uwolnić. Problem nie polega tylko na tym, że kod wygląda źle. Możesz zdecydować, że chcesz zintegrować swój mały program z większym, długo działającym. Chwilę później musisz spędzić godziny na tropieniu wycieków pamięci.
Poleganie na funkcji systemu operacyjnego sprawia, że ​​kod jest mniej przenośny.

Yacoby
źródło
16
Kiedyś spotkałem się z win98 na platformie wbudowanej i na podstawie tego doświadczenia mogę powiedzieć, że NIE zwalnia on pamięci podczas zamykania programów.
San Jacinto
8
@Ken To był przykład. Istnieje również granica między YAGNI a niechlujnym kodowaniem. Brak uwalniania zasobów krzyżuje to. Zasada YAGNI miała być również stosowana do funkcji, a nie do kodu, który sprawia, że ​​program działa poprawnie. (A brak zwalniania pamięci jest błędem).
Yacoby
5
+1: najważniejszą rzeczą do rozważenia jest to, że zarządzanie pamięcią jest, jak słusznie stwierdza Yacoby: „cechą systemu operacyjnego” . O ile się nie mylę, język programowania nie definiuje tego, co dzieje się przed lub po wykonaniu programu.
D.Shawley,
9
Ręczne zwalnianie pamięci zajmuje więcej czasu, zajmuje więcej kodu i wprowadza możliwość wystąpienia błędów (powiedz mi, że nigdy nie widziałeś błędu w kodzie zwalniania alokacji!). Celowe pomijanie czegoś, co jest gorsze pod każdym względem, nie jest „niechlujne”. Chyba że zamierzasz uruchomić go na jakimś starożytnym / małym systemie, który nie może zwolnić stron po zakończeniu procesu lub zintegrować go z większym programem (YAGNI), wygląda to na stratę netto. Wiem, że ego programisty rani, gdy myślisz o tym, żeby samemu tego nie oczyścić, ale w jakim praktycznym sensie jest to lepsze?
Ken
6
Każdy, kto proponuje wycieki pamięci w SO, powinien zostać pozbawiony reputacji i odznak
Ulterior
53

Ogólnie rzecz biorąc, nowoczesne systemy operacyjne ogólnego przeznaczenia czyszczą po zakończonych procesach . Jest to konieczne, ponieważ alternatywą jest utrata zasobów przez system z upływem czasu i konieczność ponownego uruchomienia z powodu źle napisanych programów lub po prostu z rzadko występującymi błędami, które powodują wyciek zasobów.

Posiadanie programu jawnie zwalniającego jego zasoby może być dobrą praktyką z różnych powodów, takich jak:

  • Jeśli masz dodatkowe zasoby, które nie są czyszczone przez system operacyjny przy wyjściu, takie jak pliki tymczasowe lub jakiekolwiek zmiany w stanie zasobu zewnętrznego, będziesz potrzebować kodu, aby poradzić sobie z tymi wszystkimi rzeczami przy zamykaniu, a to często elegancko łączy się z uwolnieniem pamięci.
  • Jeśli Twój program zaczyna mieć dłuższe życie, nie będziesz chciał, aby jedynym sposobem na zwolnienie pamięci było wyjście. Na przykład, możesz chcieć przekonwertować swój program na serwer (demona), który działa, obsługując wiele żądań dotyczących poszczególnych jednostek pracy, lub twój program może stać się małą częścią większego programu.

Jednak tutaj jest powód, aby pominąć zwalnianie pamięci: wydajne zamykanie . Na przykład załóżmy, że aplikacja zawiera dużą pamięć podręczną w pamięci. Jeśli po zakończeniu przechodzi przez całą strukturę pamięci podręcznej i zwalnia ją po jednym kawałku, nie służy to żadnemu użytecznemu celowi i marnuje zasoby. Zwłaszcza rozważ przypadek, w którym strony pamięci zawierające pamięć podręczną zostały zamienione na dysk przez system operacyjny; chodząc po strukturze i uwalniając ją, przenosisz wszystkie te strony z powrotem do pamięci naraz , marnując znaczną ilość czasu i energii bez żadnych rzeczywistych korzyści, a być może nawet powodując zamianę innych programów w systemie!

Jako pokrewny przykład, istnieją serwery o wysokiej wydajności, które działają, tworząc proces dla każdego żądania, a następnie zamykając go po zakończeniu; w ten sposób nie muszą nawet śledzić alokacji pamięci i nigdy w ogóle nie zwalniają ani nie usuwają elementów bezużytecznych, ponieważ wszystko po prostu znika z powrotem w wolnej pamięci systemu operacyjnego na koniec procesu. (To samo można zrobić w procesie przy użyciu niestandardowego alokatora pamięci, ale wymaga to bardzo starannego programowania; zasadniczo tworzenie własnego pojęcia „lekkich procesów” w procesie systemu operacyjnego).

Kevin Reid
źródło
11

Przepraszam za posty tak długo po ostatnim poście w tym wątku.

Jeszcze jeden punkt. Nie wszystkie programy kończą się wdzięcznie. Awarie i klawisze Ctrl-C itp. Spowodują niekontrolowane zamknięcie programu. Jeśli system operacyjny nie uwolniłby twojej sterty, nie wyczyścił stosu, nie usunął zmiennych statycznych itp., W końcu doszłoby do awarii systemu z powodu wycieków pamięci lub gorzej.

Poza tym, awarie / awarie w Ubuntu i podejrzewam, że wszystkie inne nowoczesne systemy operacyjne mają problemy z „obsługiwanymi” zasobami. Gniazda, pliki, urządzenia itp. Mogą pozostać „otwarte”, gdy program się kończy / ulega awarii. Jest to także dobrą praktyką jest zamykanie czegokolwiek za pomocą „uchwytu” lub „deskryptora” w ramach czyszczenia przed wdzięcznym zakończeniem.

Obecnie pracuję nad programem, który intensywnie używa gniazd. Kiedy utknę w zawieszeniu, muszę z niego wyskoczyć ctrl-c, tym samym wyrywając moje gniazda. Dodałem std :: vector, aby zebrać listę wszystkich otwartych gniazd i obsługę sigaction, która przechwytuje sigint i sigterm. Przewodnik przegląda listę i zamyka gniazda. Planuję zrobić podobną procedurę czyszczenia do użycia przed rzutami, która doprowadzi do przedwczesnego zakończenia.

Czy ktoś chciałby skomentować ten projekt?

Wes Miller
źródło
1
Cieszę się, że to powiedziałeś, ponieważ miałem program, który pozostawiał zasoby gniazd, a nasz system Ubuntu wymagał ponownego uruchamiania co dwa tygodnie lub pamięć zaczęła się kończyć, a pamięci jest dużo. Nie jestem pewien, czy zasoby systemowe zostaną zniszczone, jeśli zapomnisz je wyczyścić.
octopusgrabbus
8
Stackoverflow nie jest forum; nie ma nic złego w odpowiedzi na stare pytanie. meta.stackexchange.com/questions/20524/reviving-old-questions
mk12
6

To, co się tutaj dzieje ( w nowoczesnym systemie operacyjnym ), polega na tym, że program działa wewnątrz własnego „procesu”. Jest to jednostka systemu operacyjnego, która jest wyposażona we własną przestrzeń adresową, deskryptory plików itp. Twoje mallocwywołania alokują pamięć ze „sterty” lub nieprzydzielonych stron pamięci, które są przypisane do twojego procesu.

Kiedy program się kończy, jak w tym przykładzie, wszystkie zasoby przypisane do twojego procesu są po prostu przetwarzane / niszczone przez system operacyjny. W przypadku pamięci wszystkie przypisane do Ciebie strony pamięci są po prostu oznaczane jako „wolne” i przetwarzane w celu wykorzystania w innych procesach. Strony są koncepcją niższego poziomu niż to, które obsługuje Malloc - w rezultacie specyfika malloc / free jest po prostu zmywana, gdy całość jest czyszczona.

To moralny odpowiednik tego, że kiedy skończysz używać laptopa i chcesz go przekazać znajomemu, nie przejmuj się indywidualnym usuwaniem każdego pliku. Po prostu sformatuj dysk twardy.

Wszystko to zostało powiedziane, jak zauważają wszyscy inni respondenci, poleganie na tym nie jest dobrą praktyką:

  1. Zawsze powinieneś programować, aby dbać o zasoby, aw C oznacza to również pamięć. Możesz skończyć osadzenie kodu w bibliotece lub może skończyć się działaniem znacznie dłużej niż się spodziewasz.
  2. Niektóre systemy operacyjne (starsze i być może niektóre nowoczesne osadzone) mogą nie utrzymywać tak twardych granic procesu, a twoje alokacje mogą wpływać na przestrzenie adresowe innych.
Ben Zotto
źródło
4

Tak. System operacyjny czyści zasoby. Cóż ... stare wersje NetWare nie.

Edycja: jak zauważył San Jacinto, z pewnością istnieją systemy (poza NetWare), które tego nie robią. Nawet w programach do wyrzucenia staram się wyrabiać w sobie nawyk uwalniania wszystkich zasobów tylko po to, aby utrzymać ten nawyk.

Mark Wilkins
źródło
3
Nie głosuję w dół, ale jest to dość niebezpieczny post dla potomności. DOS jest nadal używany na wielu platformach wbudowanych i POWAŻNIE wątpię, czy wyczyści pamięć za Ciebie. Szerokie uogólnienie jest błędne.
San Jacinto
@San Jacinto: To dobra uwaga. Właśnie dlatego odwołałem się do NetWare, ale prawdopodobnie przydałoby się wyjaśnienie. Zmienię to trochę.
Mark Wilkins,
3
@San DOS nie jest wielozadaniowym systemem operacyjnym - po zakończeniu działania programu DOS (z wyjątkiem TSR) cała pamięć jest dostępna dla następnego programu do załadowania.
@Neil dzięki za przypomnienie, ale miałem na myśli program podobny do TSR, który uruchamia się, gdy wystąpi zdarzenie, co jest powszechnym zastosowaniem w systemach wbudowanych. Niemniej jednak dziękuję za twoją wiedzę i wyjaśnienie, gdzie zawiodłem :)
San Jacinto,
2

Tak, system operacyjny zwalnia całą pamięć po zakończeniu procesu.

Draemon
źródło
Nie rozumiem, dlaczego to zostało odrzucone. pamięć malloc'ed zostanie zwolniona, gdy proces umrze (tak mówi definicja malloc w Wikipedii)
Arve,
7
Wikipedia nie jest instrukcją dla każdego istniejącego systemu operacyjnego. Większość współczesnych systemów operacyjnych odzyskuje pamięć, ale nie wszystkie (a zwłaszcza nie wszystkie stare) to robią. Dodaj do tego, mallocmoże tylko obiecać, co C zrobi z pamięcią; z założenia C nie gwarantuje niczego w odniesieniu do zachowania poza samym C. Jeśli aplikacja nieoczekiwanie umiera, wszelkie obietnice złożone przez bibliotekę wykonawczą są nieważne, ponieważ nie istnieje już, aby im sprostać.
cHao
2

To zależy, systemy operacyjne zwykle to wyczyszczą, ale jeśli pracujesz na przykład nad oprogramowaniem wbudowanym, może ono nie zostać wydane.

Po prostu upewnij się, że go zwolniłeś, może zaoszczędzić dużo czasu później, gdy będziesz chciał zintegrować go z dużym projektem.

Charles
źródło
0

To naprawdę zależy od systemu operacyjnego, ale dla wszystkich systemów operacyjnych, jakie kiedykolwiek napotkasz, alokacja pamięci zniknie, gdy proces zostanie zakończony.


źródło
0

Myślę, że bezpośrednie uwalnianie jest najlepsze. Niezdefiniowane zachowanie jest najgorszą rzeczą, więc jeśli masz dostęp, gdy jest jeszcze zdefiniowany w twoim procesie, zrób to, istnieje wiele dobrych powodów, dla których ludzie podali to.

Co do tego, gdzie lub czy znalazłem to w W98, prawdziwe pytanie brzmiało „kiedy” (nie widziałem postu podkreślającego to). Mały program szablonowy (dla wejścia MIDI SysEx, wykorzystujący różne spacje malloc'd) zwolniłby pamięć w bicie WM_DESTROY WndProc, ale kiedy przeszczepiłem to do większego programu, zawiesił się przy wyjściu. Założyłem, że oznacza to, że próbuję uwolnić to, co system operacyjny już zwolnił podczas większego czyszczenia. Jeśli zrobiłem to na WM_CLOSE, a następnie wywołałem DestroyWindow (), wszystko działało dobrze, natychmiastowe czyste wyjście.

Chociaż nie jest to dokładnie to samo, co bufory MIDI, istnieje podobieństwo w tym, że najlepiej jest zachować proces w nienaruszonym stanie, całkowicie wyczyścić, a następnie zakończyć. Przy niewielkich fragmentach pamięci jest to bardzo szybkie. Okazało się, że wiele małych buforów działa szybciej w działaniu i czyszczeniu niż mniej dużych.

Mogą istnieć wyjątki, jak ktoś powiedział, unikając przenoszenia dużych fragmentów pamięci z powrotem z pliku wymiany na dysku, ale nawet to można zminimalizować, utrzymując więcej i mniej przydzielonych przestrzeni.


źródło