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?
c
memory-management
Andreas Grech
źródło
źródło
Odpowiedzi:
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.
źródło
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:
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).
źródło
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?
źródło
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
malloc
wywoł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ą:
źródło
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.
źródło
Tak, system operacyjny zwalnia całą pamięć po zakończeniu procesu.
źródło
malloc
moż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ć.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.
źródło
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
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