 |
|
 |
As a rule, if your code or architecture relies on details of what the garbage collection does then it is probably badly designed.
For your mutex example, surely it would be much better and safer to explicitly call m.ReleaseMutex(). (This of course will have a secondary effect of keeping m alive.)
(Your article maes a good point about the GC release point not being where some people might assume that it is.)
Anthony
|
|
|
|
 |
|
 |
Anthony Berglas wrote:
As a rule, if your code or architecture relies on details of what the garbage collection does then it is probably badly designed.
I disagree. The whole point of having a garbage collector is to avoid having to explicitly release things that you allocate. The less dependant you are on remembering to release things you allocate, the more stable your program is likely to be.
Anthony Berglas wrote:
For your mutex example, surely it would be much better and safer to explicitly call m.ReleaseMutex(). (This of course will have a secondary effect of keeping m alive.)
My own aproach to this would be using(Mutex m = new Mutex(...)) {} This is largely because I'm used to the C++ scoping rules that the using construct enforces. I'll admit I hadn't realized I might need it to keep an object alive rather than to force an object to be disposed at the right time.
I'll also add, I think the .net garbage collection scheme is insane. It only works for objects that represent only memory, and in my experience programming in C++, I've found these objects are almost an exception rather than the rule. For a garbage collection scheme to handle bitmaps, mutexes, locks, device contexts, file handles, windows, database transactions, and many other system objects and situations, a garbage collector needs to be predictable. I wonder how many years of the most common error being to forget to release (or keep alive) a system resource it will take before Microsoft creates a new system that handles these items.
Nathan Holt
|
|
|
|
 |
|
 |
