6 ms·
I said generational/compacting collector. You're talking about a mark and sweep collector. A generational/compacting collector traverses pointers from the liv
by chowells 6y ago
I said generational/compacting collector. You're talking about a mark and sweep collector.
A generational/compacting collector traverses pointers from the live roots, and copies everything it finds to the start of its memory space, and then declares the rest unused. If there is 1GB of unused memory, it's irrelevant. Only the things that can be reached are even examined.
As I said, this has the opposite problem. When the live set becomes huge, this can drag performance. When the live set is small, it doesn't matter how much garbage it produces, performance is fast.
- arcticbull 6y agoHow are finalizers invoked if the structure isn't traversed? Would it just be optimized away none of the objects have finalizers? Hence my suggestion about the area allocators being a better point of comparison.
- Reelin 6y agoMy understanding is that finalizers are special cased for a generational GC, have a tendency to introduce significant overhead, and (as with any GC scheme) generally run at unpredictable times. My impression is that RAII idioms are strongly discouraged in conjunction with most GC ecosystems.
- tsimionescu 6y agoActually, RAII idioms are generally encouraged, but finalizers are not. For the rare few classes that hold resources in GC languages, the Resource is usually Acquired at Initialization as well, and released in a special method (IDisposable.Dispose() in C#, AutoCloseable.Close() in Java). There is also usually some special syntax for automatically calling this syntax when the object exits some scope, though that scope usually has to be declared by the user (using(), try-with-resources, with() in Python etc.). The biggest difference I know of is that the 'holds-resources' property does not propagate automatically like it does in C++. It's not that hard to always remember to call using(file = new File()) [...]. However, it's much easier to forget that you have a File field in your class which you initialize in the constructor, and so your class must itself be declared IDisposable/AutoCloseable etc.
- Reelin 6y agoThis is a really good point. I had completely overlooked various deterministic resource cleanup idioms as being equivalent to RAII in all but name.
- pcl 6y agoMany languages allow a class to avoid declaring a finalized, for just this reason. The JVM is notable in this regard. And thus, most classes that compile to Java bytecode offer no-finalizer semantics.
- zucker42 6y agoJava is an example of a language with a generational copy collector by default. Most objects in Java don't have a finalizer, since after all the main point of, for example, destructors in C++ is to make sure you don't leak memory, which the GC solves. But when the `finalize` method is used is causes significant overhead. > Objects with finalizers (those that have a non-trivial finalize() method) have significant overhead compared to objects without finalizers, and should be used sparingly. Finalizeable objects are both slower to allocate and slower to collect. At allocation time, the JVM must register any finalizeable objects with the garbage collector, and (at least in the HotSpot JVM implementation) finalizeable objects must follow a slower allocation path than most other objects. Similarly, finalizeable objects are slower to collect, too. It takes at least two garbage collection cycles (in the best case) before a finalizeable object can be reclaimed, and the garbage collector has to do extra work to invoke the finalizer. [1] Sure, you're technically correct that if the objects all had finalizers that did the same thing as C++ destructors, it would be equivalent, but because of the existence of a GC we don't have to do any work for most objects. A GC is equivalent to an arena allocator in this sense. Another point is the C++/Rust pattern of each object recursively freeing the objects it owns presumably leads to slower deallocation, because in the general case it involves pointer following and non-local access. [1] https://www.ibm.com/developerworks/java/library/j-jtp01274/index.html https://www.ibm.com/developerworks/java/library/j-jtp01274/i...
- pron 6y agoWhich is why finalizers in Java have been officially deprecated [1] and might be removed altogether in a future release. [1]: https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/lang/Object.html#finalize() https://docs.oracle.com/en/java/javase/14/docs/api/java.base...
- cesarb 6y agoUnfortunately, the alternative they recommend (Cleaner) only exists since Java 9, so it cannot be used by libraries that wish to remain compatible with Java 8 (which, in my experience, is most or all of them; so far, I haven't seen any Java library which has dropped compatibility with Java 8, and many only recently dropped compatibility with Java 6 or 7).
- mcguire 6y agoMaintain a collection of objects with finalizers associated with the generation; when an object is moved out of the generation, move the finalizer record with it. Then, just before the generation is discarded, run the finalizers. If the finalizers do something stupid like resurrect the object, have the runtime system notify someone with the authority to go beat the programmer with a stick.
- Avi-D-coder 6y agoInterestingly for the Rust GC I'm working both Drop and finalizers will be supported. Do you happen to offer beat the programmer with a stick as a service?
- mcguire 6y agoUnfortunately, no, but you are going to need it. I was fooling with a cycle-collected reference counter for Rust for a while, but eventually gave up because of the difficulty in handling finalizers.
- tsimionescu 6y agoEven mark-and-sweep collectors only mark starting from GC roots, they don't mark through unreachable objects.