Java-Gaming.org Hi !
Featured games (83)
games approved by the League of Dukes
Games in Showcase (522)
Games in Android Showcase (127)
games submitted by our members
Games in WIP (589)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Memory saving techniques?  (Read 5878 times)
0 Members and 1 Guest are viewing this topic.
Offline ikaruga

Junior Newbie





« Posted 2009-09-23 16:47:24 »

My game has a list of enemies objects. What's the best way to manage memory when an enemy object is killed?
In C++, my initial reaction is to create two linked lists of objects -- one for "dead" enemies and another for "live" enemies.
As an enemy dies, I would just send it to the list of dead enemies. As I need a new enemy, I would just yank one from the list of dead enemies (recycle memory).

Unfortunately, Java has no pointers.

What's the best way of dealing with this scenario?
Offline Riven
« League of Dukes »

« JGO Overlord »


Medals: 831
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #1 - Posted 2009-09-23 16:56:46 »

Option one:
Don't even bother. You can allocate tens of thousands objects per second.


Option two:
Why would you need pointers? In Java you can also have Lists to move your objects around

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

JGO Kernel


Medals: 56
Projects: 11


Monkey for a head


« Reply #2 - Posted 2009-09-23 16:57:36 »

Unfortunately, Java has no pointers.
Le sigh. Everytime I ever hear anyone say this, it's always a C or C++ programmer who's utterly failed to grasp some of the basics of Java. I think you need to go back to the basics and actually read up on how java object references work.

Slightly more helpfully, there's nothing to stop you having a 'dead' and 'alive' lists just like you would[1] in C++. If you need a fresh enemy and your 'dead' list is empty, then just 'new' one.

However unless you've got thousands of new enemies spawning each frame, or you're on some radically constrained platform (iPhone?) then there's no reason why you shouldn't just new them when you need a new enemy, and just remove it from your alive list when it's dead (and like the gc clean up the memory).

[1] Except that even in C++, I wouldn't do things like this, as it's a needless micro-optimisation. I'd just be new-ing and delete-ing as this avoid a whole bunch of object lifetime issues which can cause obscure and difficult to track bugs.

[ TriangularPixels.com - Play Growth Spurt, Rescue Squad and Snowman Village ] [ Rebirth - game resource library ]
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Eli Delventhal

JGO Kernel


Medals: 42
Projects: 11
Exp: 10 years


Game Engineer


« Reply #3 - Posted 2009-09-23 17:37:20 »

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
public class Enemy
{
    private int var1, var2, var3;//, etc.
    private boolean isMarkedForDeletion;

    public boolean isMarkedForDeletion()
    {
        return isMarkedForDeletion;
    }

    public void die()
    {
        isMarkedForDeletion = true;
    }
}


1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
private ArrayList<Enemy> enemies;

public void mainLoop()
{
    for (int i = enemies.size()-1; i >= 0; i--)
    {
        Enemy e = enemies.get(i);

        e.act();

        if (enemyHitABullet(e))
        {
            e.die();
        }

        if (e.isMarkedForDeletion())
        {
            enemies.remove(i);
        }
    }
}


There you go. just give all your entities a marked for deletion boolean, then on the next pass they'll be deleted. Because the ArrayList does in fact just hold pointers to each Enemy (like every single object reference in Java), when it gets removed from the list, the garbage collector will find that the object has no pointers to it and will therefore get removed from memory.

Memory management in Java is quite easy - if you don't want to keep an object, don't have references to it anymore. The only difficult cases are rare - mostly involving multithreading or having a lot of people working on the same program so some people don't know what others are doing. Just have good design and you're fine.

See my work:
OTC Software
Offline Riven
« League of Dukes »

« JGO Overlord »


Medals: 831
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #4 - Posted 2009-09-23 17:52:45 »

Memory management in Java is quite easy - if you don't want to keep an object, don't have references to it anymore. The only difficult cases are rare - like if you have two objects that have references to each other, then they will never get deleted. Just have good design and you're fine.

Excuse my French: Bullshit.

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

JGO Kernel


Medals: 42
Projects: 11
Exp: 10 years


Game Engineer


« Reply #5 - Posted 2009-09-23 17:55:52 »

Excuse my French: Bullshit.
Instead of being rude, maybe tell me why I'm wrong?

@ikaruga: Have a look-see at this link, it is pretty helpful about the cases when you can have memory management problems. http://www.ibm.com/developerworks/java/library/j-jtp06243.html

