EOutOfMemory: Ursache finden

Für alles, was in den übrigen Lazarusthemen keinen Platz, aber mit Lazarus zutun hat.
Antworten
am2
Lazarusforum e. V.
Beiträge: 116
Registriert: Di 21. Dez 2010, 09:59
OS, Lazarus, FPC: Win (L 0.9.26 beta FPC 2.2.2)
CPU-Target: 32 Bit

EOutOfMemory: Ursache finden

Beitrag von am2 »

Hi,

ich habe eine Anwendung, die ich mal grob umreißen will:

- Hauptformular, bei Programmstart werden 2 zusätzliche Fenster dynamisch geöffnet, die bei Programmende geschlossen werden.
- in einer SQLite- DB sind ca. 1000 Datensätze, in denen je 2 Dateinamen von Bildern enthalten sind, die (manuell) zu vergleichen sind (daher die beiden Fenster)
- Das Ergebnis wird dann wieder in diese Datei hineingeschrieben.
- Zwischenzeitlich habe ich ein timergesteuertes Autosave eingebaut
- bis auf die beiden Fenster habe ICH DIREKT keinerlei dynamische Variablen eingebaut

Ich hab vielleicht nicht alles relevanten Informationen erwischt, aber das Wesentliche sollte es sein.

Das Programm ist immer mal wieder ohne erkennbaren Grund mit abgestürzt (ohne in ein TRY - EXCEPT hineinzufallen). Also habe ich dieses Autosave eingebaut. Jetzt ist es so, dass bei Datensatz 5 (der ganz gewöhnlich ist) das Programm sowohl in der IDE als auch außerhalb Einfach abstürzt. Von DS 1 - 4 gibt es keinerlei Auffälligkeiten, wenn ich den TASK- Manager betrachte.

Ich habe keinerlei Idee, wo ich suchen soll. Jemand einen Tip?
(Sorry, zu Unittests konnte ich mich noch nicht schlau machen)

am2

Warf
Beiträge: 1910
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: EOutOfMemory: Ursache finden

Beitrag von Warf »

EOutOfMemory occurs when memory can no longer be allocated on the heap.

Meine kristallkugel ist derzeit in der reinigung also kann ich dir nicht sagen was du in deinem Code falsch machst. Wenn ich raten müsste würde ich sagen du alloziierst irgendwo mehr speicher als du addressraum hast (nicht mal unbedingt physischer speicher. Kann sein das du physisch nicht mal 1MB brauchst und trozdem OOM läufst). Kandidaten dafür sind z.B. sehr große Listen, objekte die du in einer schleife erzeugst aber nicht freigibst, oder du hantierst mit extrem langen strings. Natürlich kann es auch etwas deutlich komplexeres sein, z.B. kannst du mit FillChar den Pointer eines Strings oder dynamischen arrays überschreiben und somit den speicher auf ewig verlieren (wenn es keine weiteren referenzen darauf gibt natürlich).

Aber pauschal kann man dir definitiv nicht helfen. Wenn es mit dem autosave feature kam, der einzige Tipp den ich dir wohl geben kann,schau dir das nochmal genauer an

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6209
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: EOutOfMemory: Ursache finden

Beitrag von af0815 »

Hast du dein Programm schon mal mit allen "Checks and Assertions" aktiv gebaut und laufen gelassen ? heaptrace auch noch aufdrehen ist keine schlechte Idee. Geht alles über die Projektoptionen und dort die Seite Debugging.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

am2
Lazarusforum e. V.
Beiträge: 116
Registriert: Di 21. Dez 2010, 09:59
OS, Lazarus, FPC: Win (L 0.9.26 beta FPC 2.2.2)
CPU-Target: 32 Bit

Re: EOutOfMemory: Ursache finden

Beitrag von am2 »

Warf hat geschrieben:Meine kristallkugel ist derzeit in der reinigung

Meine auch ...
Wie gesagt, ich alloziere direkt keinen Speicher. Wie finde ich heraus, welches der von mir benutzten Fremd- Module das wie tut?

af0815 hat geschrieben:Hast du dein Programm schon mal mit allen "Checks and Assertions" aktiv gebaut und laufen gelassen ? heaptrace auch noch aufdrehen ist keine schlechte Idee. Geht alles über die Projektoptionen und dort die Seite Debugging.

Hab ich inzwischen. Hat mich leider nicht weiter gebracht. Folgende Erkenntnisse:

ich habe wirklich JEDE Funktion/Methode in ein

Code: Alles auswählen

try
 
except on e: exception do
  showmessage(format('[%s] %s',[E.classname, e.message]))
end 

