Pula obiektów (wzorzec projektowy)

Z Wikipedii, wolnej encyklopedii

Wzorzec puli obiektów - wzorzec projektowy, który polega na użyciu puli obiektów. Pula obiektów to zbiór zainicjowanych obiektów, które są trzymane w gotowości do użycia (zamiast je alokować lub dealokować na żądanie). Klient puli obiektów żąda obiektu z tej puli i wykonuje na tym obiekcie jakieś operacje. Po skończeniu, zamiast niszczyć obiekt - zwraca do puli. Jest to szczególny typ obiektu fabrykującego.

Użycie puli obiektów może przyczynić się do znacznego wzrostu wydajności wtedy, gdy:

  • koszt inicjalizacji instancji klasy jest wysoki,
  • częstotliwość tworzenia kolejnych obiektów klasy jest wysoka,
  • liczba instancji klas będących w użyciu jest mała.

Obiekt z puli jest dostarczany w przewidywalnym czasie, podczas gdy rozrzut w czasie tworzenia nowego obiektu (szczególnie przez sieć) może być duży.

Jednakże te korzyści są w większości odczuwalne dla takich obiektów jak:

  • połączenia bazodanowe,
  • połączenia gniazdowe,
  • wątki,
  • duże obiekty graficzne (takie jak czcionki i bitmapy).

Użycie puli dla prostych obiektów (które nie wskazują na zewnętrzne zasoby lecz tylko zajmują pamięć), nie jest tak samo efektywne i może nawet obniżyć wydajność [1].

Obsługa pustych pul[edytuj | edytuj kod]

Pule obiektów wykorzystują jedną z trzech strategii postępowania w przypadku, gdy nie ma już wolnych obiektów:

  1. Zasygnalizuj klientowi błąd w dostarczaniu obiektu,
  2. Zaalokuj nowy obiekt i przez to powiększ pulę. Pule, które wykorzystują tę strategię, zazwyczaj pozwalają użytkownikowi ustawić znacznik wysokiej wody (ang. high water mark), czyli maksymalną liczbę obiektów kiedykolwiek użytych,
  3. W środowisku wielowątkowym, pula może zablokować wątek klienta do momentu, kiedy jakiś inny wątek zwróci obiekt do puli.

Pułapki[edytuj | edytuj kod]

Kiedy programista programuje pulę obiektów, musi zwrócić uwagę na to, aby zawsze stan obiektów zwróconych do puli był resetowany do jakiegoś rozsądnego stanu dla następnego ich użycia. Kiedy tak nie jest, wtedy może się zdarzyć, że obiekt będzie w takim stanie, który nie będzie oczekiwany przez klienta. Wtedy klient może zawieść. To pula jest odpowiedzialna za resetowanie obiektów, nie jej klienci. Pule obiektów z niekontrolowanymi stanami są antywzorcami.

Obecność takich nieoczekiwanych stanów nie zawsze jest niebezpieczna. Niebezpieczeństwo powstaje wtedy, gdy takie stany sprawiają, że obiekt zachowuje się różnie. Przykładowo, obiekt reprezentujący dane autoryzacyjne może spowodować chaos, gdy zawarty w nim znacznik "poprawna autoryzacja" nie jest resetowany przed ponownym wydaniem tego obiektu z puli. Wtedy bowiem, użytkownik jest wskazywany jako poprawnie autoryzowany nawet, gdy jest to już ktoś inny lub gdy użytkownik w ogóle się nie autoryzował. Jednak nie będzie niebezpieczeństwa, gdy jakiś nieistotny znacznik nie jest resetowany (np. nazwa ostatniego serwera używanego podczas ostatniej autoryzacji).

Nieodpowiednie resetowanie obiektów może spowodować także wyciek wrażliwych informacji. Jeśli obiekt zawiera poufne informacje, np. numery kart kredytowych, wtedy brak właściwego resetowania spowoduje przekazanie takich informacji do nieautoryzowanego (np. złośliwego lub wadliwego) klienta.

Jeśli pula jest używana przez wiele wątków, wtedy mogą być potrzebne pewne środki do zapobieżenia użycia tego samego obiektu przez kilka wątków jednocześnie. Nie jest to konieczne w sytuacji, gdy obiekty są niezmienialne lub w inny sposób wątkowo bezpieczne (ang. thread-safe).

Krytyka[edytuj | edytuj kod]

Część publikacji nie rekomenduje używania puli obiektów, szczególnie dla obiektów, które tylko zajmują pamięć i nie odnoszą się do zewnętrznych zasobów[2]. Krytycy mówią też, że alokacja obiektów w nowoczesnych językach programowania z odśmiecaczem jest stosunkowo szybka. Operator "new" potrzebuje tylko 10 instrukcji, a klasyczne operatory "new" i "delete" używane w implementacjach pul - setki (bo wykonują bardziej skomplikowane operacje). Dodatkowo, większość odśmiecaczy skanuje referencje do "żywych" obiektów, a nie pamięć zajmowaną przez te obiekty. To znaczy, że małym kosztem można się pozbyć każdej ilości "martwych" obiektów bez referencji. Dla porównania, utrzymywanie dużej ilości "żywych", lecz nieużywanych obiektów zwiększa czas odśmiecania [1]. W większości przypadków, programy używające odśmiecania zamiast bezpośredniego zarządzania pamięcią działają szybciej[3].

Przykłady[edytuj | edytuj kod]

W Base Class Library można znaleźć kilka implementacji tego wzorca. System.Threading.ThreadPool jest skonfigurowany tak, aby trzymać w gotowości z góry określoną ilość wątków. Kiedy wątki są zwracane, są dostępne dla innych zadań. W ten sposób można używać wątków bez ich kosztownego tworzenia i niszczenia.

Java obsługuje pulę wątków przez java.util.concurrent.ExecutorService i inne podobne klasy. Usługa wykonująca ma pewną liczbę "podstawowych" wątków, które nigdy nie są porzucane. Jeśli wszystkie wątki są zajęte, to usługa alokuje dozwoloną liczbę dodatkowych wątków, które później są porzucane w razie nieużywania przez pewien określony czas. Jeśli nie ma więcej dozwolonych wątków, to zadania są kolejkowane. Jeśli z kolei ta kolejka jest zbyt długa, to można skonfigurować wstrzymywanie żądającego wątku.

Przypisy[edytuj | edytuj kod]

  1. a b IBM - United States [online], www-128.ibm.com [dostęp 2017-11-26] (ang.).
  2. Java theory and practice: Garbage collection in the HotSpot JVM [online], www.ibm.com [dostęp 2017-11-26] (ang.).
  3. The Measured Cost of Conservative Garbage Collection. [dostęp 2008-12-25]. [zarchiwizowane z tego adresu (2009-05-30)].

Linki zewnętrzne[edytuj | edytuj kod]