See my work:
OTC Software
Offline Riven
« League of Dukes »

« JGO Overlord »


Medals: 831
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #6 - Posted 2009-09-23 18:19:03 »

Instead of being an ass, maybe tell me why I'm wrong? I don't sit and read the technical manual, I spend my time making games instead and report on what I observe - so I am mistaken sometimes.
Right. So... you can just give somebody advice, by making guesses, and when you are blatantly wrong, are not taking criticism lightly...

Ok, I will elaborate: the GC doesn't do reference counting, it determines whether an object is reachable, by other reachable objects. It holds a list of 'root' references - you can observe that in heapdumps, if you want proof. This is also one of the reasons the GC is so fast: upon GC, it doesn't figure out which objects can be disposed, it will only track which are reachable, and move those around the heap. The GC will completely ignore all garbage, unless it has an overriden finalize() method, or is tied to a ReferenceQueue.

Seriously, you seem to troll the forums lately for when I make a mistake just so you can rub it in my face and then say nothing helpful.
I've been very inactive on the forums lately. But wait, you're calling me a troll? I might be harsh, very critical, but.. a troll?? You might want to PM me about your personal grunt, I'm not going to derail this thread any further.

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

Junior Newbie





« Reply #7 - Posted 2009-09-23 18:49:20 »

Anyway...

What a useful class @Demonpants@! Doing a google search also revealed a LinkedList class.
Isn't it more efficient to use a linked list? For 1000+ enemies, I'm sure there is significant overhead each time you delete an enemy in the ArrayList (the list gets reordered, etc.). Or does it not matter as @Riven@ seems to imply?
Offline h3ckboy

JGO Coder


Medals: 5



« Reply #8 - Posted 2009-09-23 18:59:54 »

well, your main question is going to be,

are you crunching for that last inch of performance. If yes, then I assume taht an arrayist might be slower, but I am not sure. Dont lists have order though. But I guess they dont have the empty.
I suppose that a linked list mgith be faster....
Offline kappa
« League of Dukes »

JGO Kernel


Medals: 78
Projects: 15


★★★★★


« Reply #9 - Posted 2009-09-23 19:04:27 »

from the tests I've run in the past, ArrayLists are almost always faster than LinkedLists.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Orangy Tang

JGO Kernel


Medals: 56
Projects: 11


Monkey for a head


« Reply #10 - Posted 2009-09-23 19:11:01 »

Cache-coherance and not having to use an iterator usually mean an ArrayList is going to be faster. And if removal is a particular problem then a nice trick is to swap the element to be removed with the last element, and then remove it, which avoids having to reshuffle the other objects. Obviously this means you can't rely on the objects being in a particular order, but for entities that rarely matters.

[ TriangularPixels.com - Play Growth Spurt, Rescue Squad and Snowman Village ] [ Rebirth - game resource library ]
Offline ikaruga

Junior Newbie





« Reply #11 - Posted 2009-09-23 19:29:05 »

Wow. ArrayLists are faster than LinkedLists in Java -- would not have seen that coming Wink

@Orangy Tang@ - so to confirm, swapping the last element is as simple as:

1  
2  
3  
Enemy tmp = enemies.get( enemies.size() - 1 );
enemies.set( enemies.size() - 1, enemies.get(i) );
enemies.set(i,tmp);


The "set" is actually a copy by reference, right?
Offline Orangy Tang

JGO Kernel


Medals: 56
Projects: 11


Monkey for a head


« Reply #12 - Posted 2009-09-23 19:56:57 »

Wow. ArrayLists are faster than LinkedLists in Java -- would not have seen that coming Wink
It's pretty much the same in C++ as well - often std::vector is faster than std::list unless you're doing lots of insert/removes or if you're holding big types by value.

But yeah, your swap looks fine.

[ TriangularPixels.com - Play Growth Spurt, Rescue Squad and Snowman Village ] [ Rebirth - game resource library ]
Offline Eli Delventhal

JGO Kernel


Medals: 42
Projects: 11
Exp: 10 years


Game Engineer


« Reply #13 - Posted 2009-09-23 20:06:26 »

Right. So... you can just give somebody advice, by making guesses, and when you are blatantly wrong, are not taking criticism lightly...
I wasn't guessing, I honestly thought that was the case.

