【问题标题】:Why my app is taking so much ram?为什么我的应用程序占用了这么多内存?
【发布时间】:2015-05-13 10:45:18
【问题描述】:

所以我为学校项目制作了一个使用位图和表面视图的游戏应用程序。但是应用程序本身占用了这么多内存!仅当您启动它时,它才能获得高达 60mb 的内存,并且您玩得越多,内存就越高(在某一时刻,它达到了 90mb 的内存并且游戏非常滞后)。

观看 Google I/O 2011 (https://www.youtube.com/watch?v=_CruQY55HOk) 后,我意识到这可能是内存泄漏,因为应用程序以这样的方式启动:
玩了 2 分钟后,它以这样的方式结束:

应用程序本身看起来尽可能简单,具有 8 位图形且颜色不多:

我使用的所有图像仅重 400kb 那么到底为什么需要这么多内存呢?!我认为可能是声音,但所有声音加起来只有 4.45mb 的内存,这就像应用程序所需数量的 1/10。我知道位图需要很多内存,但这太荒谬了!

这是我的 onLoad:

public GameView(Context c) {
        // TODO Auto-generated constructor stub
        super(c);
        this.c = c;
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        ScoreParticleP = new PointF();
        NewScoreParticleP = new PointF();
        int srcWidth = options.outWidth;
        int srcHeight = options.outHeight;
        // it=blocks.iterator();
        // Decode with inSampleSize
        options.inJustDecodeBounds = false;
        options.inDither = false;
        options.inScaled = false;
        options.inPreferredConfig = Bitmap.Config.ARGB_8888;
        this.setKeepScreenOn(true);
        WindowManager wm = (WindowManager) c
                .getSystemService(Context.WINDOW_SERVICE);
        Display display = wm.getDefaultDisplay();
        this.screenw = display.getWidth();
        this.screenh = display.getHeight();
        this.differencew = (double) screenw / normalw;
        this.differenceh = (double) screenh / normalh;
        try{
            mediaPlayer = MediaPlayer.create(c, R.raw.nyan);
            while(mediaPlayer == null) {
                mediaPlayer = MediaPlayer.create(c, R.raw.nyan);
            }
        mediaPlayer.setLooping(true);
        if(mediaPlayer!=null)
        mediaPlayer.start();
        }
        catch(Exception e){

        }
        try{
        mediaPlayer2 = MediaPlayer.create(c, R.raw.remix);
        while(mediaPlayer2==null){
            mediaPlayer2 = MediaPlayer.create(c, R.raw.remix);
        }
        mediaPlayer2.setLooping(true);
        }
        catch(Exception e){

        }
        try{
        mediaPlayer3 = MediaPlayer.create(c, R.raw.weed);
        while(mediaPlayer3==null){
            mediaPlayer3 = MediaPlayer.create(c, R.raw.weed);
        }
        mediaPlayer3.setLooping(true);
        }
        catch(Exception e){

        }
        SharedPreferences prefs2 = c.getSharedPreferences(
                "Sp.game.spiceinspace", Context.MODE_PRIVATE);
        counter2 = prefs2.getInt("score", 0);
        this.sprite = BitmapFactory.decodeResource(getResources(),
                R.drawable.sprite, options);
        this.sprite = Bitmap.createScaledBitmap(sprite, sprite.getWidth() * 3,
                sprite.getHeight() * 3, false);
        this.heart = BitmapFactory.decodeResource(getResources(),
                R.drawable.heart);
        this.explosionheart=BitmapFactory.decodeResource(getResources(),
                R.drawable.explosionheart);
        this.heart = Bitmap.createScaledBitmap(heart, heart.getWidth() * 3,
                heart.getHeight() * 3, false);
        currentSpeed = new PointF(0, 0);
        currentDirection = new Point(0, 0);
        currentPosition = new Point(350, 350);
        this.background = BitmapFactory.decodeResource(getResources(),
                R.drawable.space);
        this.background2=BitmapFactory.decodeResource(getResources(),
                R.drawable.space2);
        this.electricExplosion = BitmapFactory.decodeResource(getResources(),
                R.drawable.effect_explosion);
        this.normalexplison = BitmapFactory.decodeResource(getResources(),
                R.drawable.effect_explosion2);
        this.background = Bitmap.createScaledBitmap(background,
                background.getWidth() * 5, background.getHeight() * 5, false);
        this.background2 = Bitmap.createScaledBitmap(background2,
                background2.getWidth() * 5, background2.getHeight() * 5, false);
        this.lost = BitmapFactory.decodeResource(getResources(),
                R.drawable.gameover);
        this.lostNew = BitmapFactory.decodeResource(getResources(),
                R.drawable.gameovernew);
        lostNew = FitAllDevices(lostNew);
        lost = FitAllDevices(lost);
        this.alien = BitmapFactory.decodeResource(getResources(),
                R.drawable.mob_alien);
        this.coin = BitmapFactory.decodeResource(getResources(),
                R.drawable.item_coin);
        partic = BitmapFactory.decodeResource(getResources(),
                R.drawable.particle_star);
        fire = BitmapFactory.decodeResource(getResources(),
                R.drawable.particle_fire);
        smoke = BitmapFactory.decodeResource(getResources(),
                R.drawable.particle_smoke);
        partic = Bitmap.createScaledBitmap(partic, partic.getWidth() * 2,
                partic.getHeight() * 2, false);
        fire = Bitmap.createScaledBitmap(fire, fire.getWidth() * 2,
                fire.getHeight() * 2, false);
        smoke = Bitmap.createScaledBitmap(smoke, smoke.getWidth() * 2,
                smoke.getHeight() * 2, false);
        electricExplosion = Bitmap.createScaledBitmap(electricExplosion,
                electricExplosion.getWidth() * 2,
                electricExplosion.getHeight() * 2, false);
        normalexplison = Bitmap.createScaledBitmap(normalexplison,
                normalexplison.getWidth() * 3,
                normalexplison.getHeight() * 3, false);
        this.alien = Bitmap.createScaledBitmap(alien, alien.getWidth() * 3,
                alien.getHeight() * 3, false);
        asteroid = BitmapFactory.decodeResource(getResources(),
                R.drawable.mob_astroid);
        bomb = BitmapFactory.decodeResource(getResources(),
                R.drawable.mob_spacebomb);
        asteroid = Bitmap.createScaledBitmap(asteroid, asteroid.getWidth() * 3,
                asteroid.getHeight() * 3, false);
        bomb = Bitmap.createScaledBitmap(bomb, bomb.getWidth() * 3,
                bomb.getHeight() * 3, false);
        goldasteroid = BitmapFactory.decodeResource(getResources(),
                R.drawable.mob_goldastroid);
        goldasteroid = Bitmap.createScaledBitmap(goldasteroid,
                goldasteroid.getWidth() * 3, goldasteroid.getHeight() * 3,
                false);
        mushroom = BitmapFactory.decodeResource(getResources(),
                R.drawable.item_mushroom);
        mushroom = Bitmap.createScaledBitmap(mushroom, mushroom.getWidth() * 4,
                mushroom.getHeight() * 4, false);
        coin = Bitmap.createScaledBitmap(coin, coin.getWidth() * 2,
                coin.getHeight() * 2, false);
        drug = BitmapFactory
                .decodeResource(getResources(), R.drawable.item_not);
        drug = Bitmap.createScaledBitmap(drug, drug.getWidth() * 4,
                drug.getHeight() * 4, false);
        rocket = BitmapFactory.decodeResource(getResources(),
                R.drawable.item_rocket);
        rocket = Bitmap.createScaledBitmap(rocket, rocket.getWidth() * 4,
                rocket.getHeight() * 4, false);
        electricExplosion = FitAllDevices(electricExplosion);
        alien = FitAllDevices(alien);
        normalexplison = FitAllDevices(normalexplison);
        explosionheart = FitAllDevices(explosionheart);
        mushroom = FitAllDevices(mushroom);
        drug = FitAllDevices(drug);
        rocket = FitAllDevices(rocket);
        bomb = FitAllDevices(bomb);
        asteroid = FitAllDevices(asteroid);
        goldasteroid = FitAllDevices(goldasteroid);
        sprite = FitAllDevices(sprite);
        heart = FitAllDevices(heart);
        player = new Spicy(sprite, heart);
        hit = soundPool.load(c, R.raw.hit, 1);
        pass = soundPool.load(c, R.raw.win, 1);
        //remix = soundPool.load(c, R.raw.remix, 1);
        destroy = soundPool.load(c, R.raw.destroy, 1);
        aliensound = soundPool.load(c, R.raw.alien, 1);
        alienexpload = soundPool.load(c, R.raw.explosion2, 1);
        //particlesound = soundPool.load(c, R.raw.particle, 1);
        bigexplosion=soundPool.load(c, R.raw.explosion, 1);
        gameLoopThread = new GameLoopThread(this);
        this.requestFocus();
        this.setFocusableInTouchMode(true);
        holder = getHolder();
        holder.addCallback(new SurfaceHolder.Callback() {

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                boolean retry = true;
                gameLoopThread.setRunning(false);
                while (retry) {
                    try {
                        gameLoopThread.join();
                        retry = false;
                    } catch (InterruptedException e) {
                    }
                }
            }

              public void surfaceCreated(SurfaceHolder holder) {     
                  if (gameLoopThread.getState()==Thread.State.TERMINATED) { 
                        gameLoopThread = new GameLoopThread(g);
                  }
                  gameLoopThread.setRunning(true);
                  gameLoopThread.start();
          }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format,
                    int width, int height) {
            }
        });
    }

