Kilka implementacji Lisp ma znakomitych Garbage Collectorów. Szczególnym problemem jest to, że aplikacje Lisp często mają wysoką szybkość alokacji małych obiektów (konektory, ...).
Jest kilka rzeczy do rozważenia.
Precise vs. Conservative GC. Nie jestem wielkim fanem konserwatywnych GC (Boehm itp.) Dla Lispa. Problem konserwatywnych GC polega na tym, że nie znajdują wszystkich śmieci. Może to stanowić problem dla długotrwałych programów i prowadzić do fragmentacji, a nie odzyskać niewykorzystanej pamięci. Precyzyjni GC używają informacji znaczników danych Lisp i mogą identyfikować każdy typ danych każdego obiektu. Konserwatywne GC zostały wynalezione do implementacji języków programowania, które nie używają oznaczonych danych (C++, ...).
kopiowanie GC, kompaktowanie GC. Aby przeciwdziałać fragmentacji pamięci w długotrwałym Lisps, przydatna może być GC, która kompaktuje i lokalizuje obiekty. Czasami pojawia się problem, gdy hashtables należy ponownie włączyć (ponieważ zmiany lokalizacji). Kopiujący GC może potrzebować więcej pamięci (ponieważ istnieje pamięć typu "from" i "a"). Ale gdy GC kopiuje obiekty z jednej przestrzeni pamięci do drugiej, automatycznie czyni ją bardziej zwartą. Bardziej zaawansowane GC (jak na Lisp Machine) mogą także sortować obiekty i przydzielać obiekty tego samego typu blisko siebie - zakładając, że przyspieszy to dostęp do obiektów.
efemeryczna GC. Oznacza to, że istnieje pierwszy etap GC, który działa wyłącznie w pamięci głównej i otrzymuje pewne wsparcie z jednostki zarządzania pamięcią, aby zidentyfikować zmienione obszary pamięci. Skanowanie pamięci głównej przebiega szybko, niż skanowanie pamięci wirtualnej, a zmiana obszarów pamięci tylko na skanowanie zmniejsza nakład pracy. Kiedy wiele obiektów jest przydzielanych i szybko zmienia się w śmieci, daje to bardzo krótkie przerwy GC.
pokoleniowy GC. Zazwyczaj GC obecnie są pokoleniowe. Istnieje więcej niż jedno pokolenie, a obiekty, które przetrwają kilka GC, są promowane do starszego pokolenia. Zwykle tylko pierwsza generacja GCed bardzo często.
Tuning. GC, powiedzmy, LispWorks i Allegro CL mają wiele gałek dostrajania. Zwłaszcza w przypadku długotrwałych aplikacji warto przeczytać instrukcję i na przykład dostroić liczbę pokoleń, ich rozmiary i inne rzeczy.
pamięć wirtualna. GC nad pamięcią wirtualną jest zwykle bardzo powolne.Unikaj tego, jeśli to możliwe - dodaj więcej pamięci RAM do komputerów.
ręczne zarządzanie pamięcią. Na przykład serwer WWW CL-HTTP wykonuje pewne ręczne zarządzanie pamięcią przy użyciu zasobów. Są to pule wstępnie przydzielonych obiektów, które można bardzo szybko zainicjować. Maszyny Lisp używały tego bardzo. Typowe ich użycie to odczyty buforów dla strumieni. Zamiast tworzyć nowe łańcuchy przy każdej operacji odczytu, przydatne jest użycie buforów wielokrotnego użytku.
alokacja stosu. Niektóre Common Lisp umożliwiają alokację w stos niektórych danych - opuszczenie bloku powoduje automatyczne zwolnienie pamięci. Zakłada to, że pamięć nie jest już przywoływana po opuszczeniu bloku.
równoczesne GC. Żadna ze zwykłych implementacji Common Lisp nie ma współbieżnego GC ORAZ wsparcia dla współbieżnych wątków Lisp. Niektóre implementacje mają współbieżne wątki Lisp, ale GC zatrzyma je wszystkie podczas pracy.
profilowanie GC. Jeśli nie masz pewności, gdzie odbywa się alokacja i co robi GC, musisz to znaleźć, korzystając z informacji profilujących.
Jeśli Twój Lisp ma precyzyjną pokoleniową GC, która działa w pamięci głównej, trudno jest mieć problemy z długimi przerwami. Clozure CL (darmowa implementacja Common Lisp) ma na przykład bardzo dobrą implementację GC. Chcesz uniknąć fragmentacji pamięci i zbierania śmieci w pamięci wirtualnej. Jeśli to konieczne, użyj 64-bitowej implementacji Lisp z większą pamięcią główną.
Wskaźniki:
można zobaczyć z dokumentacją LispWorks i Allegro CL ma wiele gałek do tuningu GC.
Common Lisp ma kilka funkcji związanych ze środowiskiem implementacji. (POKÓJ) to funkcja dająca przegląd zużycia pamięci. (POKÓJ t) podaje więcej szczegółów (tutaj LispWorks):
CL-USER 2 > (room t)
Generation 0: Total Size 1823K, Allocated 1090K, Free 725K
Segment 2008AAB8: Total Size 507K, Allocated 361K, Free 142K
minimum free space 64K,
Awaiting promotion = 0K, sweeps before promotion =10
Segment 217E7050: Total Size 1315K, Allocated 729K, Free 582K
minimum free space 0K,
Awaiting promotion = 0K, sweeps before promotion =2
Generation 1: Total Size 1397K, Allocated 513K, Free 871K
Segment 20CB9A50: Total Size 68K, Allocated 48K, Free 15K
minimum free space 0K,
Awaiting promotion = 0K, sweeps before promotion =4
Segment 216D7050: Total Size 1088K, Allocated 331K, Free 752K
minimum free space 0K,
Awaiting promotion = 0K, sweeps before promotion =4
Segment 2004E4F8: Total Size 241K, Allocated 133K, Free 103K
minimum free space 0K, static
Generation 2: Total Size 2884K, Allocated 1290K, Free 1585K
Segment 21417050: Total Size 2816K, Allocated 1227K, Free 1584K
minimum free space 0K,
Awaiting promotion = 0K, sweeps before promotion =4
Segment 20DA5DA0: Total Size 68K, Allocated 62K, Free 1K
minimum free space 117K,
Awaiting promotion = 0K, sweeps before promotion =4
Generation 3: Total Size 19373K, Allocated 19232K, Free 128K
Segment 20109A50: Total Size 11968K, Allocated 11963K, Free 0K
minimum free space 3K,
Awaiting promotion = 0K, sweeps before promotion =10
Segment 20DB6E18: Total Size 6528K, Allocated 6396K, Free 128K
minimum free space 0K,
Awaiting promotion = 0K, sweeps before promotion =10
Segment 20CCAAC8: Total Size 876K, Allocated 872K, Free 0K
minimum free space 0K,
Awaiting promotion = 0K, sweeps before promotion =10
Total Size 25792K, Allocated 22127K, Free 3310K
Którą implementację seplenia używasz? – Zifre
Kiedy mówisz "zakładam", brzmi to tak, jakbyś próbował robić optymalizacje bez konieczności profilowania. Czy widzisz konkretne problemy, które wymagają naprawy w Twoim środowisku? – Ken
Zaczynam proces informowania się o problemach, które jeszcze się nie pojawiły. Biorąc pod uwagę liczbę języków GC używanych w usługach internetowych, zakładam, że jest to w dużym stopniu rozwiązany problem. –