ArrayList is a lot faster than LinkedList because you're dealing with a single block of memory versus needing to follow pointers all over the place. LinkedList is really only faster if you're swapping items in your list in, out, and all around, changing its size, reordering it, etc. But even so, like people have already said, ArrayList will usually be faster anyway. You've really got to be moving a lot of stuff around to see a reason to use LinkedList. In a lot of situations in games (like say when you want to remove your enemies from your list), you'd store the index of object in the list somewhere for quicker removal. In a LinkedList you've got to browse through n elements to get to the nth element, whereas ArrayList is O(1). Of course, removal in LinkedList is O(1) and O(n) in ArrayList, but they this is just another place LinkedList can't really top ArrayList - in order to remove any element you usually need to get to that index anyway, so it just becomes O(1) + O(n) = O(n)... not any faster than ArrayList, even where it is "supposed" to be. It's also, IMO, annoying to use Iterator to access the items in LinkedList and not just be able to do it via array locations. Say you're using an Iterator with your object deletion case - if you try to remove anything from the list while you're using that same Iterator you can very easily get ConcurrentModificationExceptions. Index access is much cleaner and more predictable.

But of course, if you've got some sort of implementation that specifically will work better for a LinkedList, then go for it.

See my work:
OTC Software
Offline ikaruga

Junior Newbie





« Reply #14 - Posted 2009-09-23 21:10:23 »

Well that seems to settle the issue for me. Thanks all.

One last quick question: out of curiosity @Demonpants@ why are you traversing the array backwards? I noticed the same setup in another ArrayList tutorial...
Offline Eli Delventhal

JGO Kernel


Medals: 42
Projects: 11
Exp: 10 years


Game Engineer


« Reply #15 - Posted 2009-09-23 21:40:25 »

Well that seems to settle the issue for me. Thanks all.

One last quick question: out of curiosity @Demonpants@ why are you traversing the array backwards? I noticed the same setup in another ArrayList tutorial...
That has to do with removing, sorry I meant to explain that but I forgot.

Say you have an array:
A, B, C, D, E, F
And you want to remove ones that are marked for deletion, which we'll say are the bold ones, B and E. Iterating forward does this:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
index = 0;
object[index].act(); //A.act()

index = 1;
object[index].act(); //B.act();
remove[index]; //Array becomes A, C, D, E, F

index = 2;
object[index].act(); //D.act() ! Here's the problem - the object at index 2 is what was formerly at index 3

etc.


You can see that the indices being used are now off whenever something gets removed, and you'll start skipping things. Whereas if you go backwards, you know that you've already called everything that will be affected by the removes:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
index = 5;
object[index].act(); //F.act()

index = 4;
object[index].act(); //E.act();
remove[index]; //Array becomes A, B, C, D, F

index = 3;
object[index].act(); //D.act() Note that is is all correct now. Although F goes from index 5 to index 4, we've already called it so that's okay.

index = 2;
object[index].act(); //C.act();

etc.


If you really wanted to go upwards instead of downwards, you can always compensate for your remove like this:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
for (int i = 0; i < enemies.size(); i++)
{
    Enemy e = enemies.get(i);

    e.act();

    if (enemyHitABullet(e))
    {
        e.die();
    }

    if (e.isMarkedForDeletion())
    {
        enemies.remove(i);
        //Compensate for something getting removed.
        //Our browsing index is effectively one less.
        i--;
    }
}


Of course, that's n more operations (even if subtraction is super cheap) and it's bad practice to mess with your loop variable inside a for loop. Might as well go backwards, it works better all around.

See my work:
OTC Software
Offline Cero
« Reply #16 - Posted 2009-09-24 01:00:40 »

I've been very inactive on the forums lately. But wait, you're calling me a troll? I might be harsh, very critical, but.. a troll?? You might want to PM me about your personal grunt, I'm not going to derail this thread any further.

You are creating a post, with only one word, apparently disagreeing. He has to ask you if you would please explain why you are in fact disagreeing.
Not very contributing.
Where I come from, thats a troll.

Offline Riven
« League of Dukes »

« JGO Overlord »


Medals: 831
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #17 - Posted 2009-09-24 06:04:42 »

You are creating a post, with only one word, apparently disagreeing. He has to ask you if you would please explain why you are in fact disagreeing.
Not very contributing.
Where I come from, thats a troll.

You might have missed my post where I explain how the GC cleans up these circular references: by ignoring them completely.

On a sidenote: wouldn't you think your post would fit the 'not very contributing' category?

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

JGO Coder


Medals: 5



« Reply #18 - Posted 2009-09-24 08:09:47 »