eingepackt.
Der Fehler taucht IMMER bei dem gleichen Datensatz auf (auch, wenn ich vorher auf irgendwelche anderen Datensätze gehüpft bin). MEIN Code wirft keine Exception. Diese wird anscheinend geworfen, wenn die Fenster wieder angezeigt werden sollen. Zweifelsfrei habe ich irgendetwas getan, was ihn durcheinander bringt, aber ich habe keine Idee, WAS!
Der betroffene Datensatz erscheint mir auch völlig unauffällig.

Wie würdet ihr an meiner Stelle vorgehen?

Kann man - wenn das "Assemblerfenster" auftaucht, irgendwie herausfinden, aus welcher Zeile Lazarus das geworfen worden ist?

am2
Lazarusforum e. V.
Beiträge: 116
Registriert: Di 21. Dez 2010, 09:59
OS, Lazarus, FPC: Win (L 0.9.26 beta FPC 2.2.2)
CPU-Target: 32 Bit

Re: EOutOfMemory: Ursache finden

Beitrag von am2 »

Ich vermute, ich habe die Ursache gefunden...
Ich hab mal alles mit 64 bit compiliert und eine entsprechende SQLite3 - dll eingesetzt und das Problem ist weg (bei gleichen Quellen).
Wie sucht und findet man solche Fehler? (und wie kann ich ausschließen, dass er nicht bei Datensatz 37 wieder auftaucht?)

Warf
Beiträge: 1910
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: EOutOfMemory: Ursache finden

Beitrag von Warf »

Der OOM Fehler wird nicht mehr auftreten, denn 64 Bit addressraum auszuschöpfen wird schwer

Wenn heaptrc keine Memory leaks meldet kann es sein das du einfach mehr als die 4GB virtuellen Speicher bei 32 Bit brauchst. Kann vorkommen, genau dafür gibt’s 64 Bit CPUs

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1435
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: EOutOfMemory: Ursache finden

Beitrag von fliegermichl »

Hm naja also bei 1000 Datensätzen sollte da eigentlich auch bei 32 Bit nichts passieren.
Kannst du mal ein kleines Beispiel machen so das man es nachvollziehen kann?

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6209
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: EOutOfMemory: Ursache finden

Beitrag von af0815 »

Warf hat geschrieben:Der OOM Fehler wird nicht mehr auftreten, denn 64 Bit addressraum auszuschöpfen wird schwer

Wenn heaptrc keine Memory leaks meldet kann es sein das du einfach mehr als die 4GB virtuellen Speicher bei 32 Bit brauchst. Kann vorkommen, genau dafür gibt’s 64 Bit CPUs

Irrtum, bei 1.7 ist normalerweise Schluss und mit dem richtigen PE Flag gehts auch nur bis ca. 3.5. Mit 32 Bit unter Windows. Da kann die CPU noch so 64 Bittig sein. Es hängt davon ab wie das Programm compiliert wurde.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

am2
Lazarusforum e. V.
Beiträge: 116
Registriert: Di 21. Dez 2010, 09:59
OS, Lazarus, FPC: Win (L 0.9.26 beta FPC 2.2.2)
CPU-Target: 32 Bit

Re: EOutOfMemory: Ursache finden

Beitrag von am2 »

Hi,

vielen Dank für Eure Gedanken.

ich vermute mal, es liegt nicht primär an der Menge der Daten. Auch mit dem Task- Manager war zwischen diesen beiden DS keinen Anstieg. Wir reden hier über vielleicht 60 Mb im Speicher, nicht mehr.
Es gibt (in meinen Daten) irgend eine ungünstige Konstellation, die das System in die Knie zwang. Mit 64bit war es weg, wie von Geisterhand.

Eine Minimal- App erscheint mir schwierig, weil ich ja keine Ahnung habe, was den "Datensatz 6" von den restlichen unterscheidet. Und die Daten funktionieren nur bei mir, weil es letzlich Dateipfade zu Images (idR Jpegs) sind, die ich lese und lade.

Es könnte natürlich auch an der Image- Komponente liegen, diese ist ja JETZT auch 64 bit (Die Bilder sind maximal 12 MPixel)

Wie würdet ihr nach so etwas suchen? (Der Memory- Tracker findet alles schick. ein bisschen was erzeugt, alles wieder frei gegeben - kein Leck)

AM

Warf
Beiträge: 1910
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: EOutOfMemory: Ursache finden

Beitrag von Warf »

af0815 hat geschrieben:Da kann die CPU noch so 64 Bittig sein. Es hängt davon ab wie das Programm compiliert wurde.


