• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
Krohm

Finalizers confusion

8 posts in this topic

Today, I am aiming at getting a better understanding of finalizers for GC'd memory management.
Ok, they are destructors. Instead of getting called at destruction, they get called at some time in the future. Hopefully.
1st question steams from MSDN.
[quote][url="http://msdn.microsoft.com/en-us/library/0s71x931.aspx"][b]Finalize Methods and Destructors[/b][/url]
Implementing Finalize methods or destructors can have a negative impact on performance and you should avoid using them unnecessarily. Reclaiming the memory used by objects with Finalize methods requires at least two garbage collections. When the garbage collector performs a collection, it reclaims the memory for inaccessible objects without finalizers. At this time, it cannot collect the inaccessible objects that do have finalizers. Instead, it removes the entries for these objects from the finalization queue and places them in a list of objects marked as ready for finalization. Entries in this list point to the objects in the managed heap that are ready to have their finalization code called. The garbage collector calls the Finalize methods for the objects in this list and then removes the entries from the list. A future garbage collection will determine that the finalized objects are truly garbage because they are no longer pointed to by entries in the list of objects marked as ready for finalization. In this future garbage collection, the objects' memory is actually reclaimed.[/quote]So perhaps the finalizer is managed as being accessed by a delegate. 1st GC pass sets an object's delegate to [font=courier new,courier,monospace]null [/font]and adds an entry for finalization. The object is not collected but their finalize methods are run. 2nd GC pass sees those objects as not having a finalizer to run and can therefore free their memory blob with no hassle.
Or perhaps we have sets with allocation IDs to ignore. I don't find this very important.

[b]But why to require 2 passes?[/b] Cannot we just run the finalizer immediately before releasing a blob?
Perhaps this is consequence of having separated trace phases and a collection phases? Or maybe it's something dealing with multiple threads?

2nd question. To extend an object's lifetime, use [url="http://msdn.microsoft.com/en-us/library/system.gc.keepalive.aspx"]GC.KeepAlive[/url]. I don't understand why this is needed.
[source lang="csharp"][font=courier new,courier,monospace]MyWin32.HandlerRoutine hr = new MyWin32.HandlerRoutine(Handler);
MyWin32.SetConsoleCtrlHandler(hr, true);[/font]
[font=courier new,courier,monospace] // Give the user some time to raise a few events.
Console.WriteLine("Waiting 30 seconds for console ctrl events...");[/font]
[font=courier new,courier,monospace] // The object hr is not referred to again.
// The garbage collector can detect that the object has no
// more managed references and might clean it up here while
// the unmanaged SetConsoleCtrlHandler method is still using it.

// Force a garbage collection to demonstrate how the hr
// object will be handled.
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

Thread.Sleep(30000);[/font]
[font=courier new,courier,monospace] // Display a message to the console when the unmanaged method
// has finished its work.
Console.WriteLine("Finished!");[/font]
[font=courier new,courier,monospace] // Call GC.KeepAlive(hr) at this point to maintain a reference to hr.
// This will prevent the garbage collector from collecting the
// object during the execution of the SetConsoleCtrlHandler method.
GC.KeepAlive(hr); [/font] [/source]
It would appear to me a better way to deal with this problem would be to have explicit notion of what [font=courier new,courier,monospace]SetConsoleCtrlHandler [/font]does. But I see this might not be possible. In my head, [font=courier new,courier,monospace]hr [/font]would not be collected anyway as there's a reference to it on the stack.
But C# GC is smarter than that... it appears it will look ahead in code to see if something is not referenced anymore and thus clean it... perhaps C# stack/start set building does not work as I expect.
[b]Anyone knows how [font=courier new,courier,monospace]KeepAlive [/font]helps with the problem?[/b] I'm inclined to speculate it might actually be NOP just to prevent the flow analyzer from marking the reference unused.
0

Share this post


Link to post
Share on other sites
Hey, I don't know about the exact implementation of the whole thing in C#, but in general its a bad idea to use a finalize method at all. If your code needs it to work properly, then your code is unstable.

About KeepAlive: basically it tells the GC to [b]not[/b] collect something that isn't needed anymore. In the code example, "hr" is not needed anymore but they still tell the GC to keep it alive. This is something that you will probably never need. Still an example though: lets say you have a big object and you don't want the GC to destroy it while drawing, as it may result in a FPS loss, then you would call KeepAlive so it won't destroyed just yet (maybe later when a loading screen is active or whatever).

The whole point about Garbage Collection is that you don't have to worry about this stuff, but if you want to do so anyway, I suggest using a language that comes with new and delete. Edited by the_visualist
0