我做错了吗?我的应用程序只是一个从一侧出来然后转到另一侧的位图小游戏,起初它开始很慢,但是你玩的越多,位图就越多,如果有很多位图,我会理解这个数量的内存屏幕,但是当屏幕上只有播放器和背景时需要60mb!

我很乐意通过电子邮件向任何人发送该应用程序,以亲自尝试看看它是多么简单,但它却无缘无故地需要多少内存..


编辑:

我提到该应用程序会随着时间的推移占用更多内存,我知道这与我创建新位图并移动它们然后删除它们的事实有关,并且你玩得越多,创建的位图就越多,但是我尽力在之后立即删除它们并回收它们以确保它们被垃圾收集器收集。我想知道如何才能最大限度地减少使用量并仍然让我的游戏可玩:

这就是我“生成”小怪的方式(小怪获取我在 onLoad 上加载的位图):

private void spawnMob() {
    if (timer2 == 0) {
        int mobtype = randInt(1, 40);
        switch (mobtype) {
        case 1:
        case 2:
        case 3:
        case 4:
        case 5:
        case 6:
        case 7:
        case 8:
        case 9:
        case 10:
        case 11:
        case 12:
        case 13:
        case 14:
        case 15:
            Mob m = new Mob(alien, MobEffect.comeback, 1);
            Point p = new Point(0, 0);
            p.y = randInt(0, screenh - alien.getHeight());
            p.x = screenw + alien.getWidth();
            spawned.put(p, m);
            break;

        case 16:
        case 17:
        case 18:
        case 19:
        case 20:
        case 21:
        case 22:
        case 23:
        case 24:
        case 25:
        case 26:
        case 27:
        case 28:
        case 29:
        case 30:
        case 31:
        case 32:
            Mob m2 = new Mob(asteroid, MobEffect.dissapire, 0);
            Point p2 = new Point(0, 0);
            p2.y = randInt(0, screenh - asteroid.getHeight());
            p2.x = screenw + asteroid.getWidth();
            spawned.put(p2, m2);
            break;
        case 33:
        case 34:
        case 35:
        case 36:
        case 37:
        case 38:
        case 39:
            Mob m3 = new Mob(goldasteroid, MobEffect.dissapire, 1);
            Point p3 = new Point(0, 0);
            p3.y = randInt(0, screenh - goldasteroid.getHeight());
            p3.x = screenw + goldasteroid.getWidth();
            spawned.put(p3, m3);
        case 40:
            if (counter > 3) {
                Mob m4 = new Mob(bomb, MobEffect.expload, 1, 5, false,
                        false);
                Point p4 = new Point(0, 0);
                p4.y = randInt(0, screenh - bomb.getHeight());
                p4.x = screenw + bomb.getWidth();
                spawned.put(p4, m4);
            } else {
                Mob m5 = new Mob(asteroid, MobEffect.dissapire, 0);
                Point p5 = new Point(0, 0);
                p5.y = randInt(0, screenh - asteroid.getHeight());
                p5.x = screenw + asteroid.getWidth();
                spawned.put(p5, m5);
            }
            break;
        }
        if (rocketspeed >= 10) {
            timer2 = randInt(1, 8);
        } else {
            if (bleedoff != 35) {
                if (bleedoff > 1)
                    timer2 = randInt(35 / (int) bleedoff,
                            150 / (int) bleedoff);
                else
                    timer2 = randInt(35, 150);
            } else
                timer2 = randInt(1, 10);
        }
    } else {

        timer2--;

    }
}