Das ist zwar jetzt etwas sehr nitpicky, aber, wenn du ein 32 bit programm auf einem x86_64 laufen lässt, läuft es effektiv auf einer 32 bit CPU, da x86_64 (wie der name schon vermuten lässt) beide architekturen vereint.

Wir reden hier über vielleicht 60 Mb im Speicher, nicht mehr.
Es gibt (in meinen Daten) irgend eine ungünstige Konstellation, die das System in die Knie zwang. Mit 64bit war es weg, wie von Geisterhand.

Wie bereits gesagt, bei OOM läufst dir der virtuelle speicher aus. Das hat mit dem tatsächlichen Speicherverbauch herzlich wenig zu tun. Beispiel:

Code: Alles auswählen

program Test;
{$mode objfpc}{$H+}
 
begin
  Getmem(2000000000);
  ReadLn;
end.

Dieses Programm, obwohl es offensichtlich 2 GB alloziiert braucht (Windows 64 bit) nur 0,8 MB ram.
Du benutzt bibliotheken wie sqlite, kann also sehr gut sein das sqlite intern einen stack allocator oder so benutzt, der große mengen virtuellen addressraum alloziiert, ohne ihn effektiv auszunutzen.
Solang keine memory leaks erzeugt werden, du die aktuellste (stable) version der Bibliotheken verwendest, sollte es kein problem sein. Wie schon gesagt, genau dafür (weil 4GB virtueller addressraum echt wenig ist) gibt es 64 bit.

am2
Lazarusforum e. V.
Beiträge: 116
Registriert: Di 21. Dez 2010, 09:59
OS, Lazarus, FPC: Win (L 0.9.26 beta FPC 2.2.2)
CPU-Target: 32 Bit

Re: EOutOfMemory: Ursache finden

Beitrag von am2 »

Warf hat geschrieben:

Code: Alles auswählen

program Test;
{$mode objfpc}{$H+}
 
begin
  Getmem(2000000000);
  ReadLn;
end.

Dieses Programm, obwohl es offensichtlich 2 GB alloziiert braucht (Windows 64 bit) nur 0,8 MB ram.
Du benutzt bibliotheken wie sqlite, kann also sehr gut sein das sqlite intern einen stack allocator oder so benutzt, der große mengen virtuellen addressraum alloziiert, ohne ihn effektiv auszunutzen.
Solang keine memory leaks erzeugt werden, du die aktuellste (stable) version der Bibliotheken verwendest, sollte es kein problem sein. Wie schon gesagt, genau dafür (weil 4GB virtueller addressraum echt wenig ist) gibt es 64 bit.