come on as much as I like to read this stuff during science Wink.

keep it to pm messages, as not to derail this guys thread Smiley.
Offline pjt33
« Reply #19 - Posted 2009-09-24 11:38:56 »

In a lot of situations in games (like say when you want to remove your enemies from your list), you'd store the index of object in the list somewhere for quicker removal. In a LinkedList you've got to browse through n elements to get to the nth element, whereas ArrayList is O(1). Of course, removal in LinkedList is O(1) and O(n) in ArrayList, but they this is just another place LinkedList can't really top ArrayList - in order to remove any element you usually need to get to that index anyway, so it just becomes O(1) + O(n) = O(n)... not any faster than ArrayList, even where it is "supposed" to be.

<snip>

But of course, if you've got some sort of implementation that specifically will work better for a LinkedList, then go for it.
The implementation you give earlier in the thread specifically works better with LinkedList. Just replace the backwards iteration over indices with use of an iterator. Since you're already iterating through the list removal of dead enemies will be O(1) with no added O(n), whereas removal from an ArrayList in your example will be O(n).

So the two implementations that OP should be profiling are:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
    // enemies is an ArrayList
    for (int i = enemies.size()-1; i >= 0; i--)
    {
        Enemy e = enemies.get(i);

        e.act();

        if (enemyHitABullet(e))
        {
            e.die();
        }

        if (e.isMarkedForDeletion())
        {
            int n = enemies.size() - 1;
            enemies.set(i, enemies.get(n));
            enemies.remove(n);
        }
    }


and

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
    // enemies is a LinkedList
    Iterator<Enemy> it = enemies.iterator();
    while (it.hasNext())
    {
        Enemy e = it.next();

        e.act();

        if (enemyHitABullet(e))
        {
            e.die();
        }

        if (e.isMarkedForDeletion())
        {
            it.remove();
        }
    }


Both have O(1) removal time, and I would expect the ArrayList one to be faster.


It's also, IMO, annoying to use Iterator to access the items in LinkedList and not just be able to do it via array locations. Say you're using an Iterator with your object deletion case - if you try to remove anything from the list while you're using that same Iterator you can very easily get ConcurrentModificationExceptions. Index access is much cleaner and more predictable.
Huh If you have a situation where you're getting ConcurrentModificationExceptions then using index access doesn't sound very predictable at all. At least with CME you get a notification that things went wrong and a hint for what to look for.
Offline mh114

Junior Devvie





« Reply #20 - Posted 2009-09-24 13:18:01 »

@Orangy Tang@ - so to confirm, swapping the last element is as simple as:

1  
2  
3  
Enemy tmp = enemies.get( enemies.size() - 1 );
enemies.set( enemies.size() - 1, enemies.get(i) );
enemies.set(i,tmp);

This is mostly nitpicking, but in this case (removing an element) you could do just
1  
2  
3  
Enemy tmp = enemies.get( enemies.size() - 1 );
enemies.set(i, tmp);
enemies.remove( enemies.size() - 1 );

Offline JL235

JGO Coder


Medals: 10



« Reply #21 - Posted 2009-09-24 13:27:35 »

You can allocate tens of thousands of objects per second and the garbage collector can clean them up very quickly. However doing this can cause quite significant pause times on older JVMs (the majority of JVMs), especially when run on older machines. I once wrote a game which had thousands of stars move across the screen. It would very noticeably pause once a second due to garbage collection and just caching all the dead stars resolved this issue.

I even have my own implementations of HashMap and LinkedList which cache the nodes they create inside, designed specifically for times where you are repeatedly adding and removing lots of elements to a map or list. Again these were produced in response to noticeable pause times when my games were run on older JVMs.

Even if your not allocating thousands of objects per frame, when it does eventually collect there will probably be a noticeable pause.

But I'd always advise caching objects in response to GC issues, not in advance.

Offline Riven
« League of Dukes »

« JGO Overlord »


Medals: 831
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #22 - Posted 2009-09-24 14:28:18 »

You can allocate tens of thousands of objects per second and the garbage collector can clean them up very quickly. However doing this can cause quite significant pause times on older JVMs (the majority of JVMs), especially when run on older machines. I once wrote a game which had thousands of stars move across the screen. It would very noticeably pause once a second due to garbage collection and just caching all the dead stars resolved this issue.