在 onDraw 上,我确保移除小怪,这样它们不在屏幕上时就不会占用额外的内存:

    Iterator<Map.Entry<Point, Mob>> spawnedEntry = spawned
                .entrySet().iterator();
        while (spawnedEntry.hasNext()) {
            Map.Entry<Point, Mob> entry = spawnedEntry.next();
            if(entry.getValue().destroycomplete||entry.getValue().dissapired){
                spawnedEntry.remove();
            }
            else
            entry.getValue().draw(canvas, entry.getKey());

同样适用于 Mob 类:

if(!MobEffectstarted)
        if(!destroycomplete)
    c.drawBitmap(mob,p.x,p.y, null);
        else
            mob.recycle();

编辑 2:

这是eclipse的记忆工具告诉我的:
它肯定是位图。


【问题讨论】:

  • 如果您的应用随着时间的推移占用更多 RAM,我不认为问题出在您的 onload 上。我的第一个猜测是,在你的游戏逻辑的某个地方,你正在重新创建一个对象并放弃旧实例,而重用旧对象对你来说会更好。
  • 沿着同一条线,请记住,仅仅因为您的图像在文件形式(jpg、png 等)中相对较小,并不能转化为它们在 RAM 中的样子。在文件形式中,图像被压缩以节省空间。一旦你对它们进行解码,它们就会被放大为像素的原始表示,这要大得多,尤其是在使用 ARGB8888 格式的情况下。再加上放大图像(上面的代码就是这样做的),你的 RAM 使用率就会大幅上升。
  • @CoreyOgburn 好吧,我的问题分为两个,第一个是应用程序仅在启动时才占用大量内存。我想相信 5mb 文件的 60mb 是不正常的。第二个问题是我的应用程序随着时间的推移需要越来越多的 Ram,我也不明白,因为我确保回收并删除我不显示的每个位图。
  • @LarrySchiefer 我明白了,那么我该如何减少它,我见过看起来好多了并且占用更少内存的应用程序,我认为缩放不应该影响内存,因为它是相同的原始刚刚放大的像素表示..
  • 它可能使用 mip 映射进行缩放。这将创建精灵的多个实例。每种尺寸一个。

标签: java android bitmap


【解决方案1】:

为什么我的应用程序在启动时会占用这么多 RAM?

您的原始图像文件已压缩。您正在解压缩它们并将完整的位图存储在内存中。 (大约 rowBytes * 字节高度;find out bitmap size) 你也在重新调整它们的大小;例如,背景被缩放到原始大小的五倍。由于图像具有二维,因此内存消耗呈二次方。

示例:尺寸为 500x500 的图像;它在行之间分配 4 个字节(32 位模式)。 这导致位图大小为 500x4 x 500 = 1MB。

如果将此图像缩放 2 倍,则分配的内存不是两倍,而是:1000x4 x 1000 = 4MB

我能做什么?

  • 如果您不需要 Alpha 通道,您可能希望减少位图的位深度。例如使用Bitmap.Config.RGB_565 解码。
  • 通过指定样本大小,不要加载比需要更大的位图。 (更多信息请点击此处:Loading Large bitmaps
  • 确保尽可能多地重复使用位图。
  • 仅在需要时加载位图。您是否同时需要两种背景?
  • 您的背景似乎只是一个渐变;您可以使用LinearGradient 类来绘制它,省去巨大的背景图像。 (这同样适用于您的方块,如果它们不只是占位符的话。)

关于代码中的内存泄漏:

  • 确保不要不断地制造新的生物。将“死”的生物保存在单独的列表中,并在需要时将它们放回演员列表中。仅当您真正用完“待处理”的生物时才创建新的。
  • 不要在经常调用的方法中创建对象。 (例如:任何 draw() 方法。)

如果您在预Honeycomb 上进行测试,则需要回收任何已分配的位图,否则它们可能不会从本机内存中释放。

【讨论】:

  • 我已经编辑了这个问题,以便您了解为什么我的应用会随着时间的推移使用更多内存。我部分理解您的回答,我知道位图存储图像的所有数据并对其进行解压缩,但我不明白它与重新调整大小有什么关系。如果我说位图 = createScaledBitmap(bitmap);不应该用新位图替换旧位图,这意味着重新缩放不应该影响内存吗?您也可以提供解决此问题的方法吗?我可以做任何不会过多破坏游戏质量但修复内存的事情吗?
猜你喜欢
  • 2014-03-04
  • 2012-08-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-28
  • 2021-07-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多