Pamięć - zwalnianie i wycieki

Pamięć - zwalnianie i wycieki
0

Witam
Mam taki problem:
Tłumaczę właśnie część kodu z VB do delphi (kilka class) i w miare wszystko się udaje ale chciałbym sprawdzić zwalnianie pamięci.
Jest to temat który bardzo słabo znam (w literaturze mi dostepniej tez jest lakonicznie - że trzeba itp).

Z tego co zrozumiałem po uzyciu .create muszę potem użyć .free , natomiast używająć SetLenght(xxx,10) trzeba użyć finalize.

Słyszałem że są jakiej programiki do badania wycieków pamięci.

Dla chcących mi pomóc dokładniej opisuję sytuacje:

  • 20 class (każda w osobnym unicie) ,
  • średnio po 40 procedur, funkcji i property w każdej klasie.

(przepatrzyłem archiwum i nic dokładnie nie znalazłem o dokładnej mozliwości analizowania pamieci)

Misiekd
  • Rejestracja:ponad 21 lat
  • Ostatnio:ponad 12 lat
  • Postów:7923
0

jest kilka zasad, które trzeba przestrzegać

  1. należy zwolnić wszystko, co sam tworzysz, czyli jak robisz Create to należy potem zrobić Free (zamiast Free lepiej zrobić FreeAndNil(obiekt))
  2. jeśli gdzieś używasz tablic dynamicznych to jeśli są już nie potrzebne należy je zwolnić przez SetLength(tablica, 0)
    do testowania jest pod Delphi DUnit albo MemProof a tu masz opisane wycieki pamięci i jak im zapobiegać w różnych wersjach Delphi i BCB

- Ciemna druga strona jest.
- Nie marudź Yoda, tylko jedz tego tosta.
Google NIE GRYZIE!
Pomogłem - kliknij
0

Wielkie dzięki - super odp - takiej odpowiedzi chciałem i tego szukałem - jak bedziesz około Olsztyna masz krate browara. [browar]

Morris
  • Rejestracja:ponad 19 lat
  • Ostatnio:2 miesiące
  • Postów:36
0
Misiekd napisał(a)

jest kilka zasad, które trzeba przestrzegać
2. jeśli gdzieś używasz tablic dynamicznych to jeśli są już nie potrzebne należy je zwolnić przez SetLength(tablica, 0)

... lub przez tablica := nil;

Jeśli tablica dynamiczna jest zmienną lokalną w procedurze/funkcji, to nie trzeba jej zwalniać - zajmie się tym delphiowy menedżer pamięci - podobnie jak nie trzeba "czyścić" lokalnych zmiennych typu String, OleVariant lub typów interfejsowych (IUnknown i pochodne). Tego typu zmiennych lokalnych nie trzeba również inicjować przed użyciem - są automatycznie inicjowane na wartość "pustą" przez kod wygenerowany przez kompilator.


M.O.R.R.I.S.: Mechanical Obedient Replicant Responsible for Infiltration and Sabotage
morris#rarlab*com
crowa
  • Rejestracja:ponad 18 lat
  • Ostatnio:około 8 lat
  • Lokalizacja:Poznań
  • Postów:295
0

tablice nalezy zwalniac przez
Finalize()
wtedy gdy nie sa deklarowane jako lokalne oraz gdy zajmowane przez nie zasoby sa kluczowe dla aplikacji.


Tomasz Andrzejewski
Delphi (XE3-XE7) framework engineer @ InterLan
MCP: Microsoft SQL Server 2008, Implementation and Maintenance
0

Tablice dynamiczne trzeba zwalniać? Specjaliści, a stringa też zwalniacie? :D

Słyszeliście o czymś takim jak licznik odwołań? :P

ŁF
Moderator
  • Rejestracja:ponad 22 lata
  • Ostatnio:około 10 godzin
0

geniuszu, oczywiście sam nic nie zwalniasz w dynamicznej tablicy obiektów?
chyba nie wiesz co to licznik odwołań, nie ma on do rzeczy nic przy ręcznie tworzonych obiektach, jeśli obiektu nigdzie nie niszczysz, to co mu pomoże licznik, co?
a może język programowania pomyliłeś? java? c#?


0

ŁF - on ma rację. tablicy dynamicznej jako takiej nie trzeba zwalniać. Obiekty, które są w niej przechowywane, o ile nie są prymitywami - tak, ale tego z kolei nie załatwi się instrukcjami podanymi przez Miśka i kolegów. Wyobraź sobie, że do tablicy dynamicznej pakujesz wskazania do komponentów utworzonych w taki sposób:

Tab[i] := TJakisKomponent.Create(Application);

Niech Cię ręka boska broni zwalniać tu COKOLWIEK - o zwolnienie obiektów zatroszczy się Application, a tablica zostanie usunięta samoczynnie.

Proponuję potestować trochę program pod D2006 wzwyż z włączoną opcją raportowania błędów lub - w przypadku niższych wersji Delphi - z podłączonym FastMM. Wnioski są jednoznaczne - tablic dynamicznych NIE TRZEBA zwalniać.

