Autor Beitrag
goCM
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 18



BeitragVerfasst: So 17.08.08 16:04 
Bei meinem Programm zum Kopieren von Daten schlägt das Out Of Memory Phänomen zu.

Eine kurze Ablaufbeschreibung des Programms:

Die zu kopierenden Ordner werden mit FindAllFolders aufgelistet und mit ForceDirectory erstellt.
Danach werden die Dateien in einer Listbox aufgelistet, der Pfad wohin sie kopiert werden sollen wird zurechtgeschnitten und in einer zweiten ListBox bereitgestellt.
Um unerwünschte Dateien herauszufilten werden Diese dann aus den ListBoxen gelöscht.
Jetzt fängt der Kopiervorgang an und die Dateien werden mit FileCopyEx kopiert.

3 Beispiele:

1. Es läuft ohne Probleme mit wenigen Ordnern und Dateien:
Bei mehreren Ordnern die ich hinzufüge und die nicht soviele Dateien haben zeigt mit FastMM4 folgendes an:

Zitat:

Diese Anwendung hat Speicher-Lecks. Die kleineren Speicher-Lecks sind (ausschließlich von Zeigern registrierte Lecks):

5 - 12 bytes: TIdThreadSafeInteger x 1
21 - 28 bytes: TIdCriticalSection x 2


2. Es läuft mit wenigen großen Dateien

Wenn ich wenige Dateien kopiere , die aber groß sind (etwa insgesamt 2GB ), gibt es keine Probleme, die Anzeige von FastMM4 ist wie in 1.


3. Es gibt ein Out of Memory bei vielen Ordnern und Dateien
Ich füge meinen aktuellen ProfilOdrner hinzu( 3,5GBMB. 11000 Dateien, 3100 Ordner) hinzu:

Zitat:

Diese Anwendung hat Speicher-Lecks. Die kleineren Speicher-Lecks sind (ausschließlich von Zeigern registrierte Lecks):

5 - 12 bytes: TIdThreadSafeInteger x 1, Unbekannt x 719
21 - 28 bytes: TIdCriticalSection x 2
501 - 548 bytes: Unbekannt x 719

Auszug aus Fast MM-Log:
Ein Speicher-Leck hat folgende Größe : 548

Stackverfolgung - Speicherpuffer wurde zugeordnet (Rückgabeadresse):
40311E
45BDC2
45F7C3
43F645
45F914
45F7C3
45EEEB
429EAA
7E368734 [Unknown function at GetDC]
7E368816 [Unknown function at GetDC]

Der Speicherpuffer wird gegenwärtig für ein Objekt der folgenden Klasse verwendet: Unbekannt

The allocation number is: 2400766

Aktueller Speicherauszug von 256 Byte, angefangen an der Zeigeradresse: 7F827D40:
82 43 3A 5C 44 6F 6B 75 6D 65 6E 74 65 20 75 6E 64 20 45 69 6E 73 74 65 6C 6C 75 6E 67 65 6E 5C
41 64 6D 69 6E 69 73 74 72 61 74 6F 72 2E 57 49 4E 44 4F 57 53 50 43 5C 41 6E 77 65 6E 64 75 6E
67 73 64 61 74 65 6E 5C 54 68 75 6E 64 65 72 62 69 72 64 5C 50 72 6F 66 69 6C 65 73 5C 78 73 32
62 6E 71 37 34 2E 64 65 66 61 75 6C 74 5C 75 72 6C 63 6C 61 73 73 69 66 69 65 72 32 2E 73 71 6C
69 74 65 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80
80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80
80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80
80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80
‚ C : \ D o k u m e n t e u n d E i n s t e l l u n g e n \
A d m i n i s t r a t o r . W I N D O W S P C \ A n w e n d u n
g s d a t e n \ T h u n d e r b i r d \ P r o f i l e s \ x s 2
b n q 7 4 . d e f a u l t \ u r l c l a s s i f i e r 2 . s q l
i t e € € € € € € € € € € € € € € € € € € € € € € € € € € € € €
€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € €
€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € €
€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € €


Kopiert wurden im Endeffeckt nur rund 50MB von den 3,5GB.

Ich gebe noch ein paar Angaben mit die vllt helfen könnten:
Der Taskmanager zeigte mir beim Out Of Memory insgesamt (für das gesamte System) etwa 12.700 Handles an.
Mein Programm hatte etwa 1.900 Threads gestartet.

