Narzędzia użytkownika

Narzędzia witryny


wlasne_funkcje_obslugi_braku_pamieci

Własne funkcje obsługi braku pamięci

Autor: Marcin Kaczor

Problem: Podczas korzystania z dynamicznego przydziału pamięci w języku C++ (za pomocą operatora new) jeśli nie uda się zarezerwować bloku pamięci o odpowiednim rozmiarze, to zostanie rzucony wyjątek std::bad_alloc. Sprawdzanie czy po każdym wywołaniu new nie został rzucony wyjątek jest kłopotliwe. Oczywiście można używac wersji operatora new, która nie rzuca wyjątkiem new (std::nothrow) Typ, ale wtedy pomijamy obsługę błędów.

Opis: Biblioteka standardowa udostępnia możliwość zdefiniowania własnej funkcji obsługi braku pamięci za pomocą funkcji z biblioteki standardowej set_new_handler(). Funkcja obsługi braku pamięci powinna uzyskać więcej pamięci, rzucić wyjątek, przerwać program lub chociaż ustawić inna funkcje obsługi (lub odinstalować obecna), bo program będzie się wykonywał w nieskończonej pętli.

Zastosowanie: Głównym zadaniem funkcji obsługi braku pamięci jest uzyskanie większej ilości pamięci (np. przez usuniecie nieużywanych obiektów) tak aby aplikacja mogla działać dalej, czasami jest to niemożliwe, funkcja ta jest tez przydatna gdy nie chcemy przy każdym tworzeniu nowego obiektu obsługiwać wyjątku bad_alloc, w ten sposób, gdy już wystąpi problem braku pamięci możemy to obsłużyć w jednym miejscu i zamknąc aplikacje w mniej brutalny sposób, mamy szanse na pozamykanie połączeń, plików oraz poinformowanie użytkownika o tym co się stało.

Przyklad: Niżej zostanie przedstawiony przykład klasy reprezentującej obiekty zajmujące dużo pamięci, obiekty będą rejestrowane w rejestrze przez który tez jest możliwy do nich dostęp, rejestr będzie usuwać obiekty gdy będzie potrzebna pamięć.

Kod: Dołączenie nagłówków, deklaracje…

#include <new>               // zawiera funkcje set_new_handler
#include <iostream>          // obiekty cout, cerr, etc.
#include <cstdlib>           // exit() - awayjne wyjscie
#include <vector>            // wektor obietktow BigSize
 
//! wielkosc bufora powiekszajacego rozmiar BigSize
#define BS_SIZE 200000000
 
// deklaracja klasy Register potrzebna klasie BigSize
class Register;

Klasa BigSize reprezentuje duże obiekty, które alokujemy dynamicznie.

//! Klasa BigSize
/*!
  Klasa BigSize reprezentuje duze obiekty w pamieci, np. warstwy w zaawansowanym
  programie do obrobki grafiki rastrowej lub zapamietywanie histori zmian w obrazie.
*/
class BigSize
{
  friend class Register;
 
private:
  //! Prywatny konstruktor, tylko obiekty klasy Register maja prawo tworzyc obiekty
  //! BigSize. Dla celow testowych informuje uzytkownika o stworzeniu obiektu
  BigSize()
  {
    std::cout << "Creating BigSize..." << std::endl;
  }
 
  //! Prywatny konstruktor kopiujacy, zabezpiecza przed kopiowaniem obiektow,
  //! dostep do obiektu jest mozliwy tylko przez klase register
  BigSize(const BigSize &) {};
 
  //! Prywatny destruktor, tylko obiekty klasy Register maja prawo niszczyc obiekty
  //! BigSize. Dla celow testowych informuje uzytkownika o usunieciu obiektu
  ~BigSize()
  {
    std::cout << "Deleting BigSize..." << std::endl;
  }
 
private:
  //! Tablica znakow, bufor, w przykladzie tylko sztucznie powieksza rozmiar klasy
  char buffer_[BS_SIZE];
};

