Mam następujący kod:
MemoryStream foo(){
MemoryStream ms = new MemoryStream();
// write stuff to ms
return ms;
}
void bar(){
MemoryStream ms2 = foo();
// do stuff with ms2
return;
}
Czy jest jakaś szansa, że przydzielony przeze mnie MemoryStream jakoś nie zostanie później usunięty?
Otrzymałem wzajemną recenzję, w której nalegałem, abym ręcznie to zamknął i nie mogę znaleźć informacji, aby stwierdzić, czy ma on ważny punkt, czy nie.
c#
.net
memory-leaks
memorystream
Coderer
źródło
źródło
Odpowiedzi:
Jeśli coś jest jednorazowego użytku, zawsze należy to wyrzucić. Powinieneś używać
using
instrukcji w swojejbar()
metodzie, aby upewnić się, żems2
zostanie usunięty.W końcu zostanie wyczyszczony przez zbieracz śmieci, ale zawsze dobrze jest wywoływać funkcję Dispose. Jeśli uruchomisz FxCop na swoim kodzie, oznaczałoby to jako ostrzeżenie.
źródło
Nic nie wyciekniesz - przynajmniej w obecnej implementacji.
Wywołanie Dispose nie czyści pamięci używanej przez MemoryStream szybciej. Spowoduje to, że Twój strumień nie będzie zdolny do wykonywania połączeń odczytu / zapisu po połączeniu, co może być dla Ciebie przydatne lub nie.
Jeśli jesteś absolutnie pewien, że nigdy nie chcesz przechodzić z MemoryStream do innego rodzaju strumienia, nie wyrządzisz ci żadnej szkody, jeśli nie wywołasz metody Dispose. Jednakże, jest to generalnie dobra praktyka częściowo dlatego, jeśli kiedykolwiek zrobić zmianę do używania innego Stream, nie chcesz, aby ugryziony przez błąd trudny do zdobycia, ponieważ wybrał łatwe wyjście na początku. (Z drugiej strony istnieje argument YAGNI ...)
Innym powodem, dla którego warto to zrobić, jest fakt, że nowa implementacja może wprowadzić zasoby, które zostaną uwolnione w trybie Dispose.
źródło
IDisposable
jest szczególnym przypadkiem sprzecznym z normalną najlepszą praktyką, możesz argumentować, że tego przypadku nie powinieneś robić, dopóki naprawdę nie musisz, zgodnie z YAGNI zasada.Tak tam przeciek , w zależności od sposobu zdefiniowania przeciekać i jak dużo później masz na myśli ...
Jeśli przez przeciek masz na myśli "pamięć pozostaje przydzielona, niedostępna do użycia, nawet jeśli skończyłeś z niej korzystać", a przez to w dowolnym momencie po wywołaniu dispose, to tak, może być przeciek, chociaż nie jest trwały (np. czas działania aplikacji).
Aby zwolnić pamięć zarządzaną używaną przez MemoryStream, należy usunąć odwołanie do niej, anulując odwołanie do niej, aby od razu kwalifikowała się do wyrzucania elementów bezużytecznych. Jeśli tego nie zrobisz, utworzysz tymczasowy wyciek od czasu, gdy skończysz go używać, aż twoje odniesienie wyjdzie poza zakres, ponieważ w międzyczasie pamięć nie będzie dostępna do alokacji.
Zaletą instrukcji using (w porównaniu z prostym wywołaniem metody dispose) jest to, że można ZADEKLAROWAĆ swoje odwołanie w instrukcji using. Po zakończeniu instrukcji using nie tylko jest wywoływana dispose, ale również twoje odniesienie wychodzi poza zakres, skutecznie unieważniając odniesienie i czyniąc obiekt natychmiast kwalifikującym się do czyszczenia pamięci bez konieczności pamiętania o napisaniu kodu „reference = null”.
Chociaż brak odwołania do czegoś od razu nie jest klasycznym „trwałym” wyciekiem pamięci, to z pewnością ma ten sam efekt. Na przykład, jeśli zachowasz odniesienie do MemoryStream (nawet po wywołaniu dispose) i nieco dalej w swojej metodzie spróbujesz przydzielić więcej pamięci ... pamięć używana przez twój wciąż przywoływany strumień pamięci nie będzie dostępna do Ciebie, dopóki nie unieważnisz odwołania lub nie wyjdzie ono poza zakres, nawet jeśli wywołałeś dispose i skończyłeś z nim korzystać.
źródło
Dzwonienie
.Dispose()
(lub pakowanieUsing
) nie jest wymagane.Powodem, dla którego dzwonisz,
.Dispose()
jest jak najszybsze zwolnienie zasobu .Pomyśl o, powiedzmy, serwerze Stack Overflow, gdzie mamy ograniczony zestaw pamięci i tysiące przychodzących żądań. Nie chcemy czekać na zaplanowane czyszczenie pamięci, chcemy zwolnić tę pamięć jak najszybciej, aby była dostępna dla nowych żądań przychodzących.
źródło
FileStream
obiektów, a innego doMemoryStream
obiektów?Na to już odpowiedź, ale dodam tylko, że dobra, staroświecka zasada ukrywania informacji oznacza, że w przyszłości możesz chcieć dokonać refaktoryzacji:
do:
Podkreśla to, że wywołujący nie powinni przejmować się tym, jaki rodzaj strumienia jest zwracany, i umożliwia zmianę implementacji wewnętrznej (np. Podczas mockowania do testów jednostkowych).
Będziesz musiał mieć kłopoty, jeśli nie użyłeś Dispose w swojej implementacji paska:
źródło
Wszystkie strumienie implementują IDisposable. Owiń strumień pamięci w instrukcję using, a wszystko będzie dobrze i elegancko. Blok using zapewni, że Twój strumień zostanie zamknięty i usunięty bez względu na wszystko.
gdziekolwiek zadzwonisz do Foo, możesz to zrobić za pomocą (MemoryStream ms = foo ()) i myślę, że nadal powinno być dobrze.
źródło
Nie wycieknie pamięć, ale recenzent kodu ma rację, wskazując, że należy zamknąć strumień. Grzecznie to zrobić.
Jedyną sytuacją, w której możesz wyciekać pamięć, jest przypadkowe pozostawienie odniesienia do strumienia i nigdy go nie zamykasz. Nadal nie są naprawdę wyciek pamięci, ale są niepotrzebnie rozszerzając ilość czasu, który twierdzi, że go używał.
źródło
Polecam owijania w MemoryStream
bar()
wusing
oświadczeniu głównie dla spójności:.Dispose()
, ale możliwe jest, że w pewnym momencie w przyszłości może ona lub Ty (lub ktoś inny w Twojej firmie) zastąpi ją Twoim własnym, niestandardowym MemoryStream, który to robi, itp.Inną rzeczą, którą zwykle robię w przypadkach, takich jak
foo()
tworzenie i zwracanie IDisposable, jest zapewnienie, że jakakolwiek awaria między skonstruowaniem obiektu a obiektemreturn
zostanie przechwycona przez wyjątek, pozbywa się obiektu i ponownie zgłasza wyjątek:źródło
Jeśli obiekt implementuje IDisposable, po zakończeniu należy wywołać metodę .Dispose.
W niektórych obiektach Dispose oznacza to samo co Close i odwrotnie, w tym przypadku jedno i drugie jest dobre.
Teraz, jeśli chodzi o twoje konkretne pytanie, nie, nie będziesz wyciekać pamięci.
źródło
Nie jestem ekspertem .net, ale być może problemem są tutaj zasoby, a mianowicie uchwyt pliku, a nie pamięć. Wydaje mi się, że odśmiecacz w końcu zwolni strumień i zamknie uchwyt, ale myślę, że zawsze najlepiej byłoby zamknąć go jawnie, aby upewnić się, że zawartość zostanie wyrzucona na dysk.
źródło
Usuwanie niezarządzanych zasobów jest niedeterministyczne w językach ze śmieciami. Nawet jeśli wywołasz Dispose jawnie, nie masz absolutnie żadnej kontroli nad tym, kiedy pamięć zapasowa zostanie faktycznie zwolniona. Dispose jest wywoływana niejawnie, gdy obiekt wychodzi poza zakres, niezależnie od tego, czy jest to wyjście z instrukcji using, czy też wywołanie stosu wywołań z metody podrzędnej. Biorąc to wszystko pod uwagę, czasami obiekt może być opakowaniem dla zarządzanego zasobu (np. Pliku). Dlatego dobrą praktyką jest jawne zamykanie instrukcji końcowych lub używanie instrukcji using. Twoje zdrowie
źródło
MemorySteram to nic innego jak tablica bajtów, czyli obiekt zarządzany. Zapomnij o pozbyciu się lub zamknięciu, nie ma to żadnego efektu ubocznego poza nadmiernym zakończeniem finalizacji.
Po prostu sprawdź konstruktor lub metodę przepłukiwania MemoryStream w reflektorze i będzie jasne, dlaczego nie musisz martwić się o zamknięcie lub wyrzucenie go inaczej niż tylko po to, aby postępować zgodnie z dobrą praktyką.
źródło