views:

295

answers:

5

In C++ I'm using boost::shared_ptr and boost::weak_ptr to automatically delete objects that are no longer needed. I know these work with reference counting.

In Java, memory is managed by a garbage collector, which consideres the built-in object references as strong, WeakReference as weak and SoftReference as something in between (may be collected by the GC, but may as well survive the GC), which is really handy for caching objects for some time, but throwing them away as soon as free memory is getting low.

So now I'm back in C++ and I miss the comfort of having soft references. I wonder if soft referencing is practicable with reference counting at all. When the last strong reference to an object is cleared, and there remains a soft reference, when would it be deleted after all? I could think of some schemes, but none of them seem clever to me.

Just in case there are proper semantics for soft references along with reference counting, I wonder if this has already been implemented, maybe in a way that's even compatible with boost::shared_ptr (or the C++ TR1 equivalent std::shared_ptr for that matter).

If the answer to both questions is no, what are the alternatives in an object caching scenario?

EDIT: Of course I'm talking of a situation when caching is actually useful, because the objects are costly to construct (think of several access to a database and queries of a network), yet there are too many to keep them all forever.

+3  A: 

No, there is no such thing in C++. Nor should there be. Every object serves an important purpose. If it didn't, why do you still have it? Keeping objects around in this way is a memory leak. If you need an object, you need it. If you don't, destroy it. There is no in-between between useful and useless, either it serves a purpose or it doesn't.

If you're desperate, it is not impossible to write your own garbage collector and implement such a thing yourself. But I'd recommend against needing them or using them at all.

Edit: In object caching, people normally use LRU caches. When you have a cache miss, the reference to the least recently used object is destroyed (if the cache is full), the new object is created and put in as the most recently used, and all the others are moved down. However, you typically need to be retrieving items from disk before you actually need a caching strategy in C++. The cost of creating most objects is simply not that great.

DeadMG
I'm not sure I see how a soft reference, as Brian describes it, is a memory leak while a LRU cache is not. Either you need the object, or you don't.
Dennis Zickefoose
In resonse to your edit I edited my answer, too. I forgot to mention that may objects are rather coslty to create. The least-recently-used strategy is actually a good idea. Anyway, I'm asking myself if I need to put that manually into the implementation of a cache-mananging object, or if there is similar functionality which can be put into something like a weak reference.
Brian Schimmel
@Dennis: An LRU has a fixed size. It can be cleaned. It's controlled by the owning object. Soft references are uncontrolled. If you implement an LRU, then you have control over that memory. If you use soft references, anyone could use a soft reference. LRU caches are clearly defined and owned. Soft references are a gaggle of everybody.
DeadMG
@Brian: the language itself has no similar functionality. The only reason it exists in Java is that you already have a garbage collector it can piggy-back on. without a GC, someone else has to make the decision of when to destroy the referenced object. And then it might as well be implemented in a third-party library. There's no single "right answer" which could be incorporated in the language itself.
jalf
"Gaggle of everybody" = this object is worth having if there's space for it, not if there's not. "LRU cache" = I will use exactly 1GB of memory, and I don't care if that means your other apps won't run. On a desktop OS, running out of memory is a rare issue and doesn't require much technique on the part of apps. "If there's space" is basically always true. On constrained systems it can be really important that apps co-operate over memory use, and soft references are one (relatively blunt) tool for doing so.
Steve Jessop
@Steve: I already expressed my opinion that "this object is worth having if there's space for it and not if there's not" is quite flawed.
DeadMG
+1  A: 

You can implement your own LRU cache, and a new smart_pointer associated with such a cache. I don't think such a structure exists in Boost or standard C++ (off the top of my head anyway). If you are doing a web application or something... you can use libmemcached, which is the C Interface to memcached.

I find it hard to think of a situation where such an object would be so costly to construct / destroy... while it would be cheap to reinitialize... that a LRU cache would become useful. But if you really need one, you have the tools to actually build it.

Dragontamer5788
+5  A: 

As others pointed out, you can find referenced counted pointers (and their attendant weak counterparts) in the Boost library, but what's missing from the soft reference idea is some awareness of the runtime environment's memory constraints. In Java, for example, a SoftReference isn't materially different from a WeakReference in its capabilities; rather, it's the contract for how the runtime will preserve or evict the two kinds of references in the face of memory pressure that differs.

In order to mimic this behavior in C++, you'd have to build a memory-aware reference cache that held strong references on objects that the rest of your application would hold weakly. When the cache determined that the application was scratching its memory usage ceiling -- or any other constraining criteria -- it would release the strong references, surrendering the objects for "collection" (reaching the zero reference count) and allowing the weak references in use to later detect invalidation.

seh
... and the explanation for why neither C++ nor Boost provides this is presumably that "scratching its memory usage ceiling" is a difficult concept to generalise. I have worked on a system where processes could register with the kernel to be informed of low memory situations, which hints how much memory they should free if possible. But I think that's too rare to be a useful model for how your reference cache would know to start freeing things, and indeed Java's garbage collector relies on nothing so sophisticated.
Steve Jessop
+2  A: 

If you really want to replicate this behavior you may use a garbage collector (like this: http://www.hpl.hp.com/personal/Hans_Boehm/gc/) and use it to take care of your object or a subset of them, where using SoftReferences would be useful.

But I'd prefer to go for a solution more native to C++ than replicating Java bahavior - but nothing stops you of doing that.

Vitor Py
Thank you for mentioning this C++ mark-and-sweep library.
rwong
A: 

It would be necessary for the reclaimable object to implement locking. While a piece of code is actively using that object (from whatever thread it is running on), it does not expect that object to suddenly become unavailable. It is impossible to guarantee correct behavior if this ever happens. When the object is unlocked and resource runs low, a resource reclaimer can find and release unlocked and least recently used objects. Because of this, this form of resource reclamation always require cooperation between the objects (to support locking and to re-generate itself if it was reclaimed and was needed again) and the objects' users (to release locking when the object is not actively used).

Edited: my previous understanding of weak and soft reference was completely wrong. Weak and soft references seem to be an artifact of mark-and-sweep system. Weak reference, in particular, was used to enable the mark-and-sweep to reclaim circular-referencing objects which would otherwise be un-finalizable. I do not know if any library implements mark-and-sweep for C++. If there is one, I would like to know.

rwong
You're absolutely right about the locking, except that this problem is already solved: The same locking problem would arise for any weak reference. boost::weak_ptr solves this by providing a method called lock() which returns a boost::shared_ptr (which is a strong reference). As long as someone uses the object, he's holding the shared_ptr, which automatically behaves like a lock. I think this approach is rather clever, and I think a soft_ptr (if only it existed...) could use the same trick.
Brian Schimmel
@Brian: yes you're totally right. I didn't actually use any of those myself, so my answer is sort of a speculation - not to be taken too seriously.
rwong