Narastające użycie pamięci w C#

Narastające użycie pamięci w C#
0

Witam,
Piszę pewną aplikację w C#.NET i zauważam, że zużycie pamięci wraz z działaniem programu wciąż rośnie.

Wiem, że w C# jest Garbage Collector i on powinien sprzątać nieużywane obiekty.

Kolega mi powiedział, że muszę gdzieś wciąż tworzyć nowe obiekty, a pozostają mi referencje do starych obiektów i GC ich nie usuwa.

Tylko jak to zlokalizować i poprawić?

Pozdrawiam.

fourfour
  • Rejestracja:prawie 11 lat
  • Ostatnio:prawie 9 lat
  • Postów:627
n0name_l
  • Rejestracja:ponad 12 lat
  • Ostatnio:ponad 4 lata
  • Postów:2412
0

http://memprofiler.com/ [Nie testowane]

Ale jak juz to szukaj mniej-wiecej tymi samymi keywordami :)

0

No poszukam jakiegoś profilera, bo ręcznie tego nie znajdę...

0

Znalazłem w moim projekcie kod podobny do tego:

Kopiuj
public static class MyClass
{
    public static List<Something> listOfSomethings = new List<Something>();
    public static Something st;
}

//...

public static void CreateSomething()
{
    var something = new Something();
    MyClass.listOfSomethings.Add(something);

    MyClass.st = new Something();
}

Byłem zmuszony zrobić to statycznie i nie wiem czy takie rozwijązanie nie powoduje jakichś wycieków pamięci.

ŁF
Moderator
  • Rejestracja:ponad 22 lata
  • Ostatnio:dzień
2

Żaden obiekt, dodany do czegoś takiego, nie będzie usuwany z pamięci. Żaden obiekt, zawarty w obiekcie dodanym do takiej listy, nie będzie usuwany z pamięci, i tak dalej, i tak dalej.

PS. Chyba, że implementuje IDisposable, a .Dispose() zostanie wywołane "ręcznie" - jest wtedy szansa, że część zasobów takiego obiektu zostanie zwolniona. Każdy obiekt trzeba z takiej listy usunąć albo ustawić referencję do takiego obiektu na null.


edytowany 1x, ostatnio: ŁF
0
ŁF napisał(a):

Żaden obiekt, dodany do czegoś takiego, nie będzie usuwany z pamięci. Żaden obiekt, zawarty w obiekcie dodanym do takiej listy, nie będzie usuwany z pamięci, i tak dalej, i tak dalej.

Dzięki za ten post, pewnie tu jest ten wyciek. Mógłbyś odpowiedzieć mi na pytania w komentarzach do kodu poniżej?

Kopiuj
public static class MyClass
{
    public static List<Something> listOfSomethings = new List<Something>();
    public static Something st;
}
 
//...
 
public static void CreateSomething()
{
    var something = new Something();

    MyClass.listOfSomethings.Add(something);

    //czy po utworzeniu nowej listy jak poniżej,
    //obiekty poprzedniej listy zostaną usunięte?
    MyClass.listOfSomethings = new List<Something>();
 
    MyClass.st = new Something();

    //czy poprzednie Something zostanie usunięte po przypisaniu nowego?
    MyClass.st = new Something();
}

Jeśli te obiekty o które pytam powyżej nie zostają usuwane to jak je usunąć? Nie używać static?

somekind
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:4 dni
  • Lokalizacja:Wrocław
1

A czemu w ogóle używasz static?

0
somekind napisał(a):

A czemu w ogóle używasz static?

Żeby mieć dostęp do klasy czy składowej z całego programu.

0

Odpowie ktoś na te pytania w komentarzach do kodu? @ŁF jesteś?

ŁF
Nie podbijaj wątku.
fourfour
  • Rejestracja:prawie 11 lat
  • Ostatnio:prawie 9 lat
  • Postów:627
0
zaraz_sie_zaloguje napisał(a):

Odpowie ktoś na te pytania w komentarzach do kodu? @ŁF jesteś?

ŁF napisał(a):

Żaden obiekt, dodany do czegoś takiego, nie będzie usuwany z pamięci. Żaden obiekt, zawarty w obiekcie dodanym do takiej listy, nie będzie usuwany z pamięci, i tak dalej, i tak dalej.

PS. Chyba, że implementuje IDisposable, a .Dispose() zostanie wywołane "ręcznie" - jest wtedy szansa, że część zasobów takiego obiektu zostanie zwolniona. Każdy obiekt trzeba z takiej listy usunąć albo ustawić referencję do takiego obiektu na null.

