| Autor |
Beitrag |
Jürgen Eggeling
Hält's aus hier
Beiträge: 3
|
Verfasst: Fr 21.03.03 17:28
Wir haben zu zweit eine SW geschrieben, die einmal pro Sekunde eine Paradox-Tabelle via SQL abfragt.
Dabei wird zuerst ein Filter gesetzt (Select * from Tabellenname where ...), dann die DB geöffnet, mit der gefilterten Datenmenge u.U. etwas gemacht und dann die DB wieder geschlossen. Hinter dem Schlüsselwort where folgt ein relativ komplexer Ausdruck mit mehreren zusammengesetzten Bedingungen inclusive Datumsvergleichen.
Unsere SW verbraucht dabei Speicher, d.h. wenn die SW gestartet wurde liegt laut Taskmanager der Speicherverbrauch bei z.B. 7,5 MB um dann in 64 KB (manchmal auch nur in 4 KB) Schritten langsam zu wachsen. Das geht so lange, bis das System crasht.
Bis jetzt ist nur klar, dass es irgend wie mit der Filteranweisung zusammenhängt. Läßt man den Filter weg (Select * from ... ohne Where) ist das Speicherproblem verschwunden.
Wer hat eine Ahnung, woran das liegen könnte?
Danke
|
|
kiwicht
      
Beiträge: 1021
Win 7, MacOS
Delphi x, VBA, PHP, ...
|
Verfasst: Sa 22.03.03 00:41
Titel: L
| Jürgen Eggeling hat folgendes geschrieben: | [...]
wenn die SW gestartet wurde liegt laut Taskmanager der Speicherverbrauch bei z.B. 7,5 MB |
.... 7,5 ist glaub ich noch keine Masse, in dem Bereich spielen sich so die meistens meiner Progs ab, aber egal, darum gings ja nicht
| Zitat: |
um dann in 64 KB (manchmal auch nur in 4 KB) Schritten langsam zu wachsen.
|
also wachsen heisst ja irgendwie immer, das es irgendwo ne Schleife gibt, gewollt oder ungewollt...
| Zitat: |
Bis jetzt ist nur klar, dass es irgend wie mit der Filteranweisung zusammenhängt.
|
ist es euch irgendwie klar, oder seid ihr sicher? Das wäre nämlich von essentieller Dringlichkeit zu wissen...
| Zitat: |
Wer hat eine Ahnung, woran das liegen könnte?
|
... noch ka, aber poste doch bitte mal ruhig die ganze SQL-Anweisung, das wa ma alle schmulen können...  und sag doch gleich mal, was für eine DB-Engine du benutzt.... Interbase, BDE etc. pp. ...
np 
|
|
Jürgen Eggeling 
Hält's aus hier
Beiträge: 3
|
Verfasst: Mo 24.03.03 20:55
Eine Schleife gibt es nicht; nur den Timer (also auch eine Art Schleife). Der Speicherverbrauch hängt im Originalprogramm direkt mit der Frequenz des Timers zusammen. Halbierung der Timerzeit halbiert auch fast exakt die Zeit bis zum Crash. Wird die Filteranweisung auskommentiert, wächst der Speicherverbrauch nicht mehr an.
Die SQL-Anweisung ist nicht fest codiert, sondern wird (zumindest in der "richtigen" Software) dynamisch in Abhängigkeit von den Anforderungen erstellt.
Ich habe ein kleines Testprogramm geschrieben und die wichtigsten Routinen der eigentlichen Applikation hineinkopiert. Das Ergebnis ist etwas länglich...
Einige Anweisungen sind überflüssung und nur zum Testen (Memo-Feld etc.) eingebaut.
Gruß Jürgen
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164:
| type TEventDataRecord = RECORD Create : TDateTime; UserID : String[5]; MasterID : ShortInt; SubID : ShortInt; ShortInt1 : ShortInt; ShortInt2 : ShortInt; ShortInt3 : ShortInt; ShortInt4 : ShortInt; ShortInt5 : ShortInt; LongInt1 : LongInt; LongInt2 : LongInt; END;
procedure Apply_SQL_Command ( SystemLog, SystemEvent, UserEvent, Alarm : Boolean; SubID : ShortInt; FromDate : TDateTime; ToDate : TDateTime ) ;
const Start_SQL = 'SELECT * FROM EVENT'; S_Date1 = '(CreateAt BETWEEN "xfromdate" AND "xtodate")'; S_Date2 = '(CreateAt = "xfromdate")'; S_SubID = '(SubID=xsubid)';
var count, mastercount, spos, slen : Integer; NewSQL, V_Date, V_SubID, V_MasterID, Replace : string;
function PatchStr ( MainStr, OldStr, NewStr : string ) : string; begin spos := pos ( OldStr, MainStr ); slen := length ( OldStr ); delete ( MainStr, spos, slen ); insert ( NewStr, MainStr, spos); Result := MainStr; end;
begin
// SQL Variablen und Basisstring generieren Count := 0; Mastercount := 0; DBForm.Query1.SQL.Clear ; DBForm.Query1.SQL.Add (Start_SQL) ;
V_MasterID := '(MasterID BETWEEN 0 AND 9)'; if (SystemLog) then begin if count > 0 then NewSQL := 'AND ' else NewSQL := 'WHERE ('; NewSQL := NewSQL + V_MasterID; inc(count); inc(mastercount); DBForm.Query1.SQL.Add(NewSQL) ; end;
V_MasterID := '(MasterID BETWEEN 10 AND 19)'; if (SystemEvent) then begin if count > 0 then if mastercount > 0 then NewSQL := 'OR ' else NewSQL := 'AND ' else NewSQL := 'WHERE ('; NewSQL := NewSQL + V_MasterID; inc(count); inc(mastercount); DBForm.Query1.SQL.Add(NewSQL) ; end;
V_MasterID := '(MasterID BETWEEN 20 AND 29)'; if (UserEvent) then begin if count > 0 then if mastercount > 0 then NewSQL := 'OR ' else NewsQL := 'AND ' else NewSQL := 'WHERE ('; NewSQL := NewSQL + V_MasterID; inc(count); inc(mastercount); DBForm.Query1.SQL.Add(NewSQL) ; end;
V_MasterID := '(MasterID BETWEEN 30 AND 39)'; if (Alarm) then begin if count > 0 then if mastercount > 0 then NewSQL := 'OR ' else NewSQL := 'AND ' else NewSQL := 'WHERE ('; NewSQL := NewSQL + V_MasterID; inc(count); inc(mastercount); DBForm.Query1.SQL.Add(NewSQL) ; end;
if (SubID > 0) then begin if count > 0 then NewSQL := 'AND ' else NewSQL := 'WHERE '; Replace := IntToStr (SubID) ; V_SubID := PatchStr( S_SubID, 'xsubid', Replace ); NewSQL := NewSQL + V_SubID; inc(count); DBForm.Query1.SQL.Add(NewSQL) ; end;
if (FromDate > 0) AND (ToDate > 0) then begin if count > 0 then NewSQL := 'AND ' else NewSQL := 'WHERE '; if FromDate = ToDate then begin Replace := DateTimeToStr(FromDate) ; V_Date := PatchStr( S_Date2, 'xfromdate', Replace ) ; end else begin Replace := DateTimeToStr(FromDate) ; V_Date := PatchStr( S_Date1, 'xfromdate', Replace ) ; Replace := DateTimeToStr(ToDate) ; V_Date := PatchStr( V_Date, 'xtodate', Replace ) ; end ; NewSQL := NewSQL + V_Date ; DBForm.Query1.SQL.Add(NewSQL) ; end;
if Mastercount > 0 then DBForm.Query1.SQL.Add(')') ;
end; // GenerateSQLCommand
procedure TDBForm.Timer1Timer(Sender: TObject); begin
DateNow := Date+Time ; // nur zur Simulation eines Zeitbereiches
// Session mit richtigem Pfad initialisieren und öffnen Session1.Active := FALSE ; Session1.NetFileDir := '..\Databases' ; Session1.Active := TRUE ;
// Datenbankpfad setzen DBForm.Query1.Close; DBForm.Query1.DatabaseName := '..\Databases' ;
// Datenbank filtern und öffnen Apply_SQL_Command ( FALSE, FALSE, FALSE, FALSE, 01, DateLast, DateNow) ; DBForm.Query1.Open ; DBForm.Query1.Refresh;
// Zum Test Abfragestring und Anzahl der Ergebnisse anzeigen DBForm.Memo1.Lines := DBForm.Query1.SQL ; Label4.Caption := IntToStr (DBForm.Query1.RecordCount) ;
// Datenbank und Session schliessen Query1.Close ; Session1.Close ; DateLast := DateNow ;
end;
procedure TDBForm.FormShow(Sender: TObject); begin DateLast := Date+Time ; end; |
Moderiert von UGrohne: Code-Tags eingefügt (gehts auch kürzer? )
|
|
Klabautermann
      

