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

  1. 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.
  2. 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ę.
  3. 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ę.
  4. 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ą.
  5. 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.
  6. 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.
  7. 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.