I was randomly poking around some of the lesser visited areas of the java api in the hope that java.lang.reflect.Proxy could solve a problem of mine (it can't) when I restumbled upon WeakReference.

Now I've been thinking more and more about resource management recently, in particular since most of the time I take the easy option and just load everything at game boot. That works but is sloppy and as I start adding more animation and landscape tiles into RescueSquad it starts getting impractical.

// Sentinal for 'ice' resource now vanishes, pool tidies and leaves just 'grass' and 'lava' in memorypool.tidy();}

(obviously for a proper version 'Texture' could become a generic Resource interface for any native resource that needs explicit cleanup).

The 'trick' is to dish out handles to our Texture object, which have both an accessable Texture *plus* a hard reference to a Sentinal object. Internally we keep a hard reference to the same Texture, but a weak reference to the Sentinal. Once a new level has finished loading we scan through and check which sentinals have now vanished (ie. there are no more handles pointing at them). Since we still kept a proper hard ref to the (now unused) Texture object, we can happily destroy it.

This seems like a neat idea to me, since it means Stage/etc. classes don't have to remember to return/release Textures back to the resource pool, as we let the gc do the heavy lifting for us. Does anyone see any big gaping holes in this trick that I've missed?

Hey, that's a pretty cool idea! I've been using text level files to show all the resources I will need, then I do manual comparison to see what I should unload or keep. It works well enough, but you've got a very elegant solution.

Also having used Objective-C a lot lately the way they handle garbage collection gave me an idea for this. Basically, you call "retain" on something you want to keep in memory and "release" on something you want to remove from memory. This isn't the same as C++, however, because retain and release don't allocation or deallocate anything, they just increment or decrement a retain count variable. Then when the autorelease pool (basically the garbage collector) fires at the end of the timestep it removes anything with a retain count <= 0. So when using Objective-C it is quite easy to first iterate through all the stuff getting loaded in for the next level, add a retain to anything that already exists, then calling a release on everything from the last level. You'll end up with a retain count of 2 for items that are in both levels, then it will be decremented back to 1 correctly.

You could probably take this idea to Java, and simply have a list of integers corresponding with your loaded resources, then add 1 to it whenever you load a new resource. Wouldn't be as useful as your method, but could do the trick and might be easier to understand.

Yeah, the retain/release reference counting works well in languages like C++ and Objective-C where you can put things on the stack and not worry about manually having to call destroy() on them. Java's finalisers fill that void for a gc environment but in practice they don't really cut it IMHO. C# has it's IDisposable which is an improvement - you still have to manually call destroy() but at least the compiler complains if you forget.

The only thing I worry about with this is the unpredictability of the gc - calling System.gc() may not actually remove the sentinels by the end of loading, so tidy() might not do anything. The workaround to that would be calling tidy() in the game loop every frame, which might not be too bad - the majority of resources get removed when expected, and any stragglers probably won't last more than a few frames.

Because there's a difference between a ResourceHandle (which is dished out to game code) and a ResourceEntry (which is an internal structure to ResourcePool). The handles have a strong reference to the sentinel, and so keep it alive while it's being used. The entries have the weak reference, so tell us when all of the handles have vanished.

To do this kind of stuff I use a custom hashtable, where the entries extend SoftReference. Inside the table is a ReferenceQueue to nuke entries when removed. The last 'n' references are held in a circular array to prevent collection of recently added items. Also has a touch method to allow user to place an entry into the array.

I've been vaguely thinking about goofing with playing with "java.lang.management.MemoryNotificationInfo" to build something more clever. Sun explicitly states not to use it in this manner, but I don't know of any other option.

ResourcePool.tidy could be a bit cleaner and ResourceEntry.isInUse() could be eliminated if you used a ReferenceQueue. That would also make calling it every loop virtually free if there's nothing to remove.

Also I'm not sure why you're using WeakReference rather than SoftReference - my experience has been that in practice they behave in the same way, but in theory SoftReference is more appropriate for resource management.

I don't see why you'd want to use a Soft rather than Weak reference - i'm explicitly after the case when an object is no longer reachable, which is predictable and consistent. Using a soft reference means that it's going to suck up an unknown amount of memory based on the whim of when the VM decides it's low on memory. Users don't like to see apps taking up huge chunks of memory even if they would otherwise be not using it.

Orangy - what you're doing really is abuse and probably massively unreliable. I too had the wizbang idea of munging all that Resource stuff in SPGL to use WeakReferences to automatically call destroy() on unreferenced resources - but actually that's not what I really wanted to do when I thought really hard about it.

The problem actually stemmed from precisely the lazy initialisation code that you're describing - eg. create some prerequisite resource, then use it several times, then destroying it when it's no longer referenced.

First of all what you effectively need to do is decide on the set of resources you need to have created at some moment in time - say, the start of a level - and look at the set of resources which are "in use" right now, and then work out the "difference". In SPGL terms this would be like calling create() which lazily loads all dependent resources, but you'd actually set a flag or something to prevent the actual creation of the resources - instead you'd just get a graph of all the required resources. Then you'd destroy() all the resources not in that graph and call create() once more with the flag unset.

That's half the story

The other half is, sometimes you create a resource solely to use as a source for other resources. Imagine a BufferedImage resource, where create() simply loads the BI from an URL. You then have a whole bunch of sprite resources you create from the BI. The problem is that after creating all your sprites you don't need that BI any more in the heap at all.

It'd be nice to solve that problem using the WeakReference hack except for the fact that GC might not actually do anything about it. In fact I've turned off GC using a VM option before to solve glitching problems at inopportune moments (live TV graphics).

What I think the process should be to solve this is to have resources marked as "transient". That is, you specifically set some resources as being used temporarily. At any point you can then simply destroy() all transient resources in one go. You could also detect transient resources and make them into SoftReferences so that the GC would at least throw them out when memory got tight (in which case you'd need to track them in a ReferenceQueue and destroy() them when they wound up in there). One thing you specifically can't do though is mark stuff as "transient" that has been sent outside the VM. That's stuff like textures and buffers etc.

I realise this isn't quite as automatic as you were hoping for but it isn't really an automatic problem like garbage collection. Indeed you can see the rather strange solution to similar issues in garbage collection with the various different sorts of Reference object and complete lack of guarantees about when or if things end up in any particular state

For the most part though the 2-pass graph/sweep method is entirely adequate and you'll only need to go as far as marking stuff as transient when you're into heavy memory optimisation mode.

Yeah, while I quite like this trick I'm not sure if it's robust enough to be used as a basis for resource management in a game. Ideally Java would have something like C#'s IDisposable but since that requires proper language and/or VM support that's not going to happen really.

At work when we've needed *strict* resource management it's always been done manually with loading "sections" - usually defined in a text file you'd have sections like "PreGameMenus", "InGame", "PostGame" etc. that listed all the resources that would be loaded when that section started. That makes it reasonably easy to diff two sections and decide what's new and needs to be loaded in, and what's unneeded and can be ditched. The problem is that it's very manual so if you use an extra resource in code you've got to remember to add it into all the relevant loading sections or you get a crash at runtime.

The WeakReference approach is at the other end of the scale really, as it tries to figure out the sections and unions of resources automatically depending on what the code is *actually* doing.

A two-pass solution is something I've thought about but not really looked into - it certainly seems to be a useful compromise in that the resources to load come from the code rather than an external file that needs to be kept in sync, but also doesn't rely on the gc behaving nicely. However it means that your actual game code for creating a new map and populating it with objects becomes more complicated, which is a big loss IMHO. But I've not looked into it properly yet, so maybe there's ways to make the two-pass method work while still keeping the game code suitably simple.

As far as I can see, some experimental tweaks to Resource/Resources in SPGL will be able to manage what we want without breaking too much existing code. I suspect I'll have to go through all the doCreate() implementations to put in a check to see whether it's actually being asked to create things or just make a graph. I think it should solve 99% of the problem, the remaining marking with "transient" being something you'd only do when you start performance tuning at the end of the development cycle.

I don't see why you'd want to use a Soft rather than Weak reference - i'm explicitly after the case when an object is no longer reachable, which is predictable and consistent. Using a soft reference means that it's going to suck up an unknown amount of memory based on the whim of when the VM decides it's low on memory. Users don't like to see apps taking up huge chunks of memory even if they would otherwise be not using it.

Using ReferenceQueue is a good idea though.

I may be out of date with respect to recent changes in the way the VM handles memory, but the way I'm used to it working is that the VM never shrinks its heap and clears soft references to softly reachable objects before expanding its heap. Therefore soft refs don't make it take up any more memory and do improve loading time if the player keeps dying at the start of level 2 and restarting at level 1.

I may be out of date with respect to recent changes in the way the VM handles memory, but the way I'm used to it working is that the VM never shrinks its heap and clears soft references to softly reachable objects before expanding its heap. Therefore soft refs don't make it take up any more memory and do improve loading time if the player keeps dying at the start of level 2 and restarting at level 1.

java-gaming.org is not responsible for the content posted by its members, including references to external websites,
and other references that may or may not have a relation with our primarily
gaming and game production oriented community.
inquiries and complaints can be sent via email to the info‑account of the
company managing the website of java‑gaming.org