Wiem, że istnieją takie rzeczy jak malloc / free dla C i nowy / using-a-destructor do zarządzania pamięcią w C ++, ale zastanawiałem się, dlaczego nie ma „nowych aktualizacji” tych języków, które pozwalają użytkownikowi masz opcję ręcznego zarządzania pamięcią lub system może to zrobić automatycznie (odśmiecanie)?
Trochę nowiutkiego pytania, ale był w CS dopiero od około roku.
garbage-collection
Mroczny Templariusz
źródło
źródło
Odpowiedzi:
Odśmiecanie wymaga struktur danych do śledzenia przydziałów i / lub liczenia referencji. Powodują one narzut pamięci, wydajności i złożoności języka. C ++ jest zaprojektowany tak, aby był „bliski metalowi”, innymi słowy, ma wyższą wydajność w porównaniu z funkcjami wygody. Inne języki sprawiają, że kompromis jest inny. Jest to jeden z czynników przy wyborze języka, który preferujesz.
To powiedziawszy, istnieje wiele schematów zliczania referencji w C ++, które są dość lekkie i wydajne, ale znajdują się one w bibliotekach, zarówno komercyjnych, jak i open source, a nie części samego języka. Zliczanie referencji w celu zarządzania czasem życia obiektu nie jest takie samo jak wyrzucanie elementów bezużytecznych, ale rozwiązuje wiele tego samego rodzaju problemów i lepiej pasuje do podstawowego podejścia C ++.
źródło
Ściśle mówiąc, nie ma w ogóle zarządzania pamięcią w języku C. malloc () i free () nie są słowami kluczowymi w języku, ale tylko funkcjami wywoływanymi z biblioteki. To rozróżnienie może być teraz pedantyczne, ponieważ malloc () i free () są częścią standardowej biblioteki C i zostaną zapewnione przez każdą standardową implementację C, ale nie zawsze tak było w przeszłości.
Dlaczego miałbyś chcieć języka bez standardu zarządzania pamięcią? To sięga początków C jako „przenośnego zestawu”. Istnieje wiele przypadków sprzętu i algorytmów, które mogą korzystać, a nawet wymagać specjalistycznych technik zarządzania pamięcią. O ile mi wiadomo, nie ma możliwości całkowitego wyłączenia natywnego zarządzania pamięcią Java i zastąpienia go własnym. Jest to po prostu niedopuszczalne w niektórych sytuacjach o wysokiej wydajności / minimalnych zasobach. C zapewnia prawie całkowitą elastyczność w wyborze dokładnie takiej infrastruktury, z której będzie korzystał twój program. Zapłacona cena jest taka, że język C zapewnia bardzo małą pomoc w pisaniu poprawnego, wolnego od błędów kodu.
źródło
malloc()
lubfree()
. (przykładami są kompilatory MLAP dla PIC)Prawdziwa odpowiedź jest taka, że jedynym sposobem na stworzenie bezpiecznego, wydajnego mechanizmu wyrzucania elementów bezużytecznych jest obsługa języka dla nieprzejrzystych odniesień. (Lub, odwrotnie, brak wsparcia na poziomie językowym dla bezpośredniej manipulacji pamięcią).
Java i C # mogą to zrobić, ponieważ mają specjalne typy odwołań, których nie można manipulować. Daje to środowisku wykonawczemu swobodę robienia rzeczy, takich jak przenoszenie przydzielonych obiektów w pamięci , co ma kluczowe znaczenie dla wysokowydajnej implementacji GC.
Dla przypomnienia, żadna współczesna implementacja GC nie wykorzystuje zliczania referencji , więc jest to całkowicie czerwony śledź. Współczesne GC korzystają z kolekcji generacyjnej, w której nowe alokacje są traktowane zasadniczo tak samo, jak alokacje stosu w języku takim jak C ++, a następnie okresowo wszelkie nowo alokowane obiekty, które są jeszcze żywe, są przenoszone do osobnej przestrzeni „ocalałych” i całej generacji przedmiotów zostaje natychmiast zwolniony.
Takie podejście ma zalety i wady: zaletą jest to, że przydziały sterty w języku obsługującym GC są tak szybkie, jak przydziały stosu w języku, który nie obsługuje GC, a wadą jest to, że obiekty, które muszą wykonać czyszczenie przed zniszczeniem albo wymagają osobnego mechanizmu (np.
using
słowa kluczowego C # ), w przeciwnym razie ich kod czyszczenia będzie działał niedeterministycznie.Zauważ, że kluczem do wysokowydajnej GC jest obsługa języka dla specjalnej klasy referencji. C nie obsługuje tego języka i nigdy nie będzie; ponieważ C ++ ma przeciążenie operatora, może emulować typ wskaźnika GC, chociaż trzeba to zrobić ostrożnie. W rzeczywistości, gdy Microsoft wynalazł swój dialekt języka C ++, który działałby pod CLR (środowisko uruchomieniowe .NET), musiał wynaleźć nową składnię dla „odniesień w stylu C #” (np.
Foo^
), Aby odróżnić je od „odniesień w stylu C ++” (npFoo&
.).To, co ma C ++ i co jest regularnie używane przez programistów C ++, to inteligentne wskaźniki , które są tak naprawdę tylko mechanizmem liczenia referencji. Nie uważałbym, że liczenie referencji jest „prawdziwym” GC, ale zapewnia wiele takich samych korzyści, kosztem mniejszej wydajności niż ręczne zarządzanie pamięcią lub prawdziwe GC, ale z korzyścią deterministycznego zniszczenia.
Ostatecznie odpowiedź naprawdę sprowadza się do funkcji projektowania języka. C dokonał jednego wyboru, C ++ dokonał wyboru, który umożliwił kompatybilność wsteczną z C, jednocześnie zapewniając alternatywy, które są wystarczająco dobre dla większości celów, a Java i C # dokonały innego wyboru, który jest niezgodny z C, ale jest również wystarczająco dobry dla większość celów. Niestety, nie ma srebrnej kuli, ale znajomość różnych dostępnych opcji pomoże ci wybrać odpowiedni dla dowolnego programu, który obecnie próbujesz zbudować.
źródło
std::unique_ptr
jest to „obsługa nieprzejrzystych odniesień na poziomie językowym”? (Nie chodziło mi o wsparcie, które miałem na myśli, i nie sądzę, aby było to wystarczające, chyba że usunięto również obsługę bezpośredniej manipulacji pamięcią z C ++.) Wspominam o inteligentnych wskaźnikach w mojej odpowiedzi i rozważamstd:unique_ptr
inteligentny wskaźnik , ponieważ faktycznie wykonuje zliczanie referencji, obsługuje tylko specjalne przypadki, w których liczba referencji wynosi zero lub jeden (istd::move
jest to mechanizm aktualizacji liczby referencji).std::unique_ptr
nie ma liczby referencji istd::move
nie ma w ogóle nic wspólnego z referencjami (więc „brak” wydajności). Rozumiem jednak twój punkt widzenia, ponieważstd::shared_ptr
liczba referencji, które są implikacjami, została zaktualizowana przezstd::move
:)malloc
ifree
. Tak, GC może być znacznie szybszy. (Pamiętaj, że powiedziałem „może być” - oczywiście na dokładność każdego programu ma wpływ wiele czynników.)Ponieważ przy użyciu mocy C ++ nie ma takiej potrzeby.
Herb Sutter: „ Nie pisałem skasować od lat ”.
patrz Pisanie nowoczesnego kodu C ++: ewolucja C ++ na przestrzeni lat 21:10
Może zaskoczyć wielu doświadczonych programistów C ++.
źródło
„Wszystko” śmieciarz to proces, który jest okresowo uruchamiany w celu sprawdzenia, czy w pamięci znajdują się jakieś niepowiązane obiekty i czy są one usuwane. (Tak, wiem, że jest to rażące uproszczenie). To nie jest właściwość języka, ale ramy.
Istnieje śmieciarze napisane dla C i C ++ - ten na przykład.
Jednym z powodów, dla których nikt nie został „dodany” do języka, może być sama ilość istniejącego kodu, który nigdy nie użyłby go, ponieważ używają własnego kodu do zarządzania pamięcią. Innym powodem może być to, że typy aplikacji napisane w C i C ++ nie potrzebują narzutu związanego z procesem odśmiecania.
źródło
malloc
ifree
, zepsułeś mój poprawny program.free
dopóki nie skończyłem. Ale twój proponowany moduł wyrzucania elementów bezużytecznych, który nie zwalnia pamięci, dopóki jawnie go nie wywołam, wcale nie jest urządzeniem do usuwania elementówfree
bezużytecznych.C został zaprojektowany w czasach, w których zbieranie śmieci było zaledwie opcją. Przeznaczony był również do zastosowań, w których zbieranie śmieci zasadniczo nie działałoby - środowiska typu „bare metal” w czasie rzeczywistym z minimalną pamięcią i minimalnym wsparciem środowiska wykonawczego. Pamiętaj, że C był językiem implementacji pierwszego unixa, który działał na pdp-11 z 64 * K * bajtami pamięci. C ++ było pierwotnie rozszerzeniem do C - wybór już został dokonany i bardzo trudno jest przeszczepić zbieranie śmieci do istniejącego języka. Tego rodzaju rzeczy muszą być wbudowane z parteru.
źródło
Nie mam dokładnych cytatów, ale zarówno Bjarne, jak i Herb Sutter mówią coś w tym stylu:
W nowoczesnym C ++ używasz inteligentnych wskaźników i dlatego nie masz śmieci.
źródło
Pytasz, dlaczego te języki nie zostały zaktualizowane i zawierają opcjonalny moduł wyrzucania elementów bezużytecznych.
Problem z opcjonalnym wyrzucaniem elementów bezużytecznych polega na tym, że nie można mieszać kodu korzystającego z różnych modeli. Oznacza to, że jeśli napiszę kod, który zakłada, że używasz modułu wyrzucania elementów bezużytecznych, nie możesz go użyć w programie, w którym wyłączono funkcję wyrzucania elementów bezużytecznych. Jeśli to zrobisz, wycieknie wszędzie.
źródło
Czy możesz sobie wyobrazić pisanie modułu obsługi urządzenia w języku z funkcją odśmiecania? Ile bitów może zejść, gdy GC działa?
Lub system operacyjny? Jak możesz uruchomić zbieranie śmieci, zanim jeszcze uruchomisz jądro?
C jest przeznaczony do niskiego poziomu blisko zadań sprzętowych. Problem? jest to tak fajny język, że jest dobrym wyborem do wielu zadań na wyższym poziomie. Czary językowe są świadome tych zastosowań, ale muszą w pierwszej kolejności obsługiwać wymagania dotyczące sterowników urządzeń, osadzonego kodu i systemów operacyjnych.
źródło
Krótka i nudna odpowiedź na to pytanie jest taka, że dla osób piszących moduły do śmiecia musi istnieć język, który nie jest wykorzystywany do usuwania śmieci. Nie jest łatwo koncepcyjnie mieć język, który jednocześnie pozwala na bardzo precyzyjną kontrolę nad układem pamięci i na którym działa GC.
Drugim pytaniem jest, dlaczego C i C ++ nie mają śmieciarek. Cóż, wiem, że C ++ ma ich kilka, ale nie są zbyt popularne, ponieważ są zmuszeni do radzenia sobie z językiem, który nie został zaprojektowany do edycji GC, a ludzie, którzy nadal używają C ++ w ten wiek nie jest tak naprawdę tym, który tęskni za GC.
Ponadto zamiast dodawać GC do starego języka nieobjętego GC, w rzeczywistości łatwiej jest stworzyć nowy język, który ma większość tej samej składni, jednocześnie obsługując GC. Java i C # są tego dobrym przykładem.
źródło
Istnieją różne problemy, w tym ...
delete
lubfree
wyraźnej. Podejście GC nadal ma tę zaletę - brak wiszących odniesień - a analiza statyczna może wychwycić niektóre przypadki, ale znowu nie ma jednego idealnego rozwiązania dla wszystkich przypadków.Zasadniczo częściowo dotyczy wieku języków, ale i tak zawsze będzie miejsce dla języków innych niż GC - nawet jeśli jest to trochę niszowe miejsce. I na poważnie, w C ++ brak GC nie jest wielkim problemem - twoja pamięć jest zarządzana inaczej, ale nie jest niezarządzana.
C ++ zarządzany przez Microsofty ma co najmniej pewną zdolność do mieszania GC i innych niż GC w tej samej aplikacji, umożliwiając mieszanie i łączenie zalet każdego z nich, ale nie mam doświadczenia, aby powiedzieć, jak dobrze to działa w praktyce.
Linki do reporing kurwa do moich powiązanych odpowiedzi ...
źródło
Odśmiecanie jest zasadniczo niezgodne z językiem systemowym używanym do opracowywania sterowników dla urządzeń obsługujących DMA.
Jest całkiem możliwe, że jedyny wskaźnik do obiektu będzie przechowywany w rejestrze sprzętu na niektórych urządzeniach peryferyjnych. Ponieważ śmieciarz nie wiedziałby o tym, pomyślałby, że obiekt jest nieosiągalny i zebrałby go.
Ten argument ma podwójne znaczenie dla kompaktowania GC. Nawet jeśli starałeś się zachować w pamięci odniesienia do obiektów używanych przez sprzętowe urządzenia peryferyjne, podczas gdy GC przeniósł obiekt, nie wiedziałby, jak zaktualizować wskaźnik zawarty w rejestrze konfiguracji urządzeń peryferyjnych.
Teraz potrzebujesz mieszanki nieruchomych buforów DMA i obiektów zarządzanych przez GC, co oznacza, że masz wszystkie wady obu.
źródło
Ponieważ C i C ++ są językami stosunkowo niskiego poziomu przeznaczonymi do ogólnego zastosowania, nawet na przykład do działania na 16-bitowym procesorze z 1 MB pamięci w systemie wbudowanym, na który nie można sobie pozwolić na marnowanie pamięci za pomocą gc.
źródło
W C ++ i C. są moduły do czyszczenia pamięci. Nie jestem pewien, jak to działa w C, ale w C ++ możesz wykorzystać RTTI, aby dynamicznie odkryć swój wykres obiektu i użyć go do zbierania elementów bezużytecznych.
O ile mi wiadomo, nie można pisać w Javie bez śmietnika. Okazało się to trochę wyszukiwania .
Kluczowa różnica między Javą a C / C ++ polega na tym, że w C / C ++ wybór należy zawsze do Ciebie, podczas gdy w Javie często nie ma opcji z założenia.
źródło
Jest to kompromis między wydajnością a bezpieczeństwem.
Nie ma gwarancji, że twoje śmieci będą gromadzone w Javie, więc może krążyć wokół, zużywając dużo miejsca, podczas gdy skanowanie w poszukiwaniu niereferencyjnych obiektów (tj. Śmieci) również trwa dłużej niż jawne usuwanie lub uwalnianie nieużywanego obiektu.
Zaletą jest oczywiście to, że można zbudować język bez wskaźników lub bez wycieków pamięci, więc bardziej prawdopodobne jest, że wygeneruje poprawny kod.
Czasami debaty te mogą mieć „religijną” przewagę - ostrzegamy!
źródło
Oto lista nieodłącznych problemów GC, które sprawiają, że nie można go używać w języku systemowym takim jak C:
GC musi działać poniżej poziomu kodu, którego obiektami zarządza. Po prostu nie ma takiego poziomu w jądrze.
GC musi od czasu do czasu zatrzymać zarządzany kod. Zastanów się teraz, co by się stało, gdyby zrobił to z twoim jądrem. Całe przetwarzanie na twoim komputerze zatrzymałoby się, powiedzmy, na milisekundę, podczas gdy GC skanuje wszystkie istniejące przydziały pamięci. Zabiłoby to wszystkie próby stworzenia systemów, które działałyby według ściśle określonych wymagań w czasie rzeczywistym.
GC musi być w stanie rozróżnić wskaźniki i wskaźniki niepowiązane. Oznacza to, że musi być w stanie spojrzeć na każdy istniejący obiekt pamięci i być w stanie stworzyć listę przesunięć, w których można znaleźć jego wskaźniki.
To odkrycie musi być idealne: GC musi być w stanie ścigać wszystkie odkryte wskazówki. Gdyby wyłuskał fałszywy wynik pozytywny, prawdopodobnie by się zawiesił. Jeśli nie wykryje fałszywego negatywu, prawdopodobnie zniszczy obiekt, który jest nadal w użyciu, zawiesi zarządzany kod lub po cichu uszkodzi jego dane.
To absolutnie wymaga, aby informacje o typie były przechowywane w każdym istniejącym obiekcie. Jednak zarówno C, jak i C ++ pozwalają na zwykłe stare obiekty danych, które nie zawierają informacji o typie.
GC jest z natury powolnym biznesem. Programiści uspołecznieni z Javą mogą nie zdawać sobie z tego sprawy, ale programy mogą być o rząd wielkości szybsze, jeśli nie są zaimplementowane w Javie. Jednym z czynników spowalniających pracę Javy jest GC. To wyklucza użycie języków GCed, takich jak Java, w superkomputerach. Jeśli Twoja maszyna kosztuje milion energii rocznie, nie chcesz płacić nawet 10% tej kwoty za wywóz śmieci.
C i C ++ to języki, które są tworzone w celu obsługi wszystkich możliwych przypadków użycia. I, jak widać, wiele z tych przypadków użycia jest wykluczonych przez wyrzucanie elementów bezużytecznych. Tak więc, aby obsłużyć te przypadki użycia, C / C ++ nie może być odśmiecany.
źródło