Klasa Register zarządzająca obiektami klasy BigSize. W tym przykładzie gdy zostanie o to poproszona usuwa wszystkie obiekty klasy BigSize, ale można ją urozmaicić o licznik odwołań lub czas ostatniego odwołania do poszczególnych obiektów i usuwać ostatnio nieużywane obiekty lub rzadko używane.

//! Klasa Register
/*!
  Klasa Register tworzy, usuwa i przechowyje wskazania na obiekty BigSize,
  zaimplemneotwna jako Singleton, mozna przy implemntacji wykorzystac rowniez
  wzorzec fabryki
*/
class Register
{
private:
  //! Prywatny konstrukotr domyslny i kopiujacy
  Register() : counter_(0) {};
  Register(const Register &r);
 
public:
  //! Metoda pobierajaca referencje do obiektu
  static Register& getInstance()
  {
    static Register instance;
    return instance;
  }
 
  //! Tworzenie nowego obiektu klasy BigSize
  BigSize *getNewBigSize()
  {
    BigSize *bs = new BigSize();
    objects_.push_back(bs);
    counter_++;
    return bs;
  }
 
  //! zwolnienie nieuzywanych obiektow, w tym przykladzie wszystkie obiekty sa nieuzywane
  //! metoda zwaraca true zostala zwolniona jakakolwiek ilosc pamieci, w przeciwnym razie false
  bool deleteUnusedObjects()
  {
    bool retval = counter_ > 0 ? true : false;
 
    while (counter_--) {
      delete objects_.back();
      objects_.pop_back();
    }
    counter_ = 0;
    return retval;
  }
 
  //! Pobranie obiektu klasy BigSize o zadanym indeksie, niewykorzystywane w przykladzie
  BigSize *getBigSize(int index)
  {
    if (index > 0 && index < counter_)
      return objects_[index];
    else
      return NULL;
  }
 
private:
  int counter_; /**< licznik obiektow */
  std::vector<BigSize *> objects_; /**< wektor obiektow */
};

Funkcja obsługi braku pamięci, w tym przykładzie nieużywana, ale pokazuje, że można mieć więcje niż jedną funkcję obsługi braku pamięci i mogą mieć one różne zadania, ta stara się logować powód zamknięcia aplikacji i wychodzi z aplikacji z błędem.

//! Funkcja obslugi braku pamieci, zamyka awaryjnie aplikacje
void KillApplication()
{
  // informacja dla uzytkownika, w zaleznosci od potrzeb moze byc zapisana do logu
  std::cerr << "Pamiec zostala wyczerpana, awaryjne zamykanie aplikacji." << std::endl;
  // awaryjne zamykanie aplikacji, zapisanie stanu, zamkniecie plikow, polaczen, etc.
  exit(1);
}

Funkcja obsługi braku pamięci, zajmuje się zwalnianiem nieużywanej pamięci.

//! Funkcja obslugi braku pamieci, sprzata pamiec
void CleanUpMemory()
{
  // informacja dla uzytkownika, potrzebna raczej tylko dla aplikacji testowej
  std::cout << "Pamiec zostala wyczerpana, usuwanie niepotrzebnych obiektow." << std::endl;
  if (!Register::getInstance().deleteUnusedObjects()) { // nie udalo sie zwolnic pamieci
    std::set_new_handler(KillApplication); // ustawienie nowej funkcji obslugi braku pamieci
    return;
  }
  exit(1); // program moze kontynuowac dzialanie, bo zwolniono czesc nieuzywanej pamieci,
           // ale w tym przykladzie dzialalby w petli nieskonczonej, dlatego zostanie zamkniety
}

Funkcja main, ustawia nową funkcję obsługi braku pamięci i zajmuje pamięć.

int main ()
{
  std::set_new_handler(CleanUpMemory); // ustawienie nowej funkcji obslugi braku pamieci
  for(;;) {
    Register::getInstance().getNewBigSize(); // zajmowanie pamieci :-)
  }
  return 0;
}
wlasne_funkcje_obslugi_braku_pamieci.txt · ostatnio zmienione: 2008/12/03 22:54 przez marcinkaczor