McLovin
McLovin

Reputation: 3417

An object is still alive after I kill its holder in Java?

I'm struggling with the next bug for hours. My program structure is as follows

enter image description here

The main function initializes a Game object and calls startMainWindow(), which initializes a GameLogic logic object and a MainWindow object (passing logic to it as an argument, and also this). Both object are local variables in startMainWindow().

The problem is, say when the player finishes the game and I want to start the high score GUI, I call _game.startHighScoreWindow() from withing MainWindow. The problem is that the GameLogic object keeps running! I easily checked it by putting a console write command in GameLogic.actionPerformed(). It doesn't get released from memory. This also happens even if I call this.dispose(); before _game.startHighScoreWindow().

Why is _gameLogic still in memory?

Some code:

PacmanGame:

public void startMainWindow(int levelIndex, int levelsDone, int score) {
    gameGui = null; //desperate attempt
    highScoreGui = null;
    mainMenuGui = null;
    spaceGui = null;
    gameLogic = null;
    System.gc();

    gameLogic = new GameLogic(_sprites, _levels[levelIndex], levelsDone, score);

    gameGui = new MainWindowGui(this, gameLogic, _sprites, _levels);
}

public void startHighscoreGui(int score, String time) {
        gameGui = null;
        highScoreGui = null;
        mainMenuGui = null;
        spaceGui = null;
        gameLogic = null;
        System.gc();

        highScoreGui = new HighscoreGui(this, score, time);
    }

MainWindow:

public class MainWindow extends JFrame implements KeyListener {

    private PacmanGame _game;
    private GameLogic _gameLogic;
    private GamePanel _gamePanel; //JPanel
    private ToolbarPanel _toolbarPanel; //JPanel
    private Sprite[] _sprites;
    private Level[] _levels;

    public MainWindow(PacmanGame game, GameLogic gameLogic, Sprite[] sprites, Level[] levels) {
        // create our window
        super("Pacman");

        // initialize fields
        _game = game;
        _sprites = sprites;
        _gameLogic = gameLogic;
        _levels = levels;
        this._gamePanel = new GamePanel(_gameLogic, _sprites);
        this._toolbarPanel = new ToolbarPanel(_gameLogic, sprites, this);
        ...
    }

    public void playerWon(String time) {
        Level levelObj = _gameLogic.getLevelObject();
        int nextLevelIndex = (levelObj.getNum()) % 3;

        if(_gameLogic.getLevel() == 3) //done!
        {
            this.dispose();
            _game.startHighscoreGui(_gameLogic.getScore(), time);
        }
        else //next level
        {
            _gameLogic.nextLevel(_levels[nextLevelIndex]);
        }
    }

Edit: In a desperate attempt I tried making all the GUIs and GameLogic static variables in PacmanGame, and then set them to null everytime a function is called in PacmanGame. It kept acting the same as before, that is, with having all those objects local to each function.

Upvotes: 0

Views: 217

Answers (2)

maaartinus
maaartinus

Reputation: 46482

The problem is that the GameLogic object keeps running!

If something keeps running, than you should stop it. It's not the job of the GC.

It doesn't get released from memory.

Again, it's not the job of the GC to release everything immediately or alike. Its only job to try hard you won't run out of memory. It does it by collecting garbage, but that's an implementation detail; it could buy you new RAM instead and it'd fine.

Your problem is using the wrong tool for the job.

Just provide an explicit method to terminate your GameLogic and you're done. For a thread running in a loop, use a volatile boolean stop variable to exit the loop. For a window, use dispose, etc....

That's all. Just forget that there's a GC. It's not there to help you with game logic.

Upvotes: 1

Stephen C
Stephen C

Reputation: 719239

In answer to your questions:

An object is still alive after I kill its holder in Java?

An object becomes eligible for deletion when it becomes unreachable. Reachable means that there is some path to the object starting from either a static or a local variable in some stackframe of some method that has not yet returned.

And some things are "a bit special" for example, a Thread object remains reachable while the thread is running ... irrespective of other references to the Thread. Similar things apply to objects related to the GUI.


Why is _gameLogic still in memory?

It is not possible to tell without seeing the code of an MCVE. (Your UML diagrams and English commentary don't say ... and they are not necessarily accurate anyway!)

Looking at the snippets that you posted, your GameLogic object could still be reachable because there is a reference to it in the _gameLogic field of your MainWindow class. And the MainWindow object could still be reachable because it is displayed.

But as we said, an MCVE is necessary if you want proper answers; i.e. answers that are better than educated guesses.

It is also worth noting / repeating

  • Calling System.gc() is a bad idea. It rarely works.
  • Prefixing variables with an underscore is not standard Java style. (I'd call it ugly.)
  • Windowing objects won't ge garbage collected if they are currently displayed. There are hidden references (in the SWT / AWT / JVM) that ensure that windowing objects are reachable while they are displayed.
  • Calling dispose() on class that extends JFrame does not clean up fields declared by the class itself. It doesn't know about them.

Upvotes: 3

Related Questions