# Spiel laggt nach unbestimmter Zeit tierisch.



## Pansa (11. Jul 2014)

Hi 
Zurzeit Entwickle ich ein 2D Spiel in Java.Leider habe ich seit einiger Zeit ein Problem auf dessen Lösung ich einfach nicht komme :bahnhof: .Wie man schon dem Titel entnehmen kann, laggt das Spiel nach einiger Zeit tierisch rum.Das laggen wird durch nichts hervorgerufen sondern tritt beliebig auf.Es fängt langsam an, und wird dann immer schlimmer bis es dann komplett abstürzt.Beim Taskmanager habe ich beobachtet dass das Spiel, wenn der Fehler auftritt immer mehr Ram und CPU Leistung verbraucht.

Ich weiß nicht woran es liegt,meine Vermutung ist aber, das Irgendetwas mit den Threads nicht stimmt.Nachdem ich die Thread  "synchronized" habe und Thread.yield(); Eingesezt habe, tritt das problem erst später auf.Es ist aber nur eine Vermutung,dass es an den Threads liegt. Da Eclipse keine Exception wirft macht es die Fehlersuche ziemlich kompiliziert^^.

Hier mal die beiden Threads:

```
long lastFrame = System.currentTimeMillis();
        while(true){
        
            synchronized (frame) {
		
	    
            long thisFrame = System.currentTimeMillis();
            float timeSinceLastFrame = (float) ((thisFrame - lastFrame)/1000.0);
            lastFrame = thisFrame;
              
            frame.update(timeSinceLastFrame);
              
            frame.repaint();
              
            try{
            
                Thread.sleep(0);
            } catch (InterruptedException e){
            
                e.printStackTrace();
            }
        }
            Thread.yield();
    }
    }
```

Und hier der Andere:



```
@Override
    public void run(){
    
        while(true){
        
            synchronized (this) {
		
	    
            if(changed){
            
                for(int i = 0;i < enemies.size();i++){
                
                    enemies.get(i).setPath(graph.astar(graph.getNodeID(enemies.get(i).getTilePosX(), enemies.get(i).getTilePosY()),
                            graph.getNodeID(world.getPlayerTilePosX(), world.getPlayerTilePosY())));
                }
                for(int i = 0;i < hamarrtroll.size();i++){
                
                    hamarrtroll.get(i).setPath(graph.astar(graph.getNodeID(hamarrtroll.get(i).getTilePosX(), hamarrtroll.get(i).getTilePosY()),
                            graph.getNodeID(world.getPlayerTilePosX(), world.getPlayerTilePosY())));
                }
            }
            changed = false;
            try {
            
                Thread.sleep(0);
            } catch (InterruptedException e){
            
                e.printStackTrace();
            }
            }
            Thread.yield();
        }
    }
}
```

Mein Betriebssystem ist Win7 (64bit) und die neueste Java version (auch 64 bit) habe ich mir gerade eben nochmal geholt.
Ich hoffe ihr könnt mir helfen,falls ihr nochmehr Infos braucht sacht bescheid .
LG Pansa


----------



## Bananabert (11. Jul 2014)

Moin,

meine Vermutung wäre, du erstellst dauernd neue Objekte oder hast in deiner render Methode kein g.dispose() aufgerufen.

Ansonsten mal mit einem profiler versuchen herauszufinden, wo es hapert.


----------



## Pansa (11. Jul 2014)

Bananabert hat gesagt.:


> Moin,
> 
> meine Vermutung wäre, du erstellst dauernd neue Objekte oder hast in deiner render Methode kein g.dispose() aufgerufen.
> 
> Ansonsten mal mit einem profiler versuchen herauszufinden, wo es hapert.



Ah jetz wo du dispose() erwähnst,ich habe dispose in einer anderen klasse in einer repaint() methode.Da ich im Internet gelesen habe,  dass dispose() den GarbageCollector bei der Arbeit hindert(oder so ähnlich, ist schon nen paar Stunden her ^^) und so diese Ruckler entstehen.Ich habe Dispose() dann ersmal auskommentiert.