Ich würde mich über ein paar Winks mit dem Betonpfeiler sehr freuen.
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: So 17.08.08 17:01 
Zeig uns mal deinen Code.

user profile icongoCM hat folgendes geschrieben:
Mein Programm hatte etwa 1.900 Threads gestartet.

Was versprichts du dir davon? Hat dein System 1900 Festplatten oder hat deine Festplatte 1900 Schreib- und Leseköpfe?
goCM Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 18



BeitragVerfasst: So 17.08.08 17:28 
user profile iconLuckie hat folgendes geschrieben:

Was versprichts du dir davon? Hat dein System 1900 Festplatten oder hat deine Festplatte 1900 Schreib- und Leseköpfe?

Ich weiß es nicht besser :/

ausblenden volle Höhe Delphi-Quelltext
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:
//>>>>>>><<<<<<<<<<<<<>>>>>>>>>><<<<<<<<<<>>>>>>>>>>>>>>>
//-------------------------------------------------------
//                Ordner anlegen      am Ziel
//-------------------------------------------------------
//>>>>>>><<<<<<<<<<<<<>>>>>>>>>><<<<<<<<<<>>>>>>>>>>>>>>>

            begin
              for i:=0 To ListBox7.Count-1 Do
               begin
                label5.Caption:='Ordner werden angelegt';
                {$IOChecks off}
                 S:='';
                 S:=ListBox11.Items[0]+ListBox7.Items[i];
                 ForceDirectories(S);
                 Application.ProcessMessages;
                 {$IOChecks on}
               end;
                  ListBox7.Clear;
                    begin
                      excludeFiles;
                    end;
// ---------------------------------------
//   Einzelne Dateien kopieren
//-----------------------------------------
                      begin
                       Sicherungsdatei;             //Dateiliste wird angelegt
                       label5.Visible:=True;
                       label5.Caption:='Dateien werden kopiert';
                        begin
                          SendMailwithAtt;         // Dateienliste wird versandt
                          GroupBox9.Visible:=True;
                          ProgressBar1.visible:=True;
                            for p:=0 To ListBox4.Count -1 Do
                             begin
                              cancelCopy := False;
                              New(Params);
                              Params.Dest := ListBox11.Items[0]+ListBox18.Items[p];
                              Params.Source :=ListBox4.Items[p];
                              Params.Handle := Handle;
                              CloseHandle(BeginThread(nil, 0, @CopyExThread, Params, 0, ThreadID));
                              GetExitCodeThread(Params.Handle,ExitCode);
                             end;
                        end;
                      end;


Nachdem Du das mit den 1900 Threads angesprochen hast:
Wäre folgendes eine Lösung?:
ausblenden Delphi-Quelltext
1:
2:
3:
              Params.Handle:=BeginThread(nil, 0, @CopyExThread, Params, 0, ThreadID);
              WaitForSingleObject(Params.Handle,2000);
              GetExitCodeThread(Params.Handle,ExitCode);

Die Anzahl an Threads geht jetzt auf 10 rauf und läuft damit bisher stabil durch.
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: So 17.08.08 19:22 
Gibst du den Speicher im Thread auch wieder frei, den du mit params belegt hast?
goCM Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 18



BeitragVerfasst: So 17.08.08 19:53 
user profile iconLuckie hat folgendes geschrieben:
Gibst du den Speicher im Thread auch wieder frei, den du mit params belegt hast?

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
function CopyExThread(p: PCopyEx): Integer;
var
  Source: String;
  Dest: String;
  Handle: THandle;
  Cancel                 : PBool;
begin
begin
  Source := p.Source;
  Dest := p.Dest;
  Handle := p.Handle;
  Cancel := PBOOL(False);
  CopyFileEx(PChar(Source), PChar(Dest), @CopyFileProgress, Pointer(Handle), Cancel, 0);
  Dispose(p);
  result := 0;
end;
CloseHandle(Handle);   //Hier ist die Freigabe
end;


Das Programm bleibt stabil bei den Threads und Handles. Gibt es eigentlich TShirts mit: I love Luckie für Dein Tutorial ?
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Mo 18.08.08 00:04 
user profile icongoCM hat folgendes geschrieben:
user profile iconLuckie hat folgendes geschrieben:
Gibst du den Speicher im Thread auch wieder frei, den du mit params belegt hast?

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
function CopyExThread(p: PCopyEx): Integer;
var
  Source: String;
  Dest: String;
  Handle: THandle;
  Cancel                 : PBool;