0
fourfour napisał(a):
ŁF napisał(a):

Żaden obiekt, dodany do czegoś takiego

Jakiego "czegoś takiego"? Składowej, listy, klasy?

somekind
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:4 dni
  • Lokalizacja:Wrocław
0
zaraz_sie_zaloguje napisał(a):

Żeby mieć dostęp do klasy czy składowej z całego programu.

Ale po co? Klasa powinna być dostępna tylko tam, gdzie jest potrzebna, a nie wszędzie.

0
somekind napisał(a):
zaraz_sie_zaloguje napisał(a):

Żeby mieć dostęp do klasy czy składowej z całego programu.

Ale po co? Klasa powinna być dostępna tylko tam, gdzie jest potrzebna, a nie wszędzie.

Klasa przechowuje rzeczy ogólne, główne, wspólne dla całego programu. Jedna strona zapisuje coś w tej klasie, inne odczytują itp.

ŁF
Moderator
  • Rejestracja:ponad 22 lata
  • Ostatnio:dzień
0
zaraz_sie_zaloguje napisał(a):

Jakiego "czegoś takiego"? Składowej, listy, klasy?

Obiektu statycznego GC nie może sam usunąć (może to zrobić dopiero po usunięciu obiektu z poziom kodu, czyli coś w stylu Sth StatycznyObiekt = null czy = new Sth()). Siłą rzeczy nie może usunąć obiektów, do których obiekt statyczny trzyma referencje, ani obiektów, do których referencje są trzymane w obiektach, do których obiekt statyczny trzyma referencje - itp, itd.


0
ŁF napisał(a):
zaraz_sie_zaloguje napisał(a):

Jakiego "czegoś takiego"? Składowej, listy, klasy?

Obiektu statycznego GC nie może sam usunąć (może to zrobić dopiero po usunięciu obiektu z poziom kodu, czyli coś w stylu Sth StatycznyObiekt = null czy = new Sth()). Siłą rzeczy nie może usunąć obiektów, do których obiekt statyczny trzyma referencje, ani obiektów, do których referencje są trzymane w obiektach, do których obiekt statyczny trzyma referencje - itp, itd.

No dobra, czyli obiekt statyczny sobie istnieje i tyle. A mi zużycie pamięci rośnie. Tak jakby coś nie było zwalniane.

somekind
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:4 dni
  • Lokalizacja:Wrocław
0
zaraz_sie_zaloguje napisał(a):

Klasa przechowuje rzeczy ogólne, główne, wspólne dla całego programu. Jedna strona zapisuje coś w tej klasie, inne odczytują itp.

Co oznacza, że powinieneś przeprojektować swój program. Globalna statyczna klasa to nie jest sposób na przekazywanie danych między innymi klasami. Zapoznaj się ze słowem kluczowym return.

zaraz_sie_zaloguje napisał(a):

No dobra, czyli obiekt statyczny sobie istnieje i tyle. A mi zużycie pamięci rośnie. Tak jakby coś nie było zwalniane.

No nie jest zwalniane, bo jest statyczne, @ŁF to chyba cztery razy powtórzył...

edytowany 1x, ostatnio: somekind
0
somekind napisał(a):
zaraz_sie_zaloguje napisał(a):

No dobra, czyli obiekt statyczny sobie istnieje i tyle. A mi zużycie pamięci rośnie. Tak jakby coś nie było zwalniane.

No nie jest zwalniane, bo jest statyczne, @ŁF to chyba cztery razy powtórzył...

Wiem, że nie jest zwalniane, bo jest statyczne, ale dlaczego zużycie pamięci ciągle rośnie.

Ta składowa mimo, że jest statyczna:

Kopiuj
static public Something sth;

zawsze będzie miała rozmiar Something, więc zużycie pamięci nie powinno rosnąć.

Czy chcecie powiedzieć, że to co przypisuję do statycznej składowej nie jest usuwane?
Np.

Kopiuj
static public Something sth;
sth = new Something();
sth = new Something();
sth = new Something();

Czy powyższy kod powoduje, że w pamięci powstają trzy obiekty something? Czy dwa są usuwane z zostaje ostatni (jak jedno Something nadpisuje drugie to do tamtego nie ma już referencji).

Coś nie mogę Was zrozumieć...

n0name_l
  • Rejestracja:ponad 12 lat
  • Ostatnio:ponad 4 lata
  • Postów:2412
0

Wszystko bedzie z czasem usuniete do czego nie masz referencji.
Jesli robisz:

Kopiuj
static Something sth;
sth = new Something()
sth = new Something() // = null

