3 ms·
I don't view finalizers and destructors as different concepts. The notion only matters if you actually need cleanup behavior to be deterministic rather than jus
by jchw 10mo ago
I don't view finalizers and destructors as different concepts. The notion only matters if you actually need cleanup behavior to be deterministic rather than just eventual, or you are dealing with something like thread locals. (Historically, C# even simply called them destructors.)
- mort96 10mo agoThere's a huge difference in programming model. You can rely on C++ or Rust destructors to free GPU memory, close sockets, free memory owned through an opaque pointer obtained through FFI, implement reference counting, etc. I've had the displeasure of fixing a Go code base where finalizers were actively used to free opaque C memory and GPU memory. The Go garbage collector obviously didn't consider it high priority to free these 8-byte objects which just wrap a pointer, because it didn't know that the objects were keeping tens of megabytes of C or GPU memory alive. I had to touch so much code to explicitly call Destroy methods in defer blocks to avoid running out of memory.
- cogman10 10mo agoFor GCed languages, I think finalizers are a mistake. They only serve to make it harder to reason about the code while masking problems. They also have negative impacts on GC performance. Java is actively removing it's finalizers.
- adrianN 10mo agoSometimes „eventually“ is „At the end of the process“. For many resources this is not acceptable.
- rramadass 10mo ago> I don't view finalizers and destructors as different concepts. They are fundamentally different concepts. See Destructors, Finalizers, and Synchronization by Hans Boehm - https://dl.acm.org/doi/10.1145/604131.604153 https://dl.acm.org/doi/10.1145/604131.604153
- jchw 10mo agoIt would suffice to say I don't always agree with even some of the best in the field, and they don't always agree with each other, either. Anders Hejlsberg isn't exactly a random n00b when it comes to programming language design and still called the C# equivalent a "destructor", though it is now known as a finalizer in line with other programming languages. They are things that clean up resources at the end of the life of an object; the difference between GC'd languages and RAII languages is that in a GC'd runtime the lifespan of an object is non-deterministic. That may very well change the programming model, as it does in many other ways, but it doesn't make the two concepts "fundamentally different" by any means. They're certainly related concepts...
- rramadass 10mo agoThey are related but fundamentally different. It is a vital semantic difference (influencing the programming model itself) since destructors (C++ style) are synchronous and deterministic while finalizers (Java style) are asynchronous and non-deterministic. It is because of all the problems that the finalize method was deprecated in Java 9 and marked "deprecated for removal"(JEP 421) in Java 18. More details at https://stackoverflow.com/questions/56139760/why-is-the-finalize-method-deprecated-in-java-9 https://stackoverflow.com/questions/56139760/why-is-the-fina... and https://inside.java/2022/01/12/podcast-021/ https://inside.java/2022/01/12/podcast-021/ PS: JEP 421: Deprecate Finalization for Removal - https://openjdk.org/jeps/421 https://openjdk.org/jeps/421 Also details alternative features/techniques to use.
- jchw 10mo agoI grasp the entirety of why people differentiate "finalizers" from "destructors", but in my opinion, the practical differences in their application are not the result of the concept itself being fundamentally different, it's a result of object lifetimes being different between GC'd and non-GC'd languages. In my opinion, the concept itself is pretty close to identical. You want to clean up resources at the end of the lifetime of an object. And yes, it's practically a mess because the object lifetime ends at a non-deterministic point in the future and usually not even necessarily on the same thread. Being a big fan of Go and having had to occasionally make use of finalizers for lack of a better option in some limited scenarios, I really genuinely do grasp this, but I dispute that it has anything to do with whether or not a language has try...finally, anymore than it has anything to do with a language having any other convenient structured control flow measures, like pattern matching or else blocks on for loops. (I do also realize that finalizer behavior in some languages is weird, for performance reasons and sometimes just legacy reasons. Go is one such language.) But I think we've both hit a level of digression that wouldn't be helpful even if we were disagreeing about the facts (which I don't really think we are. I think this is entirely about frames of reference rather than a material dispute over the facts.) Forgetting whether finalizers are truly a form of destructor or not, the point I was trying to make really was that I don't view RAII/scoped destructors as being equivalent or alternatives to things like `finally` blocks or `defer` statements. In C++ you basically use scope guards for everything because they are the only option, but I think C++ would still ultimately benefit from at least having `finally`. You can kind of emulate it, but not 100%: `finally` blocks are outside of the scope of the exception and can throw a new exception, unlike a destructor in an exception frame. Having more options in structured control flow can sometimes add complexity for little gain, but `finally` can genuinely be useful sometimes. (Though I ultimately still prefer errors being passed around as value types, like with std::expected, rather than exception handling blocks.) I believe the reason why we don't have languages (that I can think of) that demonstrate this exact combination is specifically because try/catch exception blocks fell out of favor at the same time that new compiled/"low-level" programming languages started picking up steam. A lot of new programming language designs that do use explicit lifetimes (Zig, Rust, etc.) simply don't have try...catch style exception blocks in the first place, if they even have anything that resemble exceptions. Even a lot of new garbage collected languages don't use try...catch exceptions, like of course Go. Now honestly I could've made a better attempt at conveying my position earlier in this thread, but I'm gonna be honest, once I realized I struck a nerve with some people I became pretty unmotivated to bother, sometimes I'm just not in the mood to try to win over the crowd and would rather just let them bury me, at least until the thread died down a bit.