Dlaczego języki takie jak C i C ++ nie mają funkcji czyszczenia pamięci, podczas gdy Java? [Zamknięte]

57

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.

Mroczny Templariusz
źródło
9
W tym semestrze mamy moduł rozwoju iPhone'a. Po 2 latach programowania aplikacji na Androida pytanie to uderzyło większość klasy. Dopiero teraz widzimy, ile godzin Java tak naprawdę nas zaoszczędziło, że nie musimy wyśledzić nieprzyjemnych błędów zarządzania pamięcią i nie musimy pisać kodu płyty kotłowej.
siamii
7
@NullUserException, ponieważ nie określa sposobu na odzyskanie pamięci, który prawie implikuje GC.
Winston Ewert
1
@ bizso09: Czy oglądałeś już ARC? Nie ma potrzeby powolnego / grubego / niedeterministycznego GC, gdy masz wsparcie systemu dla liczenia referencji: developer.apple.com/technologies/ios5
JBRWilkinson 10.1011
3
Odpowiedzi na to całkiem dobre pytanie są pełne religijnych bzdur.
abatishchev
1
W C i C ++ można wziąć wskaźnik, rzucić go na int i dodać do niego liczbę. Później odejmij liczbę od int i rzutuj wynik z powrotem do wskaźnika. Otrzymasz taki sam wskaźnik jak poprzednio. Powodzenia w implementacji GC, która NIE gromadzi tej pamięci, podczas gdy jej adres jest przechowywany tylko w zmiennej, która ma również inną wartość. Wiem, że przykład jest głupi, ale lista połączona z XOR używa czegoś podobnego. Chciałbym opublikować to jako odpowiedź, ale pytanie jest zamknięte.
Marian Spanik,

Odpowiedzi:

71

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 ++.

Kylben
źródło
26
Drugim problemem jest to, że GC jest niedeterministyczna. Obiekt może, ale nie musi być w pamięci długo po tym, jak program go „upuścił”. Cykl życia przeliczania jest deterministyczny, gdy upuszczane jest ostatnie odwołanie, pamięć jest usuwana. Ma to wpływ nie tylko na wydajność pamięci, ale także na debugowanie. Częstym błędem programowania jest obiekt „zombie”, pamięć odniesienia, która teoretycznie została upuszczona. GC znacznie bardziej maskuje ten efekt i produkuje błędy, które są przerywane i niezwykle trudne do wyśledzenia.
kylben
22
- nowoczesne gc nie śledzą przydziałów ani nie liczą referencji. Tworzą wykres ze wszystkiego, co aktualnie znajduje się na stosie, a jedynie kondensują i usuwają wszystko inne (uproszczone), a GC zwykle zmniejsza złożoność języka. Nawet korzyść z wydajności jest wątpliwa.
Joel Coehoorn
13
Er, @kylben, cały sens automatycznego zapisywania GC w języku polega na tym, że odwoływanie się do obiektów zombie staje się niemożliwe , ponieważ GC uwalnia tylko obiekty, których nie można odwołać! Dostajesz rodzaj trudnych do śledzenia błędów, o których mówisz, gdy popełnisz błędy przy ręcznym zarządzaniu pamięcią.
Ben,
14
-1, GC nie liczy referencji. Dodatkowo, w zależności od wykorzystania pamięci i schematu alokacji, GC może być szybszy (z nadmiernym zużyciem pamięci). Zatem argument dotyczący wydajności jest również błędny. Tylko bliskość metalu jest właściwie ważnym punktem.
deadalnix
14
Ani Java, ani C # nie używają liczenia referencji: schematy GC oparte na liczeniu referencji są dość prymitywne w porównaniu i działają znacznie gorzej niż współczesne śmieciarki (głównie dlatego, że muszą robić zapisy pamięci, aby zmienić liczbę referencji za każdym razem, gdy kopiujesz referencję!)
mikera
44

Ś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.

