Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!
Werbeanzeige
Community-Fossil
@DeKugelschieber
Gerade was passendes für dich gefunden: https://github.com/ksimka/go-is-not-good Purer Zufall, aber dachte es passt.
Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Chromanoid« (14.05.2016, 03:09)
Ich hab mir Go damals, als es rauskam, kurz angeschaut. Ein kurzer Blick in die Doku genügt mir aber, um recht schnell zu beschließen, dass ich von einer Sprache, die Dinge wie Typeswitches als Feature ansieht und dafür keine Lösung für Ressourcenmanagement anbietet (ja, es gibt defer, aber das ist auch nur die selbe Krücke, auf die sich z.B. auch C# und Java mit Using-Blöcken bzw. Try-With-Resources stützen, weil das Fundament der GC eine ordentliche Lösung unmöglich macht) nicht viel halten kann...allein schon weil sie für das, was ich mache, dadurch völlig unbrauchbar ist...
Könntest du das etwas genauer beschreiben. Ich sehe nicht wirklich das Problem ein
Quellcode
1 2 3 4 5 resource, err := os.Open("irgendwas") defer resource.Close() // edit: äh das gehört natürlich hier hin if err != nil { // behandel mal }
zu machen oder eben das Equivalent in C++.
Mag sein das Go nicht das richtige für deinen Anwendungsfall ist, vor allem wenn man auf niedriger Ebene mit Daten arbeiten muss, aber allgemein ist das doch recht elegant?
Java-Quelltext |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
ScheduledExecutorService executor = Executors.newScheduledExecutorService(8); //some variable for all tasks Object someVar = ""; for (int i = 0; i < 100; i++) { //Task1 extends Callable<Object> Task1 task = new Task(someVar); //... //add task to executor service, task will executed (maybe later) in thread pool Future<Object> future = executorService.schedule(task1); } |
Sagte ich doch schon, es setzt auf Reference-Counting auf. Ohne gewissenhafte Verwendung von weak/unowned references baut man sich in Rust allerdings sehr schnell den Speicher voll. Die Mittel sind da und sind zum Glück einfach zu verwenden, nur ist es gewöhnungsbedürftig, wenn auch wesentlich weniger gewöhnungsbedürftig (weil gefühlt unnötig [das Gefühl trügt aber eben]) als in C# oder Java. Reference-Count auf 0 heißt auch automatisch Freigabe des Objekts zu diesem Zeitpunkt und nicht erst irgendwann. In C++ ist eine Ownership bei einer Referenz wesentlich schwerer "versehentlich" zu übertragen als dass man bei Rust das weak/unowned vergisst und dann plötzlich eine ungewollte Ownership bekommt. Da ist C++ schon deutlich ausdrucksstärker, wenn auch ulkigerweise impliziter zur gleichen Zeit.Einen GC gibt es in Rust übrigens nicht mehr.
Community-Fossil
Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer
Jein. Der GC tut das zwar, aber wann er das tut, ist völlig undefiniert. Und genau das ist das Problem, was wir hier lang und breit diskutieren und wo ich (und andere) ein (aus Sicht des Entwicklers) deterministisches Verhalten bevorzugen.Der GC in Java schaut sich an, welche Klassen / Instanzen welche Referenzen auf was setzen und wenn keine Referenz besteht, wird die Variable freigegeben.
Natürlich. Aber mit manueller Speicherverwaltung ist es schwerer zyklische Graphen abzubilden. Übrigens meine ich nicht zyklische Abhängigkeiten von Code sondern von Daten.Zyklische Graphen in Geschäftsprozessen haben übrigens nichts mit zyklischen Abhängigkeiten von Code zu tun. Das eine kann wunderbar ohne das andere existieren - und bei schlechten Entwicklern leider auch andersrum.
Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von »Chromanoid« (20.05.2016, 00:34)
Community-Fossil
Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer
Wenn man sie so implementiert, dass jedes Element der Besitzer des Nachfolgers ist, dann schon, klar. (das ist übrigens kein Geschäftsprozess, das ist ein Datenmodell) Das Problem ist dennoch sauber lösbar, indem ganz klar geregelt wird, ob man ein Element nur referenzieren will oder die Ownership eines ListElements an ein anderes ListElement übergibt. Dazu benötigt man natürlich intern zwei Pointer - einen strong und einen weak, die gekapselt nach außen transparent nur als einer erscheinen. Und dann ist das Ganze absolut kein Problem. Weder mit "manueller" Speicherverwaltung wie in C++ über uniue_ptr und raw-pointer, noch in Rust/Swift über Strong Reference und Weak/Unowned Reference. Und das ist keineswegs "schwierig". Über unique_ptr in C++ ist es bei so einer Implementierung nicht mal möglich "aus Versehen" etwas mit Ownership zu übertragen, obwohl man nur eine schwache Referenz übergeben wollte. Und damit ist man wesentlich sicherer als in GC-Sprachen, wo ich selbst schon oft genug Memory-Leaks suchen durfte, weil sich die Entwickler darauf verlassen, dass ja irgendwann eh alles freigegeben wird und man nie irgendwas auf null setzen muss. Explizites null-setzen ist sogar noch viel übler als in C++ sauber über unique_ptr zu arbeiten.Das einfachste Beispiel ist wohl eine Ringliste: (...)
Da wird's dann mit Referenzen zählen schwierig.
Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von »BlueCobold« (20.05.2016, 06:59)
Wir reden über Geschäftsprozesse und andere fachliche Vorgänge und Strukturen, die durch Daten technisch repräsentiert werden. Also über das, was als Code und zur Laufzeit dabei herauskommt, wenn man eine Anwendung gemäß fachlichen Anforderungen programmiert hat.Wie ich schon sagte, zyklische Abhängigkeiten in Geschäftsprozessen haben nichts mit zyklischen Abhängigkeiten in Code zu tun. Also über was reden wir jetzt? Über Daten oder über Geschäftsprozesse?
Doch das finde ich schon. Das war ja nur ein kleines Beispiel. Stell Dir die Problematik mal in einer riesigen Anwendung vor, mit zig Entwicklern unterschiedlichstem Kenntnisstand. Darüber Gedanken machen, muss man sich ja die ganze Zeit, bei jeder Code Review, bei jedem Fitzel der Anwendung. In einer GC-Sprache kann ich das ganze genauso, wie es die Pfeile zeigen, nachbauen, ohne mir Gedanken über Besitz und Speicher machen zu müssen.Das Problem ist dennoch sauber lösbar, indem ganz klar geregelt wird, ob man ein Element nur referenzieren will oder die Ownership eines ListElements an ein anderes ListElement übergibt. Dazu benötigt man natürlich intern zwei Pointer - einen strong und einen weak, die gekapselt nach außen transparent nur als einer erscheinen. Und dann ist das Ganze absolut kein Problem. [...] Und das ist keineswegs "schwierig".
In GC-Sprachen gibt es streng genommen keine Memory-Leaks. Also jedenfalls nicht im eigentlichen Sinne des Wortes, dass man Speicher nicht freigibt, den man eigentlich freigeben muss und so nicht mehr referenzierbaren Speicher erzeugt. Das was Du meinst, sind versehentlich hergestellte Referenzen von Objekten die aus irgendeinem Grund langfristig existieren sollen, zu Objekten, die nicht mehr existieren sollen. Wenn man Null-setzen muss, hat man mMn etwas falsch gemacht. Meinetwegen können wir das aber auch Memory Leak nennen. Das schöne ist halt , dass man buchstäblich die Wurzel des Übels bei GC-Sprachen i.d.R. sehr leicht finden kann, in dem man den Heap automatisch analysieren lässt (und zwar geht das auch im Nachhinein mit einem Heap-Dump vom Produktiv-System). Sicher muss man in diesem Sinne bei GC-Sprachen auch mal an Speicher denken, aber die Problemfläche ist eben viel viel kleiner. Bei manueller Speicherverwaltung trifft man auf das Problem eher selten, weil es vorher mit einem Crash wegen Verweis auf bereits freigegebenen Speicher wegfliegt o.Ä.Und damit ist man wesentlich sicherer als in GC-Sprachen, wo ich selbst schon oft genug Memory-Leaks suchen durfte, weil sich die Entwickler darauf verlassen, dass ja irgendwann eh alles freigegeben wird und man nie irgendwas auf null setzen muss.
Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von »Chromanoid« (20.05.2016, 08:54)
Community-Fossil
Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer
Genau das sehe ich als schlecht an. Denn man muss das auch in einer GC-Sprache sehr wohl, sonst muss ein armes Schwein wie ich wieder irgendwann tagelang Memory-Leaks suchen gehen. Die Vorstellung, dass man sich darüber in GC-Sprachen keine Gedanken machen muss, ist de fakto falsch.In einer GC-Sprache kann ich das ganze genauso, wie es die Pfeile zeigen, nachbauen, ohne mir Gedanken über Besitz und Speicher machen zu müssen.
Doch, die gibt es. Nämlich immer dann, wenn irgendwo irgendwelche Dinge weiterhin Kram referenzieren, der schon lange hätte freigegeben werden sollen. Diese "das macht der GC alles"-Vorstellung ist nicht korrekt. Ich muss auch als Entwickler eben hier und da gewisse Referenzen auf null setzen in GC-Sprachen. Das macht aber fast nie jemand. Das Ergebnis sind OutOfMemory-Probleme. Solches null-Setzen ist aber an sich auch kein schöner Stil. Nur leider in der Praxis unvermeidbar. Genauso, wie ein GC.collect/flush eben auch in gewissen Situationen unbedingt notwendig ist, so grauenvoll das auch weh tut solchen Code zu sehen oder selbst schreiben zu müssen.In GC-Sprachen gibt es streng genommen keine Memory-Leaks. Also jedenfalls nicht im eigentlichen Sinne des Wortes, dass man Speicher nicht freigibt, den man eigentlich freigeben muss
Nein, eben nicht. Schön wär's.dass man buchstäblich die Wurzel des Übels bei GC-Sprachen i.d.R. sehr leicht finden kann
Die Popularität liegt mMn in der selben falschen Grundannahme begründet, die auch du hier aufführst. Nämlich, dass man sich um das Speichermanagement keine Gedanken machen müsste. Mal davon abgesehen, dass C++ vor dem 11er Standard wirklich saumäßig hässlich war und Swift/Rust auch erst sehr junge Konzepte sind. Dazu kommt noch, dass diese GC-Sprachen "zufällig" eine saugeile Standard-Bibliothek mitbringen, die quasi schon alles hat, während non-GC-Sprachen irgendwie (Gott weiß warum, ich vermute begründet durch ihr Alter) diese eben nicht bieten und man sich mit furchtbarem Bibliotheks-Mist herum ärgert, wie selbst in C++ heute noch.Wie erklärst Du Dir die ungemein hohe Popularität von GC-Sprachen?
Dieser Beitrag wurde bereits 6 mal editiert, zuletzt von »BlueCobold« (20.05.2016, 10:24)
In GC-Sprachen gibt es streng genommen keine Memory-Leaks. Also jedenfalls nicht im eigentlichen Sinne des Wortes, dass man Speicher nicht freigibt, den man eigentlich freigeben muss und so nicht mehr referenzierbaren Speicher erzeugt.
Aber mit manueller Speicherverwaltung ist es schwerer zyklische Graphen abzubilden.
Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von »dot« (20.05.2016, 10:42)
Genau das sehe ich als schlecht an. Denn man muss das auch in einer GC-Sprache sehr wohl, sonst muss ein armes Schwein wie ich wieder irgendwann tagelang Memory-Leaks suchen gehen. Die Vorstellung, dass man sich darüber in GC-Sprachen keine Gedanken machen muss, ist de fakto falsch.
Sicher muss man in diesem Sinne bei GC-Sprachen auch mal an Speicher denken, aber die Problemfläche ist eben viel viel kleiner.
So mache ich das in Java, wenn es mal passiert. Das musste ich in über zehn Jahren Berufserfahrung vielleicht vier Mal oder so machen: http://blog.rejeev.com/2009/04/analyzing…ak-in-java.html Das schlimmste sind ClassLoader-Leaks aber auch die kann man so nachvollziehen.Nein, eben nicht. Schön wär's.
Ich kenne dieses Vorgehen so aus der Praxis nicht.Das Ergebnis sind OutOfMemory-Probleme. Solches null-Setzen ist aber an sich auch kein schöner Stil. Nur leider in der Praxis unvermeidbar. Genauso, wie ein GC.collect/flush eben auch in gewissen Situationen unbedingt notwendig ist, so grauenvoll das auch weh tut solchen Code zu sehen oder selbst schreiben zu müssen.
Doch, die gibt es. Nämlich immer dann, wenn irgendwo irgendwelche Dinge weiterhin Kram referenzieren, der schon lange hätte freigegeben werden sollen.
Das was Du meinst, sind versehentlich hergestellte Referenzen von Objekten die aus irgendeinem Grund langfristig existieren sollen, zu Objekten, die nicht mehr existieren sollen. [...] Meinetwegen können wir das aber auch Memory Leak nennen.
Das liegt daran, dass es wesentlich leichter ist tolle Standardbibliotheken mit GC-Sprachen zu schreiben.Dazu kommt noch, dass diese GC-Sprachen "zufällig" eine saugeile Standard-Bibliothek mitbringen, die quasi schon alles hat, während non-GC-Sprachen irgendwie (Gott weiß warum, ich vermute begründet durch ihr Alter) diese eben nicht bieten und man sich mit furchtbarem Bibliotheks-Mist herum ärgert, wie selbst in C++ heute noch.
Rust ist eine Sprache zur Systementwicklung - nicht zur Anwendungswentwicklung:Ich finde es im Gegenteil sogar sehr erhellend, dass solche neuen Sprachen eben nicht mehr einen GC nutzen wollen, weil sie offensichtlich aus den Fehlern gelernt haben.
Swift hat als Zielplattform iOS und ist eine neue Alternative zu Objective-C, das auch schon ARC verwendet. Das sehe ich jetzt nicht als Konkurrenz für Sprachen in denen man richtig fette Anwendungen entwickelt.Zitat von »https://doc.rust-lang.org/book/«
Rust is a systems programming language focused on three goals: safety, speed, and concurrency. It maintains these goals without having a garbage collector, making it a useful language for a number of use cases other languages aren’t good at: embedding in other languages, programs with specific space and time requirements, and writing low-level code, like device drivers and operating systems.
Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Chromanoid« (20.05.2016, 10:43)
Werbeanzeige