LibGDX Mälu Haldamine¶
1. Sissejuhatus¶
Mälu haldamine on oluline aspekt suurte mängude arendamisel, kuna see mõjutab nii mängu jõudlust kui ka stabiilsust. Graafika, heliefektid ja muud ressursid võivad võtta palju mälu ning enamik neist ei ole hallatud Java prügikoguja (Garbage Collector) poolt. Selle asemel hallatakse neid otse süsteemitasandil, näiteks GPU või helidraiverite poolt. Seetõttu on oluline ressursside eluiga teadlikult hallata ja need õigel ajal vabastada.
1. "Disposable" Liides¶
LibGDX-is on paljud klassid märgistatud liidesega Disposable
, mis tähendab, et neid tuleb manuaalselt vabastada, kui need ei ole enam kasutusel.
Näited klassidest, mis kasutavad "Disposable" liidest:
Graafilised ressursid:
Texture
TextureAtlas
Pixmap
BitmapFont
Shader
Skin
Graafika ja joonistamine:
TileMapRenderer
Stage
Shape.
SpriteBatch
ShapeRenderer
ModelBatch
Heli ja füüsika:
Sound
Music
com.badlogic.gdx.physics.box2d.World
Mängu ressursside haldus:
AssetManager
FrameBuffer
Mesh
Kuidas "Disposable" objekte õigesti vabastada¶
Kui loote Disposable
objekti, tuleb see pärast kasutamist vabastada meetodiga .dispose()
.
Näide:
Texture texture = new Texture("menu_background.png");
...
texture.dispose(); // Tekstuuri vabastamine, kui see pole enam vajalik
Kui mängul on mitu ekraani (näiteks menüü ja mänguekraan), tuleb veenduda, et ühele ekraanile loodud ressursid vabastatakse, kui mängija liigub teisele ekraanile.
@Override
public void hide() {
texture.dispose(); // Vabastab tekstuuri, kui ekraan pole nähtav
}
1. Objektide Taaskasutamine (Object Pooling)¶
Mälu optimeerimiseks võib kasutada "Object Pooling" tehnikat. See on kasulik eriti siis, kui samatüübilisi objekte tuleb luua ja hävitada korduvalt (nt. kuuliobjektid "shooter" mängus). Selle asemel, et iga kord uus objekt luua, hoitakse neid mälus ja taaskasutatakse, kui objekt on vaba. Kui kõik objektid on juba kasutuses siis luuakse alles uus.
Kuulide korduvkasutamine Object Pooling tehnikaga¶
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Pool;
public class Bullet implements Pool.Poolable {
public Vector2 position;
public boolean alive;
/**
* Bullet constructor. Just initialize variables.
*/
public Bullet() {
this.position = new Vector2();
this.alive = false;
}
/**
* Initialize the bullet. Call this method after getting a bullet from the pool.
*/
public void init(float posX, float posY) {
position.set(posX, posY);
alive = true;
}
/**
* Callback method when the object is freed. It is automatically called by Pool.free()
* Must reset every meaningful field of this bullet.
*/
@Override
public void reset() {
position.set(0,0);
alive = false;
}
/**
* Method called each frame, which updates the bullet.
*/
public void update (float delta) {
// update bullet position
position.add(1*delta*60, 1*delta*60);
// if bullet is out of screen, set it to dead
if (isOutOfScreen()) alive = false;
}
}
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Pool;
public class World {
// array containing the active bullets.
private final Array<Bullet> activeBullets = new Array<Bullet>();
// bullet pool.
private final Pool<Bullet> bulletPool = new Pool<Bullet>() {
@Override
protected Bullet newObject() {
return new Bullet();
}
};
public void update(float delta) {
// if you want to spawn a new bullet:
Bullet item = bulletPool.obtain();
item.init(2, 2);
activeBullets.add(item);
// if you want to free dead bullets, returning them to the pool:
Bullet item;
int len = activeBullets.size;
for (int i = len; --i >= 0;) {
item = activeBullets.get(i);
if (item.alive == false) {
activeBullets.removeIndex(i);
bulletPool.free(item);
}
}
}
}
See lähenemine vähendab prügikogumise koormust ja parandab jõudlust, eriti kui objekte luuakse tihti.
obtain()
hangib objekti, free()
vabastab objekti tagasi taaskasutamiseks, et vältida pidevat uute objektide loomist ja mälu kulutamist.
1. AssetManager – Ressursside Keskne Haldamine¶
Kui mäng kasutab palju ressursse (tekstuure, fonte, helisid), võib olla kasulik kasutada AssetManager
klassi, mis aitab neid efektiivselt hallata.
AssetManager kasutamine¶
AssetManager assetManager = new AssetManager();
// Laeme ressursid
assetManager.load("player.png", Texture.class);
assetManager.load("background_music.mp3", Music.class);
assetManager.finishLoading();
// Kasutame ressursse
Texture playerTexture = assetManager.get("player.png", Texture.class);
Music backgroundMusic = assetManager.get("background_music.mp3", Music.class);
// Vabastame kõik ressursid mängu lõpetamisel
assetManager.dispose();
AssetManager
on kasulik, kuna see:
Laeb ressursse taustal, vältides mängu külmumist.
Vältib sama ressursi korduvat laadimist.
Haldab automaatselt
Disposable
objekte, vabastades needdispose()
kutsumisel.
Siit lingilt saab lehele, kus seletatakse AssetManagerist pikemalt. https://gamedevdoc.pages.taltech.ee/assetManagement/assetManager.html
1. VisualVM – Mälulekete ja Jõudluse Diagnostika¶
VisualVM on Java rakendus, mis aitab tuvastada mälu lekkeid ja jälgida mängu jõudlust reaalajas.
VisualVM-i kasutamine:¶
1. Käivita oma mäng ja ava VisualVM. Link allalaadimiseks - https://visualvm.github.io/
Leia protsess, mis vastab sinu mängule.