I even have my own implementations of HashMap and LinkedList which cache the nodes they create inside, designed specifically for times where you are repeatedly adding and removing lots of elements to a map or list. Again these were produced in response to noticeable pause times when my games were run on older JVMs.

Even if your not allocating thousands of objects per frame, when it does eventually collect there will probably be a noticeable pause.

But I'd always advise caching objects in response to GC issues, not in advance.

You can work around this issue by tweaking the GC parameters. To ruin it, some moron at Sun decided these JVM args were unsafe for applets and webstarted apps, so we're stuck with writing these object-pools.

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

JGO Kernel


Medals: 42
Projects: 11
Exp: 10 years


Game Engineer


« Reply #23 - Posted 2009-09-24 14:32:37 »

Huh If you have a situation where you're getting ConcurrentModificationExceptions then using index access doesn't sound very predictable at all. At least with CME you get a notification that things went wrong and a hint for what to look for.

That's a really good idea to move the object you're removing to the end of the ArrayList before you get rid of it - I've never thought to do that before, actually. And as for the ConcurrentModificationException, I have to say I've never understood exactly why I would sometimes get it with Iterators, but let me clarify.

I think I was doing something like this:
1  
2  
3  
4  
5  
6  
7  
8  
for (Iterator i = list.iterator(); i.hasNext();)
{
    Object o = i.next();
    if (o.isMarkedForDeletion())
    {
        list.remove(o);
    }
}

The above will give you a ConcurrentModificationException, but aside from that is silly to do because you're not even bothering to use the Iterator you've already created. I'm guessing the below won't give you an Exception:
1  
2  
3  
4  
5  
6  
7  
8  
for (Iterator i = list.iterator(); i.hasNext();)
{
    Object o = i.next();
    if (o.isMarkedForDeletion())
    {
       i.remove();
    }
}

And that's obviously the way you should be doing it - but I really was referring to the fact that when I'm coding these things very quickly I'll sometimes forget to use the Iterator the whole time and then will get Exceptions. And when I've done a lot of code at once then a ConcurrentModificationException starts to look very weird, given that the remove loop is usually a pretty tiny part of things. So I'll spend a little while wondering where I'm messing with Threads incorrectly (where I usually see those type of Exceptions), finally coming back to the "tiny" mistake in the remove loop. All around, that makes ArrayList just simpler to me - it seems a lot more obvious when you go out of bounds, which is really the only mistake you can make with an ArrayList.

See my work:
OTC Software
Offline Riven
« League of Dukes »

« JGO Overlord »


Medals: 831
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #24 - Posted 2009-09-24 15:03:05 »

The reason those ConcurrentModificationExceptions are thrown:

1. The Java Collections API is written to be fail fast, this means:
    if anything happens that shouldn't be happening, try to crash ASAP

2. The Iterator is only reliable if the collection it iterates over is not modified.
    Every modification inc()s the private modcount field in the collection
    The modcount is saved when the Iterator is created, and upon every
    access, the current modcount is compared, and if not equal, it fails

3. It doesn't matter whether the 'concurrent' modification is done on the same
    thread that created the Iterator - it's still unsafe. When you call it.next(),
    a call to it.remove() MUST remove the current (targetted) object. If you call
    collection.remove(obj), the current (targetted) object might be gone, so the
    next invocation of a Iterator method will fail so that it can guarantee that
    if it does not throw an Exception, it removes the CORRECT object.
   

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

Junior Devvie





« Reply #25 - Posted 2009-09-24 22:00:38 »

It's a sensible policy, but i have already fallen afoul of it before.
I don't remember if i was just using non-modification query methods (get), but it's likely that i was (thus the surprise).

Anyway there are some gotchas with memory still in java. 2 I have experienced, one that from my understanding shouldn't happen and one that is a "classic".

1) I once was working with a library that allocated a huge byte array. The normal jvm (default arguments, client) had no problems with one call of the method, but two threw a exception.
Slightly weird but, i think this is what was happening:
byte [] reference;
.....
inside method
reference = new byte[huge];

