This is killing my computer D: (simple task)

I made this as a serverside. It is still very very simple - the only things it has is a 5 second game loop, and I played around with some packets :stuck_out_tongue:
Sadly the game-loop is killing my computer CPU wise.

This is my classes:

Main class:



package Mads;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;

/**
 * @author Mads
 */
public class Main {
    public static void main(String[] args) throws IOException {

        //Making and running the socket
        DatagramSocket socket = null;
        try {
            socket = new DatagramSocket(4445);
        } catch (SocketException e) {}

        //Starting the game cycle
        //Thread iC = new Thread(new iniCycle());
        //iC.start();

        //Rechieving a packet
        byte[] buf = new byte[256];
        DatagramPacket packet = new DatagramPacket(buf, buf.length);
        socket.receive(packet);

        //Figure what to respond
        String message = "COOL_MATE";
        buf = message.getBytes();

        //Send the response
        InetAddress address = packet.getAddress();
        int port = packet.getPort();
        packet = new DatagramPacket(buf, buf.length, address, port);
        socket.send(packet);
    }
}

iniCycle class:



package Mads;

/**
 *
 * @author Mads
 */

public class iniCycle implements Runnable {

    public long timer = System.currentTimeMillis();
    public boolean isTrue = true;

    public void run() {
        while (isTrue = true) {
            if (System.currentTimeMillis() - timer >= 5000) {

                //Handling the timer
                //System.out.println("Valid");
                timer = System.currentTimeMillis();

                //Executing the actual game loop
                Thread gC = new Thread(new gameCycle());
                gC.start();
            }
            else {

            }
        }
    }
}

gameLoop class:



package Mads;

/**
 * @author Mads
 */
public class gameCycle implements Runnable {

    public gameCycle() {

    }
    public void run() {

        //System.out.println("gameCycle initialized");

         
    }
}

Any ideas, why this is soooo heavy?

iniCycle.class

while (isTrue = true)

should be

while (isTrue == true)

or just

while (isTrue)