ŁF
Moderator
  • Rejestracja:ponad 22 lata
  • Ostatnio:około 10 godzin
0
TBSO napisał(a)

Wyobraź sobie, że do tablicy dynamicznej pakujesz wskazania do komponentów utworzonych w taki sposób:

Tab[i] := TJakisKomponent.Create(Application);

Niech Cię ręka boska broni zwalniać tu COKOLWIEK - o zwolnienie obiektów zatroszczy się Application, a tablica zostanie usunięta samoczynnie.

przykład banalny:

Kopiuj
var
  a : array of TObject;
...

procedure TForm1.Button1Click(Sender: TObject);
var
  i : integer;
begin
  setlength(a,100000);
  for i := 0 to length(a)-1 do a[i] := TObject.Create();
  a := nil;
end;

poklikaj w buttona i zobacz, czy pamięć na pewno się zwalnia. co do stringów i innych typów nie będących obiektami, w tym tablic dynamicznych - masz rację, Delphi zwalnia pamięć za mnie.


0

Przeoczyłeś że tam było ".Create(Application)"?

Jak napisałem - zwalnianie obiektów przypisanych do elementów tablicy to jedna rzecz, to trzeba rozpatrywać indywidualnie - obiekty posiadające właściciela są zwalniane przez niego. Natomiast sama tablica dynamiczna zwalniania nie wymaga, no ale tutaj się już chyba zgodziliśmy :)

ŁF
Moderator
  • Rejestracja:ponad 22 lata
  • Ostatnio:około 10 godzin
0

masz rację, ale z jednym dużym ALE. Application jest globalne dla całego programu, jeśli będzie ownerem, to spowoduje, że pamięć zostanie zwolniona dopiero przy zakończeniu aplikacji. w zasadzie to samo tyczy się wszystkich formatek i ich elementów, tak więc jest to metoda na zrobienie wycieków pamięci. co prawda będą posprzątane przy zamykaniu aplikacji, ale kogo to wtedy obchodzi?
tak więc dalej będę obstawać przy założeniu, że KAŻDY obiekt utworzony ręcznie POWINIEN być zwolniony ręcznie, a już zawsze, gdy kod pisze programista-amator, który nie panuje nad tym, kiedy jaki owner jest niszczony.

poza tym - założę się, że nawet nie sprawdziłeś w praktyce tego, co pisałeś. a ja to zrobiłem. zmień w moim kodzie TObject na TForm1, poklikaj na buttona i sam sprawdź, co się dzieje z pamięcią i uchwytami.


0
ŁF napisał(a)

masz rację, ale z jednym dużym ALE. Application jest globalne dla całego programu, jeśli będzie ownerem, to spowoduje, że pamięć zostanie zwolniona dopiero przy zakończeniu aplikacji. w zasadzie to samo tyczy się wszystkich formatek i ich elementów, tak więc jest to metoda na zrobienie wycieków pamięci. co prawda będą posprzątane przy zamykaniu aplikacji, ale kogo to wtedy obchodzi?
tak więc dalej będę obstawać przy założeniu, że KAŻDY obiekt utworzony ręcznie POWINIEN być zwolniony ręcznie, a już zawsze, gdy kod pisze programista-amator, który nie panuje nad tym, kiedy jaki owner jest niszczony.

poza tym - założę się, że nawet nie sprawdziłeś w praktyce tego, co pisałeś. a ja to zrobiłem. zmień w moim kodzie TObject na TForm1, poklikaj na buttona i sam sprawdź, co się dzieje z pamięcią i uchwytami.

Pudło - sprawdziłem. Warstwy sieci neuronowych łączyłem przy pomocy tablic dynamicznych, i miałem mnóstwo okazji żeby przetestować. Wyjście jednej było jednocześnie wejściem drugiej, i w tym momencie fakt, że we i wy były tą samą tablicą, tylko z licznikiem odwołań - oszczędzał mi mnóstwo roboty.

Kopiuj
warstwa[i].output := JakasDynamicznaTablicaIntegerow;
warstwa[i+1].Input := JakasDynamicznaTablicaIntegerow;

Przestawienie czegokolwiek na wyjściu przez warstwę i automatycznie zmieniało wejscie w warstwie i+1. Nawiasem mówiąc - lepiej to zrobic na obiektach, ale na czas testów jest to wyśmienite rozwiązanie.

Co do zwalniania przez TApplication - rany, czepiasz się. Podałem DOWOLNEGO ownera, żeby nie komplikować i nie pisać kawałków kodu. Wyobraź sobie inny kod, tym razem niemal z życia wzięty - stosuję podobne konstrukcje na codzień:

Kopiuj
procedure DoSomething(SomeComponentCount: integer);
var
  frm: TSomeForm;
  SomeComponent: TSomeComponent;
  len: integer;
  DynArr: array of TSomeComponent;
  i: integer;
