I am writing a simple space sim and I have various lists and references that a game object can belong to. For instance, I may have a CapitalShip. It may be in the list of ships in space, the list of ships of a given side, the list ships inside a nebula. References to that ship may also be held by other ships that have it as a target, etc.

The issue I am struggling with, is when a ship dies, is remembering to remove the reference from everywhere i need to so the object is gone. Right now I manually remove it from everywhere it might be, but this doesn't seem like it would scale very well. I was thinking of adding an Active/Inactive flag that is checked whenever the list is iterated over but it seems like a hack to have to check every time you use a list.

How do other people make sure that objects that are "dead" are removed from everywhere they need to be? I think this is probably an easy problem where I am overlooking an obvious solution, but any advice would be great.

Right now I manually remove it from everywhere it might be, but this doesn't seem like it would scale very well.

I can think of a number of optimisations, which trade off one thing or another - but in the end you should keep it simple, unless it is actually slowing (you) down.

Does your game even need to scale very well? Keep it small, get things done, publish your game. You can always decide to multiply the number of units by factor 10 later, and worry about performance then.

Hi, appreciate more people! Σ ♥ = ¾Learn how to award medals... and work your way up the social rankings!

Well, for Objects, I usually would have one static reference to the list. People might think it is horrible to make a list of Objects a global field. I think when it comes to deleting it from multiple references, it makes the process much easier for me.

Now, I don't use Objects anymore, and instead create an int for each Object. Then I just track the integers with a simple boolean (Object in [T], Object out [F]). With this method, I don't need to use static references for anything and I just replace Objects in an array if they are "dead" in my integer list.

It depends on the type of data structures you are using. For most cases trying to remove dead objects from anywhere they might be can be substituted for discarding dead elements as they are found. Sometimes searching for an object to remove is more expensive than leaving its corpse behind. Sometimes I deliberately leave the corpses behind so that planning and collision checking are done as if they were executed concurrently and then update and dispose objects in a second loop. Some data types (hash-tables, linked-lists, and this thing) make it much more efficient to remove elements given a handle to a data entry than a key or a value. If you're going to iterate over an entire list anyway once a turn or once every few turns, then it makes the most sense to combine dead object removal with one of those loops.

If you have more complicated structures than Lists, Maps, or Sets, then it might be necessary to remove elements immediately. (An event based system would probably be easiest in this scenario.) But on the other hand it might be slow to remove things one at a time as they die. Deferred removal would save on slow one at a time removals or updates, but it's basically the same strategy as removing during iteration and still requires corpses to stick around for a while.

If the data structures aren't meant to be looped over and are used for something else (like discovering nearby neighbors or some other search operation) then the last strategy I can think of is keeping a Set of recently removed items. Then if a search for your 3 nearest neighbors included only one live object you could get the next two closest and so on until you found 3 live objects. That only works if you expect it to be more likely to return lists of live objects than dead ones and if removing elements were too slow.

By the way, it sounds like you're using lists as your default data structure. If order is unimportant, then don't use ArrayList or LinkedList. Chances are good that an array based bag or generic hash table could cover most scenarios and those are well suited for iterator based removal (the first strategy).

Thanks everyone for the responses. What I've been working on is a hodgepodge of some of the ideas here. Everything that is updated each frame implements the Updatable interface which includes a method shouldUpdate() that if it evaluates to false if its dead. When I run through the list of Updatables, I remove anything where shouldUpdate returns false.

When a single object holds a reference to another (like a missile having a ship as a target), I am using Message passing to notify the holder when the receiver dies.

Riven,

When I meant scalable, I meant more from the sense of me being able to keep track of everything in my head rather than from a code performance standpoint.

ctomni,

I also tend to keep most of my lists as static members, in my case I keep them in a "GameWorld" object that makes things convenient.

pjt33, 65k,

I am thinking that I may create GameObject collections that have listeners built into them to simplify things.

Best Username,

