Jestem mylony z podstawami alokacji pamięci między stosem a stertą . Zgodnie ze standardową definicją (rzeczy, które wszyscy mówią), wszystkie typy wartości zostaną przydzielone do stosu, a typy referencyjne trafią do sterty .
Rozważmy teraz następujący przykład:
class MyClass
{
int myInt = 0;
string myString = "Something";
}
class Program
{
static void Main(string[] args)
{
MyClass m = new MyClass();
}
}
Teraz, w jaki sposób alokacja pamięci nastąpi w języku C #? Czy przedmiot MyClass
(to znaczy m
) zostanie całkowicie przydzielony do Sterty? To znaczy int myInt
i string myString
obie pójdą na stos?
A może obiekt zostanie podzielony na dwie części i zostanie przydzielony do obu lokalizacji pamięci, czyli Stack i Heap?
Odpowiedzi:
m
jest alokowany na stercie i to obejmujemyInt
. Sytuacje, w których typy pierwotne (i struktury) są przydzielane na stosie, występują podczas wywołania metody, która przydziela miejsce na zmienne lokalne na stosie (ponieważ jest szybsze). Na przykład:class MyClass { int myInt = 0; string myString = "Something"; void Foo(int x, int y) { int rv = x + y + myInt; myInt = 2^rv; } }
rv
,x
,y
Wszystko będzie na stosie.myInt
znajduje się gdzieś na stercie (i musi być dostępny przezthis
wskaźnik).źródło
Powinieneś rozważyć pytanie, gdzieJako szczegół implementacji przydzielania obiektów. Nie ma dla Ciebie znaczenia, gdzie dokładnie są przechowywane bity obiektu. Może mieć znaczenie, czy obiekt jest typem referencyjnym, czy typem wartości, ale nie musisz martwić się o to, gdzie będzie przechowywany, dopóki nie zaczniesz musieć optymalizować zachowania czyszczenia pamięci.
Podczas gdy typy odwołań są zawsze przydzielane na stercie w bieżących implementacjach, typy wartości mogą być przydzielane na stosie - ale niekoniecznie. Typ wartości jest przydzielany na stosie tylko wtedy, gdy jest to zmienna lokalna lub tymczasowa bez zmiany znaczenia, która nie jest zawarta w typie referencyjnym i nie jest przydzielona w rejestrze.
Czy jest coś, co przegapiłem?
Oczywiście byłbym niedbały, gdybym nie linkował do postów Erica Lipperta na ten temat:
źródło
„Wszystkie typy VALUE zostaną przydzielone do stosu” jest bardzo, bardzo złe; zmienne strukturalne mogą żyć na stosie jako zmienne metod. Jednak pola typu żyją z tym typem . Jeśli deklarowanym typem pola jest klasa, wartości znajdują się na stercie jako część tego obiektu. Jeśli typ deklarujący pola jest strukturą, pola są częścią tej struktury, gdziekolwiek ta struktura znajduje.
Nawet zmienne metod mogą znajdować się na stercie, jeśli są przechwytywane (metoda lambda / anon) lub stanowią część (na przykład) bloku iteratora.
źródło
object x = 12;
w metodzie, liczba 12 zostanie zapisana na stercie, nawet jeśli jest to liczba całkowita (typ wartości).null
, albo odwołują się do obiektu sterty odpowiedniego typu. Dla każdego typu wartości istnieje odpowiadający mu typ obiektu sterty; próba zapisania typu wartości w miejscu przechowywania typu referencyjnego spowoduje utworzenie nowego obiektu odpowiadającego mu typu obiektu sterty, skopiowanie wszystkich pól do tego nowego obiektu i zapisanie odniesienia do obiektu w lokalizacji przechowywania typu referencyjnego. C # udaje typ wartość i typ obiektu są takie same, ale ...List<T>.Enumerator
który jest przechowywany w zmiennej tego typu, będzie wykazywał semantykę wartości, ponieważ jest to typ wartości. Jednak,List<T>.Enumerator
który jest przechowywany w zmiennej typuIEnumerator<T>
, będzie zachowywał się jak typ referencyjny. Jeśli ktoś uważa ten drugi za inny typ niż pierwszy, różnicę w zachowaniu można łatwo wyjaśnić. Udawanie, że są tego samego typu, znacznie utrudnia rozumowanie.Doskonałe wyjaśnienie:
Część 1: http://www.c-sharpcorner.com/uploadfile/rmcochran/csharp_memory01122006130034pm/csharp_memory.aspx
Część 2: http://www.c-sharpcorner.com/UploadFile/rmcochran/csharp_memory2B01142006125918PM/csharp_memory2B.aspx
Część 3: http://www.c-sharpcorner.com/UploadFile/rmcochran/chsarp_memory401152006094206AM/chsarp_memory4.aspx
Część 4: http://www.c-sharpcorner.com/uploadfile/rmcochran/csharp_memory_401282006141834pm/csharp_memory_4.aspx
źródło
Stos
Rozważ następującą metodę:
public static int Factorial (int x) { if (x == 0) { return 1; } return x * Factorial (x - 1); }
Ta metoda jest rekurencyjna, co oznacza, że wywołuje samą siebie. Za każdym razem, gdy metoda jest wprowadzana, na stosie przydzielana jest nowa wartość int , a za każdym razem, gdy metoda kończy działanie, przydział int jest zwalniany .
Sterta
Rozważ następującą metodę:
using System; using System.Text; class Test { public static void Main() { StringBuilder ref1 = new StringBuilder ("object1"); Console.WriteLine (ref1); // The StringBuilder referenced by ref1 is now eligible for GC. StringBuilder ref2 = new StringBuilder ("object2"); StringBuilder ref3 = ref2; // The StringBuilder referenced by ref2 is NOT yet eligible for GC. Console.WriteLine (ref3); // object2 } }
W powyższym przykładzie zaczynamy od utworzenia obiektu StringBuilder, do którego odwołuje się zmienna ref1, a następnie wypisujemy jego zawartość. Ten obiekt StringBuilder jest następnie natychmiast kwalifikowany do wyrzucania elementów bezużytecznych, ponieważ nic go później nie używa. Następnie tworzymy kolejny obiekt StringBuilder, do którego odwołuje się zmienna ref2, i kopiujemy to odwołanie do ref3. Mimo że ref2 nie jest używany po tym punkcie, ref3 utrzymuje ten sam obiekt StringBuilder przy życiu - zapewniając, że nie kwalifikuje się on do kolekcji, dopóki nie skończymy używać ref3.
źródło
proste środki
Typ wartości można ustawić na STOSIE, jest to szczegół implementacyjny, który można przypisać do jakiejś futurystycznej struktury danych.
więc lepiej jest zrozumieć, jak działa wartość i typ referencyjny, typ wartości zostanie skopiowany przez wartość, co oznacza, że gdy przekażesz typ wartości jako parametr do FUNKCJI, niż zostanie on skopiowany przez naturę, oznacza to, że będziesz mieć całkowicie nową kopię .
Typy referencyjne są przekazywane przez referencje (ponownie nie rozważaj, że referencja będzie przechowywać adres ponownie w niektórych przyszłych wersjach, może być przechowywana w innych strukturach danych).
więc w twoim przypadku
myInt jest wartością typu int, która jest ekapsulowana w klasie, która wykracza poza typ referencyjny, więc zostanie powiązana z instancją klasy, która będzie przechowywana w „THE HEAP”.
sugerowałbym, możesz zacząć czytać blogi pisane przez ERIC LIPPERTS.
Blog Erica
źródło
Za każdym razem, gdy tworzony jest w nim obiekt, trafia do obszaru pamięci zwanego stertą. Zmienne pierwotne, takie jak int i double, są przydzielane na stosie, jeśli są lokalnymi zmiennymi metodami, oraz w stercie, jeśli są zmiennymi składowymi. W metodach zmienne lokalne są umieszczane na stosie, gdy wywoływana jest metoda, a wskaźnik stosu jest zmniejszany po zakończeniu wywołania metody. W aplikacji wielowątkowej każdy wątek będzie miał swój własny stos, ale będzie współdzielił ten sam stos. Dlatego w kodzie należy zachować ostrożność, aby uniknąć problemów z równoczesnym dostępem do przestrzeni sterty. Stos jest bezpieczny wątkowo (każdy wątek będzie miał swój własny stos), ale sterta nie jest bezpieczna wątkowo, chyba że jest chroniona przez synchronizację za pośrednictwem kodu.
Ten link jest również przydatny http://www.programmerinterview.com/index.php/data-structures/difference-between-stack-and-heap/
źródło
m jest odniesieniem do obiektu MyClass, więc m jest przechowywany na stosie głównego wątku, ale obiekt MyClass jest przechowywany na stercie. Dlatego myInt i myString są przechowywane w stercie. Zauważ, że m jest tylko odniesieniem (adresem do pamięci) i znajduje się na głównym stosie. kiedy m zostanie zwolniony, GC wyczyści obiekt MyClass ze sterty Aby uzyskać więcej informacji, przeczytaj wszystkie cztery części tego artykułu https://www.c-sharpcorner.com/article/C-Sharp-heaping-vs-stacking-in-net- część-i /
źródło