|
||||||||||||||||||
Add Forum to Favorites | Send Topic To a Friend | View Forum FAQ | Track this topic |
Last Thread Next Thread ![]() |
| Low Latency Garbage Collection via Reference Counting |
|
![]() DirectAnim Member since: 6/5/2005 From: Troy, NY, United States |
||||
|
|
||||
| Interestingly, I was looking at a smart pointer discussion in the MSDN libraries (Templates and Smart Pointers, under "When Should You Use Templates?") and I realized that their implimentation was incomplete! They didn't have a copy constructor for the smart pointer. If you tried to implement such a system in your code, you'd end up facing hell because the constructor would be called more often than the constructor. The only copy constructor specified took a pure pointer type, so the pointer would be dereferenced, assigned, but in between, it would be freed. Real interesting stuff! |
||||
|
||||
![]() Black Knight GDNet+ Member since: 1/1/2002 From: Toronto, Canada |
||||
|
|
||||
| Hmm so when using this smart pointer. Can we still use delete to free the memory or is it enough to just set it to null and let the smart pointer call delete. [ShadowyTree]|[MyPortfolio]|[glBase](OpenGL3.2 Framework at lolstage) _________ |
||||
|
||||
![]() KulSeran Member since: 12/9/2003 |
||||
|
|
||||
| @Author Interesting article. Does cover some of the issues but glosses over others. Things like boost::smart_prt and auto_prt and shared_prt are not mentioned. And like all good things, it is important to note that there are already good implementations of these systems out there, that would be less error prone. Also to note, from looking back at the Enginuity tutorials, a good smart_prt class lends itself nicely to having other housekeeping beside just a refrence count, since you can do some really nice memory profiling with it. (easily thrown in the GC part of that implementation) Mostly directed at the fact that your pro/con section was lacking: And, while 'low latency' may be usefull in some cases, it is also nice to be able to defer smart_prt deletion till the end of a frame, necesitating a GC of some kind (this is outside the scope of what this article is trying to cover, but noteworthy). As such, maybe some discussion on these facts, and other main ideas about memory management, and why you'd choose between a time-spreading GC acting like new/delete, a non-time-spreading GC acting like new/delete, or a GC acting as a pool allocator, ect. The multi-threading is a bit misleading too. As locking will really hurt performance, expecially with the overhead of setting up mutexes/criticall sections for each refcounted object. Where you could be using something like the windows API InterlockedIncrement() and InterlockedDecrement() to take care of the locking for you and insure atomic transactions on the refcount. Thus sidestepping the setup costs and their associated overheads in both the refcount structure and in the operation times. Lastly, there is no mention of warning DO NOT MIX smart_prt code with non-smart_ptr code unless you realize the risks (or know that you should be using a auto/shared pointer in some cases)
foo ( RefCntPointer<A> prt )
{
}
...
A *ptr1 = new A;
foo ( ptr1 ); // didn't realize that you 'smartpointered' something that is called from non-'smartpointer' code.
delete ptr1; //<--crash cauze ptr1 got deleted when the smart pointer left scope
Thusly the distinctions of auto, smart, and shared pointers so that you know who should be owning what, and don't end up in the above situation. @Black Knight The idea is that you do like java or any other GC language. You just forget about the pointer in one way or another. You let the pointer go out of scope (any function/class will handle this as it is destroied/leaves scope) or forcefully set it to NULL if you have some other use for it, that you know won't fit into the "auto destroy" functionality. So you never actually call delete on it anymore. [Edited by - KulSeran on June 20, 2007 3:22:25 AM] -------- My Projects |
||||
|
||||
![]() Cygon Member since: 10/13/1999 From: Sachsenberg, Germany |
||||
|
|
||||
| Great to see this topic finally getting some coverage. I've been using smart pointers and externalized reference counting for several large and performance-critical projects and, so far, I've had zero memory leaks and zero problems related to smart pointer usage. Using some creative programming it was even possible to reduce the performance overhead of allocating the counter variables to zero :) Regarding the article, I would have wished for some references to the Boost and Loki libraries, both of which provide elegant and well-tested implementations of smart pointers. Whatever you stance on heaviness of the boost library is, its shared_ptr<>, weak_ptr<>, intrusive_ptr<> and scoped_ptr<> classes can be used with very little overhead. - intrusive_ptr<> is what's being discussed in the article - shared_ptr<> uses external reference counting (the reference counter is allocated by the shared_ptr<> class), so you can use it on any class without special preparations. I prefer it to intrusive refcounting because it leaves me the freedom to still use my classes on the stack without having the subRef() or Release() method still sitting there in every class and worrying whether it needs to be called on this instance or not. - weak_ptr<> solves the circular reference problem. A weak_ptr<> does not increase the refcount of the pointed-to object, but notices when the pointee is destroyed. - scoped_ptr<> is a simple helper for heap-allocated RAII objects. Loki essentially has one ingenious parameterized SmartPtr class that can take on all the roles listed above and more. The article could also provide some tips, like that passing smart pointers as const references (which is good practice with classes anyway) to methods eliminates the needless addRef()/subRef() cycle that would otherwise occur when the smart pointer is copied in order to be passed as an argument and then destroyed again when the method is left. Oh well, I didn't want to sound overly critical. The article is seriously good work, it's just that just a bit more research could have made it even better :) -Markus- |
||||
|
||||
![]() Cygon Member since: 10/13/1999 From: Sachsenberg, Germany |
||||
|
|
||||
Quote: I see what you're getting at, but in this one specific example, the smart pointer's constructor would of course have been marked as 'explicit' by any programmer worth his money, forcing you to write A *ptr1 = new A; foo ( my_smart_ptr(ptr1) ); or even A *ptr1 = new A; foo ( my_smart_ptr::acquire(ptr1) ); Of course, the general risk remains. Just wanted to show off my l33t knowledge of the explicit keyword :D -Markus- |
||||
|
||||
![]() Black Knight GDNet+ Member since: 1/1/2002 From: Toronto, Canada |
||||
|
|
||||
From what I have tried this is perfectly legal :Pvoid main() { //scope { RefCntPointer<Entity> p(new Entity); //explicity delete and free memory p = NULL; //the destructor of p gets called when scope ends //it tries to delete the pointer but cant because its //already set to null. } } So I cant use delete on p; but only set it to null if I want to free it explicity.If I ever forgot to set it to null it will be free when it gets out of scope because its destuctor calls the subRef which checks the reference count etc. Hmmm. [ShadowyTree]|[MyPortfolio]|[glBase](OpenGL3.2 Framework at lolstage) _________ |
||||
|
||||
All times are ET (US)![]() |
Last Thread Next Thread ![]() |
|