While using Android Profiler to find memory leaks in an app running on Android 11 physical device, I noticed that the app holds memory in native heap even when it's in the background i.e. when no Activity or Service is running (cached state). This was tested with both debug and release builds of the app. It's not due to bitmaps. Neither the app uses JNI code, except that loaded by the framework.
This is what dumpsys meminfo <PID>
shows:
...
Pss Private Private SwapPss Rss Heap Heap Heap
Total Dirty Clean Dirty Total Size Alloc Free
------ ------ ------ ------ ------ ------ ------ ------
Native Heap 80769 80708 0 58 81384 207724 22890 180724
Dalvik Heap 7093 7004 0 41 7684 14178 5986 8192
...
TOTAL 128567 117212 2676 188 128567 221902 28876 188916
...
And this is what I found in /proc/<PID>/smaps
:
...
730 [anon:dalvik-/system/framework/boot-telephony-common.art]
962 /system/framework/framework-res.apk
1159 /system/framework/framework.jar
1349 [anon:dalvik-/apex/com.android.art/javalib/boot.art]
1992 /memfd:jit-cache (deleted)
2527 [anon:dalvik-zygote space]
3091 [anon:dalvik-LinearAlloc]
3924 [anon:dalvik-main space (region space)]
4048 /dev/kgsl-3d0
5965 [anon:dalvik-/system/framework/boot-framework.art]
9896 [anon:dalvik-classes.dex extracted in memory from /data/app/.../base.apk]
80629 [anon:scudo:primary]
128169 kB
Note the 80 MB PSS: Native Heap 80769
and 80629 [anon:scudo:primary]
. This is more than 60% (and sometimes more than 70%) of the total held PSS. While for other apps running on the device the average is 25%.
So I did app startup profiling using Perfetto native heap profiler:

And it shows memory allocations mainly for /memfd:jit-cache (deleted)
(90%), due to String.matches
calls. Nothing else is recorded.
Malloc Debug (am dumpheap -n
) also dumps only around 30 MB of memory.
A forced garbage collection makes no difference. I have also gone through the related questions like this and this but they are not helpful.
I also checked scudo documentation and the source code but could not find any helpful hints.
How to debug this possible leak in native memory?