Charles E. Grant
źródło
2
+1 za ogólną dobrą odpowiedź, ale także szczególnie za „Zapłaconą ceną jest to, że język C zapewnia bardzo niewielką pomoc w pisaniu poprawnego, wolnego od błędów kodu”
Shivan Dragon
2
C ma zarządzanie pamięcią - ale po prostu działa, więc ludzie ledwo to zauważają. Jest pamięć statyczna, rejestry i stos. Dopóki nie zaczniesz alokować z kupy, jesteś w porządku i elegancki. To przydział sterty, który wszystko psuje. Jeśli chodzi o Javę, każdy może napisać własne środowisko uruchomieniowe Java - jest wiele do wyboru, w tym coś, co można nazwać „Java systemu”. .NET może zrobić prawie wszystko, co potrafi C - pozostaje tylko w tyle za natywnymi możliwościami C ++ (np. Klasy są zarządzane tylko w .NET). Oczywiście masz również C ++ .NET, który ma wszystko, co robi C ++ i wszystko, co robi .NET.
Luaan,
1
@Luaan Powiedziałbym, że to bardzo hojna definicja posiadania „zarządzania pamięcią” „Dopóki nie zaczniesz alokować z kupy, jesteś w porządku i elegancko. To alokacja kupy psuje rzeczy”, To tak, jakby powiedzieć doskonały samolot, po prostu nie potrafi latać.
Charles E. Grant,
1
@ CharlesE.Grant Cóż, czysto funkcjonalny język może zrobić wszystko z tego rodzaju zarządzaniem pamięcią. To, że alokacja sterty jest dobrym kompromisem w niektórych przypadkach użycia, nie oznacza, że ​​jest to punkt odniesienia dla wszystkich języków / środowisk wykonawczych. To nie jest tak, że zarządzanie pamięcią przestaje być „zarządzaniem pamięcią” tylko dlatego, że jest proste, proste i ukryte za kulisami. Projektowanie statycznej alokacji pamięci to nadal zarządzanie pamięcią, podobnie jak dobre wykorzystanie stosu i wszystkiego, co masz do dyspozycji.
Luaan
„dowolna implementacja zgodna ze standardem” nie jest prawdziwa, tylko w przypadku implementacji zgodnego standardu środowiska hosta. Niektóre platformy / biblioteki standardowe, większość dla 8 lub 16-bitowego mikrokontrolera, nie zapewniają malloc()lub free(). (przykładami są kompilatory MLAP dla PIC)
12431234123412341234123
32

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. usingsł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 ++” (np Foo&.).

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ć.

Daniel Pryden
źródło
4
To jest faktyczna odpowiedź na pytanie
rdzeń
1
Jeśli chodzi o część c ++, dziś powinieneś spojrzeć na std :: unique_ptr i std :: move :)
Niclas Larsson
@NiclasLarsson: Nie jestem pewien, czy rozumiem twój punkt widzenia. Czy mówisz, że std::unique_ptrjest 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żam std:unique_ptrinteligentny wskaźnik , ponieważ faktycznie wykonuje zliczanie referencji, obsługuje tylko specjalne przypadki, w których liczba referencji wynosi zero lub jeden (i std::movejest to mechanizm aktualizacji liczby referencji).
Daniel Pryden
std::unique_ptrnie ma liczby referencji i std::movenie ma w ogóle nic wspólnego z referencjami (więc „brak” wydajności). Rozumiem jednak twój punkt widzenia, ponieważ std::shared_ptrliczba referencji, które są implikacjami, została zaktualizowana przez std::move:)
Niclas Larsson,
2
@ Mike76: Po stronie alokacji, alokator GC będzie działał tak szybko, jak alokacja stosu, a GC może jednocześnie zwolnić tysiące obiektów. Bez względu na to, co zrobisz z implementacją liczenia refilacji, alokacja i dezalokacja nigdy nie będą szybsze niż malloci free. 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.)
Daniel Pryden,
27

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 ++.

Lior Kogan
źródło
Ciekawy. Mój materiał do czytania na dziś.
surfasb
Bah, wideo. Ale nigdy nie mniej interesujące.
surfasb
2
ciekawe wideo. Najlepsze było 21 minut i 55 minut. Szkoda, że ​​wywołania WinRT nadal wyglądały na bumpf C ++ / CLI.
gbjbaanb
2
@ dan04: To prawda. Ale jeśli napiszesz w C, otrzymasz to, o co prosisz.
DeadMG
6
Zarządzanie inteligentnymi wskaźnikami nie jest bardziej wymagające niż upewnienie się, że nie ma niepotrzebnych referencji w środowisku śmieci. Ponieważ GC nie umie czytać w twoich myślach, to też nie jest magia.
Tamás Szelei
15

„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.

