No Mr. GameObject, I expect you to die.

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

Here’s the proof:

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;

public class WeakReferenceProof {
	public static void main(String[] args) {
		Object monitor = new ArrayList<String>(64 * 1024);
		WeakReference<Object> weak = new WeakReference<Object>(monitor);
		monitor = null;

		int counter = 0;
		do {
			Object ref = weak.get();
			System.out.println("weak.ref=" + ref);
			ref = null;
			counter++;

			System.out.println("garbage: " + createGarbage());
		} while (weak.get() != null);

		System.out.println("Done, after " + counter + " iterations");
	}

	private static int createGarbage() {
		// create some varying size garbage
		List<byte[]> holder = new ArrayList<>();
		for (int i = 0; i < 64; i++) {
			holder.add(new byte[i]);
		}

		// prevent HotSpot from optimizing everything away
		int sum = 0;
		for (byte[] data : holder) {
			sum += data.length;
		}
		return sum;
	}
}

It obviously terminates fairly quickly, within a few seconds. If your statement would be true, it would hold on to the reference indefinitely, making the process run forever.

[quote=“Best_Username_Ever,post:19,topic:40000”]
Well, I’ll even go the extra mile and make a really tight ‘update loop’, creating the ideal conditions for your reported ‘effectively strong reference’:


	public static void main(String[] args) {
		Object monitor = new ArrayList<String>(64 * 1024);
		final WeakReference<Object> weak = new WeakReference<Object>(monitor);
		monitor = null;

		new Thread(new Runnable() {
			@Override
			public void run() {
				while (weak.get() != null) {
					System.out.println("garbage: " + createGarbage());
				}
			}
		}).start();

		int counter = 0;
		do {
			Object ref = weak.get();
			System.out.println("weak.ref=" + ref);
			counter++;
		} while (weak.get() != null);

		System.out.println("Done, after " + counter + " iterations");
	}

Same deal.

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. ::slight_smile:

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.

// Don't use this code as an example
Map<GameObject, WeakReference<GameObject>> thing = new IdentityHashMap<GameObject, WeakReference<GameObject>>();

// Object creation
// Don't use this code as an example
GameObject o = new GameObject();
thing.put(o, new WeakReference<GameObject>(o));

// Object removal
public void killCharacter(GameObject o)
{
  // Don't use this code as an example
  o.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?

[quote=“Best_Username_Ever,post:22,topic:40000”]
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.

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

[quote=“Best_Username_Ever,post:22,topic:40000”]
That would be a broken garbage collector.

[quote=“Best_Username_Ever,post:22,topic:40000”]
You probably didn’t see that I already added the multi-threaded example that also didn’t nullify the reference.

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.

You summarized everything that was already said :slight_smile:

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.

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=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]