begin
  frm := TSomeForm.Create(Application);
  for i := 0 to SomeComponentCount - 1 do
  begin
    SomeComponent := TSomeComponent.Create(frm);
    SomeComponent.Parent := frm;
   {... tutaj ustawianie innych właściwości SomeComponent ...}
    len := Length(DynArr);
    SetLength(DynArr, len+1);
    DynArr[len] := SomeComponent;
  end;
end;

TSomeForm w zdarzeniu OnClose ustawia Action := caFree;

Jak widzisz - nic nie trzeba zwalniać, SomeComponent jest wywalane przez frm w chwili jej zamknięcia - i bardzo dobrze, bo o to chodzi.

0

W pośpiechu pominąłem instukcję frm.Show :)
No i - w tym konkretnym przypadku - nie ma sensu ustawiać w pętli co chwila rozmiaru DynArr, wystarczy przed pętlą dać SetLength(DynArr, SomeComponentCount). Ale reszta konstrukcji - mam nadzieję, że jasna?

ŁF
Moderator
  • Rejestracja:ponad 22 lata
  • Ostatnio:około 10 godzin
0

nie zrozumielismy się. jeśli frm żyje sobie sobie tak długo, jak cała aplikacja, to tak samo długo żyją też jego dzieci. jeśli to wizualne kontrolki lub inne rzeczy wymagane do jego bieżącego działania, to wszystko ok. ale jeśli to jakieś zmienne wykorzystywane do przechowywania tymczasowych danych, to jest to w zasadzie wyciek pamięci. i to niewykrywany przez zewnętrzne narzędzia, bo przecież owner trzyma adresy danych do zwolnienia.


0

No cóż, faktycznie trochę inaczej rozumiem pojęcie wyciek pamięci, a forma MDI ma żyć z definicji tak długo, jak jej użytkownik każe. Tym niemniej - cała dyskusja zaczęła się od tego, czy trzeba zwalniać tablice dynamiczne (o elementach tablic dyskusja zaczęła się znacznie później). I w tym fragmencie dyskusji specjalisci_... miał rację - samych tablic jako takich zwalniać nie trzeba, ani przez SetLength, ani przez podstawienie nilem. Kogoś fantazja poniosła i tyle :)

// co do tematu z wątku - jak najbardziej się z Tobą zgadzam - Ł

b0bik
  • Rejestracja:około 22 lata
  • Ostatnio:10 dni
  • Postów:1112
0

Ale żeście się rozgadali. Po pierwsze jak duskutować o samoczynnym zwalnianiu, to pasowało by nadmienić jakich klas to dotyczy w przypadku Delphi - ktoś gotów pomyśleć że nie trzeba zwalniać np TStringList. A więc dotyczy to pochodnych TComponent (za helpem):

...
For components created programmatically, that is, not created in the form designer, call Create and pass in an owner components the AOwner parameter. The owner disposes of the component when it is destroyed. If the component is not owned, then use Free when it needs to be destroyed.
...

Po drugie, po co podawać jakieś skomplikowane przykłady skoro wystarczy prosty test:

Kopiuj
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  TTest = class (TComponent)
  private
    fName : String;
  public
    constructor Create(aOwner : TComponent; aName : String);
    destructor Destroy; override;
  end;

var
  Form1: TForm1;
  t1, t2 : TTest;

implementation

{$R *.DFM}

{ TTest }

constructor TTest.Create(aOwner: TComponent; aName : String);
begin
  inherited Create(aOwner);
  fName := aName;
end;

destructor TTest.Destroy;
begin
  Application.MessageBox(PChar(fName + ' jest zwalniany'), nil, 0);
  inherited;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  t1 := TTest.Create(Application, 'Application');
  t2 := TTest.Create(nil, 'Bez ownera');
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
//  t2.Free; // przy zakomentowanej linijce będzie wyciek pamięci
end;

end.

To tyle dla potwierdzenia słów jednego z was. Jak to działa, to jedna sprawa są jeszcze inne aspekty. To że coś jest automatycznie zwalniane OK, pytanie tylko czy jesteśmy świadomi kiedy ... i przykład książkowy:

"a gdyby tu było przedszkole w przyszłości ... i kontruktor alokował by bitmapę dwu-megową" - to by sobie żyła aż aplikacja/forma się nie zamknie, bez względu na to czy była by potrzeba czy nie.

Dlatego też IMHO najlepiej jest przyjąć zasadę tworzymy-zwalniamy, a konkretnie

t := ttest.create();
try
...
finally
freeandnil(t)
end;

Tak jest po prostu czytelniej. Wiem jest więcej kodu - ale IMHO tak jest lepiej.

Aha ktoś tam napisał że coś jest automatycznie inicjowane. Otóż w przypadku zmiennych globalnych czy pól klas OK, ale w przypadku zmiennych lokalnych (czy Integer, czy pochodnych TObject) - FALSE !!! Sprawdźcie sami jak nie wierzycie. Podstawowa zasada przy używaniu zmienych lokalnych jest taka żeby nadać im początkową wartość !!! Zresztą, jak ktoś czasem zwraca uwagę przed czym kompilator nas ostrzega, to tego typu informacja właśnie się tam znajdzie.

Pozdro

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)