Beiträge: 6366
Erhaltene Danke: 60
Windows 7, Ubuntu
Delphi 7 Prof.
|
Verfasst: Di 25.03.03 10:21
Hallo,
das ist jetzt mal wieder nurt geraten, aber vieleicht hängt das Problem damit zusammen, das ein Timerdurchlauf noch nciht beendet ist, wenn der nächste schon wieder ausgelöst wird. So das dein Timer dan pseudo-rekursiv ist und so den Speicher füllt. Wenn du bei deinem Query die Where-Anweisung weg lässt, dann wird dieses schneller angearbeitet und der Effekt tritt nicht auf. Da ja jeder Procedureaufruf im Selben Thread läuft, ist auch nicht zu erwarten, das sie "paralel" beendet werden.
Kurz und gut, ich würde dir empfehlen, den Timer zu deaktivieren, bis er abgearbeitet ist:
Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| procedure TDBForm.Timer1Timer(Sender: TObject); begin Timer1.Enabled := FALSE; // Den Timer deaktivieren DateNow := Date+Time ; // nur zur Simulation eines Zeitbereiches
[...]
DateLast := DateNow ; Timer1.Enabled := TRUE; // Den Timer wieder aktivieren end; |
Gruß
Klabautermann
|
|
Jürgen Eggeling 
Hält's aus hier
Beiträge: 3
|
Verfasst: Do 27.03.03 22:07
Titel: Timer
Hallo Klabautermann,
danke für den Tipp, vermutlich liegt es jedoch nicht daran. In der "richtigen" Software beträgt der Timerzyklus 5 Sekunden. Das solllte auf einem Athlon System locker reichen. Bei mir läuft das Ganze im Prinzip noch bei 0,1 Sekunden über das Netz.
Es ist sicherlich ein besserer Stil, wenn man am Anfang der Routine den Timer anhält und am Ende wieder startet. Ich werde das mal testen.
Gruß Jürgen.
|
|
|