Open irwinb opened 13 years ago
Look deeper: init->loadTexture, and you are not the first, who thinks so.
Ok, thank you for the clarification.
I took the recycle and "bmp = null" line out and am running into the OOM problem again. What else could be calling it if loadTexture calls recycle on bmp? Is it required to nullify the bitmap for it to properly be recycled?
The bmp should be collected along with the parent CCTexture2D. Are you using the CCTextureCache to manage your textures?
As long as you don't hold a reference to the CCTexture2D object, then it should be GC'd along with all of its members.
Yes, i am using CCTextureCache to manage my textures. Maybe the bitmap doesn't get released soon enough after it is loaded and before the next one begins loading? If I set the bitmap to null in the method, the problem hasn't shown up.
Well then I'm a bit stumped from your description alone... Can you post a code snippet so that I can walk through the actual operations you're performing. Maybe there is something that's not apparent at first glance?
Actually I just got this same exception when I tried to run the Sprites Test.
E/AndroidRuntime( 3836): FATAL EXCEPTION: GLThread 20
E/AndroidRuntime( 3836): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
E/AndroidRuntime( 3836): at android.graphics.Bitmap.nativeCreate(Native Method)
E/AndroidRuntime( 3836): at android.graphics.Bitmap.createBitmap(Bitmap.java:468)
E/AndroidRuntime( 3836): at org.cocos2d.opengl.CCTexture2D.initWithImage(CCTexture2D.java:255)
E/AndroidRuntime( 3836): at org.cocos2d.grid.CCGridBase$1.load(CCGridBase.java:134)
E/AndroidRuntime( 3836): at org.cocos2d.opengl.GLResourceHelper$2.perform(GLResourceHelper.java:94)
E/AndroidRuntime( 3836): at org.cocos2d.opengl.GLResourceHelper.update(GLResourceHelper.java:127)
E/AndroidRuntime( 3836): at org.cocos2d.nodes.CCDirector.drawCCScene(CCDirector.java:700)
E/AndroidRuntime( 3836): at org.cocos2d.nodes.CCDirector.onDrawFrame(CCDirector.java:665)
E/AndroidRuntime( 3836): at org.cocos2d.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1245)
E/AndroidRuntime( 3836): at org.cocos2d.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1056)
My first guess is that the loadTexture(gl) method in CCTexture2D.init (where the recycling happens) gets queued and may get executed after the next bitmap is loaded into memory.
private static CCTexture2D createTextureFromFilePath(final String path) {
CCTexture2D tex = new CCTexture2D();
tex.setLoader(new GLResourceHelper.GLResourceLoader() {
@Override
public void load(Resource res) {
try {
InputStream is = CCDirector.sharedDirector().getActivity().getAssets().open(path);
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inPreferredConfig = ((CCTexture2D)res).pixelFormat();
Bitmap bmp = BitmapFactory.decodeStream(is, null, opts);
is.close();
((CCTexture2D)res).initWithImage(bmp);
//------Additions here---------
bmp.recycle();
bmp = null;
//-----------------------------
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
return tex;
}
you should load your textures in GL thread (schedule or perform in GLResourceHelper).
Does that solve the issue of the resources not being released?
I always load textures in the GL thread, as per Issue 20.
may be you can provide some demo for your problem?
Well, I can recreate this problem over and over by just switching between the tests. It doesn't always happen at the same point, but usually after I switch between 8 or 9 different tests, I will try to load another one and get the same exception:
E/AndroidRuntime( 2075): FATAL EXCEPTION: GLThread 19
E/AndroidRuntime( 2075): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
E/AndroidRuntime( 2075): at android.graphics.Bitmap.nativeCreate(Native Method)
E/AndroidRuntime( 2075): at android.graphics.Bitmap.createBitmap(Bitmap.java:477)
E/AndroidRuntime( 2075): at org.cocos2d.opengl.CCTexture2D.initWithImage(CCTexture2D.java:264)
E/AndroidRuntime( 2075): at org.cocos2d.grid.CCGridBase$1.load(CCGridBase.java:134)
E/AndroidRuntime( 2075): at org.cocos2d.opengl.GLResourceHelper$2.perform(GLResourceHelper.java:94)
E/AndroidRuntime( 2075): at org.cocos2d.opengl.GLResourceHelper.update(GLResourceHelper.java:128)
E/AndroidRuntime( 2075): at org.cocos2d.nodes.CCDirector.drawCCScene(CCDirector.java:700)
E/AndroidRuntime( 2075): at org.cocos2d.nodes.CCDirector.onDrawFrame(CCDirector.java:665)
E/AndroidRuntime( 2075): at org.cocos2d.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1245)
E/AndroidRuntime( 2075): at org.cocos2d.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1056)
I also get a heap dump, if you can make sense of this, here it is too:
I/DEBUG ( 70): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
I/DEBUG ( 70): Build fingerprint: 'sprint/SPH-P100/SPH-P100:2.3.4/GINGERBREAD/EF17:user/release-keys'
I/DEBUG ( 70): pid: 2075, tid: 2076 >>> org.cocos2d <<<
I/DEBUG ( 70): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 00000000
I/DEBUG ( 70): r0 0005e9f0 r1 00000007 r2 442e1140 r3 00000024
I/DEBUG ( 70): r4 00000000 r5 000f5c70 r6 ad3b8920 r7 406df260
I/DEBUG ( 70): r8 442e1168 r9 45cd9f8c 10 00000000 fp 801a5374
I/DEBUG ( 70): ip 00000000 sp 100ffa88 lr ad32c0d4 pc ad32c0e4 cpsr 60000010
I/DEBUG ( 70): d0 0063006400350032 d1 002e006400320000
I/DEBUG ( 70): d2 0064006900720036 d3 0047004300430000
I/DEBUG ( 70): d4 0000000000388540 d5 0444000000008000
I/DEBUG ( 70): d6 0000000000000000 d7 0000000000000000
I/DEBUG ( 70): d8 0000000000000000 d9 0000000000000000
I/DEBUG ( 70): d10 0000000000000000 d11 0000000000000000
I/DEBUG ( 70): d12 0000000000000000 d13 0000000000000000
I/DEBUG ( 70): d14 0000000000000000 d15 0000000000000000
I/DEBUG ( 70): d16 000000004077a180 d17 3ff0000000000000
I/DEBUG ( 70): d18 42eccefa43de3400 d19 3fbc71c71c71c71c
I/DEBUG ( 70): d20 4008000000000000 d21 3fd99a27ad32ddf5
I/DEBUG ( 70): d22 3fd24998d6307188 d23 3fcc7288e957b53b
I/DEBUG ( 70): d24 3fc74721cad6b0ed d25 3fc39a09d078c69f
I/DEBUG ( 70): d26 0000000000000000 d27 0000000000000000
I/DEBUG ( 70): d28 0000000000000000 d29 0000000000000000
I/DEBUG ( 70): d30 0000000000000000 d31 0000000000000000
I/DEBUG ( 70): scr 80000012
I/DEBUG ( 70):
I/DEBUG ( 70): #00 pc 0002c0e4 /system/lib/libandroid_runtime.so
I/DEBUG ( 70): #01 pc 00032150 /system/lib/libandroid_runtime.so
I/DEBUG ( 70): #02 pc 00017e74 /system/lib/libdvm.so
I/DEBUG ( 70):
I/DEBUG ( 70): code around pc:
I/DEBUG ( 70): ad32c0c4 e1a0e00f e59cf1a4 e3010f03 ebffe6a1
I/DEBUG ( 70): ad32c0d4 e5962028 e1a04000 e1a0a000 e59f02c0
I/DEBUG ( 70): ad32c0e4 e5d43000 e08f6000 e3530000 0a000019
I/DEBUG ( 70): ad32c0f4 e1a00003 e3a01000 ea000000 e7da0001
I/DEBUG ( 70): ad32c104 e7d6c001 e2811001 e27ce001 33a0e000
I/DEBUG ( 70):
I/DEBUG ( 70): code around lr:
I/DEBUG ( 70): ad32c0b4 e3a03001 e1a00005 e1a01007 e595c000
I/DEBUG ( 70): ad32c0c4 e1a0e00f e59cf1a4 e3010f03 ebffe6a1
I/DEBUG ( 70): ad32c0d4 e5962028 e1a04000 e1a0a000 e59f02c0
I/DEBUG ( 70): ad32c0e4 e5d43000 e08f6000 e3530000 0a000019
I/DEBUG ( 70): ad32c0f4 e1a00003 e3a01000 ea000000 e7da0001
I/DEBUG ( 70):
I/DEBUG ( 70): stack:
I/DEBUG ( 70): 100ffa48 40791d70
I/DEBUG ( 70): 100ffa4c 45cd9f48
I/DEBUG ( 70): 100ffa50 19475fdc
I/DEBUG ( 70): 100ffa54 00000001
I/DEBUG ( 70): 100ffa58 003686b8
I/DEBUG ( 70): 100ffa5c 0000a000
I/DEBUG ( 70): 100ffa60 406df260
I/DEBUG ( 70): 100ffa64 442e112c
I/DEBUG ( 70): 100ffa68 0011eb90
I/DEBUG ( 70): 100ffa6c 8014920f /system/lib/libdvm.so
I/DEBUG ( 70): 100ffa70 ad3b8920
I/DEBUG ( 70): 100ffa74 000f5c70
I/DEBUG ( 70): 100ffa78 ad3b8920
I/DEBUG ( 70): 100ffa7c 406df260
I/DEBUG ( 70): 100ffa80 df002777
I/DEBUG ( 70): 100ffa84 e3a070ad
I/DEBUG ( 70): #00 100ffa88 ad3b8920
I/DEBUG ( 70): 100ffa8c 00000001
I/DEBUG ( 70): 100ffa90 000f5c70
I/DEBUG ( 70): 100ffa94 406d2578
I/DEBUG ( 70): 100ffa98 00000000
I/DEBUG ( 70): 100ffa9c 45cd9f8c
I/DEBUG ( 70): 100ffaa0 000f5c70
I/DEBUG ( 70): 100ffaa4 ad332154 /system/lib/libandroid_runtime.so
I/DEBUG ( 70): #01 100ffaa8 100ffae8
I/DEBUG ( 70): 100ffaac 00000000
I/DEBUG ( 70): 100ffab0 100ffb70
I/DEBUG ( 70): 100ffab4 45cd9f98
I/DEBUG ( 70): 100ffab8 100ffac4
I/DEBUG ( 70): 100ffabc 80117e78 /system/lib/libdvm.so
Here is a stack trace of all the running threads on the org.cocos2d process when it crashes. This was dumped to data/anr/traces.txt
----- pid 3167 at 2011-08-31 20:39:15 -----
Cmd line: org.cocos2d
DALVIK THREADS:
(mutexes: tll=0 tsl=0 tscl=0 ghl=0 hwl=0 hwll=0)
"main" prio=5 tid=1 NATIVE
| group="main" sCount=1 dsCount=0 obj=0x4001f190 self=0xce38
| sysTid=3167 nice=0 sched=0/0 cgrp=default handle=-1345006496
at android.os.MessageQueue.nativePollOnce(Native Method)
at android.os.MessageQueue.next(MessageQueue.java:119)
at android.os.Looper.loop(Looper.java:117)
at android.app.ActivityThread.main(ActivityThread.java:3687)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:507)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:842)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600)
at dalvik.system.NativeStart.main(Native Method)
"GLThread 20" prio=5 tid=9 NATIVE
| group="main" sCount=1 dsCount=0 obj=0x406a00d8 self=0x580400
| sysTid=3190 nice=0 sched=0/0 cgrp=default handle=5356056
at com.google.android.gles_jni.EGLImpl.eglSwapBuffers(Native Method)
at org.cocos2d.opengl.GLSurfaceView$EglHelper.swap(GLSurfaceView.java:978)
at org.cocos2d.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1252)
at org.cocos2d.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1056)
"Binder Thread #3" prio=5 tid=10 NATIVE
| group="main" sCount=1 dsCount=0 obj=0x40776430 self=0x352e50
| sysTid=3181 nice=0 sched=0/0 cgrp=default handle=5956024
at dalvik.system.NativeStart.run(Native Method)
"Binder Thread #2" prio=5 tid=8 NATIVE
| group="main" sCount=1 dsCount=0 obj=0x40522938 self=0x2717b0
| sysTid=3174 nice=0 sched=0/0 cgrp=default handle=2004080
at dalvik.system.NativeStart.run(Native Method)
"Binder Thread #1" prio=5 tid=7 NATIVE
| group="main" sCount=1 dsCount=0 obj=0x40511980 self=0x270dd8
| sysTid=3173 nice=0 sched=0/0 cgrp=default handle=2003608
at dalvik.system.NativeStart.run(Native Method)
"Compiler" daemon prio=5 tid=6 VMWAIT
| group="system" sCount=1 dsCount=0 obj=0x4050d828 self=0x2d2cb0
| sysTid=3172 nice=0 sched=0/0 cgrp=default handle=1513056
at dalvik.system.NativeStart.run(Native Method)
"JDWP" daemon prio=5 tid=5 VMWAIT
| group="system" sCount=1 dsCount=0 obj=0x4050d778 self=0x2d2af0
| sysTid=3171 nice=0 sched=0/0 cgrp=default handle=1174928
at dalvik.system.NativeStart.run(Native Method)
"Signal Catcher" daemon prio=5 tid=4 RUNNABLE
| group="system" sCount=0 dsCount=0 obj=0x4050d6b8 self=0x171528
| sysTid=3170 nice=0 sched=0/0 cgrp=default handle=1174864
at dalvik.system.NativeStart.run(Native Method)
"GC" daemon prio=5 tid=3 VMWAIT
| group="system" sCount=1 dsCount=0 obj=0x4050d610 self=0x1713f0
| sysTid=3169 nice=0 sched=0/0 cgrp=default handle=2002200
at dalvik.system.NativeStart.run(Native Method)
"HeapWorker" daemon prio=5 tid=2 NATIVE
| group="system" sCount=1 dsCount=0 obj=0x4050d558 self=0x11eb90
| sysTid=3168 nice=0 sched=0/0 cgrp=default handle=2673264
at com.google.android.gles_jni.GLImpl.glDeleteFramebuffersOES(Native Method)
at org.cocos2d.grid.CCGrabber.finalize(CCGrabber.java:99)
at dalvik.system.NativeStart.run(Native Method)
----- end 3167 -----
Okay, that last bit is nonsense it seems, so ignore that. I installed the Eclipse Memory Analyzer and keep switching the tests until the application crashed. Then, before clicking close I went to DDMS and click the Dump HPROF button.
The Eclipse Memory Analyzer reported that at the time the WeakHashMap reloadMap inside of GLResourceHelper was holding on to all of its keys and values, none of them were being released. Every single that had called GLResourceHelper.addLoader was being held inside of reloadMap (both res and loader).
So overtime this was adding up and causing the leak.
I don't know why those objects weren't being collected since WeakHashMap is designed explicitly to be garbage collected.
hi , is there a fix for this particular issue ? im getting the same behavior , loading textures using CCtexturecache one after the other raises the OOM exception as it seems that the bitmaps aren't released at the time of the next allocation , but having a timer and allocating the textures with an interval of 100ms for example then im able to load as many textures as i want
@dustinanon ,tnousis have you fixed the issue? how to fix it
In CCTexture2D.java, the method "public void initWithImage(Bitmap)" does not seem to call recycle() on the provided bitmap under all circumstances. I think the OOM issues I have been running into are a result of this. I temporarily fixed this by calling recycle on the bitmap at the end of the routine.