if (System.currentTimeMillis() - timer >= 5000) {
...

does a busy wait so for those entire 5 seconds that you’re waiting, the JVM is running a loop that does nothing, which means it can do the loop really, really fast and thus uses up the CPU. You need to use the Thread.sleep(5000) to sleep for about 5 seconds.

or you could also add a


Thread.yield();

in you else statement .

It is because I want the game loop to be precise no matter how long it takes to execute. If I use Thread.sleep() there will eventually be some millis delay, and that would be growing over time

Make the Thread sleep for 25msec for example or read some tutorials about how wait() and notify() works in Java Threads
 Before starting make something it’s better to read some documentation before


EDIT: The code you have is has infinite cycle
 probably your PC has 2+ core CPU, this is what saving you from complete freeze of machine


EDIT2: This question should go to “Newless Clubies” section :wink:

You’ll never get something that’s precise. A lost ms or two is no big deal and if you write your loop intelligently it will automatically be picked up in the next timestep. You’re essentially doing a fixed timestep right now, but you’re doing it wrong.

Here is a fixed game loop test I made. It just draws a ball bouncing around, using a fixed timestep and a target FPS. If the FPS would exceed the target, it yields the thread. This keeps the game from being a processor hog (unless you need it).

Note that you very rarely need a 60 GAME_HERTZ, I just kept it there for the test. Oftentimes you can get away with a game hertz of 20, and it looks fine as long as the FPS is high.


import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class GameLoopTest extends JFrame implements ActionListener
{
	private GamePanel gamePanel = new GamePanel();
	private JButton startButton = new JButton("Start");
	private JButton quitButton = new JButton("Quit");
	private boolean running = false;
	private int fps = 60;
	private int frameCount = 0;
	
	public GameLoopTest()
	{
		super("Fixed Timestep Game Loop Test");
		Container cp = getContentPane();
		cp.setLayout(new BorderLayout());
		JPanel p = new JPanel();
		p.setLayout(new GridLayout(1,2));
		p.add(startButton);
		p.add(quitButton);
		cp.add(gamePanel, BorderLayout.CENTER);
		cp.add(p, BorderLayout.SOUTH);
		setSize(500, 500);
		
		startButton.addActionListener(this);
		quitButton.addActionListener(this);
	}
	
	public static void main(String[] args)
	{
		GameLoopTest glt = new GameLoopTest();
		glt.setVisible(true);
	}
	
	public void actionPerformed(ActionEvent e)
	{
		Object s = e.getSource();
		if (s == startButton)
		{
			running = !running;
			if (running)
			{
				startButton.setText("Stop");
				runGameLoop();
			}
			else
			{
				startButton.setText("Start");
			}
		}
		else if (s == quitButton)
		{
			System.exit(0);
		}
	}
	
	//Starts a new thread and runs the game loop in it.
	public void runGameLoop()
	{
		Thread loop = new Thread()
		{
			public void run()
			{
				gameLoop();
			}
		};
		loop.start();
	}
	
	//Only run this in another Thread!
	private void gameLoop()
	{
		//This value would probably be stored elsewhere.
		final double GAME_HERTZ = 60.0;
		//Calculate how many NS each frame should take for our target game hertz.
		final double TIME_BETWEEN_UPDATES = 1000000000 / GAME_HERTZ;
		//At the very most we will update the game this many times before a new render.
		final int MAX_UPDATES_BEFORE_RENDER = 5;
		//We will need the last update time.
		double lastUpdateTime = System.nanoTime();
		//Store the last time we rendered.
		double lastRenderTime = System.nanoTime();
		
		//If we are able to get as high as this FPS, don't render again.
		final double TARGET_FPS = 60;
		final double TARGET_TIME_BETWEEN_RENDERS = 1000000000 / TARGET_FPS;
		
		//Simple way of finding FPS.
		int lastSecondTime = (int) (lastUpdateTime / 1000000000);
		
		while (running)
		{
			double now = System.nanoTime();
			int updateCount = 0;
			
			//Do as many game updates as we need to, potentially playing catchup.
			while( now - lastUpdateTime > TIME_BETWEEN_UPDATES && updateCount < MAX_UPDATES_BEFORE_RENDER )
			{
				updateGame();
				lastUpdateTime += TIME_BETWEEN_UPDATES;
				updateCount++;
			}
			
			//If for some reason an update takes forever, we don't want to do an insane number of catchups.
			//If you were doing some sort of game that needed to keep EXACT time, you would get rid of this.
			if (lastUpdateTime - now > TIME_BETWEEN_UPDATES)
			{
				lastUpdateTime = now - TIME_BETWEEN_UPDATES;
			}
			
			//Render. To do so, we need to calculate interpolation for a smooth render.
			float interpolation = Math.min(1.0f, (float) ((now - lastUpdateTime) / TIME_BETWEEN_UPDATES) );
			drawGame(interpolation);
			lastRenderTime = now;
			
			//Update the frames we got.
			int thisSecond = (int) (lastUpdateTime / 1000000000);
			if (thisSecond > lastSecondTime)
			{
				System.out.println("NEW SECOND " + thisSecond + " " + frameCount);
				fps = frameCount;
				frameCount = 0;
				lastSecondTime = thisSecond;
			}

			//Yield until it has been at least the target time between renders. This saves the CPU from hogging.
			while ( now - lastRenderTime < TARGET_TIME_BETWEEN_RENDERS && now - lastUpdateTime < TIME_BETWEEN_UPDATES)
			{
				Thread.yield();
				now = System.nanoTime();
			}
		}
	}
	
	private void updateGame()
	{
		gamePanel.update();
	}
	
	private void drawGame(float interpolation)
	{
		gamePanel.setInterpolation(interpolation);
		gamePanel.repaint();
	}
	
	private class GamePanel extends JPanel
	{
		float interpolation;
		float ballX, ballY, lastBallX, lastBallY;
		int ballWidth, ballHeight;
		float ballXVel, ballYVel;
		
		int lastDrawX, lastDrawY;
		
		public GamePanel()
		{
			ballX = lastBallX = 100;
			ballY = lastBallY = 100;
			ballWidth = 25;
			ballHeight = 25;
			ballXVel = (float) Math.random() * 10 - 5;
			ballYVel = (float) Math.random() * 10 - 5;
		}
		
		public void setInterpolation(float interp)
		{
			interpolation = interp;
		}
		
		public void update()
		{
			lastBallX = ballX;
			lastBallY = ballY;
			
			ballX += ballXVel;
			ballY += ballYVel;
			
			if (ballX + ballWidth/2 >= getWidth())
			{
				ballXVel *= -1;
				ballX = getWidth() - ballWidth/2;
			}
			else if (ballX - ballWidth/2 <= 0)
			{
				ballXVel *= -1;
				ballX = ballWidth/2;
			}
			
			if (ballY + ballHeight/2 >= getHeight())
			{
				ballYVel *= -1;
				ballY = getHeight() - ballHeight/2;
			}
			else if (ballY - ballHeight/2 <= 0)
			{
				ballYVel *= -1;
				ballY = ballHeight/2;
			}
		}
		
		public void paintComponent(Graphics g)
		{
			//BS way of clearing out the old rectangle to save CPU.
			g.setColor(getBackground());
			g.fillRect(lastDrawX-1, lastDrawY-1, ballWidth+2, ballHeight+2);
			g.fillRect(5, 0, 75, 30);
			
			g.setColor(Color.RED);
			int drawX = (int) ((ballX - lastBallX) * interpolation + lastBallX - ballWidth/2);
			int drawY = (int) ((ballY - lastBallY) * interpolation + lastBallY - ballHeight/2);
			g.fillOval(drawX, drawY, ballWidth, ballHeight);
			
			lastDrawX = drawX;
			lastDrawY = drawY;
			
			g.setColor(Color.BLACK);
			g.drawString("FPS: " + fps, 5, 10);
			
			frameCount++;
		}
	}
}

I’ve fallen into this trap myself, so I don’t recommend this. Somewhere online Sun does recommend users not to use yield as an alternative to sleep. I used yield instead of sleep for some of my sound code and on XP and Linux I continued to get terrible performance (although it ran perfectly on Vista).

Last time I checked, even single core CPUs could do context-switching :wink:

True, have tried now putting 5 infinite cycles running and still getting machine response, though it’s kinda slow, but anyway, better then restart :D. But I know for sure, infinite cycle with forks© would kill machine in 10 seconds :stuck_out_tongue: (not applies to the case, but anyways :P)