You make a good point about paying attention to the distinction between killing things right away and removing it when you iterate the next time through. I was using "list" in more of a colloquial way instead of java.util.List. I've implemented an array backed Bag data structure that I am integrating into my code more and more where it makes sense.

I would centralize removal and then remove things from everywhere they can be. If you have tons of references all over the place, maybe that is what is necessary for your app or maybe you can find ways to minimize them.

Riven's answer to another question got me thinking, I wonder if I can help solve this problem by using weak references. If for instance, the update list, was made out of weak references then I wouldn't have to explicitly remove it, as long as I removed it from everywhere else.

The problem with WeakReferences is that they will eventually be removed, not immediately. Your soon-to-be-removed object can still linger around, hours later, if your app/game produces barely any garbage.

Hi, appreciate more people! Σ ♥ = ¾Learn how to award medals... and work your way up the social rankings!

Riven's answer to another question got me thinking, I wonder if I can help solve this problem by using weak references. If for instance, the update list, was made out of weak references then I wouldn't have to explicitly remove it, as long as I removed it from everywhere else.

If you want to take that route, you just need to use a mutable wrapper class everywhere:

1 2 3 4 5 6 7 8 9 10

publicclassWrapper<T>{privateTwrapped;

publicWrapper(Twrapped) { this.wrapped = wrapped; }

publicvoidclear() { wrapped = null; }

publicTget() { returnwrapped; }}

But it doesn't solve the problem of tidying up the cleared wrappers from the lists. All it does is make things fail harder, which isn't a bad thing but falls short of what you could achieve with other approaches.

The problem with WeakReferences is that they will eventually be removed, not immediately. Your soon-to-be-removed object can still linger around, hours later, if your app/game produces barely any garbage.

"Java-Gaming.org" Did you forget? No one relies on destructors outside of C++.

Serious part: This was a little confusing the first time I saw it. Let me see if I can explain it. WeakReferences will not tell you if an object is "garbage collector dead" (unreachable via strong references) or "0 HP dead" (a dead character in a game). An object may be gc dead, but there is no way of knowing that until the garbage collector can get around to that point. Most of the time 0 HP dead and gc dead objects may still be reachable via weak references; because they won't be removed until the garbage collector has time to reach them. If at any time a weak reference is used to create a new strong reference it becomes gc live again, meaning it could stick around indefinitely.

But weak references alone should not extend the lifetime of an object if they are not accessed. On the other hand if you use weak references for much more than using a WeakHashMaps the way it's intended to be used, then you're basically using them as a strong reference.

The problem with WeakReferences is that they will eventually be removed, not immediately. Your soon-to-be-removed object can still linger around, hours later, if your app/game produces barely any garbage.

"Java-Gaming.org" Did you forget? No one relies on destructors outside of C++.

You completely missed the point. The problem the OP is trying to solve is to get rid of references to 'dead' objects, not to-be-collected objects. If WeakReferences are still lingering around, his game-logic will encounter them (for an undetermined amount of time), which was exactly what he didn't want. What that has to do with destructors...

Then you go on describing the problems of WeakReferences, which was exactly the point I was making.

You missed the joke. See, it's funny if you think how C++ programmers always... and you said... never mind. You were talking about Java, right? If so, you probably should explain what you actually meant about lingering objects. And if not, why is it bad that a one piece of garbage not get collected if that's the only garbage created over a span of several hours?

No object can linger in a collection longer than it can linger on the heap. That still does not explain the assertion "The problem with WeakReferences is that they will eventually be removed, not immediately. Your soon-to-be-removed object can still linger around, hours late."

I never said it would linger longer on the heap than in the collection. What I said was that they lingered as referenceable objects, while being logically dead and thus break logic that assumes the dead objects are no longer accessible to the game logic.

The OP wants logic that removes references to dead objects, and WeakReferences cannot be used to implement that, as WeakReference.get() can/will still return a non-null value long after the object is flagged 'dead'.

