Wywoływanie destruktora na istniejącym obiekcie?

0

Napisałem coś w rodzaju web crawlera (nie do żadnych niecnych czynów). Ma on przez pewien czas trzymać w pamięci pewne informacje i w zależności od tego co znajdzie później musi zostawić część poprzednich informacji i wyrzucić resztę które się przedawniły.
Podczas prób programu szybko się okazało że potrzebuję trzymać w pamięci jednocześnie ponad 8 GB danych które się stale wymieniają.
W związku z tym używanie zwykłej pamięci stało się dla komputera i dla mnie zbyt uciążliwe więc postanowiłem zrzucać treść informacji na dysk a w obiekcie trzymać tylko nazwę pliku.

Obiekt trzyma nazwę pliku, w destruktorze (~Obiekt) napisałem funkcję która usuwa plik związany z obiektem.

W uproszczeniu klasa wygląda tak:

public Obiekt(MemoryStream m)
{
  this.fileName = LosowaNazwa() + (StatycznyLicznik ++);
  SaveToDisc(m, this.fileName);  
}

~Obiekt()
{
  if (File.Exists(this.fileName))
    File.Delete(this.FileName);
}

obiekty przechowuję w kolejce, a gdy chce usunąć pliki to po prostu usuwam obiekt z kolejki
Podczas działania programu okazało się że pliki są usuwane zanim je usunę z kolejki w losowym momencie - sprawdzałem pod debugerem i nie ma nigdzie usuwania tych obiektów z kolejki - pliki natomiast są usuwane

Dałem breakpoint na destruktor i okazuje się że ten jest wywoływany - nie mogę się dowiedzieć dlaczego bo stack nic nie wykazuje poza wątkiem garbage collectora
Czy jest możliwe że program przy przekazywaniu obiektu do różnych funkcji tworzy kopię obiektu, zamiast działać na oryginale i próbując pozbyć się tej kopii wywołuje destruktor który wskazuje na ten sam plik?

Jestem pewien że nie ma kolizji w nazwie pliku i że nie tworzę jednego obiektu dwa razy a także że nie usuwam ich nigdzie (cały czas istnieje twarda referencja w kolejce)

Próbowałem zmienić destruktor na IDisposable i to rozwiązało problem - pliki nie znikają kiedy nie powinny; ale tu z kolei zostają zbyt długo - metoda Dispose wydaje się nie być wywoływana za każdym razem, lub jej wywołanie jest bardzo opóźnione - nie jest również wykonywana przed wyłączeniem na siłę programu w związku z czym na dysku twardym zalegają niepotrzebne pliki

0

W C# nie istnieje pojęcie destruktora. To co wygląda jak destruktor w C++, w C# jest finalizerem i jest odpalane przez Garbage Collector (nad czym generalnie nie masz jak zapanować). Jeśli GC zauważy, że obiekt nie jest już nigdzie wykorzystywany, albo zaczyna brakować pamięci, wówczas zaczyna sprzątać, odpalając finalizery w takich obiektach.
Do tego co Ty potrzebujesz, powinieneś wykorzystać IDisposable i metodę Dispose (co de facto zrobiłeś). Jeśli uznasz, że dany obiekt już nie będzie potrzebny (czyli przy usuwaniu z kolejki), wywołaj na nim Dispose. Wówczas plik zostanie usunięty. Po wywołaniu Dispose na obiekcie, pamiętaj o przypisaniu do zmiennej null.

0
michal84 napisał(a):

Jeśli GC zauważy, że obiekt nie jest już nigdzie wykorzystywany, albo zaczyna brakować pamięci, wówczas zaczyna sprzątać, odpalając finalizery w takich obiektach.

Pytanie brzmiało dlaczego finalizer jest odpalany gdy żaden z powyższych warunków nie jest spełniony
W tej chwili używam metody Dispose, ale tak jak pisałem - nie jest ona odpalana choćby wtedy gdy wyłączam program przez menedżer zadań

0

A wywołujesz Dispose z kodu? Czy polegasz na tym, że CLR sam ją wywoła?
Zamykanie aplikacji przez manager zadań jest tak na prawdę zabijaniem procesu, a nie zamykaniem aplikacji. W takiej sytuacji Dispose nie jest niestety wywoływany.

0

dobrze - z tym się już mogę uporać - nie jest to problemem

temat tyczy się bardziej kwestii technicznej i mojej ciekawości na temat tego dlaczego jest odpalany finalizer w momencie w którym nie powinien być

0

Nie mogę sobie sytuacji w której destruktor wywołbby się szybciej niż powinien, chyba że Twój Obiekt to struktura, a nie klasa. Jeśli nie jest strukturą to pokaż więcej kodu.

0

Podczas działania programu okazało się że pliki są usuwane zanim je usunę z kolejki w losowym momencie - sprawdzałem pod debugerem i nie ma nigdzie usuwania tych obiektów z kolejki - pliki natomiast są usuwane

Pics or it didn't happen.

Czym jest ta twoja kolejka (jaka klasa) i jak zdefiniowana (zmienna lokalna w funkcji, pole klasy..?)

0

Co do destruktorów to nie są one odpalane w momencie zerwania ostatniej referencji działa tutaj mechanizm trochę bardziej "subtelny".
Po zgubieniu ostatniej referencji obiekt sobie żyje aż GC stwierdzi że jednak nie jest potrzebny.
GC zauważa jednak że obiekt ma Destruktor i nie usuwa go z pamięci tylko wrzuca do specjalnej kolejki.
Istnieje sobie pewien wątek który sobie zagląda do tej kolejki i jak coś w niej jest to bierze to i wywołuje destruktor.

W C# istnieje tylko jedna uzasadniona przyczyna do pisania Destruktorów:
Jeżeli tworzysz klasę która implementuje IDisposable i nie masz pewności że ty sam oraz inni programiści korzystający z tej klasy wywołują na niej w odpowiednim momencie Dispose (czy to ręcznie czy to przez using) to w destruktorze piszesz kod który wygląda tak:
if (DisposeNieWywolany)
{
Dispose();
}
Ogólnie stosuj IDisposable i wywołuj go ręcznie (chyba że nie masz pewności że to ostatnie odwołanie do obiektu).

1 użytkowników online, w tym zalogowanych: 0, gości: 1