To ten pierwszy Something sobie kiedys zniknie.

Ale.. nikt nie wie czy to w tym miejscu masz problem z pamiecia. Po prostu uzyj profilera zamiast na slepo czas marnowac.

somekind
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:4 dni
  • Lokalizacja:Wrocław
0
zaraz_sie_zaloguje napisał(a):

Ta składowa mimo, że jest statyczna:

Kopiuj
static public Something sth;

zawsze będzie miała rozmiar Something, więc zużycie pamięci nie powinno rosnąć.

Jeśli rozbudowujesz graf obiektów, którego korzeniem jest zmienna sth, to zużycie pamięci wzrośnie, a to prawdopodobnie właśnie robisz.

ŁF
Moderator
  • Rejestracja:ponad 22 lata
  • Ostatnio:dzień
0
zaraz_sie_zaloguje napisał(a):
somekind napisał(a):
zaraz_sie_zaloguje napisał(a):

No dobra, czyli obiekt statyczny sobie istnieje i tyle. A mi zużycie pamięci rośnie. Tak jakby coś nie było zwalniane.

No nie jest zwalniane, bo jest statyczne, @ŁF to chyba cztery razy powtórzył...

Wiem, że nie jest zwalniane, bo jest statyczne, ale dlaczego zużycie pamięci ciągle rośnie.

Masz problemy z czytaniem ze zrozumieniem. Nie jest zwalniane, bo jest statyczne i - prawdopodobnie - dlatego zużycie pamięci rośnie.

zaraz_sie_zaloguje napisał(a):

Ta składowa mimo, że jest statyczna:

Kopiuj
static public Something sth;

zawsze będzie miała rozmiar Something, więc zużycie pamięci nie powinno rosnąć.

Zauważ, że masz DWIE zmienne statyczne, pierwsza jest listą i o ile jej rozmiar nie będzie znacznie rosnąć, bo tylko trzyma referencje, o tyle przyczepione do niej obiekty (i całe drzewko siedzących w tych obiektach dalszych obiektów) będą siedzieć w pamięci tak długo, aż z tej listy nie zostaną usunięte.

zaraz_sie_zaloguje napisał(a):

Czy chcecie powiedzieć, że to co przypisuję do statycznej składowej nie jest usuwane?
Np.

Kopiuj
static public Something sth;
sth = new Something();
sth = new Something();
sth = new Something();

Czy powyższy kod powoduje, że w pamięci powstają trzy obiekty something? Czy dwa są usuwane z zostaje ostatni (jak jedno Something nadpisuje drugie to do tamtego nie ma już referencji).

Chcemy powiedzieć, że czytasz bez zrozumienia. Zgaduję, że to przez to, że brakuje Ci podstaw programowania obiektowego. W pamięci powstaną trzy obiekty (chyba, że kompilator to zoptymalizuje, ale nie sądzę), dwa po jakimś czasie zostaną zwolnione, bo referencja do nich ginie, trzeci będzie wisiał. Wiesz, co to jest referencja do obiektu? Rozumiesz, jak działa GC? Rozumiesz różnice pomiędzy zmienną statyczną a niestatyczną?


Azarien
  • Rejestracja:ponad 21 lat
  • Ostatnio:dzień
0

jak jedno Something nadpisuje drugie to do tamtego nie ma już referencji

Obiekt, do którego nie ma referencji może zostać zniszczony i usunięty z pamięci.
Nie od razu i nie bardzo wiadomo kiedy — ale najpóźniej w momencie wyjścia z programu.

Obiekt, do którego referencja jest trzymana gdzieś w statycznej tablicy (i referencja do tej tablicy istnieje) nie zostanie zniszczony póki program działa.

Jeśli zgubiona zostanie każda referencja do tej tablicy (nadpisana nullem, albo referencją na inną tablicę) to tablica może zostać usunięta. Powoduje to utratę referencji na obiekty zawarte w tablicy, i jeśli do danego obiektu była to jedyna czy ostatnia referencja, to taki obiekt może zostać zniszczony. I tak dalej.

Dotnetowy GC jest odporny na problem referencji cyklicznych: czyli jeśli obiekt A wskazuje na B, a B na A, ale zarówno A jak i B zostają zgubione, to oba te obiekty mogą zostać usunięte przez GC, mimo że istnieje referencja na A i istnieje referencja na B.

edytowany 2x, ostatnio: Azarien
0

Dziękuję serdecznie, trochę mi się rozjaśniło :)

Ostatnie pytanie (mam nadzieję).

Załóżmy, że mamy taki kod:

Kopiuj
public class Class1
{
    public List<string> list1;
}

static public class Class2
{
    static public List<Class1> listOfClass1;
}

(...)
Class1.list1 = new List<string>();
Class1.list1.Add("test");
Class2.listOfClass1 = new List<Class1>();
Class2.listOfClass1.Add(new Class1());

Class2.listOfClass1 = null;

Czy w takim kodzie GC może usunąć z pamięci listę list1, obiekty klasy Class1 i listę listOfClass1?

fourfour
  • Rejestracja:prawie 11 lat
  • Ostatnio:prawie 9 lat
  • Postów:627
0

A jak myślisz, i dlaczego tak myślisz?

0
fourfour napisał(a):

A jak myślisz, i dlaczego tak myślisz?

Mam dwie wersje.

Lista list1, obiekty klasy Class1 i lista listOfClass1 są usuwane, bo przypisanie null do listOfClass1 spowoduje, że nie ma referencji do listy.

Nie wszystko jest usuwane, bo jest przypisanie null do listOfClass1, ale obiekt Class1 zawiera listę list1, która ma referencje do stringa.

Więc nie wiem, czy przypisanie nulla lub nowej referencji sprawia, że wszystko "zagnieżdżone" ginie, czy tak jak w C++ trzeba stopniowo wszystko "likwidować - od potomków do rodzica".

fourfour
  • Rejestracja:prawie 11 lat
  • Ostatnio:prawie 9 lat
  • Postów:627
0
zaraz_sie_zaloguje napisał(a):
fourfour napisał(a):

A jak myślisz, i dlaczego tak myślisz?

Więc nie wiem, czy przypisanie nulla lub nowej referencji sprawia, że wszystko "zagnieżdżone" ginie, czy tak jak w C++ trzeba stopniowo wszystko "likwidować - od potomków do rodzica".

Odpowiedzi:

ŁF napisał(a):

Obiektu statycznego GC nie może sam usunąć (może to zrobić dopiero po usunięciu obiektu z poziom kodu, czyli coś w stylu Sth StatycznyObiekt = null czy = new Sth()). Siłą rzeczy nie może usunąć obiektów, do których obiekt statyczny trzyma referencje, ani obiektów, do których referencje są trzymane w obiektach, do których obiekt statyczny trzyma referencje - itp, itd.

somekind napisał(a):

Jeśli rozbudowujesz graf obiektów, którego korzeniem jest zmienna sth, to zużycie pamięci wzrośnie, a to prawdopodobnie właśnie robisz.

Azarien napisał(a):

Obiekt, do którego nie ma referencji może zostać zniszczony i usunięty z pamięci.
Nie od razu i nie bardzo wiadomo kiedy — ale najpóźniej w momencie wyjścia z programu.

Obiekt, do którego referencja jest trzymana gdzieś w statycznej tablicy (i referencja do tej tablicy istnieje) nie zostanie zniszczony póki program działa.

Jeśli zgubiona zostanie każda referencja do tej tablicy (nadpisana nullem, albo referencją na inną tablicę) to tablica może zostać usunięta. Powoduje to utratę referencji na obiekty zawarte w tablicy, i jeśli do danego obiektu była to jedyna czy ostatnia referencja, to taki obiekt może zostać zniszczony. I tak dalej.

edytowany 1x, ostatnio: fourfour
0

@fourfour - dziękuję za wklejanie kolejny raz tego samego.

Ktoś nie rozumie to się próbuje wytłumaczyć jakoś innymi słowami, obrazowo.

Jak dziecko zapyta co to jest krowa, a Ty powiesz: "Takie zwierze."

A dziecko dalej: "Ale no co to jest".

A Ty mu będziesz powtarzał: "Takie zwierze. Takie zwierze."

Więcej razy powtórzysz to myślisz, że zrozumie? Co najwyżej zapamieta ;)

somekind
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:4 dni
  • Lokalizacja:Wrocław
0
zaraz_sie_zaloguje napisał(a):

Czy w takim kodzie GC może usunąć z pamięci listę list1, obiekty klasy Class1 i listę listOfClass1?

Ponieważ przypisałeś null do tej zmiennej, to graf obiektów który wskazywała przestaje być gdziekolwiek wykorzystywany, więc obszar pamięci, na który wskazywała wcześniej może zostać zwolniony.

zaraz_sie_zaloguje napisał(a):

Więcej razy powtórzysz to myślisz, że zrozumie? Co najwyżej zapamieta ;)

Trzy razy już dostałeś odpowiedź, że krowa ma łaty, rogi, ogon i cycki.

Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)