Speicher wird nicht freigegeben

Für allgemeine Fragen zur Programmierung mit PureBasic.
Benubi
Beiträge: 186
Registriert: 22.10.2004 17:51
Wohnort: Berlin, Wedding

Speicher wird nicht freigegeben

Beitrag von Benubi »

Hallo!

Ich stoße bei diesem und stieß bei meinem letzten größen Source (20,000-30,000 Zeilen) auf ein seltsames Problem: der Speicher wird nicht freigegeben, bzw. in Taskmanager steigt der verbrauchte Speicher kontinuierlich an. Egal ob Windows oder Konsole: wenn ich die Fenster minimiere/maximiere wird der Speicher erst freigegeben. Je mehr passiert mit Alloc/Free, desto schneller steigt der Verbrauch. Im Leerlauf sind es ca. 4k die Sekunde, und wenn da größere String Operationen und viel Alloc/Free genutzt wird sind das mal schnell 3-4 MB die Sekunde (auch bei Fullscreen, windowed screen oder reine Konsolen app). Hier muß wohl irgendein Garbage Collection Zyklus bei Windows angeschubst werden. Der Speicherverbauch steigt egal ob im minimierten Fenster oder maximierten/normalen/2D Fullscreen. Erst wenn man hin und her wechselt fällt der Verbrauch auf die tatsächlich genutzte Menge. Nach ein paar Minuten ist dann der Speicher voll, obwohl eigentlich nur 1,5MB benötigt werden inclusive Lua mit nur ca. 200k. Lua wurde so konfiguriert daß es auch nur PB-Funktionen für alloc/free benutzt und kommt nicht als Verursacher in Frage weil der Bug auch vorher auffiel. Der Lua Verbrauch ist <200k während Verbrauch im Taskmanager auf über 200MB ist und steigt beispielsweise, obwohl nur 2-3 Bilder/Sprites da sind und ansonsten nur Text und Skripte verarbeitet werden (viele alloc/free Aufrufe). Dann minimiert/maximiert man ein Fenster der App, egal welches, und der Verbrauch fällt im Taskmanager wieder auf 1,5MB. Was soll ich jetzt tun?

WindowsXP 32 bit.

