Żeby GC nie tykał obiektu

0

Witam, temat powiązany ze "wskaźniki po raz nty"

Myślałem, że coś takiego załatwi sprawę(funkcja GetStream):

private MyStream agentStream = null;
private MyStream mainStream;


public void myFunc(parametry)
{
  mainStream.Dispose();
  mainStream = null;

  mainStream = new MyStream(parametry);
}

public MyStream GetStream()
{
  if(agentStream != null)
  {
    agentStream.Dispose();
    agentStream = null;
  }

  agentStream = new MyStream(mainStream);
  return agentStream;
}

Ale okazuje się, że GC w całej swojej łaskawości w pewnym momencie raczy zmienić położenie agentStream w pamięci. Metody te wywołuję w innej klasie w takiej kolejności:

myFunc();
stream = GetStream();
myFunc();

I w pewnym momencie stream traci referencje. Jak zrobić, żeby GC nie tykał w ogóle agentStream?

1

To nie GC tyka Ci strumień. Tylko najpierw go tworzysz, potem referencję do niego przypisujesz do zmiennej stream, a potem ten sam strumień zaraz zwalniasz/zamykasz. Ten kod to jest jakiś WTF.

0
byku_guzio napisał(a)

To nie GC tyka Ci strumień. Tylko najpierw go tworzysz, potem referencję do niego przypisujesz do zmiennej stream, a potem ten sam strumień zaraz zwalniasz/zamykasz. Ten kod to jest jakiś WTF.

Moment moment. Funkcja GetStream tworzy NOWY strumień na podstawie istniejącego. Czyli tworzy NOWY obiekt. I referencje do tego NOWEGO obiektu przekazuję do stream, tak? Obiekt agentStream jest niszczony tylko w tej funkcji i nigdzie indziej. A jego adres się zmienia w czasie.

0

Rzeczywiście nie przyjrzałem się. Pokazałeś za mało kodu. Sam z siebie obiekt nagle nie zniknie, gdzieś coś musisz mieszać.

0
byku_guzio napisał(a)

Rzeczywiście nie przyjrzałem się. Pokazałeś za mało kodu. Sam z siebie obiekt nagle nie zniknie, gdzieś coś musisz mieszać.

Ale agentStream nigdzie nie ginie :) Po prostu zmienia swoje miejsce w pamięci. Funkcja GetStream to jedyne miejsce, gdzie jest cokolwiek robione z obiektem agentStream. Po prostu po mojemu GC przesuwa go sobie w zależności od uznania.

1

Ciekawe, bo ja w twoim kodzie GetStream() widzę, że zamiast zwracać starą referencję, to zerujesz starą i przypisujesz jej nową wartość, więc to ty robisz "przesunięcie".
Naprawdę nie rozumiem po co ponownie tworzysz ten obiekt i równocześnie narzekasz na jakieś przesunięcie.
Pierwsze słyszę, by GC cokolwiek przesuwał, gdyby tak było to nie nazywałby się kolekcjonerem śmieci. Gdyby nawet jakimś cudem GC miał taką funkcjonalność to na pewno wszystkie referencje do danego obiektu zostałby zaktualizowane i to "przesuniecie" byłoby dla ciebie niedostrzegalne.

0

Panowie, jeszcze raz.
Wywołuję myFunc.
Potem wywołuję GetStream, w którym zwracam nowo utworzony obiekt - agentStream. I wszystko działa.
Potem wywołuję znów myFunc. Nie wywołuję już później GetStream, a położenie agentStream się zmienia i przestaje mi działać.

0

Na jakie podstawie to stwierdzasz, że coś ci się przesunęło?
Jedynie co mi przychodzi do głowy co może robić takie cuda, to przywracanie śmieci (nie pamiętam profesjonalnej nazwy). W fazie usuwania obiektu przez GC można przywrócić go do życia, może twój kod robi takie cudo i dlatego widzisz takie dziwne zachowanie.

edit: Jedynu przykład jaki znalazłem na takie coś. (patrz koniec najlepszej odpowiedzi). Teraz wyobraź sobie, że ktoś w finalize() ustawia ponownie referencje, wtedy będziesz miał wrażenie, ze GC coś przesuwa.

1

co robi to MyStream(mainStream);???
BTW ten kod jest jakiś zjebany - sam piszesz, że tworzysz psa, przypinasz do psa smycz i masz psa na smyczy, potem zabijasz i zjadasz psa oraz tworzysz nowego i dziwisz się dlaczego żaden pies nie jest przypięty do smyczy

2

GC przesuwa obiekty w pamięci, to raz.
Wszystkie referencje są poprawiane, to dwa. Nie ma po prostu takiej opcji żeby GC zwolnił obiekt do którego prowadzi jakakolwiek referencja.

Wywołuję myFunc.
Potem wywołuję GetStream, w którym zwracam nowo utworzony obiekt - agentStream. I wszystko działa.
Potem wywołuję znów myFunc. Nie wywołuję już później GetStream, a położenie agentStream się zmienia i przestaje mi działać.

Te wszystkie funkcje są same w sobie poprawne, chociaż w sumie nie mają specjalnego sensu.
Podaj najprostszy przykład pełnego (gotowego do skompilowania) kodu w którym twój błąd występuje. Zapewniam że błąd leży po twojej stronie i nie ma nic wspólnego z GC.

0

Twoje problemy wynikają z ciągłego myślenia w kategoriach „wskaźników”, i wyobrażasz sobie jak to GC ci coś tam sam przesuwa czy niszczy.

