Java-Gaming.org    
Featured games (81)
games approved by the League of Dukes
Games in Showcase (487)
Games in Android Showcase (110)
games submitted by our members
Games in WIP (553)
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 leak(?) and inefficient buffering problem  (Read 1169 times)
0 Members and 1 Guest are viewing this topic.
Offline xela0301

Junior Newbie





« Posted 2012-02-01 19:25:01 »

Just for quick self introduction, I'm fairly new to computer programming.
My background is just a semester of single CS introductory class. (I was majoring mechanical engineering back then a year ago.)
This semester I decided upon a personal long term project, and you might have guessed that it is making a game.
I delved into some forums such as this and watched tutorial videos, and I got "something" going on for now.
However there seems to be few problems with my program.

First thing is that the program seems to have a memory leakage.
Currently, the only task it does is to perpetually update steady, unchanging game state.
However, I checked on the task manager and the program kept on hogging more memory space.
It had some occassional memory drops, but it just kept on increaseing in the long run.
The parts that I coded by myself didn't seem to have problems, but I'm not sure of some parts that I got from "follow-along"s.

Second probem is that its image buffering sucks.
I'm using a double buffering method from a video tutorial of user "thejavahub", but I don't see much effect.
Also it takes more than 20ms just to do the task I mentioned above, which I believe will cause the game to go lower than 50 fps.
Below I'm posting two classes of my program that should hold enough source code for those willing to help.

