PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Variablen und Objekte richtig loswerden


@work
2009-03-07, 16:34:55
Hallo Forum,

dieser Thread fällt unter die Kategorie "Was ich noch nie so richtig verstanden habe".
Mir geht es um... ich weiß nichtmal um was genau, ich glaube man nennt es Speicherverwaltung. Was man mit Variablen und Objekten anstellen muss, wenn man sie nicht mehr braucht, oder was man mit ihnen sogar machen MUSS, wenn man ein stabiles Programm schreiben möchte...
Primär interessiert mich das für PHP, desweiteren für C# (und somit wohl automatisch für jede.net Sprache) und schließlich für C/C++ ... damit ich mal einen Vergleich habe.

Angefangen habe ich damals mit PHP, und ich glaube, da liegt das Problem begraben - aufgrund der fehlenden Härte, wie sie andere Sprachen aufweisen.
Dort kann ich ja Variablen initialisieren, wie ich lustig bin. Und wenn ich sie nichtmehr brauche, dann lass ich sie einfach links liegen und mache garnichts damit. Selbiges gilt für Objekte. Zwar habe ich unset() und NULL entdeckt, aber ich habe mir sagen lassen, PHP löscht sowieso alles automatisch, wenn das Skript durchgelaufen ist. Ich solle mir da um nichts sorgen machen. Das gilt wohl nur für PHP, oder?

Weiter gehts mit C#. Habe mir dazu ein Buch durchgelesen, welches aber wohl für blutige Anfänger geschrieben wurde. Wenn es um irgendetwas mit Speicher geht, schreibt das Buch immer nach dem Motto "Scheiss drauf, der Garbage Collector macht das schon". Also programmiere ich genauso weiter wie in PHP. "NULL" habe ich bisher nicht verwendet, weil ich es nicht besser weiß. Gilt hier das gleiche wie für PHP, einfach links liegen lassen?
Das kann ich irgendwie garnicht glauben. Der GC weiß doch garnicht, was ich noch brauche, und was nicht, imho verschlingt der GC doch auch nur "genullte" Objekte?
Das einzige, was ich in diese Richtung mache, ist Dispose aufzurufen, also bei den Klassen, die es implementieren. Aber das hat ja nicht unbedingt was mit dem Speicher zu tun, sondern eher mit anderen Fremdressourcen.

Weiter gehts mit C/C++. Ich habe zwar noch nie darin programmiert, allerdings aus Interesse (vor allem gerade wegen der Speichersache) mal Tutorials im Netz angeschaut. Was ich sehnsüchtig erwartet habe war eine Codestelle, an der man deutlich sieht, wie Variablen gelöscht werden (irgendwie muss der Speicher ja frei werden, einen GC gibts ja nicht). Und was war? Pustekuchen. Das Programm läuft aus und das wars.

In welcher der 3 Sprachen muss man nun was tun?

Danke :)

Oid
2009-03-07, 16:45:30
Zumindest in C werden die meisten Variablen ja auf dem Stack angelegt. Wenn die entsprechende Funktion, in der die Variable benötigt wird, verlassen wird, wird die Variable wieder aus dem Stack "entfernt".

Mit anderen Programmiersprachen kenn ich mich zwar nicht so aus, aber ich denke mal, dass es bei den meisten Hochsprachen Schutzmechanismen gibt. Wenn man also nicht extrem auf die Performance wert legt, muss man sich um die "Entsorgung" von Variablen keine Gedanken machen.

RattuS
2009-03-07, 17:55:48
Bei Unterprogrammen (a.k.a Funktionen/Prozeduren/Methoden) werden Variablen im Stack angelegt. Am Ende des Unterprogrammes wird durch die beiden Opcodes ret/leave veranlasst, dass sich der Stackpointer ESP (Register, das die Positonierung auf dem Stack verwaltet) anpasst. Der Stack ansich behält seine Daten, da sie sowieso permanent überschrieben werden. Bei Variablen, die im Heap angelegt werden, muss der Speicherbereich natürlich wieder freigegeben werden. Nothing/null hat nichts mit dem Speicherinhalt ansich zu tun. Es steht lediglich dafür, dass die Variable auf eine ungültige Adresse im Speicher zeigt. Das "Säubern" der Variable bedeutet eigentlich nur, dass der Speicherbereich genullt (mit 0 beschrieben wird) wird und das Heap-Handle aufgelöst wird. Im .NET macht das der GC recht zuverlässig, soviel ich weiß. Wobei ich an dieser Stelle auch gern wüsste, ob .Dispose beim 3.5er Framework überhaupt noch nötig ist. :confused:

pest
2009-03-07, 18:03:37
unter C++ habe ich mir angewöhnt alles mit delete zu löschen was ich mit new erstellt hatte
wenn die app beim delete dann abschmiert erkennt man recht schnell inkonsistenzen im code, was mir z.Z. bei delphi total fehlt

Monger
2009-03-07, 18:23:58
Der Garbage Collector in Java und .NET ist ziemlich ähnlich. In beiden Fällen werden die Referenzen zu einem Objekt gezählt. Steht der Zähler auf Null, darf (und nicht etwa muss) der GC irgendwann mal zuschlagen - schließlich gibt es niemanden mehr der dieses Objekt erreichen könnte.

Das Genulle ist Quatsch. Der GC kann ohnehin erst frühestens nach Verlassen der Methode aktiv werden, und selbst dann nicht zwingend. Am einfachsten den Scope von Variablen immer so gering halten wie möglich, und dafür sorgen dass Collections sauber sind, dann ist die Gefahr auch niedrig dass man irgendwelche Referenzen vergisst.

Die Philosophie hinter dem GC ist, dass es meistens nicht allzu klug ist im Programm selber den Speicher sauber zu halten. In aller Regel passiert das dann nämlich immer dann, wenn das Programm selber gerade viel zu tun hat. Dann zusätzlich nochmal Rechenleistung zu verbraten um an dringend benötigte Ressourcen ranzukommen, führt gerne zu Engpässen.

Der GC springt also an, wenn a) sowieso gerade nix auf der CPU passiert, oder b) eben doch ganz dringend Speicher gebraucht wird.
Da steckt ne Menge Intelligenz dahinter, und da der GC nicht nur ein, sondern alle Programme in der .NET Runtime sieht, kriegt er das (in aller Regel) auch wesentlich besser hin als man das selbst je lösen könnte.

Fazit: Finger weg vom GC! Und im übrigen auch vom Finalize.

RattuS
2009-03-07, 18:32:57
Das Freigeben macht also nur in Situationen Sinn, bei denen der Speicher sofort wieder sauber sein muss (z.B. vor/nach speicherintensiven Operationen/Rekursion) und nicht auf Freiraum für den GC gewartet werden kann.

Monger
2009-03-07, 18:50:36
Das Freigeben macht also nur in Situationen Sinn, bei denen der Speicher sofort wieder sauber sein muss (z.B. vor/nach speicherintensiven Operationen/Rekursion) und nicht auf Freiraum für den GC gewartet werden kann.
Nein! Noch einmal: vertrau dem GC!

Der GC kann viel besser als du beurteilen wann wirklich Speicher benötigt wird, und woher er den am besten kriegt. Vergiss nicht, dass dein Programm wahrscheinlich nicht die einzige Software auf dem PC ist.
Oft genug überschätzt man auch, wie wichtig Speicher ist. CPU Zeit ist meistens viel kostbarer. Speicherintensive Operationen sind oft genug auch CPU-intensiv, und manchmal ist es dann tatsächlich besser ein wenig zu swappen als die CPU durch weitere rechenintensive Aufräumarbeiten zu belasten.
Und nicht vergessen: was auf deinem Rechner super läuft, kann auf einer anderen Zielplattform ganz anders aussehen. Versuch nicht schlauer als der GC zu sein - das schaffst du nämlich wahrscheinlich nicht.

Der_Donnervogel
2009-03-07, 19:10:32
Das von Monger geschriebene stimmt für Sprachen mit GC, aber da der Threadstarter ja auch noch C++ angesprochen hat. Dort ist es nicht so, dass der ganze Speicher automatisch wieder aufgeräumt wird. Dies gilt dort nur für den Stack. Alle Variablen die auf dem Heap liegen (also über new angefordert werden) müssen explizit mittels delete wieder freigegeben werden, oder der Speicher bleibt belegt bis das Programm beendet wird. @TS: Bei C++ Programmen am besten nach dem Destructor suchen (Funktion die wie die Klasse heißt aber mit einem ~ beginnt; wird aufgerufen wenn ein Objekt zerstört wird), dort sollten sich meistens auch delete-Anweisungen finden.

rotalever
2009-03-07, 22:56:31
Als einfachstes Beispiel in C++

void function()
{
int a[2]; // wird automatisch beim verlassen der Funktion freigegeben
a[0] = 3;
int * b = new int[2]; // muss über delete gelöscht werden
b[0] = 3;
delete [] b;
return;
}

moBi
2009-03-08, 03:56:15
Bei "klassischen" Variablen ist das richtige Loswerden meiner Erfahrung als nicht profesioneller Programmierer nach eher weniger ein Problem. Lokale Variablen werden automatisch bei Funktions/Methodenende gekillt(Stack) und globale Variablen sind ja eigentlich nicht zum Löschen gedacht und eher zu vermeiden.