begin
begin
  Source := p.Source;
  Dest := p.Dest;
  Handle := p.Handle;
  Cancel := PBOOL(False);
  CopyFileEx(PChar(Source), PChar(Dest), @CopyFileProgress, Pointer(Handle), Cancel, 0);
  Dispose(p);
  result := 0;
end;
CloseHandle(Handle);   //Hier ist die Freigabe
end;
Ich meinte eigentlich die Zeile mit Dispose.

user profile icongoCM hat folgendes geschrieben:
Das Programm bleibt stabil bei den Threads und Handles.
Windows ist wahrscheinlich einfach die Puste ausgegangen. Du hast 2 GB Arbeitsspeicher (bei einem 32-Bit Betriebssystem) für deinen Prozess. Jeder Thread bekommt aber einen eigenen Stack mit einer Anfangsgröße von einem MB, wenn ich das richtig in Erinnerung habe, der bei Bedarf entsprechend wächst. Deine 1900 Threads (= 1900 MB für die Stacks) haben also schon deine 2 GB Adressraum belegt, bzw. sie haben es versucht. Din programm hat sich quasi selber die Luft zum Atmen genommen. ;)

Moderiert von user profile iconNarses: Beitragstext aus dem Zitat rausgeschoben
Logikmensch
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 390

Win XP
Delphi 2007 Prof., XE2, XE5
BeitragVerfasst: Di 19.08.08 09:05 
Ich schließe mich an. Für jede zu kopierende Datei einen eigenen Thread zu erstellen, halte ich für den falschen Ansatz. Was man damit an "Gleichzeitigkeit" erreichen würde (wenn es denn ginge), würde mit gegen Null strebender Performance zunichte gemacht.

Es spricht doch nichts dagegen, die Liste im Hauptthread anzulegen und sie in einem anderen Thread (dem "Kopiersklaven") abarbeiten zu lassen, aber dann bitte Datei für Datei. Der Kopierthread kann dann das, was er bereits erledigt hat, entweder aus der Liste rausschmeißen oder irgendwie markieren, damit er die bereits abgearbeiteten Listeneinträge nicht nochmal abzuarbeiten versucht.

_________________
Es gibt keine Probleme - nur Lösungen!
goCM Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 18



BeitragVerfasst: Di 19.08.08 18:10 
Knapp über 55.000 Dateien hat es ohne Probleme kopiert, mit 220.000 läuft gerade ein Test durch.
Das Programm könnte ein bisschen schneller sein bei der Initialisierung, was bei mir das Auflisten der Ordner und Dateien und vorallem das Zurechtparsen der Zielpfade darstellt. Beim Parsen kann ich sicherlich noch ein bisschen was optimieren und so ein bisschen Zeit rausholen.

Das Einzige was im Moment wirklich stört ist, das die Progressbar nicht mehr anspringt^^
Logikmensch
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 390

Win XP
Delphi 2007 Prof., XE2, XE5
BeitragVerfasst: Mi 20.08.08 12:39 
Ja, okay,

aber ich sehe den Nutzen nicht. Das System wird die Dateikopiererei dadurch sicher nicht wirklich (nur scheinbar) schneller abarbeiten, wenn alle auf einen Schlag gestartet werden. Es muss ja nicht nur die geballte Ladung an Threads bearbeitet werden, sondern auch die mit jedem Thread geöffneten Ressourcen (Datei-Handle, etc.).

Okay, auf einer richtigen Hochleistungsmaschine hat diese Methode eventuell Sinn. ;-)

_________________
Es gibt keine Probleme - nur Lösungen!
goCM Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 18



BeitragVerfasst: Mi 20.08.08 20:47 
user profile iconLogikmensch hat folgendes geschrieben:
Ja, okay,

aber ich sehe den Nutzen nicht. Das System wird die Dateikopiererei dadurch sicher nicht wirklich (nur scheinbar) schneller abarbeiten, wenn alle auf einen Schlag gestartet werden. Es muss ja nicht nur die geballte Ladung an Threads bearbeitet werden, sondern auch die mit jedem Thread geöffneten Ressourcen (Datei-Handle, etc.).

Okay, auf einer richtigen Hochleistungsmaschine hat diese Methode eventuell Sinn. ;-)



Werden sie ja nicht mehr, das "gleichzeitige" Öffnen war ja Ursache der Out of Memory Effekte. Seitdem die Threadanzahl begrenzt wurde, läuft es ohne Probleme.