/*EDIT
I forgot to mention what my questions were:
1) What's causing the memory leak? How do I prevent it? (Or is it even a memory leak?)
2) How do I get a smooth panning mechanism instead of the current ugly one?
3) What do you think about the program's "drawing algorithm"? Any improvement and/or criticism?
3) Any other criticism that is not related to my questions?

I want to learn programming well and from lots of perspective.
Harsh criticisms (criticisms, not insults) or even minor details such as missed programming convention is welcomed.

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  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
178  
179  
180  
181  
182  
183  
184  
185  
186  
187  
188  
189  
190  
191  
192  
193  
194  
195  
196  
197  
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

@SuppressWarnings("serial")
public class GameBoard extends JPanel implements Runnable {
   // Dependent to tile image size.
  private final int SCALE = 16;
   private final long PERIOD = 20 * 1000000; // Milliseconds to Nanoseconds
 
   private static int uIndex = 0;
   private static int vIndex = 0;
   private static int wIndex = 1;
   long beforeTime;
   long afterTime;
   long difference;
   long sleepTime;
   private Image TILE_0;
   private Image TILE_0_TOP;
   private Image TILE_A;
   private Image TILE_A_TOP;
   private Image TILE_B;
   private Image TILE_B_TOP;
   
   private Graphics bufferGraphics;
   private Image bufferImage;
   private Thread game;
   private volatile boolean running = false;
   
   private GameTile tileGrid;
   
   public GameBoard() {
      loadTileImage();
      tileGrid = new GameTile(2);
      setBackground(Color.WHITE);
      setFocusable(true);
      requestFocus();
     
      addKeyListener(new KeyAdapter() {
         @Override
         public void keyPressed(KeyEvent e) {
            switch(e.getKeyCode()) {
            default:
               break;
            case KeyEvent.VK_UP:
               if(uIndex < tileGrid.getU() / 2)
                  uIndex++;
               break;
            case KeyEvent.VK_DOWN:
               if(uIndex > -(tileGrid.getU() / 2))
                  uIndex--;
               break;
            case KeyEvent.VK_LEFT:
               if(vIndex < tileGrid.getV() / 2)
                  vIndex++;
               break;
            case KeyEvent.VK_RIGHT:
               if(vIndex > -(tileGrid.getV() / 2))
                  vIndex--;
               break;
            case KeyEvent.VK_PAGE_UP:
               if(wIndex < tileGrid.getW() - 2)
                  wIndex++;
               break;
            case KeyEvent.VK_PAGE_DOWN:
               if(wIndex > 2)
                  wIndex--;
               break;
            case KeyEvent.VK_SPACE:
               uIndex = 0;
               vIndex = 0;
               break;
            case KeyEvent.VK_ESCAPE:
               System.exit(0);
               break;
            }
         }
         @Override
         public void keyReleased(KeyEvent e) {}
         @Override
         public void keyTyped(KeyEvent e) {}
      });
   }
   
   public void addNotify() {
      super.addNotify();
      startGame();
   }
   private void startGame() {
      if(game == null || !running) {
         game = new Thread(this);
         game.start();
         running = true;
      }
   }
   public void stopGame() {
      if(running) {
         running = false;
      }
   }
   @SuppressWarnings("static-access")
   public void run() {
      while(running) {
         beforeTime = System.nanoTime();
         gameUpdate();
         gameRender();
         paintScreen();
         afterTime = System.nanoTime();
         difference = afterTime - beforeTime;
         if(difference < PERIOD) {
            sleepTime = PERIOD - difference;
            try {
               game.sleep(sleepTime / 1000000);
            } catch (InterruptedException e) {}
            System.out.printf("%d.%03d ms\n", difference / 1000000, difference % 1000000 / 1000);
         } else {
            System.out.printf("LAG! %d.%03d ms\n", difference / 1000000, difference % 1000000 / 1000);
         }
         
      }
   }
   private void gameUpdate() {
      if(running || game != null) {
         // TODO Update game board.
     }
   }
   private void gameRender() {
      if(bufferImage == null) {
         // Create the buffer.
        bufferImage = createImage(800, 600);
         if(bufferImage == null) {
            System.out.println("ERROR: Buffer image still does not exist!");
            return;
         } else {
            bufferGraphics = bufferImage.getGraphics();
         }
      }
      // Clear the screen.
     bufferGraphics.setColor(Color.WHITE);
      bufferGraphics.fillRect(0, 0, 800, 600);
      draw(bufferGraphics);
   }
   public void draw(Graphics g) {
      drawTile(g);
   }
   public void paintScreen() {
      Graphics g;
      try {
         g = this.getGraphics();
         if(bufferImage != null && g != null) {
            g.drawImage(bufferImage, 0, 0, null);
         }
         Toolkit.getDefaultToolkit().sync();
         g.dispose();
      } catch(Exception e) {}
   }
   
   private void loadTileImage() {
      TILE_0 = new ImageIcon("tile0.png").getImage();
      TILE_0_TOP = new ImageIcon("tile0top.png").getImage();
      TILE_A = new ImageIcon("tileA.png").getImage();
      TILE_A_TOP = new ImageIcon("tileAtop.png").getImage();
      TILE_B = new ImageIcon("tileB.png").getImage();
      TILE_B_TOP = new ImageIcon("tileBtop.png").getImage();
   }
   private void drawTileImage(int i, int j, int k, Graphics g) {
      if(tileGrid.getTileID(i, j, k) == 0)
         g.drawImage(TILE_0, toX(i, j), toY(i, j, k), null);
      else if(tileGrid.getTileID(i, j, k) == 1)
         g.drawImage(TILE_A, toX(i, j), toY(i, j, k), null);
      else if(tileGrid.getTileID(i, j, k) == 2)
         g.drawImage(TILE_B, toX(i, j), toY(i, j, k), null);
   }
   private void drawTileTopImage(int i, int j, int k, Graphics g) {
      if(tileGrid.getTileID(i, j, k) == 0)
         g.drawImage(TILE_0_TOP, toX(i, j), toY(i, j, k), null);
      else if(tileGrid.getTileID(i, j, k) == 1)
         g.drawImage(TILE_A_TOP, toX(i, j), toY(i, j, k), null);
      else if(tileGrid.getTileID(i, j, k) == 2)
         g.drawImage(TILE_B_TOP, toX(i, j), toY(i, j, k), null);
   }
   private void drawTile(Graphics g) {
      for(int i = 0; i < tileGrid.getU(); i++) {
         for(int j = 0; j < tileGrid.getV(); j++) {
            drawTileImage(i, j, wIndex - 1, g);
            drawTileImage(i, j, wIndex, g);
            drawTileTopImage(i, j, wIndex + 1, g);
         }
      }
   }
   private int toX(int i, int j) {
      return((getWidth() / 2 - SCALE) - SCALE * (i + uIndex) + SCALE * (j + vIndex));
   }
   private int toY(int i, int j, int k) {
      return((getHeight() / 2 - SCALE * tileGrid.getV() / 2) + SCALE * (i + uIndex + j + vIndex) / 2 - SCALE * (k - wIndex));
   }
}


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  
44  
45  
46  
47  
48  
49  
public class GameTile {
   private static int u;
   private static int v;
   private static int w;
   private static int[][][] tileGrid;
   public GameTile(int size) {
      if(size == 0)
         size = 1;
      u = (64 * size);
      v = (64 * size);
      w = (64 * size);
      tileGrid = new int[u][v][w];
      generateTileGrid();
   }
   private void generateTileGrid() {
      int test;
      for(int i = 0; i < u; i++) {
         for(int j = 0; j < v; j++) {
            for(int k = 0; k < w; k++) {
               test = ((i + j + k) % 4);
               if(test == 0)
                  tileGrid[i][j][k] = 1;
               else if(test == 1)
                  tileGrid[i][j][k] = 2;
               else
                  tileGrid[i][j][k] = 0;
            }
         }
      }
   }
   public void setTileID(int i, int j, int k, int tileID) {
      tileGrid[i][j][k] = tileID;
   }
   public int getU() {
      return u;
   }
   public int getV() {
      return v;
   }
   public int getW() {
      return w;
   }
   public int getTileID(int i, int j, int k) {
      return tileGrid[i][j][k];
   }
   public int[][][] getTileGrid() {
      return tileGrid;
   }
}
Offline Magn919

Junior Member


Medals: 6
Exp: 4 years



« Reply #1 - Posted 2012-02-01 22:05:01 »

Looking at your code i don't see anything obvious that would cause memory leak, but i do have a few criticisms. Wink

The first one, and possibly causing the memory leak, is that you silence exceptions, i see you do this a few times.
Try not to silence exceptions, because if your program is throwing a lot of silenced exceptions, it could easily cause all sorts of performance problems, and try to use specific exceptions that you know could be thrown rather than the standard Exception.

The second one, try to add more comments or arrange you code based on functionality, to make it easier to read, because large walls of code is tend to be hard to read, and limits the amount of people who would post here.

As for your drawing algorithm, it looks fine, i don't know how much of an improvement it is as i have little experience with it, but you could try using bufferstrategy rather than a bufferedimage its supposed to be faster.

For every new problem, a new source of solutions has come to exist.
Offline ra4king

JGO Kernel


Medals: 345
Projects: 2
Exp: 5 years


I'm the King!


« Reply #2 - Posted 2012-02-01 22:07:59 »

@Magn919
Silencing Exceptions doesn't cause memory leaks...

@OP
You create a new Image every time you render Wink

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Magn919

Junior Member


Medals: 6
Exp: 4 years



« Reply #3 - Posted 2012-02-01 22:17:39 »

I didn't mean to say it "did", i meant it could cause performance problems if the program would start spamming exceptions, which in turn could cause memory leak, but thanks for correcting me, wasn't really sure about what impact exceptions had on memory.

For every new problem, a new source of solutions has come to exist.
Offline xela0301

Junior Newbie





« Reply #4 - Posted 2012-02-01 22:38:13 »

First of all, thank you for your time and help.

@Magn919
Going point by point, most of the silencing(?) was done because it was shown as error unless something was done about it.
Like I said I'm very new to programming, so what do you suggest as best course of action for the exceptions?
Should I just call System.exit(0); line?

Organizational skill is something I need to improve on and quickly. Thank you for pointing that out.
I can fix the commenting habit by myself, but in terms of actual code I think I need more knowledge to really keep things tidy.
What I plan on doing along my long term project is to tidy up all the code everytime I reach certain "checkpoints".

The last point I didn't quite understand. It would be more helpful if you could be more detailed about the bufferstrategy.

@ra4king
I'm missing a huge chuck of programming knowledge to see how that create new Image loop is happening.
What I had in mind was that loadTileImage() method will only get called once in GameBoard class' default constructor.
Could you please write out a simple explanation of how and why the Images are being continuously created?
Offline ra4king

JGO Kernel


Medals: 345
Projects: 2
Exp: 5 years


I'm the King!


« Reply #5 - Posted 2012-02-01 23:09:37 »

Ah my bad, I didn't see that it was in an if conditional statement.
I was talking about line 130 Smiley

Looking at the rest of the code, nothing seems wrong or potentially causing a leak.

Concerning BufferStrategy, that offers faster rendering due to efficient double buffering done behind the scenes like page flipping. This post should explain a bit how it works.

Offline Stranger

Senior Member


Medals: 6



« Reply #6 - Posted 2012-02-02 07:43:47 »

you don't call dispose() for bufferGraphics. It might cause resource leaks.

Also, it's  better to put dispose() to finally clause in paintScreen().

Anton
Offline Riven
« League of Dukes »

JGO Overlord


Medals: 783
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #7 - Posted 2012-02-02 08:09:55 »

It is safe not to call dispose() on a Graphics instance.

The resource is guaranteed to be released eventually.

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

Senior Member


Medals: 6



« Reply #8 - Posted 2012-02-02 10:59:16 »

It is safe not to call dispose() on a Graphics instance.

The resource is guaranteed to be released eventually.

Correct me if I'm wrong. The following is taken from javadoc for System.runFinalizersOnExit( ):

Quote
By default, finalization on exit is disabled.

But System.exit(0) is used in GameBoard (line 74), so Graphics.finalize() might not be called ...

Anton
Offline Riven
« League of Dukes »

JGO Overlord


Medals: 783
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #9 - Posted 2012-02-02 11:14:05 »

It is safe not to call dispose() on a Graphics instance.

The resource is guaranteed to be released eventually.

Correct me if I'm wrong. The following is taken from javadoc for System.runFinalizersOnExit( ):

Quote
By default, finalization on exit is disabled.

But System.exit(0) is used in GameBoard (line 74), so Graphics.finalize() might not be called ...

Why worry about a 'memory leak' if it only happens during the termination of your process?!

Upon process termination, all graphics resources are reclaimed anyway, just like any open file-handles are closed.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
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.

CopyableCougar4 (24 views)
2014-08-22 19:31:30

atombrot (34 views)
2014-08-19 09:29:53

Tekkerue (30 views)
2014-08-16 06:45:27

Tekkerue (28 views)
2014-08-16 06:22:17

Tekkerue (18 views)
2014-08-16 06:20:21

Tekkerue (27 views)
2014-08-16 06:12:11

Rayexar (65 views)
2014-08-11 02:49:23

BurntPizza (41 views)
2014-08-09 21:09:32

BurntPizza (33 views)
2014-08-08 02:01:56

Norakomi (42 views)
2014-08-06 19:49:38
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

List of Learning Resources
by SilverTiger
2014-07-31 11:54:12

HotSpot Options
by dleskov
2014-07-08 01:59:08
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!