What are the rules for when dispose() is required?

前端 未结 4 1355
清歌不尽
清歌不尽 2021-01-13 08:46

Although I have been coding for some time, I\'m really just barely into what I would call an intermediate level coder. So I understand the principle of dispose(), which is

相关标签:
4条回答
  • 2021-01-13 09:15

    I understand the principle of dispose(), which is to release memory reserved for variables and/or resources.

    You do not understand the purpose of dispose. It is not for releasing memory associated with variables.

    What I don't understand is just exactly what requires a release, when to employ dispose().

    Dispose anything that implements IDisposable when you are sure that you are done with it.

    For example, we don't dispose variables like string, integer or booleans. But somewhere we cross 'a line' and the variables and/or resources we use need to be disposed. I don't understand where the line is.

    The line is demarcated for you. When an object implements IDisposable, it should be disposed.

    I note that variables are not things that are disposed at all. Objects are disposed. Objects are not variables and variables are not objects. Variables are storage locations for values.

    Is there a single principle or a few broad principles to apply when knowing when to use dispose()?

    A single principle: dispose when the object is disposable.

    I don't feel like I understand the basics of knowing when to use dispose().

    Dispose all objects that are disposable.

    One comment I saw asked if memory is released when a variable goes out of scope, and that got my attention because until I saw the response was no, it doesn't get released just because it goes out of scope, I would have thought that it does get released when it goes out of scope.

    Be careful in your use of language. You are confusing scope and lifetime, and you are confusing variables with the contents of the variables.

    First: the scope of a variable is the region of program text in which that variable may be referred to by name. The lifetime of a variable is the period of time during the execution of the program in which the variable is considered to be a root of the garbage collector. Scope is purely a compile-time concept, lifetime is purely a run-time concept.

    The connection between scope and lifetime is that a local variable's lifetime is often starting when control enters the scope of the variable, and ending when it leaves. However, various things can change the lifetime of a local, including being closed over, in an iterator block, or in an async method. The jitter optimizer may also shorten or extend the life of a local.

    Remember also that a variable is storage, and that it may refer to storage. When the lifetime of a local ends, the storage associated with the local might be reclaimed. But there is no guarantee whatsoever that the storage associated with the thing the local refers to will be reclaimed, at that time or ever.

    So that's why my question is "What determines when a dispose() is really necessary?"

    Dispose is necessary when the object implements IDisposable. (There are a small number of objects that are disposable that need not be disposed. Tasks, for instance. But as a general rule, if it is disposable, dispose it.)

    My question is less one of how and more one of when.

    Only dispose a thing when you are done with it. Not before, and not later.

    When should I dispose my objects in .NET?

    Dispose objects when they implement IDisposable, and you are done using them.

    How do I know if something falls into the category of what I must dispose?

    When it is implementing IDisposable.

    I just don't know what objects or things I create require that I am responsible for disposing.

    The disposable ones.

    most experienced and professional developers know when something they've created needs to be disposed of. I don't understand how to know that.

    They check to see if the object is disposable. If it is , they dispose it.

    The point of Dispose is to free unmanaged resources". OK, but my question is how do I know by looking at something that it is an unmanaged resource?

    It implements IDisposable.

    I don't understand how to use that type of explanation to categorize what I need to dispose() and what I don't. There's all kinds of stuff in the .net framework; how do I separate out things that require I dispose() of them? What do I look at to tell me I'm responsible for it?

    Check to see if it is IDisposable.

    After that, the answer goes on to speak at great length about how to dispose(), but I'm still stuck back at what needs to be disposed.

    Anything that implements IDisposable needs to be disposed.

    my question is about when, meaning what needs to be disposed(), as opposed to when I'm done with it. I know when I'm done with things, I just don't know which things I'm responsible for when I'm done with them.

    The things that implement IDisposable.

    I was hoping there is a general principle for what must be disposed rather than a long list of specific items which would not be particularly useful for people like me who are looking for simple guidelines.

    The simple guideline is that you should dispose disposable things.

    Again, I get it that memory release is important, and also that a lot of experience and expertise goes into learning why and how, but I'm still left struggling to understand what needs to be disposed. Once I understand what I have to dispose(), then I can begin the struggle to learn how to do it.

    Dispose things that implement IDisposable by calling Dispose().

    So is this still a bad question?

    It is a very repetitive question.

    Your patience is a kindness.

    Thanks for taking this somewhat silly answer in the humour in which it was intended!

    WRT scope != lifetime & variables != objects, very helpful.

    These are very commonly confused, and most of the time, it makes little difference. But I find that often people who are struggling to understand a concept are not at all well served by vagueness and imprecision.

    In VS is it so simple as looking in Object Browser / Intellisense to see if the object includes Dispose()?

    The vast majority of the time, yes.

    There are some obscure corner cases. As I already mentioned, the received wisdom from the TPL team is that disposing Task objects is not only unnecessary, but can be counterproductive.

    There are also some types that implement IDisposable, but use the "explicit interface implementation" trick to make the "Dispose" method only accessible by casting to IDisposable. In the majority of these cases there is a synonym for Dispose on the object itself, typically called "Close" or some such thing. I don't much like this pattern, but some people use it.

    For those objects, the using block will still work. If for some reason you want to explicitly dispose such objects without using using then either (1) call the "Close" method, or whatever it is called, or (2) cast to IDisposable and dispose it.

    The general wisdom is: if the object is disposable then it does not hurt to dispose it, and it is a good practice to do so when you're done with it.

    The reason being: disposable objects often represent a scarce shared resource. A file, for example, might be opened in a mode that denies the rights of other processes to access that file while you have it opened. It's the polite thing to do to ensure that the file is closed as soon as you're done with it. If one process wanted to use a file, odds are pretty good another one will soon.

    Or a disposable might represent something like a graphics object. The operating system will stop giving out new graphics objects if there are more than ten thousand active in a process, so you've got to let them go when you're done with them.

    WRT implementing IDisposable @Brian's comment suggests in "normal" coding I likely don't need to. So would I only do that if my class pulled in something unmanaged?

    Good question. There are two scenarios in which you should implement IDisposable.

    (1) The common scenario: you are writing an object that holds onto another IDisposable object for a long time, and the lifetime of the "inner" object is the same as the lifetime of the "outer" object.

    For example: you are implementing a logger class that opens a log file and keeps it open until the log is closed. Now you have a class that is holding onto a disposable, and so it itself should also be disposable.

    I note that there is no need in this case for the "outer" object to be finalizable. Just disposable. If for some reason the dispose is never called on the outer object, the finalizer of the inner object will take care of finalization.

    (2) the rare scenario: you are implementing a new class which asks the operating system or other external entity for a resource that must be aggressively cleaned up, and the lifetime of that resource is the same as the lifetime of the object holding onto it.

    In this exceedingly rare case you should first, ask yourself if there is any way to avoid it. This is a bad situation to be in for a beginner-to-intermediate programmer. You really need to understand how the CLR interacts with unmanaged code in order to get this stuff solid.

    If you cannot avoid it, you should prefer to not attempt to implement the disposal and finalization logic yourself, particularly if the unmanaged object is represented by a Windows handle. There should already be wrappers around most of the OS services represented by handles, but if there is not one, what you want to do is carefully study the relationship between IntPtr, SafeHandle and HandleRef. IntPtr, SafeHandle and HandleRef - Explained

    If you really truly do need to write the disposal logic for an unmanaged, non-handle-based resource, and the resource requires backstopping the disposal with finalization, then you have a significant engineering challenge.

    The standard dispose pattern code may look simple but there are real subtleties to writing correct finalization logic that is robust in the face of error conditions. Remember, a finalizer runs on a different thread and can run on that thread concurrent with the constructor in thread abort scenarios. Writing threadsafe logic which cleans up an object while it is still being constructed on another thread can be extraordinarily difficult and I recommend against trying.

    For more on the challenges of writing finalizers, see my series of articles on the subject: http://ericlippert.com/2015/05/18/when-everything-you-know-is-wrong-part-one/

    A question you did not ask but I will answer anyways:

    Are there scenarios in which I should not be implementing IDisposable?

    Yes. Many people implement IDisposable any time they wish to have a coding pattern that has the semantics of:

    • Make a change in the world
    • Do stuff in the new world
    • Revert the change

    So, for example, "impersonate an administrator, do some admin tasks, revert to normal user". Or "start handling an event, do stuff when the event happens, stop handling the event". Or "create an in-memory error tracker, do some stuff that might make errors, stop tracking errors". And so on. You get the general pattern.

    This is a poor fit for the disposable pattern, but that doesn't stop people from writing up classes that represent no unmanaged resource whatsoever, but still implement IDisposable as though they did.

    This opinion puts me in a minority; lots of people have no problem whatsoever with this abuse of the mechanism. But when I see a disposable I think "the author of this class wishes me to be polite and clean up after myself when I am good and ready." But the actual contract of the class is often "you must dispose this at a particular point in the program and if you do not then the rest of the program logic will be wrong until you do". That is not the contract I expect to have to implement when I see a disposable. I expect that I have to make a good-faith effort to clean up a resource at my convenience.

    0 讨论(0)
  • 2021-01-13 09:21

    If you have an object that implements IDisposable then you must always explicitly call .Dispose() on that object. If it doesn't implement IDisposable then obviously you don't call .Dispose() because you can't.

    If you are writing your own objects then the rule as to whether or not to implement IDisposable is simply this: If your object holds references to unmanaged objects OR if it holds references to objects that implement IDisposable then it must implement IDisposable.

    The GC never calls .Dispose() for you. You must always do it - either directly or through a finalizer.

    The GC may (most likely, but not always) call a finalizer, so you can write a finalizer to call dispose, but be careful that you implement the disposable pattern correctly, and be certain that you understand that the finalizer may not ever run so if your dispose method does something vitally important it might be better to call .Dispose() directly before you lose the reference to the object.

    0 讨论(0)
  • 2021-01-13 09:26

    The Dispose pattern can be used to clear both managed and unmanaged resources. If you have unmanaged resources in your class, according to the proper IDisposable implementation you must have both Dispose and Finalize methods.

    How do I know what GC knows/doesn't?

    GC knows/interested only about managed objects. GC is supposed to clear objects those don’t have any strong references. It doesn’t know about your logics. For a simple and obvious example.

    Let’s say you have a MainView instance which is a lasting a long time and you create another LittleView that subscribe to an event in the MainView instance.

    Then you close the LittleView and it disappears. You know that you don’t need that LittleView instance anymore. But GC doesn’t know if you still need LittleView or not as there is an active event subscription to the MainWindow.

    So, GC will not bother to clear LittleView instance from memory. So, what you should do is unsubscribe the event when you close the view. Then GC knows that there are no strong references to the LittleView and it’s reachable.

    The post also complicates things saying managed resources may include unmanaged resources. Wow. This is getting deeper than I initially imagined. I'm still looking to easily recognize how to know what needs to be disposed. Is it the case this is a complicated list w/ conditions and context?

    That’s correct, managed objects also can have unmanaged resources. All those managed objects have their finalize method to clear umanaged resources.

    Wonder why you need finalize method in addition to the Dispose?

    Unmanaged resources can create the most dangerous memory leaks as such memory leaks can hold on to memory until you restart the PC. So, they are very bad.

    Let’s say there is managed instance InsA with some unmanaged. InsA has implemented the Dispose method in a way to clear unmanaged resources too. But what will happen if you don’t/forget to call the Dispose method? It will never clear that unmanaged memory. That’s why Finalization has come in to the functionality. So, if you forget/don’t call the Dispose, finalization will gurantee to execute the Dispose in a way that it release unmanaged resources.

    0 讨论(0)
  • 2021-01-13 09:32

    The garbage collector (GC) guarantees that managed memory resources that are no longer used are released before the memory limit is reached.

    Let's break that down:

    managed: Loosely, this means resources entirely within .NET/CLR. Memory allocated by non-.NET C++ libraries, for example, are not released by the GC.

    memory: The GC only provides a guarantee on memory usage. Many other resource types exist, such as file handles. The GC has no logic to ensure file handles are released expediently.

    no longer used: This means that all variables with a reference to that memory have ended their lifetime. As Eric Lippert explained, lifetime != scope.

    before the memory limit is reached: The GC monitors "memory pressure" and makes sure to release memory when needed. It uses a bunch of algorithms to decide when it is most efficient to do this, but it's important to note that the GC decides when to release resources all on its own (nondeterministically to your program).


    This leaves us with several scenarios when relying on the GC is not appropriate:

    • The object references unmanaged resources (including referencing a managed object that references an unmanaged resource).
    • The object references a resource other than memory that must be released.
    • The object references a resource that must be released at a specific point in the code (deterministically).

    In any of these cases, the object should implement IDisposable to handle resource cleanup.

    Any object instantiated that implements IDisposable must be cleaned up by either calling Dispose or using the using block (which takes care of calling Dispose for you).

    0 讨论(0)
提交回复
热议问题