[quote=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.
[/quote]

I said ‘javadoc & JSL’ for a reason. JLS refers to javadoc:
http://docs.oracle.com/javase/6/docs/api/java/lang/ref/package-summary.html
http://docs.oracle.com/javase/6/docs/api/java/lang/ref/WeakReference.html
http://docs.oracle.com/javase/6/docs/api/java/lang/ref/SoftReference.html

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.

Dude…learn: you never argue with Riven. He’s always right and he will win all arguments.

Trust me…I’ve made the same mistake :persecutioncomplex:

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.

It’s all completely OT anyway :slight_smile:

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:


if (gidrah != null) {
if (gidrah.isDead()) {
gidrah = null;
} else {
// Do something with the gidrah
}
}

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.

Cas :slight_smile:

A SoftHashMap would be even less useful as a cache, unless you have a key object that usefully overrides equals(). The WeakHashMap is great for (designed for) a particular type of caching where you need a canonical map from a particular object to information about it - see this article for an example http://www.codeinstructions.com/2008/09/weakhashmap-is-not-cache-understanding.html.

Say you’re caching expensive to construct information about an entity. Once the entity is collected the cached info is irrelevant - hence the use of the WeakHashMap.

In other cases you wouldn’t so much want a SoftHashMap as a normal map with SoftReference values (which obviously isn’t missing in the JRE - you can just put SoftReferences in a HashMap). In either case, the value should almost always be a SoftReference.

Yes, ignoring @ra4king’s advice here … :wink:

You don’t need an isDead flag if the object can notify everything holding a reference to it when it dies. Whether this notification is practical or not depends on how many things are holding the reference and what scope they’re in. It certainly is easiest to use the flag if all you’re doing is removing and skipping over dead objects.

I think at this point however we may have long ago chased off the original poster ::slight_smile:

In your previous reply you repeated everything that was already said, and well, you did it again! :slight_smile:

Let me join in on the fun! There are two simple, usable approaches:
[x] eager cleanup: removing references from all known places, when the object dies
[x] lazy cleanup: removing references when encountering them, after the object died

We also didn’t chase off the OP, as he informed us he’s doing lazy cleanups.

[quote=“sproingie,post:32,topic:40000”]
Haven’t scared me away yet! (I can’ be awfully brave behind my laptop). As Riven said, I have gone with a mostly lazy approach, although true to form, it isn’t lazy everywhere. Right now, my game entities don’t all inherit from a commonWhat I am thinking of doing is create an

For convenience, I am thinking of creating an EntityIterator that will remove dead entities as it finds them. So if the entity list looks something like: (0)Alive, (1)Alive, (2)Dead, (3)Dead, (4)Alive, (5) Alive. and you are on element 1 and call next(), it will delete entities 2 and 3 and retrieve item 4. hasNext() would have to be written so that it returns true only if there are alive entities remaining in the list.

I assume that deleting items from the backing list in a next() or hasNext() call breaks the iterator contract. But since it’s just for me, it should work fine.



// I can write 
while (entityIter.hasNext()) {
   Entity e = entityIter.next();
  // do stuff
}

// instead of
while (iter.hasNext()) {

  Entity e = iter.next();
  if (e.isDead()) iter.remove();
  else {
     // do stuff
   }
}




How do you (lazily) cleanup references in fields? You can use the same approach, although it would be a wrapper instead of an iterator. The only downside is that it really affects the verbosity of the code.

Deleting through the list itself, yes, you’ll either get ConcurrentModificationException if you’re lucky, or duplicated/skipped items if not. If you use the iterator’s .remove() method, it’s fine. If a lot of your objects die between each pass, you might just want to keep two lists and copy over all the non-dead objects to the other list, then swap the list when done. Basically a ghetto semispace collector.

I meant having the backing list get modified by calling next() or hasNext() on the iterator. Normally, you would expect either of these to modify the backing list.

Right now I am just performing the checks whenever I access the item.


if (target != null) {

   if (target.isDead()) {
      // do cleanup
   }
   else {
      // Do whatever with the target
  }
}

I’ve toyed with the idea of writing a wrapper but haven’t really tried that approach yet.

If the iterator implements .remove() (it doesn’t have to, it depends on the collection) then it’s guaranteed to be safe. It’s just faster to do the collect/swap trick if you’re doing a lot of modifications, plus it has the effect of making the updates appear atomic to other threads, as long as you synchronize the swap … but of course it’s incompatible with multiple threads actually updating the list (I only had to deal with one writer, multiple readers when I did it). Tradeoffs all around.

@sproingie: ‘actual’ doesn’t wonder whether it’s safe to do, but whether it’s correct to do, as it clearly breaks the contract of Iterator.

Calling .next() a number of times, should not have any side effects to the backing collection. As we now use an iterator to remove elements using an iterator of a collection, we are clearly being naughty. Having said that, I wouldn’t even give it another thought. Just do what works, get your game playable, and fix issues with interface contracts when you are rich and feel like it.

iterator.remove() doesn’t break the contract of Iterator, it’s part of the contract of Iterator, albeit an optional one. As for .next(), the only case I can think of where calling .next() on an iterator would modify the thing being iterated over would be in the case of some kind of lazy stream(*), but that’s not the case for any collection class in the JDK, guava, or any others I know of.

(*) - It’s rare for stream type classes to implement Iterable/Iterator anyway, since .next() can’t throw any checked exception, and IOException is a common one such classes would have to throw)