ChrisF
źródło
1
Ale pisane przyszłe programy zaczną korzystać ze śmietnika, nie?
Dark Templar,
5
Podczas gdy odśmiecanie jest teoretycznie niezależne od jakiegokolwiek języka programowania, bardzo trudno jest napisać przydatny GC dla C / C ++, a nawet niemożliwe jest stworzenie głupka (przynajmniej tak niezawodnego jak Java) - powód, dla którego Java może go wyciągnąć wyłączony, ponieważ działa w kontrolowanym środowisku zwirtualizowanym. I odwrotnie, język Java jest przeznaczony dla GC i trudno ci będzie pisać kompilator Java, który nie robi GC.
tdammers,
4
@tdammers: Zgadzam się, że zbieranie śmieci musi być obsługiwane przez język, aby było to możliwe. Jednak głównym celem nie jest wirtualizacja i kontrolowane środowisko, ale ścisłe pisanie. C i C ++ są słabo wpisane, więc pozwalają na takie rzeczy, jak przechowywanie wskaźnika w zmiennej całkowitej, rekonstruowanie wskaźników z przesunięć i takie rzeczy, które uniemożliwiają kolektorowi wiarygodne określenie, co jest osiągalne (C ++ 11 zabrania późniejszego przynajmniej konserwatystów). W Javie zawsze wiesz, co to jest odniesienie, więc możesz je dokładnie zebrać, nawet jeśli skompilujesz je w natywnym.
Jan Hudec
2
@ ThorbjørnRavnAndersen: Mogę napisać prawidłowy program C, który przechowuje wskaźniki w taki sposób, że żaden śmieciarz nigdy ich nie znalazł. Jeśli następnie podłączysz śmietnik do malloci free, zepsułeś mój poprawny program.
Ben Voigt
2
@ ThorbjørnRavnAndersen: Nie, nie zadzwoniłbym, freedopó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ów freebezużytecznych.
Ben Voigt
12

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.

ddyer
źródło
9

Nie mam dokładnych cytatów, ale zarówno Bjarne, jak i Herb Sutter mówią coś w tym stylu:

C ++ nie potrzebuje śmieciarza, ponieważ nie ma śmieci.

W nowoczesnym C ++ używasz inteligentnych wskaźników i dlatego nie masz śmieci.

ronag
źródło
1
jakie są inteligentne wskaźniki?
Dark Templar,
11
gdyby to było takie proste, nikt nie wdrożyłby żadnej GC.
deadalnix
7
@deadalnix: Racja, ponieważ nikt nigdy nie implementuje niczego zbyt skomplikowanego, powolnego, wzdętego lub niepotrzebnego. Całe oprogramowanie jest w 100% wydajne przez cały czas, prawda?
Zach.
5
@deadalnix - Podejście C ++ do zarządzania pamięcią jest nowsze niż śmieciarze. RAII został wymyślony przez Bjarne Stroustrup dla C ++. Czyszczenie niszczycieli jest starszym pomysłem, ale zasady zapewniania bezpieczeństwa wyjątków są kluczowe. Nie wiem, kiedy dokładnie sam pomysł został po raz pierwszy opisany, ale pierwszy standard C ++ został sfinalizowany w 1998 r., A Stroustrups „Design and Evolution of C ++” został opublikowany dopiero w 1994 r., A wyjątki były stosunkowo nowym dodatkiem do C ++ - po opublikowaniu „Annotated C ++ Reference Manual” w 1990 r., jak sądzę. GC został wynaleziony w 1959 roku dla Lisp.
Steve314,
1
@deadalnix - czy wiesz, że co najmniej jedna maszyna wirtualna Java używała GC z liczeniem referencji, którą można (prawie) zaimplementować za pomocą RAII w stylu C ++ przy użyciu inteligentnej klasy wskaźnika - właśnie dlatego, że była bardziej wydajna dla kodu wielowątkowego niż istniejące maszyny wirtualne? Zobacz www.research.ibm.com/people/d/dfb/papers/Bacon01Concurrent.pdf. Jednym z powodów, dla których nie widać tego w C ++ w praktyce, jest zwykła kolekcja GC - może zbierać cykle, ale nie może wybrać bezpiecznego porządku niszczyciela w obecności cykli, a zatem nie może zapewnić niezawodnego czyszczenia niszczyciela.
Steve314,
8

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.

Winston Ewert
źródło
6

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.

