Reputation: 83296
I have such a code:
public class A: IDisposable
{
public CPlusCode cPlusCode{get;set;}
public void CallB()
{
using(bCode = new B(cPlusCode))
{
//do everything in B
}
}
public void Dispose()
{
cPlusCode.Dispose();
}
}
public class B: IDisposable
{
private CPlusCode cpp;
public B(CPlusCode cPlus)
{
cpp= cPlus;
}
public void Dispose()
{
cpp.Dispose();
//dispose everything
}
}
public static void Main()
{
for(int i=0; i<100000; i++)
{
var aObject = new A();
aObject .CallB();
}
}
The issue is that when I execute Main
, and B
eats up a lot of memory to instantiate, and from my observation it seems that the memory eaten by the program is not freed up.
Can Dispose really free the memory if there are other objects pointing to it?
Upvotes: 1
Views: 238
Reputation: 116744
Dispose
is just a method. It doesn't have to do anything at all.
After calling Dispose
on an object, the object still exists, but can no longer be safely used. The runtime doesn't assist in enforcing this, however. A "solid" implementation of Dispose
(one designed to assist in catching bugs) would set a _disposed
flag inside the object to true, and every other method on the object would throw ObjectDisposedException
if that flag is true (the Dispose
method itself should silently ignore further calls). But it is totally up to the implementer how far they go in enforcing this pattern.
An example would be FileStream
. When it has an open file, the process's handle count will have increased by 1. When you call Dispose
on it, the handle count will decrease. But this is only because the author of FileStream
wrote their Dispose
method to make that happen.
Which leads to the next problem - you can see the process's handle count in Task Manager and that is a very simple counter, but how are you measuring the memory usage? Note that the numbers shown in Task Manager are far from straightforward measures.
Upvotes: 2
Reputation: 51292
If you have implemented it correctly, and you are releasing all unmanaged resources inside Dispose()
, then the object should be collected (or better, will be eligible for collection) after you release all references to that object.
Note, however, that you are not disposing object A
in your example, which is also IDisposable
. If A
contains a reference to B
, and you don't dispose A
, then this might delay the collection of B
as well (in case that A
has some unmanaged stuff which may be creating a reference to A
).
Since unmanaged code seems to be referenced by A
in your example, A
should be responsible for disposing it.
Upvotes: 0
Reputation: 700770
from my observation it seems that the memory eaten by the program is not freed up.
That is perfectly normal. The program will hang on to a certain amount of unused memory, and this will be released if the system really needs it.
One might think that the best thing for performance would be to keep the memory usage as small as possible, but it's actually the other way around. The computer doesn't benefit anything at all from having a lot of unused memory, so for best performance the application should not do the extra work to minimise the memory usage until it's really needed.
Can Dispose really free the memory if there are other objects pointing to it?
Yes and no...
Calling Dispose on an object will not free the object itself, however if the object contains other objects, those can be released by the Dispose method. That will not free any memory by itself, but it will let the garbage collector do it on the next run.
Upvotes: 0
Reputation: 116481
IDisposable
has nothing to do with reclaiming managed memory. IDisposable
allows types to free resources not handled by garbage collection such as handles etc. For normal .NET types, the garbage collector will handle reclaiming memory when the objects are no longer referenced.
Upvotes: 6
Reputation: 300769
The GC will run when it decides it needs to, so 'timely' is not relevant. It will happen when it happens; i.e. it is non-deterministic
Upvotes: 2