Z
Zeng
From this paragraph:
Does that mean it's possible for my process to get recycled for reach memory
max% even though most of the memory counted as used is no longer needed by
my app and it is not freed because GC hasn't gotten a chance to do so
safely? If that's true, sounds to me that having a finalizer in my class or
not would only *reduce* the chance of that occuring, but not eliminating, is
that accessment accurate to you?
Garbage collection is triggered in .NET either by application shutdown, heap
exhaustion, or an explicit call GC.Collect(). With the exception of
application shutdown, however, garbage collection only occurs when the
execution path reaches a safe point.
Does that mean it's possible for my process to get recycled for reach memory
max% even though most of the memory counted as used is no longer needed by
my app and it is not freed because GC hasn't gotten a chance to do so
safely? If that's true, sounds to me that having a finalizer in my class or
not would only *reduce* the chance of that occuring, but not eliminating, is
that accessment accurate to you?
billr said:I guess it's time for me to make my apologies. The point of my mentioning
Safe Points was to illustrate the workings of the GC.
Safe Points are points in the code that have been identified as it being
safe for the GC to suspend ALL threads in order to do its thing.
A problem faced by the GC is that when compacting the heap it must make sure
that it does not change the structure of the object graphs (internal
tree-like structures used for determining reachable/non-reachable objects
i.e. referenced/non-referenced objects), nor should it change the structure
of the heap. In order to achieve this, the GC is responsible for maintaining
object references (i.e. adjusting references within objects contained within
the graphs), because of course, when an object is moved in the heap, any
existing references to that object will be invalid. The only safe way to cope
with this is to suspend all application threads during garbage collection. It
is for this reason that the JIT compiler inserts Safe Points into the code,
i.e. points in the execution path that are safe for thread suspension.
Garbage collection occurs by the GC effectively "hijacking" the thread by
inserting a different return address from the safe point. When GC is
complete, .NET sends the thread back to its original return address and
normal execution continues.
Garbage collection is triggered in .NET either by application shutdown, heap
exhaustion, or an explicit call GC.Collect(). With the exception of
application shutdown, however, garbage collection only occurs when the
execution path reaches a safe point.
IMPORTANT NOTE :
================
When the GC has determined that an object is in fact garbage, the first
thing it does is check the object's metadata, and if the object implements
Finalize(), instead of destroying the object, it is marked as reachable
(which is not what you want if you want your object to be destroyed
immediately) and is moved from its original graph to another graph called the
"finalized queue". A seperate thread then iterates over the finalized queue
invoking Finalize() on all objects contained therein; then -and only then-
are the objects removed from the queue and destroyed, hence freeing up its
heap space.
So, essentially, when you implement Finalize() you are telling the GC that
when the object is no longer referenced it is NOT SAFE for deletion YET, and
Finalize() must first be invoked, which means that the object is not
destroyed for approximately two runs of garbage collection. It is for this
reason that deterministic finalization is recommended.
EXPLICIT garbage collection is not recommended becuase it is an expensive
operation involving scanning object graphs, thread suspension and resumption,
thread context switches, and EXTENSIVE use of reflection to read the objects'
metadata.
I hope that that clears up the confusion I might have injected into the
discussion, also, I hope it clears up why you should not declare a finalizer,
even if there is no body. The simple act of declaring a finalizer ensures
that the object lives for longer than you might actually intend/require.
--
Of all words of tongue and pen, the saddest are: "It might have been"
Bill.Richards @ greyskin .co .uk
http://greyskin.co.uk
Zeng said:I'm not sure on what you mean by "there is no documentation
available...because they don't apply here...". The existance of a document
doesn't depend on what part of version of my code was posted. I appreciate
your help looking into this for me, many people would just ignore posts and
move on even they know the answer or have some comments, but I don't think
we are going anywhere with it. My response was to BillR who proposes that I
should be implementing the IDisposable.
It's the production environment, if the process get recycled, all my cache
in the memory will have to be reloaded when it goes back up. That would
cause delay to the service that we want to provide. Sounds like you are not
familiar with the processModel entry with attribute memoryLimit="60" as
default, it's in the machine.config file. thanks!
process,Willy Denoyette said:Again, you don't have to implement IDisposable when you don't deal with
managed resources. What do you expect to do in your Dispose method? And No
there is no documentation available on Safe points, because they don't apply
here (at least not in the code snip you've posted).
I'm also not clear on what you mean with recycling the asp.net
isexpectingthis something you intend to do or is it something that you are
toevenbe done automatically, anyway before the asp.net process recycles, all
application domains get unloaded, and this implies a GC run and a finalizer
run, so all objects (still referenced too) will be collected , but
whentosome objects aren't collected, all memory once allocated for the process
will return to the OS when the process ends. So what are you afraid of?
Willy.
Relying on IDisposable won't work easily in the multi-threading scenarios.
Since the memory required for each object is big, I have put them on
static
variable to share it among the servicing threads. Think of CMyClass is a
spell-grammar-checker that helps looking up things and provide suggestions
for correction for many users create their documents online. In this
scenario, looks like there is the only choice
--> Make CMyClass disposable and create managing component (internal or
external to the class) just to coordinate when and which objects are done
just to call Dispose. This sounds like the intensive plumbing and
error-prone approach that I was so used to with C++
Conceptually, this is the drawback of a smart system, the programmer knows
what he/she needs to be done but can't change its behavior (which is very
very smart most of the time)
By the way, where can I find a documentation about the safe-points?
Wouldn't that be better that there is a call to wait for the system
berecycledone with collection and there is a safe point in that method? And is
there
a safe point in the component that checks for memory usage and
theyouaspnet process when 60%(default) is reached?
and just to add my two pennies worth ...
Although you may call GC.Collect(), much the same as in Java when
trywherebyto
force the VM to run garbage collection, it will still run at it's
earliest
convenience.
During compilation, your code is injected with "safe points"
theyouGC
can hijack the thread and take control to do its work. Now, when
callandGC.Collect() your code might not be in a stable enough state for the GC
to
run, and the only way that the GC can know that it is safe to run is if
it
has reached a "safe point".
Generally speaking, it is bad practice to call GC.Collect() since you
will
inadvertantly interrupt a perfectly (or near to) working system,
youasyourself can cause all sorts of naughtiness to occur.
My choice of preference (as it is for a lot of developers) is to
implement
IDisposable.
AND, yes, our code should be easily readable and left in a state that
renders it possible for future devlopers to look at it and see what is
going
on (for maintenance reasons, or whatever), however, designing our classes
"off centre" just in case, the next developer doesn't know as much
usviais a
pretty poor choice IMHO.
--
Of all words of tongue and pen, the saddest are: "It might have been"
Bill.Richards @ greyskin .co .uk
http://greyskin.co.uk
:
I finally narrowed down my code to this situation, quite a few (not
all)
of
my CMyClass objects got hold up after each run of this function
thememorysimple webpage that shows NumberEd editbox. My memory profile shows
that
those instances survive 3 rounds of GC collections - it's not what I
expected. In my real code, CMyClass occupies big amount of
andfinalizaerthey
all share one stance of another class that I don't have enough memory
hold
more than a just a few in the memory. Notice that the
theitmy
CMyClass makes a big difference in demonstrating this issue, w/o
theproblem doesn't exist. The interesting thing is if I run the page
again,
the old dangling ones got destroyed and the new ones become dangling.
Am
I
missing something about the GC? Do I need to explicitly implement and
call
dispose for these classes instead of relying on GC to promptly collect
it?
Apparently somehow these objects can survive the automatic
collections...disposing it would help. Thanks for your comment and
help.
internal class CMyClass
{
ArrayList m_array = new ArrayList( 50000 );
private string m_idStr;
private Guid m_guid;
internal CMyClass( int n )
{
for( int i = 0; i < n; ++i )
m_array.Add( Guid.NewGuid() );
m_guid = Guid.NewGuid();
m_idStr = m_guid.ToString();
}
internal Guid id
{
get{ return (Guid ) m_array[ 0 ]; }
}
~CMyClass()
{
// Trace.Write( m_guid, m_idStr );
}
}
private void RunBtn_Click(object sender, System.EventArgs e)
{
ResultEd.Text = "";
try
{
for( int i = 0; i < int.Parse( NumberEd.Text ); ++i )
{
Hashtable table = new Hashtable( 100 );
ArrayList ids = new ArrayList( 10 );
for( int j = 0; j < 10; ++j )
{
Guid id = Guid.NewGuid();
table[ id ] = null;
ids.Add( id );
}
for( int k=0; k < 10; ++k )
{
foreach( Guid id in ids )
{
table[ id ] = new CMyClass( 10 );
}
CMyClass myobj;
foreach( Guid id in ids )
{
myobj = (CMyClass) table[ id ];
myobj.id.ToString();
}
} // for k
} // for
}
catch( Exception ex )
{
ResultEd.Text = ex.Message + "...CallSTack:" +
ex.StackTrace;
}
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
ResultEd.Text = "Done";
}