Использование памяти в LibGdx

Я нахожусь в середине огромного приложения в команде разработчиков, и память - это то, что нужно рассмотреть на ранней стадии. Когда я запускаю программу как есть, она занимает около 44 МБ памяти (найдена из диспетчера задач). Затем я создаю 10 000 тел. Использование памяти теперь составляет около 83 МБ. У меня есть метод уничтожения тел, когда я нажимаю пробел, вот как он выглядит.

public static void disposeAllBodies(){
    Array<Body> bodies = new Array<Body>();
    world.getBodies(bodies);
    int destroyCount = 0;
    System.out.println("Attempting to destroy " + world.getBodyCount()+ " bodies");
    for(Body b : bodies){
        world.destroyBody(b);
        destroyCount++;
    }

    System.out.println("Successfully destroyed " + destroyCount + " body(s), " + world.getBodyCount() + " remain");


}

Он без проблем располагает всеми телами, и это были единственные вещи в приложении. После того, как они расположены, память опускается примерно до 66 МБ в течение нескольких секунд, а затем перескакивает до 78 МБ и остается там.

Итак, мне интересно, есть ли лучший способ распорядиться этими телами? Это приложение будет создавать миллионы тел, но большинство из них будет уничтожено, однако, если память просто продолжает расти, она не сможет справиться с этим, поскольку память остается довольно статической.

Кроме того, CPU идет от 0,2% (до любых тел) до 23% (при наличии 10 000 тел), тогда до 2,3% (при размещении тел). Поэтому даже центральный процессор делает больше работы после утилизации тел.

Спасибо за любую помощь!

Обновление: Код для создания тел выглядит следующим образом:

BodyDef bodyDef = new BodyDef();
    bodyDef.type = type;
    bodyDef.position.set(new Vector2(position.x, position.y));

    Body body = world.createBody(bodyDef);

    FixtureDef fixtureDef = new FixtureDef();
    Fixture fixture;
    if(isCircle){
        CircleShape circle = new CircleShape();
        circle.setRadius(dimensions.x);
        fixtureDef.shape = circle;
        fixture = body.createFixture(fixtureDef);
        circle.dispose();
    }else{
        PolygonShape rectangle = new PolygonShape();
        rectangle.setAsBox(dimensions.x, dimensions.y);
        fixtureDef.shape = rectangle;
        fixture = body.createFixture(fixtureDef);
        rectangle.dispose();
    }

Это всего лишь тела Box2D, никаких спрайтов и ничего. Спасибо!

Ответы

Ответ 1

Вы попробовали вырезанную версию кода "box2d only", который вы опубликовали, чтобы узнать, есть ли у него такая же проблема? Причина, по которой я спрашиваю, заключается в том, что вы также разместили еще один вопрос о "изменении свойств FixtureDef" в изменении свойств FixtureDef Java Libgdx, и вы указали намного больше своего общего кода. (Код из этого вопроса является подмножеством кода из другого вопроса). Если посмотреть на этот код, могут возникнуть некоторые проблемы.

В другом вопросе вы помещаете тела, bodyDefs, fixtures и fixtureDefs в HashMap, вы не показываете, как вы извлекаете/очищаете карту. Это может привести к утечке памяти. Я бы сказал, скорее всего, нет, но вы никогда не знаете.

Но я видел этот бит, который, я уверен, вызовет проблемы:

public void attachNewSprite(String internalPath){
    entitySprite = new Sprite(new Texture(Gdx.files.internal(internalPath)));
    ((Body)bodyObjects.get(BodyReferences.BODY)).setUserData(entitySprite);
}

В этом вопросе вы сказали, что вы не использовали спрайтов, но если вы сделаете что-то выше в своем коде, каждая новая текстура() займет память. Вы должны явно распоряжаться каждой создаваемой текстурой. Вы не должны создавать новую текстуру каждый раз, когда вы создаете новый Sprite. В идеале вы создаете текстуру один раз, затем используйте Sprite, который является TextureRegion, для отображения текстуры. Затем удалите текстуру, когда все закончите (в конце уровня/игры/etc). Чтобы избавиться от текстуры, вам нужно будет ссылаться на нее.

