OutOfMemoryError but seems that the heap has enough free mem

Started by kiffa, August 04, 2012, 02:28:51 PM

Previous topic - Next topic

kiffa

First i have a animated-obj(.bones) with 1566 frames(24frames/s) animations, with this obj, my code ran fine. And the heap-free-mem was about 11M which was reported by DDMS.

And then i added some more animations to this obj(1566 -> 1755),  with this one, OutOfMemoryError happened.

I don't think  1755 - 1566 = 189 frames-bones-animation will eat 11M of memory.

When i run my app, the heap changed like this:
  first, there was about 3 - 4 M free memory  ---- maybe loading obj...
  then,  the free memory suddenly jumped to 11M.
  then,  OutOfMemoryError happened:

05-13 20:08:31.500: E/AndroidRuntime(4704): java.lang.OutOfMemoryError
05-13 20:08:31.500: E/AndroidRuntime(4704):    at org.apache.harmony.luni.platform.OSMemory.malloc(Native Method)
05-13 20:08:31.500: E/AndroidRuntime(4704):    at org.apache.harmony.luni.platform.PlatformAddressFactory.alloc(PlatformAddressFactory.java:129)
05-13 20:08:31.500: E/AndroidRuntime(4704):    at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:65)
05-13 20:08:31.500: E/AndroidRuntime(4704):    at java.nio.ReadWriteDirectByteBuffer.<init>(ReadWriteDirectByteBuffer.java:48)
05-13 20:08:31.500: E/AndroidRuntime(4704):    at java.nio.BufferFactory.newDirectByteBuffer(BufferFactory.java:93)
05-13 20:08:31.500: E/AndroidRuntime(4704):    at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:65)
05-13 20:08:31.500: E/AndroidRuntime(4704):    at com.threed.jpct.GLRenderer.convertTexture(GLRenderer.java:800)
05-13 20:08:31.500: E/AndroidRuntime(4704):    at com.threed.jpct.GLRenderer.setTextures(GLRenderer.java:2270)
05-13 20:08:31.500: E/AndroidRuntime(4704):    at com.threed.jpct.GLRenderer.drawVertexArray(GLRenderer.java:2195)
05-13 20:08:31.500: E/AndroidRuntime(4704):    at com.threed.jpct.World.draw(World.java:1307)
05-13 20:08:31.500: E/AndroidRuntime(4704):    at com.threed.jpct.World.draw(World.java:1074)
05-13 20:08:31.500: E/AndroidRuntime(4704):    at com.zwenyu.scene.Main3DScene.renderWorld(Main3DScene.java:76)
05-13 20:08:31.500: E/AndroidRuntime(4704):    at com.zwenyu.scene.SceneBegin.onDrawFrame(SceneBegin.java:120)
05-13 20:08:31.500: E/AndroidRuntime(4704):    at com.zwenyu.view.Main3DSurfaceView$MyRenderer.onDrawFrame(Main3DSurfaceView.java:376)
05-13 20:08:31.500: E/AndroidRuntime(4704):    at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1363)
05-13 20:08:31.500: E/AndroidRuntime(4704):    at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1118)




EgonOlsen

If it says OOM, then there is an OOM....which means, you are using too much memory. It's as simple as that. Keep in mind that only newer Android version add native memory to the used heap, but all versions take it into account. That means if you have 16mb of VM memory and are using 6mb of that memory for some large arrays and a native buffer of 5 memory, you'll have 16-6-5=5mb left. Newer VMs will report this value while older VMs will report 10mb as free memory...but that isn't true. You still have only 5mb left.

kiffa

Thanks, Egon!

Another questions:

1, You mentioned "newer" vm, my android version is 2.3.3, is this a "newer" vm? And my ADT is the latest version.

2, Does native-buffer have its own limit?  Which i mean is that: if there are 16M free-mems left in vm, then could the max mem of native-buffer be 16M or only be some fixed limit which is smaller than 16M?

