Wygląda na to, że każda książka .net mówi o typach wartości vs. typach referencyjnych i wskazuje na (często niepoprawnie) stan, w którym każdy typ jest przechowywany - sterty lub stosu. Zwykle znajduje się w kilku pierwszych rozdziałach i jest przedstawiany jako bardzo ważny fakt. Myślę, że jest nawet objęty egzaminami certyfikacyjnymi . Dlaczego stos zamiast sterty ma nawet znaczenie dla (początkujących) programistów .Net? Przydzielasz rzeczy i to po prostu działa, prawda?
36
Odpowiedzi:
Przekonałem się, że głównym powodem, dla którego ta część informacji jest uważana za ważną, jest tradycja. W środowiskach niezarządzanych rozróżnienie między stosem a stertą jest ważne i musimy ręcznie przydzielić i usunąć używaną pamięć. Teraz zarządzanie śmieciami zajmuje się zarządzaniem, więc ignorują ten fragment. Nie sądzę, że wiadomość naprawdę się przeszła, że nie musimy się przejmować, jakiego rodzaju pamięci używa się.
Jak zauważył Fede, Eric Lippert ma kilka bardzo ciekawych rzeczy do powiedzenia na ten temat: http://blogs.msdn.com/b/ericlippert/archive/2010/09/30/the-truth-about-value-types.aspx .
W świetle tych informacji możesz dostosować mój pierwszy akapit, aby w zasadzie czytać: „Powodem, dla którego ludzie umieszczają te informacje i zakładają, że są one ważne, jest to, że są to nieprawidłowe lub niekompletne informacje w połączeniu z potrzebą tej wiedzy w przeszłości”.
Dla tych, którzy uważają, że jest to nadal ważne ze względu na wydajność: Jakie działania wykonałbyś, aby przenieść coś ze stosu na stos, gdybyś dokonał pomiaru i stwierdził, że to miało znaczenie? Bardziej prawdopodobne jest, że znajdziesz zupełnie inny sposób na poprawę wydajności w obszarze problemu.
źródło
Zgadzam się całkowicie; Cały czas to widzę.
Jednym z powodów jest to, że wiele osób przyszło do C # (lub innych języków .NET) z tła C lub C ++. Ponieważ te języki nie egzekwują reguł dotyczących okresu przechowywania, musisz znać te reguły i dokładnie wdrożyć program, aby ich przestrzegać.
Teraz znajomość tych reguł i przestrzeganie ich w C nie wymaga zrozumienia „sterty” i „stosu”. Ale jeśli rozumiesz, jak działają struktury danych, często łatwiej jest zrozumieć i przestrzegać zasad.
Pisząc książkę dla początkujących, naturalne jest, że autor wyjaśnia pojęcia w tej samej kolejności, w jakiej się ich nauczył. To niekoniecznie kolejność, która ma sens dla użytkownika. Niedawno byłem redaktorem ds. Technicznych książki dla początkujących C # 4 Scotta Dormana, a jedną z rzeczy, które mi się podobały, było to, że Scott wybrał dość rozsądną kolejność tematów, zamiast zaczynać od naprawdę zaawansowanych tematów w zarządzaniu pamięcią.
Innym powodem jest to, że niektóre strony w dokumentacji MSDN silnie podkreślają kwestie związane z pamięcią masową. Szczególnie starsza dokumentacja MSDN, która wciąż kręci się od pierwszych dni. Znaczna część tej dokumentacji zawiera subtelne błędy, które nigdy nie zostały usunięte, i musisz pamiętać, że została napisana w określonym czasie w historii i dla określonej grupy odbiorców.
Moim zdaniem tak nie jest. O wiele ważniejsze jest zrozumienie takich rzeczy jak:
I tak dalej.
To jest idealne.
Są sytuacje, w których ma to znaczenie. Odśmiecanie jest niesamowite i stosunkowo niedrogie, ale nie jest bezpłatne. Kopiowanie małych struktur jest stosunkowo niedrogie, ale nie jest darmowe. Istnieją realistyczne scenariusze wydajności, w których należy zrównoważyć koszty presji zbierania i koszty nadmiernego kopiowania. W takich przypadkach bardzo pomocne jest dokładne zrozumienie wielkości, lokalizacji i rzeczywistego czasu życia całej odpowiedniej pamięci.
Podobnie istnieją realistyczne scenariusze interakcji, w których trzeba wiedzieć, co znajduje się na stosie, a co na stercie i co może przemieszczać się śmieciarz. Właśnie dlatego C # ma funkcje takie jak „naprawiono”, „stackalloc” i tak dalej.
Ale to są wszystkie zaawansowane scenariusze. Idealnie początkujący programista nie musi się martwić o nic z tych rzeczy.
źródło
Wszyscy brakuje wam sensu. Powodem, dla którego rozróżnienie stosu / sterty jest ważne, jest zasięg .
Gdy x zniknie z zakresu, utworzony obiekt kategorycznie zniknie . Jest tak tylko dlatego, że jest alokowany na stosie, a nie na stosie. Nie ma nic, co mogłoby wejść w „...” część metody, która mogłaby zmienić ten fakt. W szczególności wszelkie przypisania lub wywołania metod mogły jedynie tworzyć kopie struktury S, a nie tworzyć nowych odniesień do niej, aby umożliwić jej utrzymanie.
Zupełnie inna historia! Ponieważ x jest teraz na stercie , jego obiekt (to znaczy sam obiekt , a nie jego kopia) mógłby równie dobrze kontynuować życie po tym, jak x zniknie z zasięgu. W rzeczywistości jedynym sposobem, w jaki nie będzie dalej istniał, jest to, że x to jedyne odniesienie do niego. Jeśli przypisania lub wywołania metod w części „...” utworzyły inne odwołania, które nadal są „aktywne” do czasu, gdy x zniknie z zakresu, obiekt ten będzie nadal działał.
To bardzo ważna koncepcja i jedynym sposobem, aby naprawdę zrozumieć „co i dlaczego”, jest poznanie różnicy między alokacją stosu i sterty.
źródło
...
zmiennej może spowodowaćx
konwersję na pole klasy generowanej przez kompilator, a tym samym poza wskazany zakres. Osobiście uważam za niesmaczny pomysł niejawnego podnoszenia, ale projektanci języków wydają się go popierać (w przeciwieństwie do wymagania, aby dowolna zmienna, która została podniesiona, miała coś w deklaracji, aby to określić). Aby zapewnić poprawność programu, często konieczne jest uwzględnienie wszystkich odniesień, które mogą istnieć do obiektu. Wiedząc, że do czasu powrotu procedury, żadna kopia przekazywanego odwołania nie będzie przydatna.structType foo
, miejsce przechowywaniafoo
zawiera zawartość swoich pól; jeślifoo
jest na stosie, podobnie są jego pola. Jeślifoo
jest na stercie, podobnie są jego pola. jeślifoo
jest w sieci Apple II, podobnie są jego pola. Natomiast jeślifoo
były typu klasy, to przytrzymaj jedennull
lub odniesienie do obiektu. Jedyną sytuacją, w którejfoo
można powiedzieć, że klasa jest w posiadaniu pól obiektu, byłoby, gdyby było to jedyne pole klasy i zawierało odniesienie do siebie.Co do tego, dlaczego obejmują ten temat, zgadzam się z @Kirk, że jest to ważna koncepcja, którą musisz zrozumieć. Im lepiej znasz mechanizmy, tym lepiej możesz zrobić świetne aplikacje, które działają płynnie.
Teraz Eric Lippert wydaje się zgadzać z tobą, że większość autorów nie zajmuje się tym tematem. Polecam lekturę jego bloga, aby lepiej zrozumieć, co kryje się pod maską.
źródło
Pomyślałem, że o to właśnie chodzi w zarządzanych środowiskach. Nawet posunąłbym się do nazwania tego szczegółem implementacji podstawowego środowiska wykonawczego, o którym NIE powinieneś przyjmować żadnych założeń, ponieważ może on ulec zmianie w dowolnym momencie.
Nie wiem dużo o .NET, ale o ile wiem, jego JIT został wykonany przed wykonaniem. Na przykład JIT może przeprowadzić analizę ucieczki i co nie, a nagle będziesz mieć obiekty leżące na stosie lub tylko w niektórych rejestrach. Nie możesz tego wiedzieć.
Podejrzewam, że niektóre książki opisują to po prostu dlatego, że autorzy przywiązują do niego wielką wagę, lub ponieważ zakładają, że ich odbiorcy to robią (np. Jeśli napisałeś „C # dla programistów C ++”, prawdopodobnie powinieneś omówić ten temat).
Niemniej jednak myślę, że nie ma nic więcej do powiedzenia niż „zarządzanie pamięcią”. W przeciwnym razie ludzie mogą wyciągać błędne wnioski.
źródło
Musisz zrozumieć, jak działa alokacja pamięci, aby efektywnie z niej korzystać, nawet jeśli nie musisz jawnie nią zarządzać. Dotyczy to prawie każdej abstrakcji w informatyce.
źródło
Mogą istnieć pewne przypadki krawędzi, w których może to coś zmienić. Domyślne miejsce na stosie to 1 megabajt, a sterty to kilka gig. Więc jeśli twoje rozwiązanie zawiera dużą liczbę obiektów, możesz zabraknąć miejsca na stosie, mając jednocześnie dużo miejsca na stosie.
Jednak w większości jest to dość akademickie.
źródło
Jak mówisz, C # ma wyodrębnić zarządzanie pamięcią, a alokacja sterty kontra stos są szczegółami implementacji, o których teoretycznie deweloper nie powinien wiedzieć.
Problem polega na tym, że niektóre rzeczy są naprawdę trudne do wyjaśnienia w intuicyjny sposób bez odwoływania się do tych szczegółów implementacji. Spróbuj wyjaśnić obserwowalne zachowanie podczas modyfikowania zmiennych typów wartości - jest to prawie niemożliwe, nie odwołując się do rozróżnienia stosu / sterty. Lub spróbuj wyjaśnić, dlaczego w ogóle mają typy wartości w języku i kiedy ich użyjesz? Musisz zrozumieć to rozróżnienie, aby zrozumieć język.
Zauważ, że książki o powiedzmy, że Python lub JavaScript nie robią z tego wielkiej rzeczy, nawet jeśli o tym wspominają. Wynika to z faktu, że wszystko jest albo przydzielone do stosu, albo niezmienne, co oznacza, że inna semantyka kopiowania nigdy nie wchodzi w grę. W tych językach abstrakcja pamięci działa, w języku C # jest nieszczelna.
źródło