Share this post


Link to post
Share on other sites
Becouse there is no guarantee when the finalizers in C# is run, if they are ever run, there should be minimal amount of code in them. Any code (that affects the program) in the finalizers can be considered as a bug.

This means the burden of destructing objects, the resource management, is left for users. You can't even rely on RAII. IDisposable helps, but you have to still manually decide where to wrap them in using block or call the Dispose.

The whole point about Garbage Collection is that it forces you to manually manage resources if you care about them being released.
0

Share this post


Link to post
Share on other sites
[quote name='Krohm' timestamp='1347693680' post='4980311']
[b]But why to require 2 passes?[/b] Cannot we just run the finalizer immediately before releasing a blob?
Perhaps this is consequence of having separated trace phases and a collection phases? Or maybe it's something dealing with multiple threads?
[/quote]
My guess is that it's becouse multiple threads. Another thread can reclaim the memory, while another thread is running the finalizers.

See Finalization Internals at [url="http://msdn.microsoft.com/en-us/magazine/bb985010.aspx"]http://msdn.microsoft.com/en-us/magazine/bb985010.aspx[/url]

[quote name='Krohm' timestamp='1347693680' post='4980311']
[b]Anyone knows how [font=courier new,courier,monospace]KeepAlive [/font]helps with the problem?[/b] I'm inclined to speculate it might actually be NOP just to prevent the flow analyzer from marking the reference unused.
[/quote]
I guess the detection for unreferenced objects happens at bytecode level, and the scope for an object is not as it's seen in C# code. So maybe KeepAlive just keeps the object referenced, as it's seen in CIL and bytecode. Edited by Codarki
1

Share this post


Link to post
Share on other sites
Well, the link does not reall say much about that... but it's some food of thought.
The reason for which the GC is ran is very simple. Consider:
[attachment=11385:gc_example.png]
Garbage set is { [i]D, E, F, G, I[/i] }.
[i]D[/i] can be collected.
[i]I[/i] must run a finalizer. Therefore, we cannot collect the object it references or we'll break its code.
Suppose we run the finalizer and collect [i]I[/i].
Ideally we could now collect [i]E[/i], [i]F[/i] and [i]G[/i]. They were marked as garbage (or better, not marked as used) since they are not reachable from the root set. Unfortunately, garbage collection does not tell us what objects [i]I[/i] uses.
The only way to clean them properly is to do another pass.

In other words, the presence of a finalizer causes an additional "root set" to be created. This root set is the set of all objects having finalizers which were considered garbage.

I'm afraid we won't get much information on what KeepAilve does (how it does it), but since it appears my theory is confirmed, I suppose I will just live with it. Edited by Krohm
0

Share this post


Link to post
Share on other sites
[quote name='Krohm' timestamp='1347693680' post='4980311']
But why to require 2 passes?
[/quote]
Even if a single pass GC would be possible, it could get time critical. And time is a major issue in GC. You don't know what happens in a finalizer, maybe releasing other stuff which would be ready for GC again, closing IO. Therefore a GC could get in a recursive trap adding more and more objects to the cleanup queue while calling more and more finalizers.
The reason to put it into a queue [i]could [/i]be to straighten the time effect (call finalize method when there's time), whereas releasing just memory is more or less straight forward and the time impact can be better estimated (cleaning up the memory in layers). Edited by Ashaman73
1

Share this post


Link to post
Share on other sites
I really like your questions.

[quote name='Krohm' timestamp='1348220955' post='4982300']
I'm afraid we won't get much information on what KeepAilve does (how it does it), but since it appears my theory is confirmed, I suppose I will just live with it.
[/quote]
What I'm most interested is at what level the GC performs, it will explain a lot. C# source code is out of the question. CIL is not that optimized. Any decent optimizer will perform dead code removal, hoist variables out from loops or even out from functions. Net has major advantage that it is just-in-time. The compiler can perform runtime profile for hotspots and do optimizations that static languages cannot. Just like C++ source code after optimizer is nowhere near the matching assembly, I believe Net optimizer makes optimizations that makes KeepAlive necessary.

PS. Someone who knows CIL or bytecode generated please butt in.

Edit: Drunk typos. Edited by Codarki
0

Share this post


Link to post
Share on other sites
[quote name='Ashaman73' timestamp='1348224530' post='4982314']Even if a single pass GC would be possible, it could get time critical. ...[/quote]Yes, it is indeed a very valuable property. The concept that finalizers could be completely deferred or even dispatched incrementally over multiple "mini-GC-ticks" didn't come to me. Thank you.
0

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0