Hier mal die repaint Methode:

```
public void repaint(){
    
        Graphics g = strat.getDrawGraphics();
        draw(g);
       // g.dispose();
        strat.show();
    }
```

Ich habe auch eine Begrenzung der Maximalen Gegner und Itemsanzahl "eingebaut",so dass Höchstens 10 Gegner und 10 Items gleichzeitig in der Welt seien können.

Danke für die Antwort ! 
LG Pansa


----------



## Pansa (11. Jul 2014)

Ich habe jetz Dispose() wieder aktiviert, und habe bemerkt dass das Spiel erst viel später abgestürzt ist(so ca nach 10-20 min).Also Dispose bringts leider auchnicht .
LG Pansa


----------



## Pansa (12. Jul 2014)

Eine endlos Schleife oder dass immer mehr Objekte erstellt werden, so das diese die laggs verursachen ist denke ich nicht richtig, denn sonst würde die Prozessorleistung ja von beginn an und stetig steigen.Es muss ja etwas sein, dass Plötzlich auftaucht und soviel Leistung verbraucht.Hat vielleicht einer ne Idee? .
Ich weiß es ist schwer eine Fehrndiagnose mit diesem wenigem Programm code zu erstellen,aber ich weiß ja selber nicht genau woran es liegt.
LG Pansa


----------



## turtle (12. Jul 2014)

> Absturz


Und da gibt es keine weiteren Informationen?

Ich tippe mal darauf, das der JVM der Speicher ausgeht. Dann müsste eigentlich noch eine Out-of-memory-Exception kommen.

Trotzdem rate ich, dein Programm mal mit extrem wenig RAM zu starten, um zu sehen, ob der Absturz eher kommt.

Danach solltest du die Vermutung erhärten, indem du in dein Spiel mal den freien Speicher anzeigen lässt, oder die JVM die Garbage-Collection ausgeben lässt (-verbose:gc).


----------



## Androbin (12. Jul 2014)

Bananabert hat gesagt.:
			
		

> Moin,
> 
> meine Vermutung wäre, du erstellst dauernd neue Objekte oder hast in deiner render Methode kein g.dispose() aufgerufen.
> 
> Ansonsten mal mit einem profiler versuchen herauszufinden, wo es hapert.



Leute,
AUF KEINEN FALL G.DISPOSE() AUFRUFEN !!!


----------



## Phash (12. Jul 2014)

stell den code rein, und wir können dir evtl. mehr helfen

die beiden Threads allein sehen erstmal ok aus.

was du machen kannst:
in deinem JDK ist im bin Ordner die Datei "jvisualvm" drin - die mal starten, während das Programm läuft
und dann auf das Programm verbinden (das ist sehr intuitiv)

da siehst du dann schon einiges an Infos

ansonsten: profiler anschliessen oder an manchen Stellen auf die Console rausschreiben lassen (da siehst du, wenn er sich in irgendwelchen Schleifen verheddert)


----------



## Pansa (12. Jul 2014)

Ich habe zum Testen mal die Methode die die Objeke und die Gegner in die Welt spawnt auskommentiert,und siehe da es stürzt nichtmehr ab.Jedoch weiß ich nicht ob es an den Gegner und an den Objekten, oder an der Methode an sich lag.Ich poste hier mal die Methode:


```
private void spawnEnemy()
    {
	if(enemyspawn = true){ 
//	    System.out.println("Gegner spawnen");
        int x = random.nextInt(width);
        int y = random.nextInt(height);
        if(tiles[x] [y].isObstacle())
        {
            spawnEnemy();
            return;
        }
        enemies.add(new Enemy(x * Texture.tilesize, y * Texture.tilesize, 0, 0));
        paththread.changed();
	}
	if(enemyspawn = false){
	     
	}
```

und hier mal die die Begrenzung der Maximalen Gegner/Objekt anzahl:

```
//	
	 tsles += tslf;
	        if(tsles >= SPAWNTIME&&enemies.size() < 10)
	        {
	            spawnEnemy();
	            tsles = 0;
	        }
```