Beeindruckendes Exempel
Also mit anderen Worten: an dieser Stelle kann ich aufgeben :(

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6209
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: EOutOfMemory: Ursache finden

Beitrag von af0815 »

am2 hat geschrieben:Also mit anderen Worten: an dieser Stelle kann ich aufgeben :(

Überhaupt nicht.

Was bei dir fehlt ist ein vereinfachtes Beispiel, das auch ein anderer auf seinen Rechner ausführen kann.

Meistens beim vereinfachen des Beispiel tritt an einem Punkt der Fehler entweder nicht mehr auf oder ist nachvollziehbar. Wenn nachvollziehbar, dann kann man auch meistens die Ursache finden.

Vor allen hast du lt. deiner Beschreibung im Post 1 genaugenommen zwei Bereiche. Zuerst die Infos aus der DB lesen, dann die beiden Bilder laden und ein Ergebnis zurückschreiben. Tritt der Fehler auch auf, wenn du keine Bilder lädst den restlichen Prozess aber gleich lässt ?
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Warf
Beiträge: 1910
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: EOutOfMemory: Ursache finden

Beitrag von Warf »

af0815 hat geschrieben:Meistens beim vereinfachen des Beispiel tritt an einem Punkt der Fehler entweder nicht mehr auf oder ist nachvollziehbar. Wenn nachvollziehbar, dann kann man auch meistens die Ursache finden.


Wenn es ein fehler ist, kann man bestimmt davon die Ursache finden. Da es aber nur hoher virtueller Speicherverbauch ist, aber der tatsächliche Speicherverbauch sehr niedrig ist, würde ich einfach mal behaupten das es kein Fehler ist, denn Virtueller addressraum ist praktisch kostenlos, und es gibt extrem viele gründe viel virtuellen speicher zu benutzen. Dazu gehören zum einen Sicherheitsgründe, z.B. wird in sicherheitskritischer software sehr gerne immer neuer adressraum alloziiert, damit du nicht ausversehen auf einen zuvor gefreeten memory bereich zugreifen kannst (und damit eventuell daten lesen kannst die zuvor protected waren), zum anderen Performance gründe, wie ein Stack allocator, der beim programm start ein paar GB alloziiert um darauf dann die einzelnen memorybereiche zu alloziieren, ohne ständig neuen addressraum vom system anfragen zu müssen, oder auch einfach zum logischen orientieren, das man z.b. sagt das alles was im Memory bereich 0-500MB liegt zu block a gehört, alles zwischen 500M und 1G zu block B, etc. Jeder dieser blöcke kann dann nur zu ein paar KB belegt werden, da Virtueller speicher praktisch kostenlos ist, hast du dar durch keine nachteile (das vereint natürlich dann auch sowohl security und performance).

SQLite ist Sicherheitskritisch und perfomancerelevant. Also kann ich mir sehr gut vorstellen das das so gewollt ist. Normalerweise arbeitet SQLite so das es wenn es sehr viel Virtuellen Addresspeicher benutzt, außer es kommt in Memory Not (also kurz vor OOM). Das problem kann sein das du ja noch mit Lazarus den FPC memory manager benutzt, welcher auch sehr auf der freiheit von virtuellem addressraum ausgelegt ist (so sortiert der FPC memory manager soweit ich weiß speicherbereiche nach ihrer größe, d.H. er teilt den addressraum in verschiedene Sektoren auf, und gibt je nach angeforderter größe einen pointer aus einem der Sektoren zurück. Daher sind TStringList Pointer z.B. immer sehr nah beieinander, ein TStringList und ein Integer Pointer aber sehr weit auseinander, da die in verschiedenen Sektoren stecken. Was also sehr gut sein kann, ist das SQLite sehr viel virtuellen speicher braucht, und der FPC auch, obwohl du effektiv sehr wenig echten speicher brauchst.

Solang der Physische speicherverbauch niedrig bleibt, keinen memory leaks erzeugt werden (was man sehr einfach mit heaptrc oder valgrind testen kann), die aktuellste version der bibliotheken verwendet wird und der virtuelle speicherverbauch nicht kontinuierlich ansteigt, ist es sehr wahrscheinlich kein fehler.

am2 hat geschrieben:Also mit anderen Worten: an dieser Stelle kann ich aufgeben


Du kannst SQLite über pragmas sagen wie sein memory allokationsverhalten sein soll, wie es sich unter memory pressure verhalten soll, etc.
Ansonsten währe es mal interresant zu sehen wie sich das ganze verält. Wenn du unter Linux bist versuch mal das folgende, installier das package top, annotiere dein Programm mit writeln anweisungen bei jedem schritt den dein programm macht und wenn es fertig damit ist, sowie ein flush(Output):

Code: Alles auswählen

...
  WriteLn('Initialization');
  Flush(Output);
  ...
  WriteLn('Initialization done');
  Flush(Output);

etc. Somit reportet dein programm jeden schritt nach außen. Dann schreib ein Shell script das so aussieht (profile.sh):

Code: Alles auswählen

#!/bin/bash
$@ | while read line
do
    echo $line
    top -b -n1 | grep $(basename $1)
done

und starte dein programm dann via:

Code: Alles auswählen

./profile.sh pfad/zu/deinem/Programm > profile.txt

Das kleine script gibt dir den status von top zurück für dein programm zurück nach jedem writeln. das sieht dann ungefähr so aus (mein test programm gibt einfach nur zahlen aus):

Code: Alles auswählen

0
  808 user 20   0    8688    244    1520.000 0.001   0:00.00 test
1
  808 user 20   0    8688    244    1520.000 0.001   0:00.00 test
2
  808 user 20   0    8688    244    1520.000 0.001   0:00.00 test
3
  808 user 20   0    8688    244    1520.000 0.001   0:00.00 test
4
  808 user 20   0    8688    244    1520.000 0.001   0:00.00 test
5
  808 user 20   0    8688    244    1520.000 0.001   0:00.00 test
6
  808 user 20   0    8688    244    1520.000 0.001   0:00.00 test
 

Relevant in dieser zeile sind die 5te und 6te spalte, bei mir 8688 und 244, die 8688 ist der virtuelle speicherverbauch und 244 der physische speicherverbauch. Somit kannst du ganz genau sehen wann dein virtueller speicherverbrauch hochgeht.

Wenn du unter windows bist, lad dir virtualbox und eine linux iso runter (ich empfehler manjaro oder opensuse tumbleweed, da kannst du lazarus einfach über den paketmanager installieren) und mach das oben.

Wie gesagt, ich vermute das es kein echter fehler ist, aber falls doch, kannst du so relativ leicht finden wo/wann es kaputt geht

Antworten