I think the OP got that point, quite early in the discussion, and solved the problem either with:

removing the dead object from every possible collection that might hold it (eager approach)

using an iterator that 'filters & removes' dead objects, to access all collections (lazy approach)

flagging an object as dead, and checking its state whenever the object is used (cumbersome approach)

Hi, appreciate more people! Σ ♥ = ¾Learn how to award medals... and work your way up the social rankings!

I'm pulling your leg a little and trying to get you to explain your first assertion. So, I guess that's basically being argumentative but for the sake of completeness. You're pretty bad at explaining this, but I am fairly certain I get it.

The way you described it, it sounds like WeakReferences in general cause memory leaks. Not that WeakReferences can't be relied upon to return null when the last strong reference is removed. Someone reading this might walk away thinking they cause leaks and/or not understand why this is a misuse of WeakReferences. My goal was to explain what you glossed over.

But you said: "There is no way to use a WeakReference in a way that it becomes effectively a strong reference."Response: o = weakReference.get(); Accessing the object you want requires creating an at least temporary strong reference. If you use a WeakReference to regularly access (in an update loop, for example) the object it refers to, then the garbage collection is going to see it was accessed and never clear the weak reference. (Effectively making it a heavy strong reference.)

I'm also not sure what circumstances would cause a weak reference to "extend the collection of the object to typically one gc cycle." I doubt that's correct, but it depends on whether you meant "to the end of the next gc cycle" (right, but true for most references) or "by one extra gc cycle" (wrong, as far as I know).

But you said: "There is no way to use a WeakReference in a way that it becomes effectively a strong reference."Response: o = weakReference.get(); Accessing the object you want requires creating an at least temporary strong reference. If you use a WeakReference to regularly access (in an update loop, for example) the object it refers to, then the garbage collection is going to see it was accessed and never clear the weak reference. (Effectively making it a heavy strong reference.)

Rapidly getting a strong reference out of the WeakReference, does not prevent the weakly referenced object from being collected.

We both know better, but three posts later I still wasn't sure if you actually knew. There are probably plenty of people here that don't know what a weak reference is. And some that do probably would even suggest using a WeakHashMap as a cache.

P.S. I agree that using WeakReferences or any layer of indirection is a pretty bad method to remove objects. It's pretty obvious that everything above this post covers everything from the original question, but now that I think of it, you might be able to use weak references even though it's a poor method and involves a ton of extra work.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

// Don't use this code as an exampleMap<GameObject, WeakReference<GameObject>> thing = newIdentityHashMap<GameObject, WeakReference<GameObject>>();

// Object creation// Don't use this code as an exampleGameObjecto = newGameObject();thing.put(o, newWeakReference<GameObject>(o));

// Object removalpublicvoidkillCharacter(GameObjecto){// Don't use this code as an exampleo.kill();thing.remove(o).clear();}

=============

That's proof that it might not cause a problem, but not proof that it never will cause a problem. A different garbage collector implementation might not work the same way. In that example your garbage and referenced object are probably all in the youngest generation and it's set up to likely trigger garbage collection after you nulled the reference. Would it still work if it were multithreaded, in an old generation, garbage collection weren't predictable, or something modified your object?

And some that do probably would even suggest using a WeakHashMap as a cache.

WeakHashMap is perfectly capable to act as a cache, as long as it doesn't store strong references in its values. The only downside of WeakHashMap is that it can only function as a very volatile cache. A SoftHashMap would probably be a better fit for a cache, but it's missing from the JRE.

In that example your garbage and referenced object are probably all in the youngest generation and it's set up to likely trigger garbage collection after you nulled the reference. Would it still work if it were multithreaded, in an old generation, garbage collection weren't predictable, or something modified your object?

You probably didn't see that I already added the multi-threaded example that also didn't nullify the reference.

Hi, appreciate more people! Σ ♥ = ¾Learn how to award medals... and work your way up the social rankings!

