Wyciek pamięci - jak to rozumieć

Wyciek pamięci - jak to rozumieć
KA
  • Rejestracja:prawie 12 lat
  • Ostatnio:10 miesięcy
  • Postów:72
0

Mam pewien projekt i wycieka gdzieś w nim pamięć... ale nie mogę zlokalizować miejsca gdzie.
Na dodatek dzieją się dziwne rzeczy przy pierwszych parunastu iteracjach - o co chodzi, że wtedy zużycie pamięci wzrasta, a potem już to się nie dzieje?
CLion, OSX El Capitan.

Parę słów o programie - na potrzeby testów menu przerobiłem do formy

Kopiuj
void showMenu(void)
{
    Graph_t *graph;
    for(int i = 0; i < 100; ++i)
    {
        graph = graphCreation();
        updateDataBaseFile(graph);
        removeGraph(graph);
    }
}

Następnie szedłem debugerem krok po kroku i co iterację sprawdzałem ilość zajętego ramu w menedżerze. Uzyskałem takie wyniki (co iterację, w KB):

576 628 644 648 656 656 656 ... 10 kolejnych iteracji ... 664 ... 20 kolejnych iteracji ... 664 ... 20 kolejnych ... 664

To co mnie zastanawia, to fakt, czemu przez pierwsze ~15 iteracji był wyciek pamięci, a potem już nie.
Po uruchomieniu normalnie i większej ilości iteracji:
http://prnt.sc/b4wcb9
http://prnt.sc/b4wcgs

Jak widać potem wycieku nie ma.
Sprawdzając funkcje odpowiadające za usuwanie dynamicznych obiektów z pamięci, również nic nie znalazłem.

Z kolei wykorzystując narzędzie Valingrad dla 100 iteracji, otrzymałem następujący wynik:

==38912== HEAP SUMMARY:
==38912== in use at exit: 22,451 bytes in 188 blocks
==38912== total heap usage: 50,268 allocs, 50,080 frees, 2,433,819 bytes allocated
==38912==
==38912== Searching for pointers to 188 not-freed blocks
==38912== Checked 9,805,504 bytes

Oraz pewne wycieki pamięci:

==38912== 513 bytes in 1 blocks are definitely lost in loss record 52 of 62
==38912== at 0x100010D81: malloc (vg_replace_malloc.c:303)
==38912== by 0x1001FC66C: __parsefloat_buf (in /usr/lib/system/libsystem_c.dylib)
==38912== by 0x1001FA9EF: __svfscanf_l (in /usr/lib/system/libsystem_c.dylib)
==38912== by 0x1001EE492: fscanf (in /usr/lib/system/libsystem_c.dylib)
==38912== by 0x100002138: createLists (ListCreation.c:87)
==38912== by 0x1000017DD: graphCreation (GraphCreation.c:82)
==38912== by 0x10000177D: showMenu (Menu.c:62)
==38912== by 0x100001753: main (main.c:25)

I tip jest taki, że rzecz się dzieje przy fscanfie w funkcji createLists, która zostaje wywołana w funkcji graphCreation.
Parę słów o funkcjach - pierwsza tworzy listy, do których zostają wczytywane połączenia pomiędzy miastami z pliku (stąd się pojawia fscanf), a potem następuje powrót do graphCreation i zostają one dodane do grafu (wcześniej graf jest dynamicznie alokowany).

updateDataBaseFile aktualizuje plik z połączeniami (normalnie w programie jest menu, gdzie można edytować te połączenia).
removeGraph oczywiście usuwa cały graf z pamięci.

Co oznacza narastanie zużycia pamięci na początku? Przeglądając usuwanie grafu nie znalazłem tam żadnego błędu, a jeśli by był - to przecież rosłoby zużycie pamięci po tych 100k iteracjach.

kq
Moderator C/C++
  • Rejestracja:ponad 11 lat
  • Ostatnio:około 5 godzin
  • Lokalizacja:Szczecin
0

Szklana kula mówi, że w linii 42 jest błąd. Bez kodu tylko kula pomoże.

Jeśli potem ilość pamięci pozostaje na tak niskim poziomie to może po prostu program nie zwrócił zaalokowanej pamięci od systemu operacyjnego? Gdy wołasz malloc(10) to malloc nie woła OSowych funkcji do alokacji pamięci, bo to zbyt wolne, tylko prosi raz o więcej a potem z tej puli daje.


edytowany 1x, ostatnio: kq
KA
Hmm, nie wiedziałem, że tak to może być zaimplementowane - właściwie to tłumaczy czemu tak się dzieje i gdzieś mi brakuje jakiegoś free(). Kodu nie wrzuciłem, bo to jest z większego projektu i bez sensu wstawiać tu 2k linii. Zaraz wrzucę zipa z całym projektem, może komuś będzie się chciało przejrzeć.
kq
Wrzuć na githuba czy coś, mało komu chce się ściągać i analizować zipa.
KA
  • Rejestracja:prawie 12 lat
  • Ostatnio:10 miesięcy
  • Postów:72
0

wrzucę na wklej.org po prostu.

Plik GraphCreation.c: http://wklej.org/id/2398711/
Plik GraphRepresentasion: http://wklej.org/id/2398712/
Plik CreateLists.c: http://wklej.org/id/2398713/
Plik ListsRepresentation.h: http://wklej.org/id/2398734/
i menu:

Kopiuj
void showMenu(void)
{
    Graph_t *graph;
    for(int i = 0; i < 100; ++i)
    {
        graph = graphCreation();
        updateDataBaseFile(graph);
        removeGraph(graph);
    }
}

Po krótce co się dzieje: w pliku DataBase.txt jest sobie spis połączeń pomiędzy miastami w formie "miasto1 miasto2 odległość", program tworzy listę connections_list gdzie dodaje te połączenia, a następnie wyszukuje unikalne miasta (tj. aby nazwy się nie powtarzały) i zapisuje je w kolejności alfabetycznej do unique_cities_list. I to potem na podstawie unique_cities_list te połączenia z connections_list są dodawane do grafu (aby zachować kolejność alfabetyczną).

edytowany 1x, ostatnio: kalwi
JA
  • Rejestracja:prawie 9 lat
  • Ostatnio:ponad 7 lat
  • Postów:40
0

Polecam dodać mniej więcej coś takiego:

Kopiuj
#define myMalloc(size,desc)                _myMalloc(size, __func__ , __LINE__ , desc )
#define myFree(a,desc)                             _myFree(a,__func__,__LINE__,desc)

void * _myMalloc( size_t Size , char * Func , int Line , char * Description)
{
     // tu jakis printf albo fprintf do pliku - co ci latwiej znalezc
     printf("MALLOC-%-10s: %s:%d\n" , Description , File , Line);
     return malloc(size);
}


void  _myFree( void * p , char * Func , int Line , char * Description)
{
     // tu jakis printf albo fprintf do pliku - co ci latwiej znalezc
     printf("FREE-%-10s: %s:%d\n" , Description , File , Line);
     free(p)
}

A potem policzyc wystąpienia każdej alokacji - powinno być łatwiej znaleźć wyciek no i moze sie przydac w przyszlosci

edytowany 1x, ostatnio: JohnAmadis
KA
dzięki, rano sprawdzę tę metodę :>
KA
  • Rejestracja:prawie 12 lat
  • Ostatnio:10 miesięcy
  • Postów:72
0

hmm, udało mi się wykryć pierwszy fragment kodu, gdzie mam wyciek (a przynajmniej wg Valgrinda). Jak nie wchodzę w tę funkcję, to wycieku nie ma, jak wchodzę to

Kopiuj
513 bytes in 1 blocks are definitely lost in loss record 49 of 59
==9661==    at 0x100010D81: malloc (vg_replace_malloc.c:303)
==9661==    by 0x1001FC66C: __parsefloat_buf (in /usr/lib/system/libsystem_c.dylib)
==9661==    by 0x1001FA9EF: __svfscanf_l (in /usr/lib/system/libsystem_c.dylib)
==9661==    by 0x1001EE492: fscanf (in /usr/lib/system/libsystem_c.dylib)
==9661==    by 0x1000022C5: createLists (ListCreation.c:87)
==9661==    by 0x10000197D: graphCreation (GraphCreation.c:82)
==9661==    by 0x10000191D: showMenu (Menu.c:62)
==9661==    by 0x1000018F3: main (main.c:25)

I zawężyłem sobie fragment funkcji:

Kopiuj
void createLists(ConnectionsList_t *connections_list, UniqueCitiesList_t *unique_cities_list)
{
    char city1[GLOBAL_CITYS_NAME_LENGTH], city2[GLOBAL_CITYS_NAME_LENGTH];

    size_t number_of_connections = 0;
    double distance;

    int result;
    FILE *fp;

    if((fp = fopen("DataBase.txt", "r")) == NULL)
    {
        printErrorDataBase(CANT_OPEN_FILE);
        return;
    }

    while((result = fscanf(fp, "%s %s %lf\n", city1, city2, &distance)) != EOF)
    {
        fclose(fp);
        return;
	// reszta kodu, nieistotne.
     }
}

Jeśli wychodzę z funkcji przed whilem, to nie ma tego wycieku. Więc musi być coś nie tak z fscanfem. Ale co?

JA
  • Rejestracja:prawie 9 lat
  • Ostatnio:ponad 7 lat
  • Postów:40
0

z Twojego postu wynika, że podejrzewasz, że wyciek pamięci leży po stronie systemu operacyjnego. Nie chce mi się w to wierzyć, bo by się zdarzało częściej, aczkolwiek musisz powiedzieć na jakim to OSie się dzieje

JA
  • Rejestracja:prawie 9 lat
  • Ostatnio:ponad 7 lat
  • Postów:40
0

Według mnie jest bardziej prawdopodobne ze to tylko echo tego co się dzieje gdzie indziej. NP może być zmienna jakas nieuzupelniona lub nieuzywana przez to ze ty robisz returna. Poza tym zrobiłeś to co ci podalem? Wklejasz do notepad++ i on ci podliczy wystąpienia każdej alokacji i releasu. Nikt ci w kodzie nie będzie szukał wycieku bo nikt tego nie kubki

06
  • Rejestracja:prawie 20 lat
  • Ostatnio:około rok
  • Postów:2440
0

Poczytaj sobie jak działa sterta. Będziesz wiedział, dlaczego zwolnienie przydzielonej pamięci nie musi (od razu) zmniejszyć ilości zajętej pamięci przez aplikację.

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)