Tymczasem w C# obiekty klas tworzy się przez new, a niszczy przez zapomnienie i nie mówienie o nich więcej.

Ponadto, klasy są typami referencyjnymi. Wyobraźmy sobie, że masz dwie zmienne:

MojaKlasa zmienna1 = new MojaKlasa();
MojaKlasa zmienna2 = zmienna1;

w tym momencie obie zmienne oznaczają ten sam obiekt. Każda zmiana w zmienna1 będzie miała również skutek w zmienna2.
Teraz jeśli jednej zmiennej przypiszesz nową wartość:

zmienna1 = new MojaKlasa();

to ta zmienna zaczyna wskazywać na nowy obiekt, a zmienna2 wciąż na stary.
Działa to więc podobnie jak wskaźniki na obiekty w C++.

Rzeczywiście, GC może przesuwać fizycznie obiekty po pamięci. Ale nigdy tego bezpośrednio nie zaobserwujesz — nie ma tak, że nagle ci obiekt ucieknie spod istniejącej zmiennej.

0
Azarien napisał(a)

Twoje problemy wynikają z ciągłego myślenia w kategoriach „wskaźników”, i wyobrażasz sobie jak to GC ci coś tam sam przesuwa czy niszczy.

Tymczasem w C# obiekty klas tworzy się przez new, a niszczy przez zapomnienie i nie mówienie o nich więcej.

Tak? A zrobisz tak np z Bitmap? ;)

Ponadto, klasy są typami referencyjnymi. Wyobraźmy sobie, że masz dwie zmienne:

MojaKlasa zmienna1 = new MojaKlasa();
MojaKlasa zmienna2 = zmienna1;

w tym momencie obie zmienne oznaczają ten sam obiekt. Każda zmiana w zmienna1 będzie miała również skutek w zmienna2.

Teraz jeśli jednej zmiennej przypiszesz nową wartość:

zmienna1 = new MojaKlasa();

to ta zmienna zaczyna wskazywać na nowy obiekt, a zmienna2 wciąż na stary.
Działa to więc podobnie jak wskaźniki na obiekty w C++.

To jest jasne. Ale zauważ, że ja tworzę nowy obiekt na podstawie istniejącego. Co znaczy, że konkretnie w tym wypadku tworzę nowego streama z zawartością tego starego. Na chłopski rozum, więc tworzę w pewnym sensie kopię, zgadza się?

A skąd wiem, że mi przesuwa w pamięci? Bo podglądam w quick watch adres agentStream. I widzę, jak on się potem zmienia.
Kod podeślę jednak pewnie za kilka dni, bo teraz jestem chory i nie idę do roboty.

0
Juhas napisał(a)

To jest jasne. Ale zauważ, że ja tworzę nowy obiekt na podstawie istniejącego. Co znaczy, że konkretnie w tym wypadku tworzę nowego streama z zawartością tego starego. Na chłopski rozum, więc tworzę w pewnym sensie kopię, zgadza się?

Właśnie nie tworzysz kopii. Tworzysz jak sam mówisz nowy obiekt. Nowy obiekt = nowa referencja = nowa pozycja w pamięci. To nie ma znaczenia, że tworzysz nowy obiekt na podstawie innego przekazywanego przez parametr.

Spójrz na kod:

            char[] tmp = Encoding.Default.GetChars(Encoding.Default.GetBytes("abc"));

            string x1 = new string(tmp);
            string x2 = new string(tmp);

            Console.WriteLine(x1 == x2); // zwraca true
            Console.WriteLine((object)x2 == (object)x1); // zwraca false

Tworzę 2 obiekty przekazując ten sam parametr. Jako iż porównuje string'i to c# porównuje ich zawartość - są takie same więc zwracamy true. Natomiast przy rzutowaniu na obiekt i wykorzystaniu operatora równości wykorzystywane jest sprawdzenie referencji. Jako iż obiekty te są 2 różnymi obiektami wynikiem operacji jest zawsze false.

Zatem to nic, że tworzysz tak samo nowy obiekt. Nowy obiekt jest nowy i nie musi być w tym samym "miejscu" co stary.

0

Tak? A zrobisz tak np z Bitmap?
Robiłem tak z Bitmap i nic złego się nie działo. Jeśli chcesz dobry przykład na uzasadnione ręczne zwalnianie — to będą to otwarte pliki, połączenia sieciowe, bazodanowe itp. Czyli nie tyle pamięć, co różnie rozumiane „zasoby”.

0
Azarien napisał(a)

Tak? A zrobisz tak np z Bitmap?
Robiłem tak z Bitmap i nic złego się nie działo. Jeśli chcesz dobry przykład na uzasadnione ręczne zwalnianie — to będą to otwarte pliki, połączenia sieciowe, bazodanowe itp. Czyli nie tyle pamięć, co różnie rozumiane „zasoby”.

Azarien, z tego co czytałem, to wszystkie obiekty związane z grafiką należy zwalniać ręcznie.
PCSA, wiem, że tworzę nowy obiekt i on jest w innym miejscu w pamięci. Ale właśnie zauważ, że zwracam ten nowy obiekt. Mówiąc: "kopia" miałem na myśli to, że ma taką samą zawartość jak ten, na którego podstawie go tworzę. Zgadza się to?

0

Juhas
poczytaj sobie na temat klonowania obiektów. Zwróć szczególną uwagę na klonowanie płytkie (shallow) i głębokie (deep):
http://stackoverflow.com/questions/5359318/how-to-clone-objects
http://msdn.microsoft.com/en-us/library/system.object.memberwiseclone.aspx

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