:(
Benutzeravatar
jacdelad
Beiträge: 341
Registriert: 03.02.2021 13:39
Computerausstattung: Ryzen 5800X, 108TB Festplatte, 32GB RAM, Radeon 7770OC
Wohnort: Riesa
Kontaktdaten:

Re: Speicher wird nicht freigegeben

Beitrag von jacdelad »

Kannst du uns einen minimalen Code geben, der das Verhalten aufweist?
PureBasic 6.04/XProfan X4a/Embarcadero RAD Studio 11/Perl 5.2/Python 3.10
Windows 11/Ryzen 5800X/32GB RAM/Radeon 7770 OC/3TB SSD/11TB HDD
Synology DS1821+/36GB RAM/130TB
Synology DS920+/20GB RAM/54TB
Synology DS916+ii/8GB RAM/12TB
Benubi
Beiträge: 186
Registriert: 22.10.2004 17:51
Wohnort: Berlin, Wedding

Re: Speicher wird nicht freigegeben

Beitrag von Benubi »

Ich weiß nicht genau wie ich das "simulieren" soll. Dieser Code verursacht auch bei mir Schwankungen zwischen 1.5-4 MB im Speicherverbrauch. Du mußt hin und wieder das Konsolenfenster maximieren/minimieren, dann merkst Du daß 1-2 MB wegfallen. Ein kontinuierliches Steigen habe ich nicht, welches ich beobachten könnte. Das Verhalten läßt sich nicht eindeutig reproduzieren (kompliziertere String Operationen, Init &Clear Structure).
:|

Code: Alles auswählen

; AllocProblem.pb
;

Global NewList all.i()
Global SizeTotal

Define.s msg$
Define   lastDate

Macro dq()
"  
EndMacro

Macro check(_V_)
  If Not _V_
    Debug "MEMORY FULL!"
    Debug "Line:"+#PB_Compiler_Line
    Debug dq()_V_ = dq() + _V_
    End
  EndIf 
EndMacro

Procedure AddAlloc(size)
  Protected buff$,i
  If size>0
    check(AddElement(all()))
    all() = AllocateMemory(size)

 
    check(all()) 
    SizeTotal = SizeTotal + size
    
    
  EndIf 
EndProcedure

Procedure ManyAllocations(max=-1,maxbytes=-1)
  Protected i
  If max<1
    max=1000
  EndIf 
  If maxbytes<1
    maxbytes=30
  EndIf 
  For i=1 To max Step 1
    AddAlloc(Random(maxbytes,2))
    
  Next 
EndProcedure

Procedure FreeAllocations(keep=100)
  If keep<0 : keep = 0 : EndIf 
  While ListSize(all())>keep ; keep 100 random pointers (simulate longer living objects")
    SelectElement(all(),Random(ListSize(all())-1,0))
    If ListIndex(all())>5 Or keep<=6
      SizeTotal = SizeTotal - MemorySize(all())
      FreeMemory(all()) ; free random pointer
      DeleteElement(all())
    EndIf 
  Wend 
EndProcedure



OpenConsole("AllocProblem")
Print("Init...") : 
AddAlloc(1024*1024*1.5) ; 1.5 MB data  img
AddAlloc(1024*200) ; 200k  img
AddAlloc(1024*200) ; 200k  img 

ManyAllocations() : PrintN("ok.")
PrintN("Press ESC to exit")
Repeat 

  ManyAllocations(Random(50000,1000),30)
  FreeAllocations(10)
  
  Delay(0)
   Print(Chr(13))
   lastDate=Date()
   msg$=LSet(FormatDate("%dd/%mm/%yyyy %hh:%ii:%ss ",lastDate)+" elems: "+Str(ListSize(all()))+" size: "+FormatNumber(SizeTotal,0),60)
   Print(msg$)
  
Until Inkey()=Chr(27)

CloseConsole()
End
Benutzeravatar
jacdelad
Beiträge: 341
Registriert: 03.02.2021 13:39
Computerausstattung: Ryzen 5800X, 108TB Festplatte, 32GB RAM, Radeon 7770OC
Wohnort: Riesa
Kontaktdaten:

Re: Speicher wird nicht freigegeben

Beitrag von jacdelad »

Hm, also Schwankungen habe ich auch, aber das sollte ja normal sein, weil du ja ständig anforderst und freigibst: Windows macht das sicher nicht jedes Mal sofort, sondern gebündelt.
Was soll "Delay(0)" bezwecken?

Ich hatte ein ähnliches Problem und habe nach sehr langer Suche festgestellt, dass ich tatsächlich an einer Stelle etwas nicht freigegeben hatte. Kann dir vielleicht der Debugger helfen?
PureBasic 6.04/XProfan X4a/Embarcadero RAD Studio 11/Perl 5.2/Python 3.10
Windows 11/Ryzen 5800X/32GB RAM/Radeon 7770 OC/3TB SSD/11TB HDD
Synology DS1821+/36GB RAM/130TB
Synology DS920+/20GB RAM/54TB
Synology DS916+ii/8GB RAM/12TB
Benubi
Beiträge: 186
Registriert: 22.10.2004 17:51
Wohnort: Berlin, Wedding

Re: Speicher wird nicht freigegeben

Beitrag von Benubi »

genau das meine ich mit dem gebündelten freigeben. Die app saugt sich voll und die gebündelte Freigabe scheint nur noch beim Maximieren/Minimieren statt zu finden. Daher frage ich auch ob man den Zyklus vielleicht selber anschubsen kann, bei jedem FlipBuffers oder großen Main loop.

Ich benutze jetzt gerade den Debugger. Es ist viel langsamer (nur ganz wenige FPS) aber der Effekt macht sich dennoch etwas bemerkbar. Wenn das Fenster über die Desktopleiste minimiert/maximiert wird, tritt wieder der Freigabe Effekt auf. Der Verbrauch sinkt von 30MB auf etwa 10MB.

Was ich in beiden Programmen tue: viele Strings verarbeiten. Das erste Programm bei dem mir das auffiel ist ein Webserver (mit dynamischer Webseiten Erzeugung und alles was dazu gehört). Jetzt im neuen wird auch eine Datei in eine "pseudo Konsole" (mit PB emuliertes terminal fenster auf screen mit 2D drawing gemalt) langsam ausgegeben. Wenn die Datei schnell ausgegeben wird (z.B. ohne geschwindigkeitsbegrenzung), dann tritt der Effekt nicht so schnell auf weil viel weniger Vergleiche und Datei-Häppchen verarbeitet werden. Ich emuliere eine Baudrate beim ausgeben, da wo es am meisten auffällt.

In letzterem Fall tritt das ganze besonders stark auf wenn ich das ganze indirekt laufen lasse (lua thread ruft lua thread ruft Datei-Verarbeiter auf). In meiner Pseudo konsole habe ich mir einen "type" Befehl geschrieben (Lua seitig) der die Datei "direkt" ausgibt, und einen "exec" Befehl der simple Batch dateien für diese selbstgebastelte Konsole ausführt (das Verursacht zusätzliche threads mit denen sich die Anzahl der alloc/free drastisch erhöht weil die diese Funktionen benutzen um den Stapel zu bauen und Skript-Zustand zu merken). Deswegen wird im Leerlauf und in einfachen Type der Verbrauch nur um wenige KB steigen je Sekunde steigen, bei der indirekten lua-Threads Variante über "exec <batch>" (in der batch wird der type Befehl aufgerufen) steigt der Verbrauch dann MB weise.

Und ohne vertikale Sync steigt der Verbrauch am schnellsten weil dann die Threads sehr oft nach Timeouts fragen und dies ebenso über Stringverarbeitung viel stattfindet (zwischen Lua und dem Hauptprogramm durch die Skripte).

Jetzt gibt es das Problem daß der Webserver nicht Lua benutzt sondern nur PB String Verarbeitung und Konsole. Hier wird mehrmals die Sekunde in der (realen OS) Konsole die Zeit angezeigt, neben den Zugriffen die hin und wieder reinkommen. Auch hier tritt der Effekt ein (ca. 4K die Sekunde) mit dem steigeneden Speicherverbrauch.

Erst wenn die Konsole beim WS minimiert oder maximiert wird, sinkt der Verbrauch wieder auf die typischen 8MB.

Ich benutze den Debugger, aber weil es langsamer ist tritt der Effekt nicht so stark auf.Weil je ausgegebene Zeile weniger overhead erzeugt wird (pseudo: if ellapsedmilliseconds ... print(nextdata)).

Beim derzeitigen Phenomen habe ich sogar die Wahl ob ich den Windowed Screen oder die (OS-) Konsole minimiere/maxmimiere um die Freigabe zu "erzwingen". Ich denke es liegt irgendwie am OS vs PB Zusammenspiel.

Die Versuchsdateien die ich mir ausgeben lasse sind zwischen 200k und 4MB groß (Star Wars ASCII Animation).

:coderselixir:


Übrigens: lua meldet dann den Fehler "not enough memory", aber es findet dann ein bißchen neuen Speicher um so ca. jede zweite oder dritte Zeile wieder ausgeben zu können. Wie wenn der Zyklus für 1-2 Schritte angestoßen würde wenn der Speicher gesättigt ist, aber nicht alles durchläuft um alles freizugeben. Daher: mein Hauptprogramm stürtzt eigentlich auch nicht wirklich ab, kann aber auch nicht mehr richtig arbeiten. Die Freigabe findet nur noch statt wenn ich die Fenster minimiere/maximiere - eine andere Möglichkeit hat mir der Zufall noch nicht geboten.

Was der zuerst gepostete Code nicht nachbilden kann ist die verschachtelte, dynamische String verarbeitung wie beim Webserver oder die multi-lua-threaded Dateiausgabe...

Code: Alles auswählen

  ; mit dieser Procedure macht der Lua interpreter ALLE allocs / frees
  ;
  ProcedureC.i __allocHandler(*ud, *ptr, osize, nsize)
    Protected res,max
    
    If (nsize = 0) And *ptr
      ;Debug "free "+osize+" *ptr "+*ptr+"  *ud "+*ud 
         FreeMemory(*ptr);
         res = #Null 
       ElseIf nsize<0
         res = #Null
        ;Debug "SHOULD NOT HAPPEN nsize<0"
       Else 
         
         If nsize<2
           nsize=2
         EndIf 
         
         If *ptr=#Null 
            ;Debug "alloc "+osize+" -> "+nsize+" *ptr "+*ptr+"  *ud "+*ud
            res = AllocateMemory(nsize)
          Else 
            ;Debug "realloc "+osize+" -> "+nsize+" *ptr "+*ptr+"  *ud "+*ud
            ; 
            If MemorySize(*ptr)=nsize Or osize=nsize
              res=*ptr
            Else 
             res=ReAllocateMemory(*ptr, nsize)
            EndIf              

         EndIf 
     EndIf 
    ProcedureReturn res
  EndProcedure
  [/quote]
Benutzeravatar
mk-soft
Beiträge: 3695
Registriert: 24.11.2004 13:12
Wohnort: Germany

Re: Speicher wird nicht freigegeben

Beitrag von mk-soft »

Das muss eine Windows XP Macke sein. Unter Windows 10 bleibt der Speicherbedarf stabil.

Das kommt mir aber auch alles bekannt vor. Damals habe ich ein Programm immer erst Minimiert und dann versteckt um den Speichern aufräumen zu lassen.
Alles ist möglich, fragt sich nur wie...
Projekte ThreadToGUI / EventDesigner V3 / OOP-BaseClass-Modul
Downloads auf MyWebspace / OneDrive
Benutzeravatar
Mijikai
Beiträge: 754
Registriert: 25.09.2016 01:42

Re: Speicher wird nicht freigegeben

Beitrag von Mijikai »

Hm hört sich komisch an als ob es es was mit dem Fenster zu tun hat.
Ich hab mal zwei Testcodes geschrieben (habe aber momentan keinen Zugriff auf Windows XP).

Wenn es an Fenster Nachrichten oder dem Haupthread liegt sollte es einen Unterschied geben.
Bin gespannt auf das Ergebnis.

Ich kenne mich nicht mit Windows XP Memorymanagment aus evtl. bleibt der Speicher ja nur
reserviert bis er neu beantragt wird.

Code1:

Code: Alles auswählen

EnableExplicit

Procedure.i Dummy()
  Static *mem
  If *mem
    FreeMemory(*mem)
  EndIf
  *mem = AllocateMemory(1073741824)
  ProcedureReturn *mem
EndProcedure

Procedure.i Main()
  If OpenWindow(0,0,0,400,400,#Null$,#PB_Window_SystemMenu|#PB_Window_ScreenCentered|#PB_Window_MinimizeGadget)
    AddWindowTimer(0,0,100)
    Repeat
      Select WaitWindowEvent()
        Case #PB_Event_Timer
          SetWindowTitle(0,"0x" + Hex(Dummy()))
        Case #PB_Event_CloseWindow
          Break
      EndSelect
    ForEver
    CloseWindow(0)  
  EndIf  
  ProcedureReturn #Null
EndProcedure

Main()

End
Code2:

Code: Alles auswählen

EnableExplicit

Global exit.i

Procedure.i Dummy(Parameter.i)
  Protected *mem
  Repeat
    If *mem
      FreeMemory(*mem)
    EndIf
    *mem = AllocateMemory(1073741824)
    Delay(100)  
  Until exit
EndProcedure

Procedure.i Main()
  Protected thread.i
  thread = CreateThread(@Dummy(),#Null)
  If thread
    If OpenWindow(0,0,0,400,400,#Null$,#PB_Window_SystemMenu|#PB_Window_ScreenCentered|#PB_Window_MinimizeGadget)
      Repeat
        Select WaitWindowEvent()
          Case #PB_Event_CloseWindow
            Break
        EndSelect
      ForEver
      CloseWindow(0)
    EndIf
    exit = #True
    WaitThread(thread)
  EndIf  
  ProcedureReturn #Null
EndProcedure

Main()

End
Noch eine Anmerkung zu dem Code den Benubi gepostet hat:

Code: Alles auswählen

 
 ProcedureC.i __allocHandler(*ud, *ptr, osize, nsize)
...
             res=ReAllocateMemory(*ptr, nsize)
...
  EndProcedure
Wird das Ergebnis von ReAllocateMemory() nie überprüft!
Hier kann Memory geleakt werden!
Zuletzt geändert von Mijikai am 15.09.2021 12:13, insgesamt 2-mal geändert.
Benubi
Beiträge: 186
Registriert: 22.10.2004 17:51
Wohnort: Berlin, Wedding

Re: Speicher wird nicht freigegeben

Beitrag von Benubi »

Das denke ich auch; aber PB muss da ein Glitch haben, weil ja andere Programme problemlos funktionieren (u.U. sogar in PB geschriebene). Ich brauche zumindest einen Workaround, muss mal sehen ob man windoof mit nem Fake maximize/minimize event austricksen kann... Eine magische API Funktion habe ich leider nicht gefunden...

Neue Erkenntnis: Wenn ich minimiere geht der Verbrauch wieder von 130MB auf 1.8MB sprunghaft zurrück ABER der Virtuelle Speicher nicht! Der wächst weiter um 100k die Sekunde, parallel zum "normalen" Speicher (der physische Speicher nehme ich an).

Hoffentlich komme ich jetzt einer Lösung nährer... :praise:

Wieso wächst der Virtuelle Speicher unaufhörlich trotz FreeMemory()? :doh:
Benubi
Beiträge: 186
Registriert: 22.10.2004 17:51
Wohnort: Berlin, Wedding

Re: Speicher wird nicht freigegeben

Beitrag von Benubi »

@Mijikai

Sorry, habe deine Antwort beim Tippen überlesen. Ich habe code 1 und 2 am laufen. Alles ist zu 100% konstant. (Bei Code 2 meckert AVG das wär nen EVO oder sowas...). Der Effekt tritt da nicht auf.

Die Idee mit den Threads ist aber gut! Ich habe im Fullscreen Modus 9 Threads; ich habe keinen davon selbst gestartet und Lua tut sowas auch nicht. Im Fenster Modus sind es aber nur 2 Threads!?!

Derzeit läuft die test app auch mit
1) Fullscreen seit 1 Stunde ab und zu im Hintergrund, 2es mal Star Wars schauen über den Umweg, 420MB Auslastung auf 640x480.
2) Windowed seit ein paar Minuten
3) Fullscreen 1920x1080 seit ein paar Minuten

Im Taskmanager steigt die Speicherauslastung bei allen dreien ähnlich schnell mit ca. 44K pro Sekunde.
Benutzeravatar
jacdelad
Beiträge: 341
Registriert: 03.02.2021 13:39
Computerausstattung: Ryzen 5800X, 108TB Festplatte, 32GB RAM, Radeon 7770OC
Wohnort: Riesa
Kontaktdaten:

Re: Speicher wird nicht freigegeben

Beitrag von jacdelad »

Wie mk-soft schrieb ist es unter Windows 10 stabil; ich habe auch unter Windows 10 getestet. Kannst du dein großes Programm mal unter Windows 10 oder wenigstens 7 laufen lassen?
PureBasic 6.04/XProfan X4a/Embarcadero RAD Studio 11/Perl 5.2/Python 3.10
Windows 11/Ryzen 5800X/32GB RAM/Radeon 7770 OC/3TB SSD/11TB HDD
Synology DS1821+/36GB RAM/130TB
Synology DS920+/20GB RAM/54TB
Synology DS916+ii/8GB RAM/12TB
Antworten