@kingruedi
-
Ja.
Darüber kann man natürlich streiten (ich persönlich stimme dir zu). Aber so wie es jetzt ist, ist es IMHO falsch.
-
Optimizer schrieb:
Aber so wie es jetzt ist, ist es IMHO falsch.
Naja, ich finde finalize uncool.
Ich werden den Sinn von nicht deterministischem Aufräumen vermutlich nie verstehen.Aber dass man RAII und finalize nicht direkt vergleichen kann - da stimme ich dir zu.
-
Es ist IMHO unmöglich, automatische Resourcenfreigabe effizient deterministisch zu implementieren. Referenzcounting ist nicht zuverlässig und weitergehende Checks sind wohl schon zu wenig performant, um nach jedem Block ausgeführt zu werden. Für nicht-kritische Resourcen ist es ja auch total egal.
-
bei C++/CLI geht doch auch beides
-
Du hast in allen 3 Sprachen keine gleichzeitig vollautomatische und deterministische Resourcenfreigabe. In C++/CLI kannst du es dir nur am feinsten aussuchen, was du willst.
-
Du hast recht, dass ist vielleicht nicht vergleichbar.
Ich finde es wird nur problematisch mit der expliziten Resourcenfreigabe, wenn man keine Aussagen über den Ablauf des Programmes machen kann. Das Problem tritt eben besonders dann auf, wenn man sich um Exceptionhandling kümmern muss. Vorallem die Form, die C++ eingeführt hat (ok, wahrscheinlich gab es das vorher schon irgend wo anders
), macht es besonders schwer. C++ federt das Problem eben mittels RAII ab. In Java und .NET ist das nicht möglich. Also versucht man an den Exceptions rumzubasteln, was ich persönlich auch eher als unbefriedigende Lösung empfinde. Das Problem lässt sich aber vermutlich nicht anders lösen. Den GC beim verlassen eines jeden Blocks durchzujagen dürfte wirklich nicht praktikabel sein. Wie skaliert eigentlich der Java und der .NET GC?
Warum hat man eigentlich nicht komplett auf finalize verzichtet?
Vielleicht geh ich an Java einfach viel zu sehr mit eine C++-Mentalität ran, weil die Programmiersprachen sich sehr ähnlich sind. Aber ich bin einfach ein großer Fan davon, wenn sich ein Programm selbst aufräumt. Egal ob der Programmierer eine Exception wirft, irgend wo eine Funktion verlässt oder der Code normal durchläuft.
-
Optimizer schrieb:
Du hast in allen 3 Sprachen keine gleichzeitig vollautomatische und deterministische Resourcenfreigabe. In C++/CLI kannst du es dir nur am feinsten aussuchen, was du willst.
Jep, wäre aber schön wenn Java sowas auch hätte das man es sich aussuchen kann was man gerne hätte.
-
kingruedi schrieb:
C++ federt das Problem eben mittels RAII ab. In Java und .NET ist das nicht möglich.
Inwiefern glaubst du, dass es nicht möglich ist?
Ich finde so was:
try { ... } // Man beachte, dass ich keine Exception fange, will ich nämlich grad nicht. finally { obj.dispose(); }
schon ziemlich weich. Was ich an C++ schöner finde, ist dass der Aufruf eines Destruktors implizit ist und nicht von Hand hingeschrieben werden muss. In C++ braucht man das aber auch sehr viel eher, weil man selbst Speicher so verwalten muss. In Java hat man kaum finally, weil meistens geht es um Speicher, dne der GC schon macht. Für ein Socket macht man dann so einen Block.
Wie skaliert eigentlich der Java und der .NET GC?
Was genau meinst du? Der GC achtet darauf, dass er möglichst spät läuft. Je später er läuft, desto weniger arbeit hat er, weil er nur lebende kopiert. Damit er nicht zu viele auf einmal kopiert (was stocken würde), macht er öfter kleine Schritte und betrachtet dabei aber nicht den ganzen Heap (Generationen).
Warum hat man eigentlich nicht komplett auf finalize verzichtet?
Für ein Socket "braucht" man es ja. Mir stellt sich die Frage, warum man auf C++ Destruktoren verzichtet hat. Vielleicht hat man das selten in Java und hat sich dann gedacht, es ist besser, das wenige dann explizit zu haben und nicht versteckt.
Aber ich bin einfach ein großer Fan davon, wenn sich ein Programm selbst aufräumt. Egal ob der Programmierer eine Exception wirft, irgend wo eine Funktion verlässt oder der Code normal durchläuft.
Sowieso. Ist aber nichit nur in C++ möglich.
-
Inwiefern glaubst du, dass es nicht möglich ist?
Ich weiß, dass man das so macht. Aber das finde ich eben ist einfach "Exception rumgewurschtel", bzw. Verwässerung. Man bastelst sich noch eine explizite Aufräumfunktion.
Sowieso. Ist aber nichit nur in C++ möglich.
In Java ist es aber nicht implizit.
Meine Bedenken sind eben folgende: Ein Objekt stellt eine Abstraktion dar, es interagiert über eine Schnitstelle und versteckt seine Implementierung. Also sollte es auch darüber bestimmen können, wie es sich aufräumt. Wenn ich nun vom Programmierer explizit erwarten muss, dass er das Objekt aufräumt, geht ein Teil der Abstraktion flöhten. Naja, die Sicht eines Java-Programmierers ist wahrscheinlich, dass Aufäumen für ein Objekt nicht selbstverständlich ist und eben ein Teil der Schnitstelle und keine Grundlage eines Objekts ist.
Was genau meinst du? Der GC achtet darauf, dass er möglichst spät läuft. Je später er läuft, desto weniger arbeit hat er, weil er nur lebende kopiert. Damit er nicht zu viele auf einmal kopiert (was stocken würde), macht er öfter kleine Schritte und betrachtet dabei aber nicht den ganzen Heap (Generationen).
Ich wollte wissen wie er zur Anzahl der Objekte skaliert, also O-Notation. (Natürlich für einen kompletten Durchlauf).
-
kingruedi: Dann hältst du nicht mehr viel von Lisp? Da gibts ja auch nur unwind-protect als Gegenstück zu try..finally.
-
kingruedi schrieb:
In Java ist es aber nicht implizit.
Meine Bedenken sind eben folgende: Ein Objekt stellt eine Abstraktion dar, es interagiert über eine Schnitstelle und versteckt seine Implementierung. Also sollte es auch darüber bestimmen können, wie es sich aufräumt. Wenn ich nun vom Programmierer explizit erwarten muss, dass er das Objekt aufräumt, geht ein Teil der Abstraktion flöhten. Naja, die Sicht eines Java-Programmierers ist wahrscheinlich, dass Aufäumen für ein Objekt nicht selbstverständlich ist und eben ein Teil der Schnitstelle und keine Grundlage eines Objekts ist.
Meine Sicht der Dinge ist, dass ein Objekt nicht von selber weiß, wann es sich aufräumen soll, aber wie. So ist es auch in Java. In C++ kann man diesen Hinweis implizit machen, was geil ist.
Aber irgendwie geht das halt nicht immer. Wenn man ein Strategiespiel hat, dann weiß ich nicht, wann ich ne Einheit aufräumen kann. Wenn sie gestorben ist? Aber vielleicht wird sie noch von einer anderen Einheit anvisiert (oder einem Geschoss). Wenn sie verfault ist? Wer garantiert, dass sie nirgendwo mehr referenziert ist? Sehr gefährlich, sie aufzuräumen. In Warcraft III wird sie _glaube ich_ erst bei Spielende aufgeräumt. Es gibt funmaps, wo während des ganzen Spielverlaufs mehr als 10.000 Einheiten beteiligt sind und der RAM-Verbrauch steigt um viele MB an mit zunehmender Spieldauer. Ist das Spiel beendet, sinkt es wieder auf ein normales Niveau. Vielleicht ist das in einem komplexen Programm wirklich die einzige Möglichkeit, dafür verbraucht das Programm dann aber zeitweise mehr Speicher als es wirklich braucht.Und für sowas funktioniert RAII nicht mehr so gut IMHO. Du kannst schon sicherstellen, dass alles freigegeben wird, aber du musst es aufschieben, wenn es komplex wird. Ein GC macht sowas mit links. Und wenn die Regeln nicht immer so einfach sind, kann ein Objekt nicht wissen, wann es sich destruieren soll. Aber das ist jetzt nur meine Sicht der Dinge.
Du kannst es vielleicht so sehen, dass finally-Blöcke in Java weit seltener sind als deterministische Destruktoren in C++. Die größten Sorgen nimmt der GC halt ab, für C++ wäre das System mit finally so indiskutabel, völlig klar.
Ich wollte wissen wie er zur Anzahl der Objekte skaliert, also O-Notation. (Natürlich für einen kompletten Durchlauf).
Das hängt sehr von der Anzahl der überlebenden Objekte ab. Die meisten sind kurzlebig, deshalb muss er vielleicht bei 2000 Objekten nur 10 kopieren.
Überlebende Objekte kommen in ne höhere Generation und werden dann seltener betrachtet. Ich glaube nicht, dass man eine O-Notation aufschreiben kann, weil Sun und Microsoft schon seit Jahren daran tüfteln, welche Generationen-Größen ideal sind. Kleine Gen0 heißt häufig GC, heißt, dass Objekte kopiert werden, die kurz später gestorben wären. Große Gen0 heißt u.U. viele Objekte auf einmal kopieren, was man für Server macht. Die machen insgesamt sauwenig Arbeit, aber der GC braucht dann nicht ne zehntel ms sondern ne Sekunde oder mehr, wenn er mal was tut.
Für Client VMs tuned sich der GC je nach Anwendungsprofil selbst und versucht dann, irgendwie eine halbe ms oder sowas als Maximum einzuhalten.Gregor hat mal jemanden im Forum reingelegt, der meinte, ein GC verschlechtere die Performance:
for( int i = 0; i < 1000000; ++i ) new Foo();
Ist natürlich gemein, weil der GC hier überhaupt nichts macht.
Kann man also eigentlich nur in Abhängigkeit von Generationengröße und Lebensdauer der Objekte angeben.
-
Bashar schrieb:
kingruedi: Dann hältst du nicht mehr viel von Lisp? Da gibts ja auch nur unwind-protect als Gegenstück zu try..finally.
so wie ich das jetzt verstanden habe, ist aber nicht sichergestellt wann der finally-block überhaupt genau aufgerufen wird.
Oder irre ich mich?unwind-protect wäre da dann konsequenter.
-
Er wird genau dann aufgerufen, wenn der zugehörige try-Block verlassen wird, auf welchem Wege auch immer.
-
also eigentlich doch genau wie der c++ destruktor eines objekts auf dem stack?
das ist dann doch wunderbar. Sogar besser als in C++ wo man für sowas erst eine Klasse erstellen muss.
-
DrGreenthumb: Das nicht-deterministische gilt für finalize(). Das wird erst aufgerufen, wenn der GC das Objekt abräumt. Was ganz anderes als finally.
-
ach, und da soll einer durchsteigen
also schließe ich daraus für mich, finalize blöd, finally toll
-
DrGreenthumb schrieb:
also eigentlich doch genau wie der c++ destruktor eines objekts auf dem stack?
das ist dann doch wunderbar. Sogar besser als in C++ wo man für sowas erst eine Klasse erstellen muss.
nein, das nehme ich zurück. Schließe mich dem an, dass das Objekt für seine Löschung verantworlich sein sollte, ohne das der Aufrufer extra cleanup() aufrufen muss.
Das unwind-protect ist eigentlich auch nur so praktisch, weil man's zB. in ein with-macro wrappen kann.
-
Aber irgendwie geht das halt nicht immer. Wenn man ein Strategiespiel hat, dann weiß ich nicht, wann ich ne Einheit aufräumen kann. Wenn sie gestorben ist? Aber vielleicht wird sie noch von einer anderen Einheit anvisiert (oder einem Geschoss). Wenn sie verfault ist? Wer garantiert, dass sie nirgendwo mehr referenziert ist? Sehr gefährlich, sie aufzuräumen.
Das Problem versteh ich nicht. Wenn die Einheit ein RefCounted Objekt ist, kann sie jeder wegschmeißen, wenn er sie nicht mehr braucht. Wenn sie der letzte wegschmeißt, wird sie freigegeben. Ist doch ok? Wo ist das RefCounting unzuverlässig?
Das Problem das ich hier sehe ist eher ein semantisches. Ist eine zerstörte verfaulende Einheit noch eine Einheit mit einem bestimmten Attribut oder sind das andere Objekte? Das kann man aber doch über Vererbung und another level of indirection lösen und hat nix mit aufräumen zu tun.
Manchmal fänd ich ein finally schon auch in c++ ganz cool. Hin und wieder kommts vor, dass ich alleine zum Zweck des exception-sicheren aufräumens eine in die Funktion eingebettete Klasse schreiben muss. Im Dtor steht dann das, was im finally-Block stehen würde. Nur muss ich viel mehr tippen: Klassendeklaration, Ctor, dem referenzen auf die zusetzenden Objekte gegeben werden etc.
-
kartoffelsack schrieb:
Manchmal fänd ich ein finally schon auch in c++ ganz cool. Hin und wieder kommts vor, dass ich alleine zum Zweck des exception-sicheren aufräumens eine in die Funktion eingebettete Klasse schreiben muss. Im Dtor steht dann das, was im finally-Block stehen würde. Nur muss ich viel mehr tippen: Klassendeklaration, Ctor, dem referenzen auf die zusetzenden Objekte gegeben werden etc.
Tust du dir einen ScopeGuard schreiben oder den von Alexandrescu nehmen.
-
ah ja: http://www.cuj.com/documents/s=8000/cujcexp1812alexandr/
sowas hab ich mir auch schon selber gebaut. Nur hatte ich da immer Probleme mit konstanten Objekten, Referenzen ... und war weit hausbackener natürlich.
Sieht gut aus. Vielen Dank!Nur, hm, wo kann ich das ganze denn fertig downloaden? Der Link, der hier angegeben ist http://zfx.p15139172.pureserver.info/DisplayThread.php?TID=15376 funzt nicht. Und in welcher Boost-Bibliothek kann ich das ganze finden?