Edit/Update:

У меня было какое-то время этим утром, поэтому я взял ваш опубликованный код и немного добавил для создания простого простого приложения с созданием тела и кодом удаления тела. Я настраиваю таймер, чтобы стрелять каждые X секунд, чтобы увидеть, что происходит, когда вы создаете/уничтожаете тела 10k, и код, который вы опубликовали, выглядит отлично. Поэтому я думаю, что ваша проблема может быть в другом месте с кодом, который вы не опубликовали. Память на моей машине будет немного изменяться (вы никогда не знаете, когда GC встретятся, но она никогда не превысила 45 МБ).

Если вы не видите что-то другое ниже, чем то, что вы делаете (или если у вас больше кодов для публикации и т.д.), я не вижу проблемы с тем, что вы поделили до сих пор.

import java.util.concurrent.ThreadLocalRandom;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
import com.badlogic.gdx.physics.box2d.CircleShape;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Timer;
import com.badlogic.gdx.utils.Timer.Task;

public class Memory implements ApplicationListener {

    private static World world;

       private static void createNewBodies(boolean isCircle, Vector2 position, Vector2 dimensions) {
            BodyDef bodyDef = new BodyDef();
            //bodyDef.type = type; //all bodies here are dynamic
            bodyDef.type =  BodyType.DynamicBody;
            bodyDef.position.set(position);

            Body body = world.createBody(bodyDef);

            FixtureDef fixtureDef = new FixtureDef();
            Fixture fixture;
            if(isCircle){
                CircleShape circle = new CircleShape();
                circle.setRadius(dimensions.x);
                fixtureDef.shape = circle;
                fixture = body.createFixture(fixtureDef);
                circle.dispose();
            }else{
                PolygonShape rectangle = new PolygonShape();
                rectangle.setAsBox(dimensions.x, dimensions.y);
                fixtureDef.shape = rectangle;
                fixture = body.createFixture(fixtureDef);
                rectangle.dispose();
            }
       }

       public static void disposeAllBodies(){
            Array<Body> bodies = new Array<Body>();
            world.getBodies(bodies);
            int destroyCount = 0;
            System.out.println("Attempting to destroy " + world.getBodyCount()+ " bodies");
            for(Body b : bodies){
                world.destroyBody(b);
                destroyCount++;
            }

            System.out.println("Successfully destroyed " + destroyCount + " body(s), " + world.getBodyCount() + " remain");

        }

       private static void buildAllBodies() {
           int minPos = 10;
           int maxPos = 400;
           int minWidthHeight = 50;

           Vector2 position = new Vector2();
           Vector2 dimensions = new Vector2();

           for (int i=0; i<10000; i=i+2) {
               position.x = ThreadLocalRandom.current().nextInt(minPos, maxPos+1);
               position.y = ThreadLocalRandom.current().nextInt(minPos*2, maxPos*2+1);
               dimensions.x = ThreadLocalRandom.current().nextInt(minWidthHeight, minWidthHeight+1);
               dimensions.y = dimensions.x;
               createNewBodies(true, position, dimensions);
               createNewBodies(false, position, dimensions);
           }
       }

       @Override
       public void create() {

           world = new World ( new Vector2(0.0f, -9.8f), true);

           Timer.schedule(new Task() {
                   @Override
                   public void run() {
                       buildAllBodies();
                       disposeAllBodies();
                   }
              }
               , 1.0f
               , 10.0f //how often to do the cycle (in seconds)
              );
       }

       @Override
       public void render() { }

       @Override
       public void dispose() {
           world.dispose();
       }

       @Override
       public void resize(int width, int height) { }

       @Override
       public void pause() { }

       @Override
       public void resume() { }
}