eryksun odpowiedział na pytanie nr 1, a ja już odpowiedział na pytanie nr 3 (oryginalna # 4), ale teraz niech odpowiedź pytanie nr 2:
Dlaczego zwolnić 50.5mb w szczególności - na jaką kwotę jest wydana?
To, na czym się opiera, to w końcu cała seria zbiegów okoliczności w Pythonie i malloc
, które są bardzo trudne do przewidzenia.
Po pierwsze, w zależności od sposobu mierzenia pamięci, można mierzyć tylko strony faktycznie zmapowane w pamięci.W takim przypadku, za każdym razem, gdy strona zostanie zamieniona przez pager, pamięć pojawi się jako "uwolniona", nawet jeśli nie została zwolniona.
Lub możesz mierzyć strony w użyciu, które mogą lub nie mogą liczyć przydzielone, ale nigdy nie dotknięte strony (w systemach, które optymistycznie przesadzają, jak linux), strony, które są przydzielane, ale oznaczone MADV_FREE
, itp.
Jeśli naprawdę mierzysz przydzielone strony (co w rzeczywistości nie jest bardzo przydatną rzeczą do zrobienia, ale wydaje się, że o to pytasz), a strony zostały faktycznie zwolnione, dwie okoliczności, w których to może happen: Albo użyłeś brk
lub odpowiednika, aby zmniejszyć segment danych (bardzo rzadko w dzisiejszych czasach) lub użyłeś munmap
lub podobnego, aby zwolnić zmapowany segment. (Jest też teoretycznie drobne wariant do tego ostatniego, że istnieją sposoby, aby zwolnić część odwzorowanym segmencie-np ukraść z MAP_FIXED
dla MADV_FREE
segmentu, który natychmiast unmap.)
Jednak większość programów nie bezpośrednio alokować rzeczy ze stron pamięci; używają przydziału w stylu malloc
. Podczas wywoływania free
program przydzielający może tylko zwolnić strony do systemu operacyjnego, jeśli akurat w rzeczywistości jest to free
ostatni obiekt na żywo w odwzorowaniu (lub na ostatnich N stronach segmentu danych). Nie ma możliwości, aby aplikacja mogła to przewidzieć, a nawet wykryć, że miało to miejsce z góry.
CPython czyni to jeszcze bardziej skomplikowanym - ma niestandardowy 2-poziomowy alokator obiektów na wierzchu niestandardowego alokatora pamięci na szczycie malloc
. (Aby uzyskać bardziej szczegółowe wyjaśnienie, patrz: the source comments). Co więcej, nawet na poziomie API C, znacznie mniej Python, nie kontrolujesz nawet bezpośrednio, kiedy obiekty najwyższego poziomu są przydzielone.
Po zwolnieniu obiektu, skąd wiadomo, czy zamierza zwolnić pamięć w systemie operacyjnym? Cóż, najpierw musisz wiedzieć, że wydałeś ostatnie referencje (w tym wewnętrzne referencje, o których nie wiedziałeś), pozwalając GC je zwolnić. (W przeciwieństwie do innych implementacji, przynajmniej CPython zwolni obiekt, gdy tylko na to pozwoli.) Zazwyczaj zwalnia co najmniej dwie rzeczy na niższym poziomie (np. Dla ciągu znaków zwalniasz obiekt PyString
, a ciąg znaków bufor).
Jeśli zrobić zwalnianie obiektu, aby wiedzieć, czy to powoduje, że następny poziom w dół, aby cofnąć przydział bloku przechowywania obiektów, trzeba znać stan wewnętrzny podzielnika obiektu, a także jak to jest realizowane. (To oczywiście nie może się zdarzyć, dopóki nie zwolnisz ostatniej rzeczy w bloku, a nawet wtedy, może się to nie zdarzyć.)
Jeśli chcesz zrobić zwolnić blok pamięci obiektów, aby dowiedzieć się, czy to powoduje wywołanie free
, musisz znać stan wewnętrzny programu przydzielającego PyMem, a także sposób jego implementacji. (Ponownie, musisz być dealokując ostatni blok w użyciu w regionie malloc
ED, a nawet jeśli, to nie może się zdarzyć.)
Jeśli zrobićfree
region malloc
ed, aby wiedzieć, czy to powoduje munmap
lub odpowiednik (lub brk
), musisz znać stan wewnętrzny malloc
, a także sposób jego implementacji. I ten, w przeciwieństwie do innych, jest wysoce specyficzny dla platformy. (I znowu, zazwyczaj musisz zwolnić ostatni w użyciu malloc
w segmencie mmap
, a nawet wtedy może się to nie zdarzyć.)
Więc, jeśli chcesz zrozumieć, dlaczego tak się stało, wypuść dokładnie 50,5 MB , będziesz musiał wyśledzić to od dołu do góry.Dlaczego malloc
odrzuciła 50,5 miliona stron, gdy wykonałeś jedno lub więcej wywołań free
(prawdopodobnie nieco więcej niż 50,5 mb)? Musisz przeczytać swoją platformę malloc
, a następnie przejrzeć różne tabele i listy, aby zobaczyć jej bieżący stan. (Na niektórych platformach może nawet wykorzystywać informacje na poziomie systemu, które są prawie niemożliwe do uchwycenia bez zrobienia migawki systemu w celu sprawdzenia w trybie offline, ale na szczęście nie jest to zazwyczaj problem.) A następnie musisz zrób to samo na 3 poziomach powyżej.
Jedyna użyteczna odpowiedź na to pytanie brzmi "Ponieważ".
O ile nie zajmujesz się tworzeniem ograniczonych zasobów (np. Osadzonych), nie masz powodu dbać o te szczegóły.
A jeśli jesteś , jesteś przy tworzeniu ograniczonych zasobów, poznanie tych szczegółów jest bezużyteczne; musisz wykonać końcowy test na wszystkich tych poziomach, a konkretnie na mmap
pamięci, której potrzebujesz na poziomie aplikacji (być może z jednym prostym, dobrze zrozumianym, podzielonym na strefy interfejsem pomiędzy).
Warto zauważyć, że to zachowanie nie jest specyficzne dla Pythona. Zwykle, gdy proces zwalnia pamięć przydzieloną stertom, pamięć nie zostaje zwolniona z powrotem do systemu operacyjnego, dopóki proces nie zakończy się. – NPE
Twoje pytanie wymaga wielu rzeczy - niektóre z nich to dupki, z których niektóre są nieodpowiednie dla SO, a niektóre z nich mogą być dobrymi pytaniami. Czy pytasz, czy Python nie zwalnia pamięci, pod ściśle określonymi okolicznościami, które może/nie może, jaki jest podstawowy mechanizm, dlaczego został zaprojektowany w ten sposób, czy są jakieś obejścia, czy coś zupełnie innego? – abarnert
@abarnert Połączyłem pytania, które były podobne. Aby odpowiedzieć na twoje pytania: Wiem, że Python wydaje trochę pamięci systemowi operacyjnemu, ale dlaczego nie wszystkie i dlaczego to robi. Jeśli istnieją okoliczności, w których nie może, dlaczego? Co też obejdzie. – Jared