Vasakul on näha lokaalselt töötavad programmid. Antud juhul näha, et mänguserver töötab lokaalselt.
Kasuta Monitor vaadet, et jälgida:
CPU kasutust
Mälu kasutust
Objektide arvu
Kui mälu kasutus kasvab pidevalt ja ei vähene, võib see viidata mälulekkele.
Kasuta Perform GC nuppu, et sundida prügi kogumist ja vaadata, kas mälu vabaneb korralikult.
VisualVM on hea tööriist, et tuvastada mälu lekkeid, ehk eelnevalt mainitud olukorda, kus programm ei vabasta mälu. Selline olukord võib tekkida ka objektidega, mida haldab Garbage Collector automaatselt.
Näide, mida Garbage Collector automaatselt haldab:
Java Garbage Collector vastutab objekti mälu vabastamise eest, kui seda enam ei kasutata. See hõlmab selliseid objekte nagu ArrayList, HashMap ja String, mis kõik on heap mälu sees ning neid haldab GC.
ArrayList: Kui loome ArrayList objekti ja lisame sinna andmeid, siis GC jälgib, et see mälu vabaneks, kui listi objekti enam ei kasutata. Näiteks:
ArrayList<String> list = new ArrayList<>(); list.add("Test"); // Kui 'list' enam ei ole kasutuses, siis GC võib selle mälu vabastada. list = null; // See seab viite nulliks, tähistades, et enam ei kasutata
HashMap: Kui loome HashMap objekti, mis sisaldab võtmeid ja väärtusi, siis pärast nende eemaldamist või kui kaardiga pole enam viiteid, saab GC nende mälumahtude vabastada:
HashMap<String, Integer> map = new HashMap<>(); map.put("One", 1); map.put("Two", 2); // Kui 'map' enam ei ole kasutuses, saab GC selle vabastada map = null; // Tagasi viidete puudumine tähendab, et objekt saab vabastatud
String: String objektid on samuti heap mälu objektid, mis GC haldab. Kui String ei ole enam kasutuses ja sellele ei viita ükski muu muutujat, siis GC saab selle mälumahtude vabastada:
String text = "Hello, World!"; // Kui 'text' enam ei ole kasutuses, saab GC selle vabastada text = null;
Miks see oluline on?
Näiteks koodis kus on mõni katkine lõpmatu loop, mis pidevalt teeb ja saadab packeteid, võib tekkida olukord, et packeteid tehakse kiiremini kui “garbage collector” suudab vanade packetite mälu vabastada, ning mingi hetk jookseb mäng kokku näiliselt ilma ühegi veateateta.
Korduvad VisualVM-i probleemid ja lahendused: Kõige tavalisem probleem on see, et VisualVM vajab JDK asukoha parameetrit käivitamiseks. Selle lahendamiseks on vaja minna terminalis VisualVM kausta (nt `.visualvm.exe`) ja anda järgmised parameetrid:
--jdkhome "C:\path\to\your\jdk" --userdir "C:\path\to\user\directory"
VisualVM on vahend töötavate Java rakenduste kohta info saamiseks. See on kasulik vahend, mille abil saab jälgida rakenduse jõudlust ja tuvastada erinevaid probleeme, sealhulgas mälu lekked.