6 ms·
Myths about finalizers, part two
- copsarebastards 11y agoGarbage collection is a leaky abstraction and finalizers are the edge where it leaks. I tend to think that using finalizers is almost always a bad idea. They're just too complicated: there's no way you can reason about them effectively. This long list of assumptions you can't make about finalizers is just an example of this: if you have to keep all of this in mind you're forgetting something else. Luckily there are alternatives.
- bunderbunder 11y agoThat's what Eric Lippert's trying to get at here, in tortuously brutal detail. Here's the last line of the post again: It is therefore very difficult indeed to write a correct finalizer, and the best advice I can give you is to not try. Since the initial release, .NET has come up with additional mechanisms to try and minimize the number of situations where you would need to implement a finalizer. At this point I find that it's always possible to avoid one. When I think I need one, I ask myself the following question: "Am I hacking on the core framework?" If the answer is no, then I probably shouldn't be writing a finalizer.
- StillBored 11y agoWell, it always seemed to me that the one good use for a finalizer was to make sure that the coder remembered to call the Dispose() method... AKA say you have a resource (like a file handle) that is being wrapped by a class. In C++ the constructor would open the file, and the destructor would close it. In a GC'ed environment because you can't determine when the instance is going to get cleaned up the programmer ends up calling a .close() or Dispose() method instead of the destructor. So the finalizer ends up just being a little piece of code that says, did the user remember to call close? No? Complain loudly enough someone will fix it.
- acqq 11y agoHave you read the article? "it is possible that the finalizer and constructor are running concurrently."
- StillBored 11y agoWell, Yah if you didn't use it.. But AFAIU the point is that it won't happen if you have to call a dispose method (the dispose operations would block the finalizer)... AKA, if you have an object that needs to be disposed, and the finalizer is running concurrently with the constructor then that is because you forgot to call the Dispose() method and any complaining it does is valid. EDIT: Although, thinking about it more, the bigger problem is the partial construction case, where you need to be really careful how you set/unset the flag used to determine if the dispose method is called. But if something is bad enough to result in the construction phase failing in some really bad way, then there should be noise about that too. I would hope!
- millstone 11y agoLet's say you have a Lock object that wraps a pthread_mutex_t. How do you expect clients to determine when they should Dispose it? The problem with Dispose is that it's infectious. If one of your instance variables needs to be Dispose'd, then you do too. This is all sorts of bad: it violates LSP, leaks implementation details, adds compatibility constraints, etc.
- fleitz 11y agoReference counting? The second line is why GC is a lot of hype... eventually you need a pthread_mutex_t and GC programming becomes just as onerous as reference counting.
- StillBored 11y agoI totally agree. This is one of a number of concepts that keep me away from most GC'ed languages. BTW: Even in C++ though, I wouldn't use a pthread mutex directly, or in a wrapper class if I haven't banned exceptions. The "correct" way is to create a resource wrapper class without public lock/unlock calls and then a second RAII lock class that can lock/unlock the wrapper. Then if an exception happens while holding a lock you at least have a chance of properly unlocking it, or noticing that it wasn't unlocked.
- toong 11y agoThe only situation where I can't find a way to avoid them is handing out managed objects as COM to unmanaged clients.
- thaumasiotes 11y agoThis perspective was best expressed by Raymond Chen (here: http://blogs.msdn.com/b/oldnewthing/archive/2010/08/13/10049634.aspx http://blogs.msdn.com/b/oldnewthing/archive/2010/08/13/10049... ): > Finalizers are a Ouija board, permitting dead objects to operate "from beyond the grave" and affect live objects. As a result, when finalizers are involved, there is a lot of creepy spooky juju going on, and you need to tread very carefully, or your soul will become cursed.
- yarrel 11y agoYes, always manage objects explicitly. If we can't use scope locks, we should use open()/close() -style semantics.
- jscottmiller 11y agoIf you're working with .net code that uses finalizers (especially if those sections were written by people unlikely to read articles like this, though good programmers screw this up too), chances are that there are latent issues in your application that will manifest as sudden application crashes with little to no debugging information. I found myself in such a situation. Books like Advanced .NET Debugging [1] will help you regain your sanity. [1] http://www.amazon.com/Advanced-NET-Debugging-Mario-Hewardt/dp/0321578899 http://www.amazon.com/Advanced-NET-Debugging-Mario-Hewardt/d...
- copsarebastards 11y agoTBH if you need to do advanced debugging, it might be better to just not use finalizers.
- jontro 11y agoI've never had to use finalizers in java. When is this functionality actually needed? Any examples when it's used. In java finalizers may not be run at all afaik.
- copsarebastards 11y agoI haven't yet come across a case where it's actually needed, but I've come across a few cases where it seems like the easiest solution.
- Dylan16807 11y agoThankfully the worst of those problems is the easiest to deal with. Put a call to GC.KeepAlive(this) at the end of your constructor. At least then you'll have some sanity in the case that the finalizer runs.