James Anderson
źródło
2
C dobre dla wysokiego poziomu? Parsknąłem drinkiem po klawiaturze.
DeadMG
5
Powiedział „wiele zadań na wyższym poziomie”. On mógłby być troll liczenia (jeden, dwa, wiele ...). I tak naprawdę nie powiedział wyżej. Żarty na bok, choć to prawda - dowód jest, że wiele istotnych projektów wyższego poziomu nie zostały pomyślnie rozwijać w C. Nie może być lepsze wybory teraz dla wielu z tych projektów, ale projekt roboczy jest silniejsze dowody niż spekulacje na temat tego, co może byłem.
Steve314,
Istnieje kilka zarządzanych systemów operacyjnych, które działają raczej dobrze. W rzeczywistości, gdy cały system jest zarządzany, wydajność związana z używaniem kodu zarządzanego spada jeszcze niżej, aż do osiągnięcia szybkości większej niż kod niezarządzany w rzeczywistych scenariuszach. Oczywiście są to wszystkie „badawcze systemy operacyjne” - nie ma prawie żadnego sposobu, aby uczynić je kompatybilnymi z istniejącym niezarządzanym kodem oprócz stworzenia w pełni zwirtualizowanego niezarządzanego systemu operacyjnego w zarządzanym systemie operacyjnym. Microsoft zasugerował w pewnym momencie, że mogą zastąpić Windows Server jednym z nich, ponieważ coraz więcej kodu serwera jest pisanych w .NET.
Luaan,
6

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.

hugomg
źródło
1
Gdzieś na programmers.se lub SO, ktoś twierdzi, że ktoś pracował nad samozaładowczym urządzeniem do zbierania śmieci - IIRC w zasadzie implementuje maszynę wirtualną za pomocą języka GC, z podzbiorem ładującym używanym do implementacji samej GC. Zapomniałem imienia Kiedy przyjrzałem się temu, okazało się, że w zasadzie nigdy nie osiągnęli skoku z podzbioru bez GC do poziomu roboczego GC. Jest to w zasadzie możliwe, ale AFAIK nigdy nie zostało osiągnięte w praktyce - z pewnością jest to trudny sposób.
Steve314,
@ Steve314: Chciałbym zobaczyć, że jeśli kiedykolwiek będziesz pamiętać, gdzie to znalazłeś!
hugomg
znalazłem to! Zobacz komentarze do stackoverflow.com/questions/3317329/… odnoszące się do maszyny wirtualnej Klein. Część problemu ze znalezieniem go - pytanie zostało zamknięte.
Steve314,
BTW - Wydaje mi się, że nie mogę rozpocząć komentarzy od @missingno - co daje?
Steve314,
@ steve314: Po napisaniu odpowiedzi na ten wątek, otrzymałem powiadomienie o wszystkich komentarzach. Wykonanie posta @ w tym przypadku byłoby zbędne i nie jest dozwolone przez SE ( choć nie pytaj mnie dlaczego ). (Jednak prawdziwą przyczyną jest to, że brakuje mojego numeru)
hugomg
5

Istnieją różne problemy, w tym ...

  • Chociaż GC zostało wynalezione przed C ++ i być może przed C, zarówno C, jak i C ++ zostały zaimplementowane, zanim GC zostały powszechnie zaakceptowane jako praktyczne.
  • Nie można łatwo wdrożyć języka i platformy GC bez bazowego języka innego niż GC.
  • Chociaż GC jest wyraźnie bardziej wydajny niż non-GC dla typowych aplikacji opracowanych w typowych ramach czasowych itp., Istnieją problemy, w których większy wysiłek programistyczny jest dobrym kompromisem, a wyspecjalizowane zarządzanie pamięcią przewyższy GC ogólnego przeznaczenia. Poza tym C ++ jest zwykle znacznie wydajniejszy niż większość języków GC, nawet bez dodatkowych wysiłków programistycznych.
  • GC nie jest ogólnie bezpieczniejszy niż RAII w stylu C ++. RAII pozwala na automatyczne czyszczenie zasobów innych niż pamięć, głównie dlatego, że obsługuje niezawodne i terminowe niszczyciele. Nie można ich łączyć z konwencjonalnymi metodami GC z powodu problemów z cyklami odniesienia.
  • Języki GC mają swoje własne charakterystyczne rodzaje wycieków pamięci, szczególnie odnoszące się do pamięci, która nigdy nie będzie ponownie używana, ale tam, gdzie istniały istniejące odniesienia, które nigdy nie zostały anulowane lub nadpisane. Konieczność wykonania tego wyraźnie nie różni się zasadniczo od potrzeby deletelub freewyraź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 ...