3, How many java-mem does world.draw use? How many native-mem does world.draw use? How to roughly estimate this?

4, I read an article from web, which says that: If a block of mem was alloced by java, it will not be reused by native even after this java-mem-block was recycled by GC(because of the mem-caching-mechanism of android).  It not mentioned the version of android.
    I don't know if this option is correct. But if it's correct, some of my doubts will be explained.

5, My codes with some doubts:

  // new MyRenderer, when running this, free-mem is 3-4M. 
      initWorld();
      initTexture();
      initLight();
      loadModel();
      initCamera();
      initAllAnimations();

// onSurfaceCreated
      //do nothing

// onSurfaceChanged

      initFrameBuffer(gl, width, height);
      initFpsShown();
      mFpsShown.fpsStart();

      Main3DScene scene = SceneManager.getInstance().buildScene(SceneManager.SCENE_BEGIN, mWorld, mFrameBuffer, mDog);
      SceneManager.getInstance().setCurrentScene(scene);
     
      try
      {
        System.gc();                             
        Log.d("jPCT-AE", "gc");

  // free-mem will jumped from 3M to 12M, which means there are some mem was recycled by GC. So in this point, there was a 12-3=9M mem-increase.
        Thread.sleep(10000);    // waitting for GC     
      }
      catch (InterruptedException e)
      {
        e.printStackTrace();
      }
   

// onDrawFrame
   
      SceneManager.getInstance().getCurrentScene().onDrawFrame(gl);   // will call world.renderScene and world.draw, and the OOM happened here(world.draw).
      mFpsShown.drawFps();
      mFrameBuffer.display();
      mFpsShown.calacFps();
     



My doubt is :
1, When new MyRenderer, DDMS says the free-mem is 3M.
2, When  onSurfaceChanged, there is a mem-increase of 9M by GC. So, there is at least 9M free-mem left.
3, When onDrawFrame, OOM happened, but i don't think 9M is not enough for my situation. Could this happen:
       There are enough mems left in heap, but some of them can't be alloced for native-buffer, so the mem-usage of native is not enough, so the OOM will happen.

EgonOlsen

Quote from: kiffa on August 05, 2012, 06:30:15 AM
1, You mentioned "newer" vm, my android version is 2.3.3, is this a "newer" vm? And my ADT is the latest version.
No, it's rather old. 4.1 is the newest one. 2.x doesn't add native memory to the used memory output.
Quote from: kiffa on August 05, 2012, 06:30:15 AM
2, Does native-buffer have its own limit?  Which i mean is that: if there are 16M free-mems left in vm, then could the max mem of native-buffer be 16M or only be some fixed limit which is smaller than 16M?
No. That's what i tried to explain in the post above. If you have a limit of 16mb (or 24 or 32 or 48 or 64...), this limit affects java and native memory to the same degree, i.e. you could have 16mb used by java or 16mb used by native buffer or 8 by java and 8 by native buffers, but not 8 by java and 10 by native, because that would be 18mb, which is above 16 and too much. The only difference between VM versions is, that older ones like yours doesn't print out the memory that the native buffer use.
Quote from: kiffa on August 05, 2012, 06:30:15 AM
3, How many java-mem does world.draw use? How many native-mem does world.draw use? How to roughly estimate this?
For textures, i posted this multiple times before. I don't want to repeat it again... ;) For geometry, it highly depends on the data. Very roughly <vertex count>*3*4*2+<vertex count>*2*4*<texture stages used>
Quote from: kiffa on August 05, 2012, 06:30:15 AM
4, I read an article from web, which says that: If a block of mem was alloced by java, it will not be reused by native even after this java-mem-block was recycled by GC(because of the mem-caching-mechanism of android).  It not mentioned the version of android.
    I don't know if this option is correct. But if it's correct, some of my doubts will be explained.
I think that this means is that the VM memory doesn't get freed once it has been allocated. That's true for now.

How are you loading your models? If you are using obj or 3ds, try to use serialized objects instead. This will reduce memory usage at load time.