Ist hier irgendetwas verkehrt?
LG Pansa


----------



## Pansa (12. Jul 2014)

Phash hat gesagt.:


> stell den code rein, und wir können dir evtl. mehr helfen
> 
> die beiden Threads allein sehen erstmal ok aus.
> 
> ...




Soll ich den ganzen Code hier zum Download freigeben?
Ich glaube keiner wird sich das alles durchlesen wollen^^.
LG pansa


----------



## Pansa (12. Jul 2014)

turtle hat gesagt.:


> Und da gibt es keine weiteren Informationen?
> 
> Ich tippe mal darauf, das der JVM der Speicher ausgeht. Dann müsste eigentlich noch eine Out-of-memory-Exception kommen.
> 
> ...



Nein,eine OutofMemory Ecxeption kommt nicht.Ich denke das es eher mit der CPU zusammenhängt,da diese ja von jetz auf gleich fast 100% Leistung fürs Spiel braucht.
Aber wie genau kann ich dem Spiel weniger bzw mehr Speicher zur verfügung stellen?
LG Pansa


----------



## turtle (12. Jul 2014)

java - Xmx<Speicher>

PS: Sehe gerade in deinem Code


> if(enemyspawn = true){


Nä, nicht? Und wenn derartige Fehler noch drin sind...

Richtig ist

```
if(enemyspawn){
```


----------



## Pansa (12. Jul 2014)

turtle hat gesagt.:


> java - Xmx<Speicher>
> 
> PS: Sehe gerade in deinem Code
> 
> ...




Hmm wenn ich das " = true" weglasse,spawnt garnichts mehr 
LG Pansa


----------



## Androbin (12. Jul 2014)

Pansa hat gesagt.:
			
		

> Hmm wenn ich das " = true" weglasse,spawnt garnichts mehr
> LG Pansa



Du meintest vermutlich if ( enemyspawn == true ), welches überprüft, ob enemyspawn true ist, hast aber stattdessen if ( enemyspawn = true ) geschrieben, welches zuerst enemyspawn gleich true setzt und dann abfrägt, ob enemyspawn true ist. Letztendlich sollte es - wie turtle bereits sagte - heißen if ( enemyspawn ), da dies bereits ein boolean ist, und nicht extra mit boolschen ausdrücken überprüft werden muss.


----------



## turtle (12. Jul 2014)

Weil enemyspawn false ist.


----------



## Pansa (12. Jul 2014)

Okay ich habe es jetz Berichtigt,dennoch besteht der Fehler weiterhin.Ist den mit der Methode sonst alles richtig?
LG pansa


----------



## turtle (12. Jul 2014)

Ich glaube nicht.

Ich sehe da drin einen rekursiven Aufruf, der nahe legt, das du die Routine nie richtig getestet hast.


```
private void spawnEnemy()
    {
    if(enemyspawn = true){ 
//      System.out.println("Gegner spawnen");
        int x = random.nextInt(width);
        int y = random.nextInt(height);
        if(tiles[x] [y].isObstacle())
        {
            spawnEnemy(); // ECHT???
            return;
        }
        enemies.add(new Enemy(x * Texture.tilesize, y * Texture.tilesize, 0, 0));
        paththread.changed();
    }
```


----------



## Pansa (12. Jul 2014)

Mir ist nichts besseres eingefallen^^.
Aber was ist denn daran so Fehlerhaft? Ich meine er sucht sich eine zufällige Position aus,dann guckt er ob diese Position ein Hindernis ist, wenn es ein Hindernis ist,sucht er sich eine neue Position.
LG Pansa


----------



## turtle (12. Jul 2014)

Ja, genau.

Und wenn die neue Position wieder ein Hindernis ist, neue Position und dann wieder ein Hindernis?

Ich würde den rekursiven Aufruf mal rauswerfen...


----------



## Pansa (12. Jul 2014)

Hab ich gemacht.Der Fehler tritt immer noch auf, nur das die CPU jetz für einpaar Sekunden fast 100% auslastung hat,und dann wieder in den "Normalzustand" kommt.Das wiederholt sich dann immer.Außerdem steigt jetz der RAM schneller an.
LG Pansa


----------



## turtle (12. Jul 2014)

Ohne Genaueres Debugging/Profiling wirst du wohl nicht herum kommen.


----------



## Pansa (12. Jul 2014)

Ich habe mir jetz den JVM Monitor geholt.Jedoch hilft dieser mir auchnicht weiter,ich sehe genau das selbe wie beim Taskmanager,nur das der JVM Monitor mir sagt das die CPU fast ausschließlich 100% Leistung verbraucht.Könnten ihr mir vllt einen guten Profiler empfhelen, bei dem ich auch sehe wo es hapert?^^
LG Pansa


----------



## Pansa (13. Jul 2014)

Nach vielen Tests habe ich bemerkt, dass die Abstürze mit den Gegner zu tun haben.Ich habe nämlich die SpawnEnemy() Methode auskommentiert und nur die spawnMana() Methode laufen lassen(Das sind die gleichen Methoden, nur dass sie unterschiedliche Objekte spawnen).Das Spiel stürzte dann nichtmehr ab und laut dem JVM Monitor verbrauchte die CPU nichtmehr 100% Leistung. Was noch wichtig zu erwähnen wäre, wenn das Spiel abgestürzt ist der CPU verbraucht (laut dem JVM Monitor) 0% bis 3% war.

Hier mal die Enemy Klasse:

```
package Game;
  
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.LinkedList;
  
public class Enemy extends MovingObject
{
    private double rotation;
    static int size;
    private final int SPEED = 100;
    private LinkedList<Node> path;
    private LinkedList<TStone> tstone;
    private final float RELOADTIME = 1.0f;
    private float tsls;
    static float LEBEN = 5f;
    public Enemy(float xpos, float ypos, float xspeed, float yspeed){
    
        super(xpos, ypos, xspeed, yspeed);
          
        path = new LinkedList<Node>();
          tstone = new LinkedList<TStone>();
        look = Texture.troll;
        size = look.getWidth()/2;
    }
    public void draw(Graphics g, int worldx, int worldy){
    
       
        for(int i = 0;i < tstone.size();i++){
           // System.out.println(i +"Stein zeichnen");
            tstone.get(i).draw(g, worldx, worldy);
        }
            g.drawImage(rotate(), (int) xpos + worldx + Frame.transx, (int) ypos + worldy + Frame.transy, null);
        
    }
    public boolean update(float tslf, LinkedList<Feuersturm> feuersturm, float targetx , float targety,Tile [] [] tiles,LinkedList<Mana> mana){
    
	tsls += tslf;
	float absx = targetx - (xpos + size/2);
	float absy = targety - (ypos + size/2);
	float abs = absx * absx + absy * absy;
	if(abs <= 400 * 400){
	    while(path.size() > 0) path.remove(0);
	    // System.out.println(path.size() +" pathgröße");
	    rotation = Math.atan2(absy ,  absx);
	    if(tsls >= RELOADTIME){
		tstone.add(new TStone(xpos + size/2, ypos + size/2, (float) (Math.cos(rotation) * TStone.MAXSPEED), (float) (Math.sin(rotation)* TStone.MAXSPEED)));	
		tsls = 0;
	    }
	    
	}
	
        if(path.size() > 0){
        
             absx = path.get(path.size() - 1).getX() * Texture.tilesize + Texture.tilesize/2 - (xpos + look.getWidth()/2);
             absy = path.get(path.size() - 1).getY() * Texture.tilesize + Texture.tilesize/2 - (ypos + look.getHeight()/2);
              
            rotation = Math.atan2(absy, absx);
              
            xspeed = (float) (Math.cos(rotation) * SPEED);
            yspeed = (float) (Math.sin(rotation) * SPEED);
        }
        else{
        
            xspeed = 0;
            yspeed = 0;
        }
          
        xpos += xspeed * tslf;
        ypos += yspeed * tslf;
          
        if(path.size() > 0&&path.get(path.size() - 1).getX() == getTilePosX()&&path.get(path.size() - 1).getY() == getTilePosY())
            path.remove(path.size() - 1);
        for(int i = 0;i < feuersturm.size();i++){
        
            if(Collision.circleToCircle(feuersturm.get(i).getX(), feuersturm.get(i).getY(), 2, xpos + size, ypos + size, size)){   //Kollision mit dem Gegner
            
        	
                feuersturm.remove();
                return true;
            }
        }
        
        
        for(int i = 0;i < tstone.size();i++){
            if(tstone.get(i).update(tslf, tiles)) tstone.remove(i);
            else if(Collision.circleToCircle(targetx, targety, Player.size/2, tstone.get(i).getX(), tstone.get(i).getY(),2)){
                    
                tstone.remove();   
            	Player.playerleben -= TStone.DAMAGE;
            	if(Player.playerleben <= 0){
            Frame.gamestate = 2;
            	}
            }
            
      
        }
        
        for(int i = 0;i < mana.size();i++){
            if(mana.get(i).update(tslf, tiles, 0, 0)) mana.remove(i);
            else if(Collision.circleToCircle(targetx, targety, Player.size/2, mana.get(i).getX(), mana.get(i).getY(),2)){//Mana Kollision
        	mana.remove();
        	Player.DERZEITMANA = Player.DERZEITMANA + 4;
        	if(Player.DERZEITMANA >= 100){
        	    Player.DERZEITMANA = 100;
        	}
            }
         }
	return false;
    }
    public BufferedImage rotate(){
    
        AffineTransform at = AffineTransform.getRotateInstance(rotation + Math.PI/2, size, size);
        BufferedImage rotatedImage = new BufferedImage(look.getWidth(), look.getHeight(), look.getType());
        Graphics2D g = (Graphics2D) rotatedImage.getGraphics();
        g.setTransform(at);
        g.drawImage(look, 0, 0,null);
        return rotatedImage;
    }
    public void setPath(LinkedList<Node> path){
    
        this.path = path;
    }
    public int getTilePosX(){
    
        return (int) ((xpos + look.getWidth()/2)/Texture.tilesize);
    }
    public int getTilePosY(){
    
        return (int) ((ypos + look.getHeight()/2)/Texture.tilesize);
    }
    
}
```


Ist hier irgendetwas verdächtigt,was solche Probleme hervorrufen könnte?
LG Pansa


----------



## turtle (13. Jul 2014)

So, auf den ersten Blick fällt mir nichts Offensichtliches auf.


```
while(path.size() > 0) path.remove(0);
```
Aber solche Dinge kommen mir abstrus vor. Was spricht gegen path.clear()?

Daher vermute ich, das du mit mehreren Thread arbeitest, ja? Alle Zugriffe auf Attribute sind aber nicht thread-safe und da lohnt sich dann ein genauerer Blick.

Ich gebe zu, das es mehrere Jahre her ist, das ich mal einen Profiler benutzen musste. Wie hier bereits geschrieben, würde ich mal jvisualvm probieren, insbesondere da im JDK bereits enthalten.


----------



## Pansa (13. Jul 2014)

jvisualvm sagt das beim Abstürzen die LinkedList Node am meisten RAM verbraucht(ca 100 - 90%), und die CPU durch die rotation() Methode mit ca 30% belastet wird.An zweiter stelle kommt die draw() Methode(weil da ja auch die rotation Methode auch zum einsatz kommt).Ich teste jetz mal ob es wirklich an der rotation liegt.
LG Pansa


----------



## Pansa (13. Jul 2014)

Ich habe die Rotation des Gegners jetz mal auskommentiert,jezt ist der gleiche Fehler aber auch bei der Rotation des Spielers.Ich glaube, dass irgendwas mit den Nodes und dem aster Algorythmus nicht stimmt.Kennt sich jemand damit aus?
LG Pansa


----------



## Thallius (13. Jul 2014)

Ich habe mir das jetzt nur kurz angesehen aber static Variablen in Objekten zu benutzen die von mehreren Threads benutzt werden ist immer eine saublöde Idee.

Gruss

Claus


----------



## Pansa (14. Jul 2014)

Ich habe herausgefunden, dass es nicht an den Gegnern sondern an dem Pathfinding liegt.Als ich das Pathfinding ausgestellt habe(Nicht den Thread) kam der Fehler nicht.Ich poste hier mal die Klasse zum Pathfinding.


```
package Game;
  
import java.util.LinkedList;
  
public class Graph 
{
    private LinkedList<Node> nodes;
    private int[] [] matrix;
    public Graph(){
    
        nodes = new LinkedList<Node>();
    }
    public void addBorder(int from, int to){
        matrix[from] [to] = 1;
        matrix[to] [from] = 1;
    }
    public void addNode(Node node){
        nodes.add(node);
    }
    public int getNodeID(int x, int y){
        for(int i = 0;i < nodes.size();i++){
            if(x == nodes.get(i).getX()&&y == nodes.get(i).getY()) return i;
        }
        return -1;
    }
    public Node getNode(int x, int y){
        for(int i = 0;i < nodes.size();i++){
            if(x == nodes.get(i).getX()&&y == nodes.get(i).getY()) return nodes.get(i);
        }
        return null;
    }
    public void createMatrix(){
        matrix = new int[nodes.size()] [nodes.size()];
        for(int i = 0;i < nodes.size();i++){
            
            for(int j = 0;j < nodes.size();j++){
        	
                int absx = nodes.get(i).getX() - nodes.get(j).getX();
                int absy = nodes.get(i).getY() - nodes.get(j).getY();
                int abs = absx * absx + absy * absy;
                if(abs == 1) addBorder(i, j);
            }
        }
    }
    private int getNextNode(){
	
        int id = -1;
        int value = Integer.MAX_VALUE;
        for(int i = 0;i < nodes.size();i++){
            
            if(!nodes.get(i).isVisited()&&nodes.get(i).getValue() < value){
        	
                id = i;
                value = nodes.get(i).getValue();
            }
        }
        return id;
    }
    public LinkedList<Node> astar(int startnode, int endnode){
	
        int id, newDis;
        int maxValue = Integer.MAX_VALUE - (int) Math.sqrt(World.width * World.width + World.height * World.height);
        for(int i = 0;i < nodes.size();i++){
            
            int absx = nodes.get(i).getX() - nodes.get(endnode).getX();
            int absy = nodes.get(i).getY() - nodes.get(endnode).getY();
            nodes.get(i).setHeuristic((int) Math.sqrt(absx * absx + absy * absy));
            nodes.get(i).setVisited(false);
            nodes.get(i).setRange(maxValue);
        }
        nodes.get(startnode).setRange(0);
        outer : for(int i = 0;i < nodes.size();i++){
        
            id = getNextNode();
            if(id == -1) break;
            nodes.get(id).setVisited(true);
            for(int ab = 0;ab < nodes.size();ab++){
            
                if(!nodes.get(ab).isVisited()&&matrix[ab] [id] > 0){
                
                    newDis = nodes.get(id).getRange() + matrix[ab] [id];
                    if(newDis < nodes.get(ab).getRange()){
                   
                       nodes.get(ab).setRange(newDis);
                        nodes.get(ab).setCmgfrom(id);
                          
                        if(ab == endnode) break outer;
                    }
                }
            }
        }
          
        LinkedList<Node> way = new LinkedList<Node>();
        id = endnode;
        way.add(nodes.get(id));
        while(id != startnode){
        
            id = nodes.get(id).getCmgfrom();
            way.add(nodes.get(id));
        }
        return way;
    }
}
```

Ich habe noch eine Hilfsklasse(Node) wo aber nur Methoden drinne sind,falls die jemand sehen möchte sagt bescheid .
Da ich mich mit dem Pathfinding sehr schwer getan habe, ist die Klasse bestimmt voller Fehler(auch wenn ich es mit nem Tutorial gemacht habe)^^.
LG Pansa


----------

