|
JLI Spieleprogrammierung
|
Vorheriges Thema anzeigen :: Nächstes Thema anzeigen |
Autor |
Nachricht |
LeeDiGer Super JLI'ler
Anmeldedatum: 31.08.2003 Beiträge: 366 Wohnort: Duisburg Medaillen: Keine
|
Verfasst am: 21.08.2008, 21:38 Titel: Speicherfreigabe: Calloc und Free |
|
|
Gegeben ist folgende Struktur:
CPP: | struct VOXELNODE
{
string color; //0:=W 1:=B 2:=G
VOXELNODE *child[8]; //Acht Teilquader bei mittiger Teilung
}; |
Mit den folgenen Funktionen initialisiere ich einen Octree mit Farbwerten in Form eines einfachen Strings. Mit dem Befehl "calloc" werden neue Childs erstellt.
CPP: | void CMarchingCube::LoadVoxel(string octreeStr)
{
RootNode = (VOXELNODE *)calloc( sizeof( VOXELNODE), 1);
RootNode->color = octreeStr.substr(0,1);
m_iStringIndex = 0;
m_iMaxRecDepth = 0;
if(octreeStr.length() > 0)
{
LoadVoxelRec(-1, RootNode, octreeStr);
}
}
void CMarchingCube::LoadVoxelRec(int iRecDepth, VOXELNODE* n, string octreeStr)
{
iRecDepth++;
if(iRecDepth > m_iMaxRecDepth)
{
m_iMaxRecDepth = iRecDepth;
}
if(n->color == "G")
{
//Erstelle Childnode und Prüfe weiterhin auf Kollisionen
for(int i = 0; i < 8; i++)
{
m_iStringIndex++;
if(n->child[i] == NULL)
{
n->child[i] = (VOXELNODE *)calloc( sizeof( VOXELNODE), 1);
n->child[i]->color = octreeStr.substr(m_iStringIndex,1);
}
LoadVoxelRec(iRecDepth, n->child[i], octreeStr);
}
}
} |
Probleme habe ich bei der Speicherfreigabe: Mir kommt es so vor als würde sie mit dem Befehl "free" überhaupt nicht greifen.
CPP: | void CMarchingCube::FreeNode(VOXELNODE *n)
{
for(int i = 0; i < 8; i++)
{
if(n->child[i])
{
FreeNode(n->child[i]);
}
}
free(n);
} |
Was könnte der Fehler sein? Für meine Begriffe müsste die Zeile "free(n)" ausreichend sein, um den kompletten Speicher das Octrees wieder freizugeben. _________________ Kein Rückzug! Kein Aufgeben! |
|
Nach oben |
|
|
FH Super JLI'ler
Alter: 36 Anmeldedatum: 16.10.2004 Beiträge: 438
Medaillen: Keine
|
Verfasst am: 22.08.2008, 00:19 Titel: |
|
|
Nach dem ersten drüberschauen, sieht der Code gut aus.
LeeDiGer hat Folgendes geschrieben: | Mir kommt es so vor als würde sie mit dem Befehl "free" überhaupt nicht greifen.
|
Woran machst du das fest?
LeeDiGer hat Folgendes geschrieben: | Für meine Begriffe müsste die Zeile "free(n)" ausreichend sein, um den kompletten Speicher das Octrees wieder freizugeben. |
Ähm... ich nehme an, du meinst, den kompletten Speicher eines Octrees, ohne seine Unterobjekte, dafür ist ja auch die Schleife gut. Und das stimmt soweit auch (sagt zumindest die Doku).
Also, ich sehe so spontan keinen Fehler, der verhindert, dass der Speicher freigegeben wird.
Eins hab ich aber noch anzumerken: Ich persönlich finde die Verwendung von new und delete schöner. calloc und free sind doch eher alter C-Style, new und delete stammen aus C++, und sind meiner Meinung nach intuitiver zu verwenden, als calloc. Aber das muss jeder für sich wissen, sollte nur mal so ein kleiner Hinweis am Rande sein.
Gruß
FH _________________ goto work, send your kids to school
follow fashion, act normal
walk on the pavement, watch T.V.
save for your old age, obey the law
Repeat after me: I am free |
|
Nach oben |
|
|
LeeDiGer Super JLI'ler
Anmeldedatum: 31.08.2003 Beiträge: 366 Wohnort: Duisburg Medaillen: Keine
|
Verfasst am: 22.08.2008, 06:48 Titel: |
|
|
Über den Taskmanager kann man den Speicherverbrauch beobachten.
Ich habs mit New und Delete mal probiert. Komischerweise klappts. Man muss nur beachten, dass man alle Zeiger mit NULL initialisiert, wenn man Abfragen mit NULL einbauen möchte. Find ich eh schon komisch, dass die Zeiger nicht automatisch schon auf NULL sind, wenn man sie neu anlegt. Egal, dann mach ich das einfach so. _________________ Kein Rückzug! Kein Aufgeben! |
|
Nach oben |
|
|
Deviloper Junior JLI'ler
Anmeldedatum: 31.05.2006 Beiträge: 77
Medaillen: Keine
|
Verfasst am: 22.08.2008, 12:25 Titel: |
|
|
? Was meist du das sie nicht automatisch NULL sind? delete löscht nur den reservierten Speicher, auf den der Zeiger zeigt, ändert aber nichts am Zeiger selbst ... dafür schreibt man dann sowas:
CPP: | template <typename pointer_type>
inline void safe_delete(pointer_type*& pointer)
{ delete pointer; pointer = NULL; } |
Wenn man mit new nen Speicher anruft, wird automatisch der Konstruktor des Datentyps aufgerufen ... oder was meinst du?
Kannst vergleich auf null schon haben, beim erstellen ...
CPP: | int* ptr_value = new (std::nothrow) int(10);
if (ptr_value == NULL) std::cerr << "FEHLER: Es konnte kein Speicher angefordert werden!"; | z.B. ... aber sollte man lassen |
|
Nach oben |
|
|
LeeDiGer Super JLI'ler
Anmeldedatum: 31.08.2003 Beiträge: 366 Wohnort: Duisburg Medaillen: Keine
|
Verfasst am: 22.08.2008, 20:37 Titel: |
|
|
Nettes Konstrukt
Aber ich meinte eigentlich z.B.:
CPP: | VOXELNODE* RootNode = new VOXELNODE(); |
Bei dieser Abfrage würde das Programm unter Umständen falsch reagieren, weil der Zeiger ja nicht mit NULL initialisiert wurde, sondern gar nicht:
CPP: | if(n->child[i] == NULL)
{
n->child[i] = new VOXELNODE();
n->child[i]->color = octreeStr.substr(m_iStringIndex,1);
for(int k = 0; k < 8; k++)
{
n->child[i]->child[k] = NULL;
}
} |
Denn bei dem "NEW"-Konstrukt muss man noch zusätzlich einfügen:
CPP: | for(int i = 0; i < 8; i++)
{
RootNode->child[i] = NULL;
} |
Bei "calloc" wäre das gar nicht nötig gewesen, weil die Childs automatisch auf NULL gesetzt werden bei der initialisierung, wie es aussieht. _________________ Kein Rückzug! Kein Aufgeben! |
|
Nach oben |
|
|
Deviloper Junior JLI'ler
Anmeldedatum: 31.05.2006 Beiträge: 77
Medaillen: Keine
|
Verfasst am: 22.08.2008, 22:41 Titel: |
|
|
Hm sorry blick dein Problem heute abend nimmer ...
CPP: | struct VOXELNODE
{
std::string color; //0:=W 1:=B 2:=G
VOXELNODE* child[8]; //Acht Teilquader bei mittiger Teilung
public:
VOXELNODE(std::string const& color)
: color(color)
{ std::fill(child, child + 8, NULL); }
}; | ... wobei kopierkonstruktor usw. eigtl. auch überladen werden sollte |
|
Nach oben |
|
|
DirectXer Dark JLI'ler
Anmeldedatum: 05.02.2005 Beiträge: 1201 Wohnort: Köln Medaillen: Keine
|
Verfasst am: 25.08.2008, 16:32 Titel: |
|
|
das stimmt, calloc setzt bei aufruf automatisch alle bytes auf 0, ist dadurch aber auch langsamer als operator new. Wenn du das auch möchtest, solltest du dir "placement new" (->google) mal anschauen, dabei kannst du operator new und operator delete überladen und so deine eigene (klassenspezifische) Version schreiben. Wenn du willst kann ich dir ein Beispiel dazu geben.
btw:
Deviloper hat Folgendes geschrieben: | CPP: | template <typename pointer_type>
inline void safe_delete(pointer_type*& pointer)
{ delete pointer; pointer = NULL; } |
|
ist net wirklich optimal, da ein "safe"-delete auch vor dem deleten auf 0 testen sollte, sonst ist es nicht wirklich "safe":
CPP: | template <typename T> inline void safe_delete( T& p )
{
if( p ) delete p;
p = 0;
} | Außerdem ist es vorteilhafter T& als T*& zu verwenden, da das Typspektrum damit auch auf Pointer-Klassen die delete implementieren erweitert wird. Das ist aber nicht zwingend notwendig.
Gruß DXer |
|
Nach oben |
|
|
Deviloper Junior JLI'ler
Anmeldedatum: 31.05.2006 Beiträge: 77
Medaillen: Keine
|
Verfasst am: 25.08.2008, 17:11 Titel: |
|
|
Was fürn Schwachsin Sorry, aber es ist nur solange safe wie der Zeiger aufn Bereich zeigt den er löschen darf und für NULL ist definiert, dass er einfach garnichts macht. Also guck bitte erstmal im Standard nach bevor du an meinem Code rummeckerst
Das einzige, was man vllt. machen könnte, wäre das mit den Referenzen, wobei ich es "ersichtlicher" finde, wenn man nen Zeiger als Parameter haben will. Aber ja, damit grenzt man das Spektrum ein |
|
Nach oben |
|
|
Fallen JLI MVP
Alter: 40 Anmeldedatum: 08.03.2003 Beiträge: 2860 Wohnort: Münster Medaillen: 1 (mehr...)
|
Verfasst am: 25.08.2008, 20:01 Titel: |
|
|
Deviloper hat Folgendes geschrieben: | Was fürn Schwachsin Sorry, aber es ist nur solange safe wie der Zeiger aufn Bereich zeigt den er löschen darf und für NULL ist definiert, dass er einfach garnichts macht. Also guck bitte erstmal im Standard nach bevor du an meinem Code rummeckerst |
Ein oder Smiley wertet keine mangelnden Manieren auf, nächstes mal bitte etwas freundlicher. |
|
Nach oben |
|
|
Deviloper Junior JLI'ler
Anmeldedatum: 31.05.2006 Beiträge: 77
Medaillen: Keine
|
Verfasst am: 26.08.2008, 14:27 Titel: |
|
|
Sorry, etwas zu unfreundlich, hast recht. Kommt denk ich nicht wieder vor. |
|
Nach oben |
|
|
DirectXer Dark JLI'ler
Anmeldedatum: 05.02.2005 Beiträge: 1201 Wohnort: Köln Medaillen: Keine
|
Verfasst am: 26.08.2008, 16:48 Titel: |
|
|
Deviloper hat Folgendes geschrieben: | Was fürn Schwachsin :D Sorry, aber es ist nur solange safe wie der Zeiger aufn Bereich zeigt den er löschen darf und für NULL ist definiert, dass er einfach garnichts macht. Also guck bitte erstmal im Standard nach bevor du an meinem Code rummeckerst | Ahja ich sehs du hast Recht. War wohl noch wegen den alten SAFE_RELEASE makros von COM-objects bei mir hängen geblieben dass der Test auf 0 wichtig wäre. Musst aber nicht unfreundlich werden wegen sowas, wie gesagt.
Gruß DXer |
|
Nach oben |
|
|
xardias JLI Master
Alter: 38 Anmeldedatum: 28.12.2003 Beiträge: 804 Wohnort: Palo Alto, CA Medaillen: Keine
|
Verfasst am: 26.08.2008, 20:11 Titel: |
|
|
LeeDiGer hat Folgendes geschrieben: | Über den Taskmanager kann man den Speicherverbrauch beobachten. |
Würde ich nicht so direkt als Referenzwert für solche Betrachtungen hernehmen. Wenn ich mich recht erinnere zeigt dieser an wie viel Speicher Windows dem Programm zur verfügung stellt, nicht wie viel es tatsächlich braucht.
Ich denke da dürfte ein (Memory) Profiler genauere betrachtungen erlauben. |
|
Nach oben |
|
|
LeeDiGer Super JLI'ler
Anmeldedatum: 31.08.2003 Beiträge: 366 Wohnort: Duisburg Medaillen: Keine
|
Verfasst am: 05.09.2008, 12:13 Titel: |
|
|
Es können aber Speicherlecks entstehen. Und wenn das Programm den Speicher nicht korrekt freigibt, dann sieht man auch im Taskmanager, dass sich da nix tut. Ansonsten müsste den Speicherverbrauch wieder geringer werden. Insofern würde ich schon sagen, dass man da schon was erkennen kann. _________________ Kein Rückzug! Kein Aufgeben! |
|
Nach oben |
|
|
FH Super JLI'ler
Alter: 36 Anmeldedatum: 16.10.2004 Beiträge: 438
Medaillen: Keine
|
Verfasst am: 05.09.2008, 17:09 Titel: |
|
|
Das Problem ist
1.: Der Taskmanager gibt KB an. Das ist nicht gerade praktisch zum nachsehen, da sich Speicherleks normalerweise im Byte-Bereich bewegen, und daher man das über lange Zeit testen muss.
2.: Weiß ich nicht, wie Windows den Speicher intern handhabt. Ich könnte mir vorstellen, dass Windows aus Performance-Gründen einmal zugewiesenen Speicher dem Programm erstmal lässt, nach dem Motto, "Wenn du nochmal RAM willst, geb ich dir den gerade wieder, und muss nicht nach leeren Stellen suchen.", und erst, wenn der Speicher anderswo gebraucht wird, diesen Speicher einem anderen Programm zuweist.
Das ist halt die Frage. Ich kenne mich zu wenig mit Speichermanagern aus, um 2. beantworten zu können, aber da Windows ja auch Programme, die dynamisch Speicher reservieren immer wieder den selben Speicher zuweist (was etliche Cheat-Programme ausnutzen), könnte ich mir das schon vorstellen.
Also alles in allem ist mir der Taskmanager zu unzuverlässig um auf Speicherleks zu testen, weil ich den Code nicht kenne, der dahinter steckt (und da Win nicht OpenSource ist, werde ich das wohl auch nie).
Ich würde mir stattdessen eine RAM-Manager-Klasse schreiben. Jede RAM-Reservierung würde ich über diese Klasse laufen lassen. Und wenn dann beim beenden (also wenn der Destruktor der Klasse aufgerufen wird) das Programms noch RAM nicht wieder freigegeben wurde (was der RAM-Manager dann ja feststellen kann, schließlich lief jede RAM-Reservierung über ihn), kann man dann in irgendein Log-File schreiben, dass RAM noch nicht wieder freigegeben wurde. Das ist meiner Meinung nach zuverlässiger.
Die Idee mit dem RAM-Manager hab ich aus irgendeinem Buch geklaut, wobei ich jetzt suchen müsste, welches das war.
Aber falls du darauf bestehst, schau ich nochmal nach.
Gruß
FH _________________ goto work, send your kids to school
follow fashion, act normal
walk on the pavement, watch T.V.
save for your old age, obey the law
Repeat after me: I am free |
|
Nach oben |
|
|
Fallen JLI MVP
Alter: 40 Anmeldedatum: 08.03.2003 Beiträge: 2860 Wohnort: Münster Medaillen: 1 (mehr...)
|
Verfasst am: 05.09.2008, 18:41 Titel: |
|
|
Ich kann nur empfehlen sich einen Leakdetector zu beschaffen, zur Not den Von Windows/VC.
Ansonsten habe ich mit dem folgenden Leakdetektor wirklich gute Erfahrungen sammeln können, er schluckt selbst nicht allzuviel Speicher und kann sehr genaue Informationen dazu geben wo sich der Leak befindet:
http://www.codeproject.com/KB/applications/visualleakdetector.aspx |
|
Nach oben |
|
|
|
|
Du kannst keine Beiträge in dieses Forum schreiben. Du kannst auf Beiträge in diesem Forum nicht antworten. Du kannst deine Beiträge in diesem Forum nicht bearbeiten. Du kannst deine Beiträge in diesem Forum nicht löschen. Du kannst an Umfragen in diesem Forum nicht mitmachen.
|
Powered by phpBB © 2001, 2005 phpBB Group Deutsche Übersetzung von phpBB.de
|