Koncepcje programowania/Zarządzanie pamięcią
Każde urządzenie komputerowe posiada jakąś pamięć. Każdy program gdzieś w tej pamięci musi być zapisany. Ponieważ ilość pamięci jest zawsze ograniczona, jednym z zadań programisty jest efektywne wykorzystanie tej przestrzeni.
Wyobraź sobie że masz 6 kwadratów, 6 bloków, 6 dostępnych komórek pamięci. Na razie wszystkie są puste. Jeśli gdzieś musimy zapisać jakąś informację, musimy wykorzystać w tym celu jakiś wolny blok pamięci. Wypełniamy więc go jakimś elementem, i mamy zajętą komórkę pamięci, coś do tego bloku zostało zapisane. Potem zapisujemy coś do kolejnego bloku i kolejnego, aż w pewnym momencie wszystkie zostaną zajęte. Bardzo zła rzecz, ponieważ po ich zajęciu nie jesteśmy w stanie zapisać kolejnych informacji, nasz program w tym momencie może powodować wycieki pamięci albo nawet przestać działać.
Dlatego aby nie dochodziło to takich incydentów, należy pamiętać by używać komórek pamięci tylko wtedy gdy są one potrzebne i kasować je, gdy potrzebne nie są, tak żebyśmy mogli w nim zapisywać kolejne dane w razie potrzeby.
Zasadniczo istnieją dwie metody zarządzania pamięcią. Pierwszy z nich już poznałeś w rozdziale o wskaźnikach. Jeśli czytałeś temat o wskaźnikach, wiesz już w jaki sposób się to robi, w sposób ręczny, programista na tu pełną kontrolę nad tym, ile jej użyć i w którym momencie ją zwolnić, musimy wprowadzać dodatkowy kod, który będzie za to odpowiedzialny. Przykład złego kodu w języku C++:
int function() {
int* wsk = new int; // zarezerwowane pamięci na liczbę typu int
*wsk = 20; // wpisanie w zarezerwowane miejsce wartości
return *wsk;
}
W powyższym przykładzie wykonanie funkcji function spowoduje rezerwację obszaru pamięci na jedną liczbę typu int i zapisanie w nim wartości 20. Wartość ta jest zwracana przez funkcję, jednak obszar pamięci o adresie wskazywanym przez wskaźnik wsk nie zostaje zwolniony (brak wywołania instrukcji delete). Oznacza to wyciek pamięci - każde kolejne wywołanie funkcji function powiększy rozmiar tego wycieku co najmniej o rozmiar typu danych int.
Tej metody z reguły używamy gdzie jest istotna wysoka wydajność, żeby móc jak najwięcej wycisnąć z sprzętu.
Są też metody automatyczne, zwane z angielskiego garbage collector czyli Odśmiecanie pamięci, zbieranie nieużytków, automatyczna dealokacja. One są łatwiejsze, ponieważ język robi to za nas, my jako programiści w ogóle nie musimy się tym przejmować, my sobie tworzymy dowolne informacje, zmienne w kodzie programu a garbage collector skasuje to, co według niego nie jest w tej chwili potrzebne. Takie metoda, choć wygodna, z reguły jest mniej efektywna od standardowego, ręcznego zarządzania pamięcią.
W zalezności od języka będzie się zaliczał do jednej bądź drugiej grupy. W przypadku c++ zaliczamy go oczywiście do pierwszej, Python z kolei zajmuje się tym w pełni automatycznie.
Zadania
edytuj- Napisz program, który przydziela pamięć dla dużej struktury danych, ale nigdy go nie rozważa. Użyj profilera pamięci, aby zidentyfikować wycieki pamięci w programie i naprawić je.
- Napisz program, który wielokrotnie przydziela i rozpowszechnia pamięć dla małej struktury danych w pętli. Użyj profilera pamięci, aby zidentyfikować problemy z fragmentacją pamięci i zoptymalizować program, aby zmniejszyć fragmentację.
- Napisz program, który wykorzystuje dynamiczną alokacja pamięci do utworzenia linkowanej listy. Zaimplementuj funkcję, która przemierza listę i rozlega całą pamięć używaną przez listę.
- Napisz program, który tworzy wiele wątków, z których każdy przydziela pamięć. Użyj technik synchronizacji, takich jak muteksy lub semafory, aby zapobiec warunkom wyścigu i zapewnić prawidłowe zarządzanie pamięcią.
- Napisz program, który symuluje system alokacji pamięci przy użyciu strategii alokacji pierwszej dopasowania. Zaimplementuj funkcję, która defragentuje przestrzeń pamięci, przesuwając przydzielone bloki w celu konsolidacji wolnej przestrzeni.
- Napisz program, który korzysta z poborcy śmieci do zarządzania pamięcią. Wdrożyć niestandardowe algorytmy zbierania śmieci, aby zoptymalizować wydajność dla konkretnego przypadku użycia programu.
- Napisz program, który tworzy dużą liczbę małych obiektów za pomocą dynamicznej alokacji pamięci. Użyj profilera pamięci, aby zidentyfikować dowolne obciążenie pamięci spowodowane przez system alokacji i zoptymalizować program, aby zmniejszyć koszty ogólne.