By all means use a using construct. It will handle exceptions properly, although you would want to test that Mutex actually treats Dispose correctly.
But it is a long experience with Java that you should only use the garbage collecter for memory, not for releasing semaphores! It is too unpredictable.
Having spent many nights in large projects tracking down obscure memory corruption bugs I can say that managed code is the only way to go. The Lisp machines had the entire operating system in Lisp (in 1970's hardware). But it has taken another 30 years for these basic techiques to catch on.
IMNSHO using C/C++ doubles the time required for a larger project. The cost to the industry over all these years must be billions.
Further, a major advantage of Java over .Net is the focus on "100% Java". I have had weird corruptions resulting from the fact that .Net coninuously calls into the underlying C/windows code.
Anthony
|
|
|
|
 |
|
 |
IMNSHO using C# doubles the time required for a larger project. The cost to the industry over all these years must be billions.
Regards,
gnk
|
|
|
|
 |
|
 |
I agree with the delegate taking care of the object. That definetly simplifies things
The code can be as follows :
public class GCDemo
{
private StartTest.ApplicationExit m_appexiting;
public GCDemo()
{
m_appexiting = new StartTest.ApplicationExit(AppExiting);
StartTest.AppExit += m_appexiting;
}
~GCDemo()
{
}
void AppExiting()
{
StartTest.AppExit -= m_appexiting;
}
}
|
|
|
|
 |
|
 |
It would seem to me, by definition at as long as a object reference is still in scope the GC will not destroy the object. If this is not true, then what's the point of scope?
Secondly, yes, The GC will not run on its own unless the system begins to run out of resources.
Thirdly, I will take issue with the idea of never calling GC.Collect(). If the program is creating new objects as it runs, eventually the program will become a pig on the system and burn all of its resources. Calling GC.Collect() after major tasks or periodically when the program is idling, will keep the program from dragging down the whole system. Would you create a c program that never called free, but kept a reference to all allocated memory and only would free something when malloc failed? That is how the GC works. It doesn't free stuff until necessary (tries not to burn CPU cycles unless necessary), but that doesn't mean you shouldn't call GC.Collect now and again to clue the GC that its ok to burn a few CPU cycles...
Overall, I believe you should use program structure to control how the GC will behave not by making some obscure calls into the GC....
peace,
Keith V, just another lunatic from the fringe....
|
|
|
|
 |
|
 |
Keith Vinson wrote:
It would seem to me, by definition at as long as a object reference is still in scope the GC will not destroy the object. If this is not true, then what's the point of scope?
Keith that is the optimization feature of JIT compiler which is destroying my object before it goes out of scope and I am trying to overcome it by the solution I provided in the article.
To demonstrate this further, remove the event handler in GCDemo class, create a GCDemo() object outside the for loop and put a GC.Collect() statement and also a GC.WaitForPendingFinalizers() just to force the GC to garbage collect. It will call the ~GCDemo() before the application exit.
Sriram Chitturi
|
|
|
|
 |
|
 |
Why not a singleton or static member variable reference then? That is what I was refering to as using program structure to control object lifetime.
In reference to your: "remove the event handler in..." test
Wow, Your statements are by tested example? Then I stand corrected. Seems like a shortcomming in the GC / optimizer, see my reply to zucchini....
K
|
|
|
|
 |
|
 |
Technically, it's not about scope but about extant references.
The GC can discard any object which is no longer referenced. This notion of referenced, for us humans, is bounded by scope. But technically a compiler that uses optimizer-type lookahead techniques can see this:
void myEventProcessingFunction() {
LargeObject[] allobjects = new LargeObject[10000];
for(int i = 0; i < allobjects.length; ++i) {
// use the array
}
while(true) {
// do something, like process items on a queue
}
}
...do you see that the compiler could release the reference of the enclosing scope to the large array after its last use, instead of technically at the closing brace? This way, for cases where you have endless loops for processing in a thread, or long waits on synchronization objects, when the GC runs it can clean up these objects that the compiler knows are never referenced again after a certain point.
In the code above that loop could run for months in a carefully written long-lived process, and retain megabytes of memory for no reason. Now in a few years of enterprise development in Java, I learned to not do these things; but here the compiler could help cover these mistakes.
Think of it another way: imagine that the compiler could see the last place where each variable is referenced in your scope, and silently inject an additional scope between the declaration and the line after that last reference?
The author is correct in that the JIT is free to "release" an object once it is no longer used, but that does not mean that:
- the JIT actually does this (does anybody know if this is theoretical or real?)
- you would have any way of knowing or caring!
- it is statistically likely (except in the long-running cases I presented) that the GC would ever run while you're in the middle of some particular function (the less often it runs, the better, except on embedded devices)
quoth KeithV:
Overall, I believe you should use program structure to control how the GC will behave not by making some obscure calls into the GC....
I agree. Ordinary cases and patterns do not require manipulation of the GC, though rare cases do benefit from some of these techniques... but these are truly rare; you should not routinely be writing GC code.
Finally...
The real point is, that the GC is trustworthy. Every object that can ever be referenced again will not be thrown away by the GC. So if you can't reference the object ever again, how could you miss it when it's gone?
There too is another purpose for delegates: that the delegate object is itself strongly referenced and holds a strong reference to the callback object, so you can therefore (if good design actually permits) allow callback objects to "dangle" from core objects in the runime base class library (such as the application shutdown example).
Of course, having said this, maybe some genius out there will come up with an interesting use for a weak-referenced delegate!
If you, gentle reader, have not yet heard of weak references and are intrerested in GC issues, please go look them up. They are a useful tool to have in your toolbox (like that funny-shaped wrench in my toolbag that I haven't used since 1993)
Cheers!
- Dave G.
|
|
|
|
 |
|
 |
Yes, In the case you created, an optimizing compiler could / should id the array as never being used after the for loop. That is because the object in question is truly local.
However, in the case of the mutex cited in the article. The object is a global, system wide entity. It would be interesting to know if compiler / GC is smart enough to figure this all out. I doubt it.
I think the GC works on scope boundaries not extant of references. I would be willing to bet that the reference counting that keeps an object alive is triggered by pushing and popping object references onto the stack frame. AKA {}(bracket preamble/post amble routines), as well as other types of assignment operations. Check the disassembly code window, does anyone see any ref counting code interspersed with your code???
I doubt an optimizing compiler would bother to call a object --reference count routine as soon as it could while not executing the code it was suppose to be executing
As a final thought does the GC even have a thread other that your default thread to run on unless you call it? I know there are framework threads hanging around but does the GC have access to them? I bet it doesn't. That would explain why the GC doesn't do clean up unless its forced to, and one application doesn't seem to be able to trigger a GC cycle in another application.
As a real world example: a NT service used to transfer files to/from other computers. Runs 24 x 7, after about a week the machine begins to drag ass. Almost all of its VM is taken up by the .NET system service. Add a few well placed calls to GC.Collect and all is well in la la land...
I guess its more fun to pontificate, than to test...
K
|
|
|
|
 |
|
 |
I still believe that the compiler is clever enough to figure out if a variable is referenced or not after a certain point.
Otherwise why do we need GC.KeepAlive() if a variable is guarenteed to be alive within { } ?
Keith Vinson wrote:
However, in the case of the mutex cited in the article. The object is a global, system wide entity. It would be interesting to know if compiler / GC is smart enough to figure this all out. I doubt it.
Please refer MSDN link here on KeepAlive() and see the example
Sriram Chitturi
|
|
|
|
 |
|
 |
Creating a delegate creates a hard reference to the object instance containing the delegate method.
Or at least it should!
If this is true then in your example, the only time the dtor will be called it in response to app shutdown, when the GC attempts to destroy all objects in order to ensure the clean closing of external resources.
Also, I believe that, all factors being equal, the GC will _never_ run unles a memory threshold has been crossed, or explicitly invoked.
Explicitly invoking the GC is very bad - it artificially forces medium-lifespan objects into the second generation.
If anybody has data that supports or denies what I have written here, please respond so we can all understand better!
|
|
|
|
 |
|
 |
Zuchchini
I accept about the delegate piece you are talking about, which infact provided a better solution to my problem. Thanks (see the "Point noted" thread above)
But to demonstrate the problem, create a GCDemo object outside the for loop and and call for GC.Collect() and also GC.WaitForPendingFinalizers(). ~GCDemo() is called before app exit !
zucchini wrote:
Explicitly invoking the GC is very bad - it artificially forces medium-lifespan objects into the second generation.
There are many reasons I want to call GC.Collect() and GC.WaitForPendingFinalizers(). A simple example is a program which shows the amount of memory allocated on the heap within a scope. I put a GC.Collect() at the beginning and ending of a scope to determine this.
Sriram Chitturi
|
|
|
|
 |
|