And the second time that huge was called, either the gc couldn't handle
reference = new byte[huge]; (because it didn't collect the old array before attribution),
or more likely, the reference escaped into some other object (never did say the code was good, it is a pure java port of unrar), so there were two arrays at the same time. Javolution pooling "solved" it with minimal pain since the array was always of the same size. Even if references escaped they were pointing to the same object by pooling, and would eventually have been set anyway.


2) Classic. Event driven programming requires callbacks to held into lists to "push" notifications from a component. These callbacks can hold arbitary references to large objects, leading to the classic pattern of event programming of the cleanup(Listener) method. You can avoid this with judicious use of reflection (because sun braindeadness didn't saw fit to unify the event notifiers into a interface) and a java special type of reference object called WeakReference. Weak references allow the gc to clean up the weak reference when all the hard (normal) references are garbage collected, so no leaks can happen (or notifications) after the listening object disappears.
Lead me to a few classes like this:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
package util.swing;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.util.logging.Logger;

/**
 * Never do stupid finalizes on PropertyChangeListeners again
 * A delegate for normal listeners that removes them
 * (and their references) automatically when the Listener
 * is no longer registered in any component.
 * @author i30817
 */

public class WeakPropertyChangeListener implements PropertyChangeListener {

    private static Logger logger = Logger.getLogger("util.swing");
    WeakReference<PropertyChangeListener> listenerRef;

    public WeakPropertyChangeListener(PropertyChangeListener listener) {
        listenerRef = new WeakReference<PropertyChangeListener>(listener);
    }

    public void propertyChange(PropertyChangeEvent evt) {
        PropertyChangeListener listener = listenerRef.get();
        if (listener == null) {
            removeListener(evt.getSource());
        } else {
            listener.propertyChange(evt);
        }
    }

    private void removeListener(Object src) {
        try {
            logger.finer("Removing unused listener " + this);
            Method method = src.getClass().getMethod("removePropertyChangeListener", new Class[]{PropertyChangeListener.class});
            method.invoke(src, new Object[]{this});
        } catch (Exception e) {
            logger.severe("Cannot remove listener: " + e);
        }
    }
}

For programming swing, that has a asynchronous event driven programming model.
Offline i30817

Junior Devvie





« Reply #26 - Posted 2009-09-24 22:44:17 »

Obviously, of course, the constructor PropertyChangeListener must be held by a hard reference outside - preferably in the the object that is registering the reference into the notifier. Then when that object goes away, the hard reference also goes away, and any registration of it also goes away.

If you don't do this, ofcouse, then adding a weaklistener does exactly nothing, since it is immediately (more or less) disposed.
Offline tom
« Reply #27 - Posted 2009-09-25 08:17:54 »

I never use Iterators directly, but I almost always use the enhanced for loop. To avoid ConcurrentModificationExceptions I usually use CopyOnWriteArrayList or iterate over a copy of the list.

Offline ikaruga

Junior Newbie





« Reply #28 - Posted 2009-09-25 14:23:11 »

Wow, again, thanx. I'm learning so much in one post.

@mh114@ - thanks for the tip. I picked up on that from @pjt33@'s reply.

So just to confirm, in @pjt33@'s code, when the current slot in the ArrayList for the current enemy object is overridden by the last, the current enemy gets taken care of by the GC, right? (In other words, there's no need to assign the current enemy to the last slot and explicitly remove it...)

1  
2  
3  
4  
5  
6  
if (e.isMarkedForDeletion())
        {
            int n = enemies.size() - 1;
            enemies.set(i, enemies.get(n));
            enemies.remove(n);
        }
Offline pjt33
« Reply #29 - Posted 2009-09-25 15:52:50 »

So just to confirm, in @pjt33@'s code, when the current slot in the ArrayList for the current enemy object is overridden by the last, the current enemy gets taken care of by the GC, right? (In other words, there's no need to assign the current enemy to the last slot and explicitly remove it...)
Correct (assuming the list is the only place you store references to the enemies).
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

xFryIx (62 views)
2014-11-13 12:34:49

digdugdiggy (41 views)
2014-11-12 21:11:50

digdugdiggy (34 views)
2014-11-12 21:10:15

digdugdiggy (30 views)
2014-11-12 21:09:33

kovacsa (52 views)
2014-11-07 19:57:14

TehJavaDev (56 views)
2014-11-03 22:04:50

BurntPizza (55 views)
2014-11-03 18:54:52

moogie (70 views)
2014-11-03 06:22:04

CopyableCougar4 (70 views)
2014-11-01 23:36:41

DarkCart (156 views)
2014-11-01 14:51:03
Understanding relations between setOrigin, setScale and setPosition in libGdx
by mbabuskov
2014-10-09 22:35:00

Definite guide to supporting multiple device resolutions on Android (2014)
by mbabuskov
2014-10-02 22:36:02

List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50

List of Learning Resources
by SilverTiger
2014-07-31 16:26:06
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
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!