Garbage Collector gibt Speicher nicht frei?

  • Antworten:7
Enrico
  • Forum-Beiträge: 43

06.11.2016, 11:01:51 via Website

Hallo,
ich programmiere momentan eine neue Spiele-App mit mehreren Levels und habe das Problem dass mein Arbeitsspeicher voll läuft. Da ich eher aus der C-Programmierung komme und in Java noch nicht so erfahren bin, hoffe ich dass mir jemand von euch sagen kann wo mein Fehler liegt.

Zur App:
Ich habe 3 Activities:
A: Level-Übersicht
B: hier drin läuft das Spiel selbst (GameActivity)
C: Ergebnis-Ansicht (ResultActivity)

Aus Activity A starte ich B wie folgt:

Intent myIntent = new Intent(this, GameActivity.class);
myIntent.putExtra("levelResource", list.get(position).resourceID);
myIntent.putExtra("levelNumber", list.get(position).number);
this.startActivity(myIntent);

Activity B sieht wie folgt aus:

public class GameActivity extends AppCompatActivity {

View gameView;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Hide the status bar.
    View decorView = getWindow().getDecorView();
    decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN);

    Intent intent = getIntent();
    int levelResource = intent.getIntExtra("levelResource", R.raw.level1);
    int levelNumber = intent.getIntExtra("levelNumber", 1);

    gameView = new gameView(this, null, levelResource, levelNumber);
    setContentView(gameView);
    Log.v("level", "created");
}

@Override
protected  void onDestroy()
{
    super.onDestroy();
    gameView = null;
    Log.v("level", "destroy");
}

}

so weit so gut, die gameView-Klasse lädt nun alles und zeichnet das Spiel auf den screen. Die Klasse benötigt für die ganzen Grafiken etwa (10-20MB RAM).
Wenn das Spiel nun vorrüber ist, erfolgt innerhalb der gameView-Klasse folgender Aufruf:

Intent myIntent = new Intent(context, ResultActivity.class);
myIntent.putExtra("levelNumber", levelNumber); //parameters
myIntent.putExtra("reachedPoints", gameEngine.getPoints()); //parameters
context.startActivity(myIntent);
context.finish();

Wie ich im Log sehe, wird nun die onDestroy() der Activity B aufgerufen.

Und hier verstehe ich nun eines nicht:
Obwohl ich gameView zu null setze (und somit keine Referenzierung mehr existieren kann), scheint diese Klasse noch im Speicher zu bleiben. Dies sehe ich am Android Monitor in Android Studio. Dies führt zu dem Problem, dass der Speicher immer voller wird. Denn jedes neue Level was ich aus Activity A starte kommt im Speicher hinzu, die alten werden aber nicht beseitigt. Irgendwann, wenn man mehrere Level gespielt hat, ist der Speicher voll und die App stürzt ab, weil sie keinen neuen Speicher mehr bekommt.
Was mache ich hier falsch? Sollte gameView=null nicht den verwendeten Speicher dieser Klasse, und aller ihrer Member wieder frei geben?
Activity B ist definitiv die einzige Activity die mit gameView irgendwas macht.

— geändert am 06.11.2016, 11:03:33

Antworten
swa00
  • Forum-Beiträge: 3.704

06.11.2016, 11:30:29 via Website

Hallo Enrico,

auf Anhieb kann ich nichts Außergewöhnliches ersehen ..

was würde passieren , wenn du anstatt finish() ein System.exit(0) verwendest.
Probiere es mal bitte aus ..

P.S. ich komme auch aus der C/C++ Ecke (hauptberuflich) und verzweifle auch manchmal ,was die da machen .
Eines ist klar Android-null ist nicht gleich C++=null - das Speichermanagement ist gewöhnungsbedürftig :-)

— geändert am 06.11.2016, 11:33:27

Liebe Grüße - Stefan
[ App - Entwicklung ]

Enrico

Antworten
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

06.11.2016, 11:36:32 via App

Was pasiert wenn du den Garbage Collector mal mit System.gc(); selber aufrufst?

LG Pascal //It's not a bug, it's a feature. :) ;)

Enrico

Antworten
Enrico
  • Forum-Beiträge: 43

06.11.2016, 13:17:59 via Website

@swa00:
Hab ich ausprobiert, immer noch das gleiche Problem.
PS: Danke, ich dachte schon ich bin der Einzige der Java an einigen Stellen etwas komisch findet...

@MOD:
Auch das habe ich ausprobiert, ohne Erfolg.

Es scheint mir, das den Garbage Collector irgendwas dazu bewegt das gameView-Objekt am Leben zu erhalten. Nur was :(

Oder gibt es eine Möglichkeit herauszufinden wo noch eine Referenz zu einen der Objekte existiert?

— geändert am 06.11.2016, 13:36:03

Antworten
swa00
  • Forum-Beiträge: 3.704

06.11.2016, 13:36:03 via Website

ok, dann müssen wir auf die suche gehen ..

einmal den Code der Activity , die sich NICHT töten lässt :-)

— geändert am 06.11.2016, 13:36:18

Liebe Grüße - Stefan
[ App - Entwicklung ]

Antworten
Enrico
  • Forum-Beiträge: 43

06.11.2016, 15:28:56 via Website

Das ist nicht so einfach.
Denn diese Activity bzw. die GameView die darauf verknüpft wird, enthält wiederum eine GameEngine, Level-Klasse, Spielelementklassen etc...
Die GameView ist quasi die Mutterklasse des gesamten Spiels (welches schon durchprogrammiert ist). Ich müsste euch also dafür den gesamten Code des Spiels liefern (20 verschiedene Klassen) ?

Aber ich habe noch eine Vermutung: Was passiert denn mit den laufenden Threads die die GameEngine (in der gameView) gestartet hat? Werden diese automatisch beendet wenn die Mutterklasse derefernziert wird? (Ich wünsch mir hier meinen Destruktor von C++ her um alles sauber zu beenden.)

Antworten
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

06.11.2016, 15:48:46 via Website

Einen konkreten Destruktor gibt es nicht, aber etwas derart:
https://www.dpunkt.de/java/Die_Sprache_Java/Objektorientierte_Programmierung_mit_Java/11.html
Damit kannst du den GC anweisen was er tun soll.
Zudem:
Threads bitte immer selber beenden. Ansonsten laufen diese im WorstCase immer weiter uach wenn es die Aufrufende Klasseninstanz (Activity) nicht mehr gibt.

LG Pascal //It's not a bug, it's a feature. :) ;)

Antworten
Enrico
  • Forum-Beiträge: 43

06.11.2016, 16:28:19 via Website

Okay, ich habs nun gelöst:
Ich habe jeder Klasse eine destroy() hinzugefügt.
In der onDestroy der Activity rufe ich dann die destroy() der gameView auf, die wiederum die destroy's der anderen Klassen und die wiederum derer darin etc....
In den jeweiligen destroy's beende ich dann alle Threads und dereferenziere alle Arrays, Objekte, Bitmaps etc...
(Nur das beenden der Threads alleine hatte nicht ausgereicht, ich muss tatsächlich auch die Objekte alle dereferenzieren)

Und siehe da. Nun schafft es auch der garbage collector die gameView bzw. Activity komplett aus dem Speicher zu nehmen :)

Den Tipp mit dem finalize muss ich mir mal anschauen. Das scheint mir dann doch einer sinnvollere Lösung zu sein.
Vielen Dank

Antworten