Skąd nasz program wie, gdzie skończyć czytać pamięć dla danej zmiennej

0

Hi, nie wiem czy dobry dział, jeśli nie, proszę o przeniesienie.

Pojawiła się w mojej głowie taka niejasność.

Otóż w momencie, kiedy inicjalizujemy zmienne, kompilator/interpeter/whatevea kojarzy sobie daną nazwę z konkretnym adresem w pamięci. Wie też ile pamięci ma zaalokować, no bo typ jakoś sobie wywnioskuje sam, czy też programista mu poda. To jakby jest jasne.

Pytanie moje brzmi jednak następująco: jak to się dzieje, że potem, poza momentem inicjalizacji, nasz program wie, ile bajtów z pamięci odczytać? Gdzie zacząć czytać, to oczywiste - ma adres powiązany ze zmienną, więc tam zaczyna. Skąd ma jednak wiedzieć kiedy skończyć, po ilu bajtach?

Czy kompilator/interpeter/whateva trzyma sobie gdzieś informacje o tym, jakiego typu jest dana zmienna pod danym adresem, zatem wie też ile bajtów ma przeczytać?

Jak to wygląda np. w Assemblerze czy C? W C sprawa mi się wydaje nieco prostsza, bo mamy wszędzie typy.

A np. w Pythonie czy innym dynamicznie typowanym języku? Okej, tutaj mamy referencje i valuesy + interpreter sobie ogarnie typ danej wartości, ale bardziej ciekawi mnie tutaj jak sobie radzi ze skomplikowanymi typami/obiektami? Pod spodem tam pewnie jakieś structy są, przynajmniej w CPythonie?

Czyli interesuje mnie trochę jak to wygląda poziom niżej, jeśli idzie o tworzenie, czytanie i wykorzystywanie zmiennych.

Będę wdzięczny za odpowiedzi czy też linki do jakiś materiałów w tej tematyce, zwłaszcza w Pythonie.

5

kojarzy sobie daną nazwę z konkretnym adresem w pamięci

Nie, nic takiego nie ma miejsca. Po kompilacji nie ma już czegoś takiego jak zmienna w ogóle. Jest zamiast niej konkretny adres ORAZ instrukcje czytające/zapisujące o odpowiednim rozmiarze!
Jeśli zrobisz jakieś zmienna = 10; to kompilator wygeneruje z tego instrukcje asemblera dokonujące zapisu w odpowiednie miejsce w pamięci odpowiedniej liczby bajtów.

A np. w Pythonie czy innym dynamicznie typowanym języku?

Dynamiczna typizacja nie ma znaczenia, bo przecież w runtime te typy jednak są znane, więc interpreter wie z jakimi typami pracuje i generuje odpowiednie instrukcje kodu maszynowego.

0

Skąd ma jednak wiedzieć kiedy skończyć, po ilu bajtach?

Podejrzewam, że większość (a może wszystkie) implementacje alokują sobie pamięć i przechowują faktyczną liczbę elementów (dlatego na ślepo mogę powiedzieć, że w każdym języku, metody len(), size() itp. mają złożoność O(1)).
Np. jak masz "listę" w pythonie, to jest to tablica, czyli spójny blok w pamięci wirtualnej (fizycznie w RAMie dane są porozrzucane wszędzie, ale system operacyjny przyznaje tzw. pamięć wirtualną każdemu procesowi; każdy proces myśli, że ma pamięć dla siebie na wyłączność i może zaalokować 2^32 - 2^48 bajtów, w zależności od CPU i implementacji OS, i myśli też że blok pamięci jest spójny).
Jak sobie deklarujesz listę w pythonie domyślnie alokowane jest X bajtów w tym bloku pamięci wirtualnej (referencja jest na pierwszy element w pamięci), jeśli próbujesz dodać X+1 element do listy, która ma zaalokowane X bajtów, wtedy program realokuje blok w pamięci wirtualnej, powiększając go zazwyczaj do rozmiaru X*2 bajtów i dopiero wtedy dodaje element.

To taka podstawa w implenetacjach list w pythonie, ArrayList, HashMap itp.Inaczej jest w przypakdu LinkedList LinkedHashMap itp... gdzie nie musimy mieć wszystkich elementów po kolei w pamięci wirtualnej.

0
qbns napisał(a):

Np. jak masz "listę" w pythonie, to jest to spójny blok w pamięci wirtualnej (fizycznie w RAMie dane są porozrzucane wszędzie,

Nooo, moge sie mylic ale listy w pythonie to ArrayList

0

OK to ja zadam inne pytanie skoro mowa o python-ie ktory jesli sie nie nie myle ma jakis tam GC, z tego co zrozumialem to GC jest zawsze czescia runtime to w jaki sposob on wie kiedy i jak zwolnic zaoolokowana pamiec na jakas tam zmienna ktora juz nie jest uzywana?

  1. dlaczego np golang ma powiedzmy ma problemy jesli chodzi o urzadzenia embedded ? tzn z tego co ja zrozumialem jednym z wiekszych problemow zeby chodzilo to bez problemow w srodowiskach embedded jest wlasnie GC z go ktory po prostu jest zbyt duzy czy cos takiego i ciezko jest go usunac i sa tam niby jakies implementacje go dla embedded ktory nie wspieraja jakis tam ficzerow samego jezyka (teraz dokladnie nie pamietam jakich)

Sorry za glupie pytanie dla wielu z was

0

@marcio OK to ja zadam inne pytanie skoro mowa o python-ie ktory jesli sie nie nie myle ma jakis tam GC, z tego co zrozumialem to GC jest zawsze czescia runtime to w jaki sposob on wie kiedy i jak zwolnic zaoolokowana pamiec na jakas tam zmienna ktora juz nie jest uzywana? tutaj akurat ci odpowiem - reference counts
python sobie ogarnia cały czas, ile odwołań do danego obiektu istnieje - ile 'rzeczy' się do niego odwołuje i go wykorzystuje. Jesli liczba taspadnie do 0 w danym momencie, czyli nic się nie odwołuje do danego obiektu, zwalnia pamięć.
Do tego dochodzi jeszcze element wykrywania pętli referencji - czyli A odwołuje się do B, B do A. Nigdzie indziej nic się do tego nie odwołuje. Python też to ogarnia.

2

tl;dr

Listę w Pythonie reprezentuje struktura PyListObject. W PyListObject znajduje się pole ob_item prowadzące do tablicy dynamicznej zawierającej wskaźniki do elementów. Przed dodaniem/usunięciem elementów obliczany jest newsize. Jeżeli newsize > allocated or newsize < (allocated // 2), tablica jest realokowana. Wzrost, IIRC, to pi razy drzwi newsize + 12.5%.

Jesli liczba taspadnie do 0 w danym momencie, czyli nic się nie odwołuje do danego obiektu, zwalnia pamięć.

Nie zawsze, nie do końca, zależy. Na przykładzie listy: Gdy ob_refcnt spadnie do 0, wykonywane jest Py_XDECREF na elementach, po czym leci dealokacja tablicy wskaźników (ob_item). Teraz, jeżeli numfree < 80, reprezentujący pustą listę PyListObject nie jest dealokowany, tylko wrzucany do puli free_list. free_list to cache pustych list.

W Pythonie robiony jest milion optymalizacyjnych wygibasów. Nawet z tymi wygibasami wersja 3.8 podczas startu alokuje prawie 50k obiektów.

> python-3.8.0b1d -c 'import sys; print(sum(tp_allocs for _, tp_allocs, _, _ in sys.getcounts()))'
48761
> 

1 użytkowników online, w tym zalogowanych: 0, gości: 1