I'm seeing my memory raise by about 100KB/hr and the main area that is
gaining ground is in dalvik/vm/Sync.c in dvmCreateMonitor.
Here is the memory dump top items:
Z 389,628 10,823 36 /system/lib/libdvm.so dvmCreateMonitor dalvik/vm/
Sync.c
Z 262,144 1 262,144 /system/lib/libdvm.so resizeHash dalvik/vm/Hash.c
224,624 1 224,624 /system/lib/libdvm.so allocateAuxStructures dalvik/
vm/DvmDex.c
147,456 1 147,456 /system/lib/libdvm.so allocateAuxStructures dalvik/
vm/DvmDex.c
132,000 10 13,200 /system/lib/libandroid_runtime.so
GraphicsJNI::setJavaPixelRef(_JNIEnv*, SkBitmap*, SkColorTable*, bool)
frameworks/base/core/jni/android/graphics/Graphics.cpp
With the backtrace on #1 being:
4000ae8c /system/lib/libc.so get_backtrace bionic/libc/bionic/
malloc_leak.c 430
4000b114 /system/lib/libc.so leak_calloc bionic/libc/bionic/
malloc_leak.c 809
4000a538 /system/lib/libc.so calloc bionic/libc/bionic/malloc_leak.c
489
6d045a60 /system/lib/libdvm.so dvmCreateMonitor dalvik/vm/Sync.c 182
6d045c1e /system/lib/libdvm.so dvmObjectWait dalvik/vm/Sync.c 978
6d0590a8 /system/lib/libdvm.so Dalvik_java_lang_Object_wait dalvik/vm/
native/java_lang_Object.c 95
6d01319c /system/lib/libdvm.so dalvik_mterp dalvik/vm/mterp/out/
InterpAsm-armv5te.S 9536
6d017be8 /system/lib/libdvm.so dvmMterpStd dalvik/vm/mterp/Mterp.c
101
6d017630 /system/lib/libdvm.so dvmInterpret dalvik/vm/interp/Interp.c
703
6d0529e2 /system/lib/libdvm.so dvmInvokeMethod dalvik/vm/interp/
Stack.c 735
6d059f1a /system/lib/libdvm.so
Dalvik_java_lang_reflect_Method_invokeNative dalvik/vm/native/
java_lang_reflect_Method.c 104
6d01319c /system/lib/libdvm.so dalvik_mterp dalvik/vm/mterp/out/
InterpAsm-armv5te.S 9536
6d017be8 /system/lib/libdvm.so dvmMterpStd dalvik/vm/mterp/Mterp.c
101
6d017630 /system/lib/libdvm.so dvmInterpret dalvik/vm/interp/Interp.c
703
6d052866 /system/lib/libdvm.so dvmCallMethodV dalvik/vm/interp/Stack.c
518
6d052886 /system/lib/libdvm.so dvmCallMethod dalvik/vm/interp/Stack.c
426
6d04783c /system/lib/libdvm.so interpThreadStart dalvik/vm/Thread.c
1495
40011c60 /system/lib/libc.so __thread_entry bionic/libc/bionic/
pthread.c 190
400117d4 /system/lib/libc.so pthread_create bionic/libc/bionic/
pthread.c 330
10,823 (from the overall list) is the number of allocations from there
that haven't been freed (I think), which never seems to lower. Looking
at the code, this seems to make sense:
static void releaseMonitor(Monitor* mon) {
// TODO
}
/*
* Free the monitor list. Only used when shutting the VM down.
*/
void dvmFreeMonitorList(void)
{
...
Any input on this would be nice. releaseMonitor doesn't seem like its
would be too hard to implement (or at least partially, enough to free
the memory I care about and fix the list accordingly to avoid a
segfault), but I'm guessing that if it was as easy as 10 lines, it
would have been written in the first place.
Yes. This has been fixed for a future release as part of the work on
the GC (chiefly by asserting that, if the object is being swept by the
GC, the only thing that could still be holding onto it is wayward JNI
code).
The monitor structures should only be being created for objects that
are wait()ed on or are synchronized and experience contention. We
usually see at most a couple dozen of these. If you're frequently
creating temporary objects and using wait/notify on them, it would
explain the behavior you're seeing.
I suppose I just need to wait for some patches to come over to AOSP?
Probably not worth the risk of my breaking a case I'm not aware of to
fix it, but it seems like that monitor list just needs to be doubly
linked and then it could clean up the list and free.
e.g.
//TODO: unlink from the monitor list (would require a lock)
// (might not -- the GC suspension may be enough)
..
memset(mon, 0, sizeof (*mon));
If this is already setting the object to empty, then it can't be too
unsafe to just free it.
I'm sure its more complex than that if it wasn't in already though :)
First off, nice work spotting the monitor leak.
There are a number of ways to work around the problem. If you are not
afraid of making a VM change, the one way to proceed would be to add a
routine which walks the monitor list identifying which monitors belong
to unmarked objects. Such objects are then unlinked from the monitor
list and their storage is freed. You may want to look at
dvmGcDetachDeadInternedStrings which does a similar thing for string
objects.
If you do not want to change the VM, you could consider using one of
the java.util.concurrent objects to synchronize on instead of an
object lock. Many of the primitives in java.util.concurrent use park
and unpark to implement locks and these primitives are, in turn,
implemented on top of a thread-private object lock. Assuming that
your thread lifetimes are longer than your object lifetimes, this
would greatly attenuate the observed leak.
A variation on the last method is to simply not wait on objects with
short lifetimes. Depending on your application, this may not be an
option.