Steve314
źródło
4

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.

Ben Voigt
źródło
Prawdopodobnie wszystkie wady obu, ale mniej przypadków każdej wady, i to samo w odniesieniu do zalet. Oczywistym jest, że istnieje więcej rodzajów zarządzania pamięcią, ale można również uniknąć złożoności, wybierając odpowiedniego konia dla każdego kursu w kodzie. Wydaje mi się to mało prawdopodobne, ale istnieje luka teoretyczna. Spekulowałem wcześniej na temat mieszania GC i innych niż GC w tym samym języku, ale nie dla sterowników urządzeń - więcej na temat posiadania aplikacji głównie GC, ale z niektórymi ręcznie zarządzanymi pamięciami bibliotek struktury danych niskiego poziomu.
Steve314,
@ Steve314: Czy nie powiedziałbyś, że pamiętanie, które obiekty trzeba ręcznie uwolnić, jest tak uciążliwe, jak pamiętanie o uwolnieniu wszystkiego? (Oczywiście, inteligentne wskaźniki mogą pomóc w obu przypadkach, więc żaden z nich nie jest wielkim problemem) I potrzebujesz różnych pul dla ręcznie zarządzanych obiektów w porównaniu do obiektów zebranych / kompaktowalnych, ponieważ zagęszczenie nie działa dobrze, gdy w całym budynku są rozproszone obiekty stałe. Tak dużo dodatkowej złożoności za nic.
Ben Voigt,
2
Nie, jeśli istnieje wyraźny podział między kodem wysokiego poziomu, który jest cały GC, a kodem niskiego poziomu, który rezygnuje z GC. Pomysł opracowałem głównie kilka lat temu, patrząc na D, który pozwala zrezygnować z GC, ale nie pozwala na ponowne włączenie. Weźmy na przykład bibliotekę drzewek B +. Cały kontener powinien być GC, ale węzły struktury danych prawdopodobnie nie - bardziej efektywne jest wykonywanie spersonalizowanego skanowania tylko przez węzły liści, niż zmuszanie GC do przeszukiwania rekurencyjnego przez węzły gałęzi. Jednak skanowanie to musi zgłosić zawarte elementy do GC.
Steve314,
Chodzi o to, że jest to zawarty element funkcjonalności. Traktowanie węzłów drzewa B + jako specjalnego zarządzania pamięcią WRT nie różni się od traktowania ich jako specjalnego WRT będącego węzłem drzewa B +. Jest to biblioteka enkapsulowana, a kod aplikacji nie musi wiedzieć, że zachowanie GC zostało pominięte / specjalne. Tyle, że przynajmniej w tym czasie było to niemożliwe w D - jak powiedziałem, nie ma możliwości, aby włączyć się ponownie i zgłosić GC jako potencjalne źródła.
Steve314,
3

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.

Petruza
źródło
1
"Wbudowany system"? W czasie standaryzacji C (1989) musiała być w stanie obsługiwać komputery z 1 MB pamięci.
dan04
Zgadzam się, cytowałem bardziej aktualny przykład.
Petruza
1 MB? Święty Schmoley, kto kiedykolwiek potrzebowałby tyle pamięci RAM? </billGates>
Mark K Cowan
2

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.

back2dos
źródło
A także, że dedykowane śmieciarki są lepiej zaimplementowane, wydajniejsze i lepiej pasują do języka. :)
Max
Nie, nie można używać RTTI do dynamicznego odkrywania wykresu obiektów w C / C ++: To zwykłe stare obiekty danych psują wszystko. Po prostu nie ma informacji RTTI przechowywanych w zwykłym starym obiekcie danych, który pozwoliłby modułowi odśmiecania rozróżniać wskaźniki i nie-wskaźniki w tym obiekcie. Co gorsza, wskaźniki nie muszą być idealnie wyrównane na całym sprzęcie, więc biorąc pod uwagę 16-bajtowy obiekt, istnieje 9 możliwych lokalizacji, w których można zapisać wskaźnik 64-bitowy, z których tylko dwie się nie pokrywają.
cmaster
2

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!

adrianmcmenamin
źródło
1

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.

cmaster
źródło