sourceforge.net/projects/fastmm jest open source i zastępuje menedżera pamięci. Tutaj możesz dowiedzieć się, jak działa zarządzanie pamięcią i skąd pochodzą informacje dotyczące przydzielania i usuwania pamięci.
1
Zauważ, że FastMM jest specyficzny tylko dla kompilatorów Delphi / C ++ Builder, nie jest to menedżer pamięci ogólnego przeznaczenia dla C ++. Nie jest nawet napisany w C ++.
Remy Lebeau,
Odpowiedzi:
181
Kiedy przydzielasz pamięć na stercie, twój alokator będzie śledził, ile pamięci przydzieliłeś. Zazwyczaj jest to przechowywane w segmencie „head” tuż przed przydzieloną pamięcią. W ten sposób, gdy nadejdzie czas na zwolnienie pamięci, de-alokator wie dokładnie, ile pamięci do zwolnienia.
Zauważ, że dotyczy to tylko przydziałów tablic w C ++. Wszystkie inne przydziały zależą od wielkości typu. Niektóre biblioteki przechowują wszystkie wielkości alokacji, zwykle tylko w trybie debugowania.
Zan Lynx
97
Nie ma absolutnie żadnego powodu, dla którego programiści nie powinni mieć tych informacji. Mogę przekazać wskaźnik do funkcji i zwolnić go, ale aby sam uzyskać rozmiar w tej samej funkcji, muszę przekazać dodatkowy parametr. Czy to ma jakiś sens?
Mark Ruzon,
26
@ Mark, ma to niewielki sens, ponieważ teoretycznie zwalnia alokatora, aby zawsze przechowywał rozmiar przydzielonego bloku (który może różnić się od rozmiaru żądanego bloku). Niektóre projekty alokatorów mogą potrzebować tych informacji do własnych celów lub mogą nie być na tyle skomplikowane, aby wykorzystywać informacje o typie do śledzenia wielkości przydziałów sterty innej niż tablica itp. Zmuszanie alokatora do przechowywania żądanego rozmiaru (abyś nie konieczność samodzielnego przekazania rozmiaru tablicy) może być niewielkim obciążeniem, ale może mieć wpływ na wydajność możliwych do wyobrażenia projektów alokatorów.
Doug McClean
33
Przepraszamy, ale ta odpowiedź nie ma sensu. To, co opisał QuantumPete, to w zasadzie „Skąd freewiadomo, ile pamięci należy zwolnić”. Tak, rozmiar bloku pamięci jest przechowywany „gdzieś” przez malloc(zwykle w samym bloku), więc o tym freewie. Jednak new[]/ delete[]to inna historia. Te ostatnie działają w zasadzie na malloc/ free. new[]przechowuje również liczbę elementów, które utworzył w bloku pamięci (niezależnie od malloc), aby później delete[]móc pobrać ten numer i użyć go do wywołania odpowiedniej liczby destruktorów.
AnT
26
To znaczy fizycznie dwa liczniki są przechowywane w bloku: rozmiar bloku (według malloc) i liczba elementów (według new[]). Zauważ, że tego pierwszego nie można użyć do obliczenia drugiego, ponieważ w ogólnym przypadku wielkość bloku pamięci może być większa niż naprawdę konieczna dla tablicy żądanego rozmiaru. Zauważ też, że licznik elementów tablicy jest potrzebny tylko dla typów z nietrywialnym destruktorem. W przypadku typów z trywialnym destruktorem licznik nie jest przechowywany przez new[]i, oczywiście, nie jest pobierany przez delete[].
AnT
23
JEDNYM z podejść do kompilatorów jest przydzielenie nieco więcej pamięci i zapisanie liczby elementów w elemencie głównym.
Przykład, jak można to zrobić:
Tutaj
int* i =newint[4];
kompilator przydzieli sizeof(int)*5bajty.
int*temp = malloc(sizeof(int)*5)
Będzie przechowywać „4” w pierwszych sizeof(int)bajtach
*temp =4;
i nastaw i
i = temp +1;
Więc ipunkty do tablicy 4 elementów, a nie 5.
I usunięcie
delete[] i;
będą przetwarzane w następujący sposób:
int*temp = i -1;int numbers_of_element =*temp;// = 4... call destructor for numbers_of_element elements
... that are stored in temp +1, temp +2,... temp +4if needed
free (temp)
Informacje nie są znormalizowane. Jednak na platformach, nad którymi pracowałem, ta informacja jest przechowywana w pamięci tuż przed pierwszym elementem. Dlatego możesz teoretycznie uzyskać do niego dostęp i sprawdzić go, jednak nie jest to tego warte.
Również dlatego musisz użyć delete [], kiedy przydzielisz pamięć nowemu [], ponieważ tablicowa wersja delete wie, że (i gdzie) musi szukać zwolnienia odpowiedniej ilości pamięci - i wywołać odpowiednią liczbę destruktorów dla obiektów.
W standardzie C ++ jest zdefiniowany jako specyficzny dla kompilatora. Co oznacza magię kompilatora. Może zerwać z nietrywialnymi ograniczeniami wyrównania na co najmniej jednej głównej platformie.
Możesz pomyśleć o możliwych implementacjach, zdając sobie sprawę, że delete[]jest zdefiniowane tylko dla wskaźników zwracanych przez new[], które mogą nie być tym samym wskaźnikiem, co zwracane przez operator new[]. Jedną z implementacji na wolności jest przechowywanie liczby tablic w pierwszej int zwracanej przez operator new[]i new[]zwracanie wskaźnika przesuniętego poza to. (Dlatego nietrywialne dopasowania mogą się złamać new[].)
Pamiętaj, że operator new[]/operator delete[]! = new[]/delete[].
Ponadto jest to prostopadłe do tego, w jaki sposób C zna wielkość przydzielonej pamięci malloc.
Ponieważ tablica, która ma zostać „usunięta”, powinna zostać utworzona przy użyciu jednego operatora „nowego”. „Nowa” operacja powinna była umieścić tę informację na stercie. W przeciwnym razie, skąd dodatkowe zastosowania nowych wiedziałby, gdzie kończy się kupa?
Nie jest znormalizowany. W środowisku wykonawczym Microsoftu nowy operator używa malloc (), a operator usuwania używa free (). Tak więc, w tym ustawieniu twoje pytanie jest równoważne z następującym: Skąd free () zna rozmiar bloku?
Za kulisami jest trochę księgowości, tj. W środowisku wykonawczym C.
Nie prawda. Zanim zadzwonisz za darmo, delete [] musi najpierw wywołać destruktory. Do tego wiedząc, całkowity rozmiar przydziałów nie wystarczy. W rzeczywistości nowe [] i delete [] działają inaczej dla zwykłych i zniszczonych typów w VC ++.
Suma
0
Jest to bardziej interesujący problem, niż mogłoby się początkowo wydawać. Ta odpowiedź dotyczy jednej możliwej implementacji.
Po pierwsze, chociaż na pewnym poziomie twój system musi wiedzieć, jak „zwolnić” blok pamięci, leżący u jego podstaw malloc / free (który zazwyczaj nazywają new / delete / new [] / delete []) nie zawsze pamięta dokładnie, ile pamięci jeśli poprosisz, może zostać zaokrąglony w górę (na przykład, gdy przekroczysz 4K, często jest zaokrąglany w górę do następnego bloku wielkości 4K).
Dlatego nawet jeśli można uzyskać rozmiar bloku pamięci, nie mówi nam to, ile wartości znajduje się w nowej [] edytowanej pamięci, ponieważ może być mniejsza. Dlatego musimy przechowywać dodatkową liczbę całkowitą, która mówi nam, ile jest wartości.
Z WYJĄTKIEM, jeśli budowany typ nie ma destruktora, to delete [] nie musi nic robić poza zwolnieniem bloku pamięci, a zatem nie musi niczego przechowywać!
Odpowiedzi:
Kiedy przydzielasz pamięć na stercie, twój alokator będzie śledził, ile pamięci przydzieliłeś. Zazwyczaj jest to przechowywane w segmencie „head” tuż przed przydzieloną pamięcią. W ten sposób, gdy nadejdzie czas na zwolnienie pamięci, de-alokator wie dokładnie, ile pamięci do zwolnienia.
źródło
free
wiadomo, ile pamięci należy zwolnić”. Tak, rozmiar bloku pamięci jest przechowywany „gdzieś” przezmalloc
(zwykle w samym bloku), więc o tymfree
wie. Jednaknew[]
/delete[]
to inna historia. Te ostatnie działają w zasadzie namalloc
/free
.new[]
przechowuje również liczbę elementów, które utworzył w bloku pamięci (niezależnie odmalloc
), aby późniejdelete[]
móc pobrać ten numer i użyć go do wywołania odpowiedniej liczby destruktorów.malloc
) i liczba elementów (wedługnew[]
). Zauważ, że tego pierwszego nie można użyć do obliczenia drugiego, ponieważ w ogólnym przypadku wielkość bloku pamięci może być większa niż naprawdę konieczna dla tablicy żądanego rozmiaru. Zauważ też, że licznik elementów tablicy jest potrzebny tylko dla typów z nietrywialnym destruktorem. W przypadku typów z trywialnym destruktorem licznik nie jest przechowywany przeznew[]
i, oczywiście, nie jest pobierany przezdelete[]
.JEDNYM z podejść do kompilatorów jest przydzielenie nieco więcej pamięci i zapisanie liczby elementów w elemencie głównym.
Przykład, jak można to zrobić:
Tutaj
kompilator przydzieli
sizeof(int)*5
bajty.Będzie przechowywać „4” w pierwszych
sizeof(int)
bajtachi nastaw
i
Więc
i
punkty do tablicy 4 elementów, a nie 5.I usunięcie
będą przetwarzane w następujący sposób:
źródło
Informacje nie są znormalizowane. Jednak na platformach, nad którymi pracowałem, ta informacja jest przechowywana w pamięci tuż przed pierwszym elementem. Dlatego możesz teoretycznie uzyskać do niego dostęp i sprawdzić go, jednak nie jest to tego warte.
Również dlatego musisz użyć delete [], kiedy przydzielisz pamięć nowemu [], ponieważ tablicowa wersja delete wie, że (i gdzie) musi szukać zwolnienia odpowiedniej ilości pamięci - i wywołać odpowiednią liczbę destruktorów dla obiektów.
źródło
Zasadniczo jest umieszczony w pamięci jako:
[informacje] [mem, o który prosiłeś ...]
Gdzie info to struktura używana przez kompilator do przechowywania ilości przydzielonej pamięci, a co nie.
Jest to jednak zależne od implementacji.
źródło
To nie jest coś, co jest w specyfikacji - zależy od implementacji.
źródło
W standardzie C ++ jest zdefiniowany jako specyficzny dla kompilatora. Co oznacza magię kompilatora. Może zerwać z nietrywialnymi ograniczeniami wyrównania na co najmniej jednej głównej platformie.
Możesz pomyśleć o możliwych implementacjach, zdając sobie sprawę, że
delete[]
jest zdefiniowane tylko dla wskaźników zwracanych przeznew[]
, które mogą nie być tym samym wskaźnikiem, co zwracane przezoperator new[]
. Jedną z implementacji na wolności jest przechowywanie liczby tablic w pierwszej int zwracanej przezoperator new[]
inew[]
zwracanie wskaźnika przesuniętego poza to. (Dlatego nietrywialne dopasowania mogą się złamaćnew[]
.)Pamiętaj, że
operator new[]/operator delete[]
! =new[]/delete[]
.Ponadto jest to prostopadłe do tego, w jaki sposób C zna wielkość przydzielonej pamięci
malloc
.źródło
Ponieważ tablica, która ma zostać „usunięta”, powinna zostać utworzona przy użyciu jednego operatora „nowego”. „Nowa” operacja powinna była umieścić tę informację na stercie. W przeciwnym razie, skąd dodatkowe zastosowania nowych wiedziałby, gdzie kończy się kupa?
źródło
Nie jest znormalizowany. W środowisku wykonawczym Microsoftu nowy operator używa malloc (), a operator usuwania używa free (). Tak więc, w tym ustawieniu twoje pytanie jest równoważne z następującym: Skąd free () zna rozmiar bloku?
Za kulisami jest trochę księgowości, tj. W środowisku wykonawczym C.
źródło
Jest to bardziej interesujący problem, niż mogłoby się początkowo wydawać. Ta odpowiedź dotyczy jednej możliwej implementacji.
Po pierwsze, chociaż na pewnym poziomie twój system musi wiedzieć, jak „zwolnić” blok pamięci, leżący u jego podstaw malloc / free (który zazwyczaj nazywają new / delete / new [] / delete []) nie zawsze pamięta dokładnie, ile pamięci jeśli poprosisz, może zostać zaokrąglony w górę (na przykład, gdy przekroczysz 4K, często jest zaokrąglany w górę do następnego bloku wielkości 4K).
Dlatego nawet jeśli można uzyskać rozmiar bloku pamięci, nie mówi nam to, ile wartości znajduje się w nowej [] edytowanej pamięci, ponieważ może być mniejsza. Dlatego musimy przechowywać dodatkową liczbę całkowitą, która mówi nam, ile jest wartości.
Z WYJĄTKIEM, jeśli budowany typ nie ma destruktora, to delete [] nie musi nic robić poza zwolnieniem bloku pamięci, a zatem nie musi niczego przechowywać!
źródło