Sobald man sich ums Loswerden kümmern muss, hat es irgendwas mit Zeigern(Arrays, dynamischer Speicher) zu tun, zumindest soweit meine begrenzte Erfahrung in C/C++.

Hier mal ein konkretes C Beispiel das zeigt was passiert, wenn man permanent Speicher allokiert und ihn nicht wieder richtig los wird. :biggrin:


#include <stdio.h>
#include <stdlib.h>
double *S; //Zeiger vom Typ double
int main(int argc, char *argv[])
{
while(1)
{
S= malloc(sizeof(double)); //Zeiger zeigt jetzt auf reservierten Speicherplatz vom "Typ" double
//Hier fehlt das rettende "free(S);" um den reservierten Speicherplatz wieder frei zu geben
}
}

Trap
2009-03-08, 13:04:36
Der Garbage Collector in Java und .NET ist ziemlich ähnlich. In beiden Fällen werden die Referenzen zu einem Objekt gezählt. [...]

Das Genulle ist Quatsch.
Wenn der GC tatsächlich Referenzen zählen würde, wäre Nullen von Selbstreferenzen kein Quatsch, ansonsten geht der Referenzzähler nämlich nie auf 0. In manchen Sprachen gibt es sowas, soweit ich weiß gehören Python und PHP dazu.

In Java und C# gibt es aber einen anderen GC, der ausgehend von allen globalen Variablen und den Variablen auf dem Stack versucht andere Objekte zu erreichen. Gelöscht werden nur unerreichbare Objekte.
Dabei ist Nullen von Referenzen nur sehr selten sinnvoll, nämlich nur bei globalen Variablen. Wobei globale Variablen in C# und Java ja anders heißen => static class properties/members

Monger
2009-03-08, 15:18:43
Dabei ist Nullen von Referenzen nur sehr selten sinnvoll, nämlich nur bei globalen Variablen.
... oder innerhalb von Arrays. Wenn das passiert, sollte man sich allerdings fragen, ob man die richtige Collection verwendet.

Bei "globalen" Variablen (sprich: Attributen) ist es in aller Regel auch so, dass die nur dann ungültig werden, wenn sie durch einen anderen gültigen Wert ersetzt werden. Ein Objekt sollte sich eigentlich ganz grundsätzlich immer in einem validen Zustand befinden - was halt meistens auch heißt, dass alle Attribute einen gültigen Wert besitzen.

Natürlich gibt es Beispiele dafür wo man Referenzen explizit auf Null setzen muss, aber das passiert sehr, sehr selten. Und das sind dann keine Performance Gründe! ;)

Coda
2009-03-08, 15:43:17
Der Garbage Collector in Java und .NET ist ziemlich ähnlich. In beiden Fällen werden die Referenzen zu einem Objekt gezählt. Steht der Zähler auf Null, darf (und nicht etwa muss) der GC irgendwann mal zuschlagen - schließlich gibt es niemanden mehr der dieses Objekt erreichen könnte.
Nein. Das würde nicht funktionieren.

RMC
2009-03-08, 15:56:14
Nein. Das würde nicht funktionieren.

Grund?

Trap
2009-03-08, 15:58:59
Den Grund hab ich doch oben schon geschrieben...

Coda
2009-03-08, 15:59:04
A zeigt auf B und B zeigt auf A, beide werden aber nirgends mehr verwendet.

Monger
2009-03-08, 16:12:42
A zeigt auf B und B zeigt auf A, beide werden aber nirgends mehr verwendet.

Klar, Inseln müssen da auch nochmal aufgelöst werden. Aber afaik gilt trotzallem sowohl für den Objekt Heap bei Java als auch bei .NET, dass da die Abhängigkeiten zu anderen Objekten gezählt werden. Dass der GC noch n bißchen komplexer ist als das, sollte klar sein.

Trap
2009-03-08, 16:18:57
Referenzen zählen halbiert die Performance bei Anwendungen wie verkettete Liste umdrehen oder ähnlichem Code mit viel Veränderungen an Referenzen.

Die JVM oder die .NET-VM benutzen generational GCs, die benutzen immer tracing und kein reference counting.

ScottManDeath
2009-03-08, 21:19:56
A zeigt auf B und B zeigt auf A, beide werden aber nirgends mehr verwendet.

Das ist auch das Problem wenn man boost::shared_ptr verwendet, denn dieser nutzt Referenzenzaehlen. Hilft aber ungemein um sich vor delete zu druecken. =)

Hab gerade mal in meinem Code der letzten 2/3 Jahre geguckt, hab da tatsaechlich kein delete drinne :eek:

Coda
2009-03-09, 00:18:47
shared_ptr ist hilft auch sich vor Copy-Constructors und operator= zu drücken ;)