One thing that may be getting overlooked is that the original problem looks to demand an immediate removal of the object from wherever it's being referenced, as a matter of game logic. This isn't something any flavor of weak reference is designed for, as the problem is really one of precise lifecycle management, not memory management.

You certainly could go with weak references, but then you'd still need some kind of "liveness" flag until it got around to being collected. You could also ensure that whatever container the object reference is added to gets notified whenever the object is being eliminated. Finally, you could use some kind of -- dare I say it -- entity system and pass only entity IDs to other containers, leaving the management of the entity's lifecycle to the entity system which holds its sole permanent reference. There's no single hard and fast answer to the problem, it all depends on how you've designed the rest of your system.

The entity system however doesn't quite solve the problem (unless the library implemented it), as you'd still be dealing with IDs that point to dead entities/objects, which have to be cleaned up somehow - for which the already suggested approaches would work just fine.

Hi, appreciate more people! Σ ♥ = ¾Learn how to award medals... and work your way up the social rankings!

Read the javadoc and JLS, instead of undermining somebody's argument by making up counter-arguments. The specification is very strict.

The JLS doesn't dictate the behavior of the automatic memory manager. It describes how objects should be finalized, but not how, when, or if they are reclaimed or determined to be finalizable/reclaimable. You could use any type of garbage collector you wanted or even no garbage collector at all.

Quote from: JLS SE7 Ch. 1 Intro

The Java programming language is a relatively high-level language, in that details of the machine representation are not available through the language. It includes automatic storage management, typically using a garbage collector, to avoid the safety problems of explicit deallocation (as in C's free or C++'s delete). High-performance garbage-collected implementations can have bounded pauses to support systems programming and real-time applications. The language does not include any unsafe constructs, such as array accesses without index checking, since such unsafe constructs would cause a program to behave in an unspecified way.

Quote from: JLS SE7 Ch. 12.6

The package java.lang.ref describes weak references, which interact with garbage collection and finalization. As with any API that has special interactions with the Java programming language, implementors must be cognizant of any requirements imposed by the java.lang.ref API. This specification does not discuss weak references in any way. Readers are referred to the API documentation for details.

It decribes very clearly when an "object becomes eligible for reclamation", which is what the GC will take as a condition to discard objects. Note that 'recent or rapid' access does not qualify as a condition to keep the object around, reachability is. Only for SoftReferences the javadoc states that a bias towards (delayed) collection for recent access of said references is encouraged. Note that this bias does not imply, nor allow, that the "object becomes ineligible for reclamation". Therefore the argument is moot.

Either way, it doesn't matter. The examples I provided clearly show that the VM is perfectly able to clean up rapidly/recently accessed weakly referenced objects.

Hi, appreciate more people! Σ ♥ = ¾Learn how to award medals... and work your way up the social rankings!

I make plenty of mistakes, and admit to that all the time (I have no pride!) - but that doesn't mean I won't defend arguments I'm sure of (and haven't proven to be wrong at) to the bitter end, FWIW. Which is arguably an incredible waste of time.

Hi, appreciate more people! Σ ♥ = ¾Learn how to award medals... and work your way up the social rankings!

I think it's been touched upon before but I will chime in with some experience:

OP: There is only one way to actually achieve what you want to do, and it first requires that you understand there is no actual way to do what you want to do, and nor do you need to do it, and there are two relatively simple variations on implementing it.

The easiest solution requiring least refactoring is to mark each object with a flag to indicate whether it is dead or not, and before every attempt to dereference the object, check for both null and this flag and if it is set, null the reference (or remove from the collection), preferably only in one place if you can, thusly:

The second way to do it is to automate this and make your own reference class that automatically checks for isDead() and nulls its reference and then returns null thereafter.

Otherwise, what you are trying to achieve is nothing to do with garbage collection. It is purely about the logical lifecycle state of your game entities, and this is an entirely different problem to garbage collection and null pointers in Java. Even in C++ you have to do this the same way.

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