性能问题分析方法(1) --- RAM
性能问题分析方法(1) — RAM
这里主要是为了让新的同学可以快速的了解如何处理相关性能问题,性能问题繁琐,难度不大,一般建议给到新人处理,此处是主要说的是方法和案例的分析思路。
1. RAM内存占用导致的问题
一般越是大的内存的手机这个问题出现概率越小,越是低RAM的手机越要做的极致。
怎么确定这个就是内存导致的问题呢:
1)先用adb shell top
(或者adb shell dumpsys cpuinfo
其实是差不多的,只是dumpsys cpuinfo
默认不是实时更新,我们也调整过让其更新)
=> 查看kswapd0、exe_cq、lmkd
进程CPU占用是否比较高,如果比较高一般都是内存压力导致的卡顿问题,主要优化方向可以从缓解内存压力方向着手
//我们一般看的是这个
11% 133/kswapd0: 0% user + 11% kernel
//exc_cq这个是mtk特有的,跟mtk的cq 功能有一定关系CONFIG_MTK_EMMC_CQ_SUPPORT
19% 295/exe_cq/0: 0% user + 19% kernel
//这个在打开system/core的lmkd才会出现,类似psi会导致这个进程的cpu占用较高
lmkd
- 使用
adb shell dumpsys -t 100 meminfo
导出手机的基本memory的概况,类似输出是下面的内容
Total PSS by OOM adjustment:
192,106K: Native
28,928K: surfaceflinger (pid 607)
…
161,042K: System
161,042K: system (pid 1331)
131,415K: Persistent
91,176K: com.android.systemui (pid 1646 / activities)
25,890K: com.android.phone (pid 2473)
…
Total RAM: 889,176K (status critical)
Free RAM: 160,668K ( 0K cached pss + 127,568K cached kernel + 33,100K free)
Used RAM: 995,795K ( 847,915K used pss + 147,880K kernel)
Lost RAM: 42,742K
ZRAM: 170,564K physical used for 515,240K in swap ( 786,428K total swap)
我们一般先看进程memory用量比较大的,还有最后的Free RAM和ZRAM
。
先介绍一下Free RAM:
Free RAM: 160,668K ( 0K cached pss + 127,568K cached kernel + 33,100K free)
=>如果cached pss
为0,那么代表手机进入低内存(event log
会出现am_low_memory
,这个就不用再次猜测,内存是主因);
=> cached kernel
这个属于kernel的缓存,一般系统kill掉进程后很大一部分会掉落到cached kernel
里面去,cached kernel
实际主要是active file + inacive file
的page 量,shrink 是顺序的回收流程, 如果前面已经shrink 操作如LMK 已经回收到memory 就不会走到file system 这边进行swap or active file 转 inactive , inactive switch 操作.
(之前也遇到这个较大出现的问题项目,cached kernel
可以到1.7G(该项目total ram是4G),修改方法就是调整lmk的策略,让其有机会跑到file system
的回收。不过注意如果这个值很小也可能是一个问题,代表file system
根本没有回收空间,内存压力大的时候不能从这里取得内存)
=> free这个小内存手机一般都比较小,这个在节点/proc/meminfo
读出相应的值,如MemFree: 83044 kB
,是系统真正free的memory。
如果出现上面问题该如何继续下去?
=> 首先我们看是native的process还是java的process,这2种调试方法不太一样
2. Native process的内存调试方法
1、使用adb shell dumpsys -t 100 meminfo +pid
查看一下该进程的内存用量分布,如surfaceflinger优化前内存用量信息如下,surfacefinger内存用量有40多M,其中大部分增量在native heap(对比正常的monkey前)
Applications Memory Usage (in Kilobytes):
Uptime: 197500108 Realtime: 199595562
Pss Private Private SwapPss Heap Heap Heap
Total Dirty Clean Dirty Size Alloc Free
------ ------ ------ ------ ------ ------ ------
Native Heap 11880 11880 0 12860 0 0 0
Dalvik Heap 0 0 0 0 0 0 0
Stack 16 16 0 40
Ashmem 44 0 0 0
Gfx dev 2744 2744 0 0
Other dev 8 0 8 0
.so mmap 1277 272 744 1160
Other mmap 17 8 8 16
EGL mtrack 16836 16836 0 0
GL mtrack 636 636 0 0
Unknown 220 220 0 492
TOTAL 48246 32612 760 14568 0 0 0
2、使用adb shell showmap +pid
导出内存地址里面的映射表,然后排序看一下哪些是异常的,如此处较大的是libc_malloc自己申请的内存,还有就是kgsl-3d0(gpu相关的内存),我们主要看libc_malloc
27136 11880 11880 0 0 0 11880 12860 12860 4 [anon:libc_malloc]
4144 2744 2744 0 0 0 2744 0 0 184 /dev/kgsl-3d0
3、针对libc_malloc
不管是google还是平台上都有提供相应调试方式,此处介绍的是google默认的调试方式
1. $adb root
2. $adb shell setenforce 0
//同时此处加上将/data/local/tmp的权限改成777
adb shell
cd /data/local/
chmod 777 tmp
3. $adb shell setprop libc.debug.malloc.program audioserver
//(如果这个设置不成功,可以adb shell, 进去设置)
4. $adb shell setprop libc.debug.malloc.options "backtrace_enable_on_signal leak_track"
5. $adb shell
6. #ps -Af|grep audioserver
//让进程重启,这样malloc_debug才能生效
7. #kill -9 [halserver_pid]
//此处指令是可以开始监控了native进程audioserver的用量了
8. #kill -45 [new_halserver_pid]
9. 此时你可以去复现问题
//如果问题复现,可以输入这个指令,将在/data/local/tmp目录生成类似改进程堆栈信息的文件
10. #kill -47 [new_halserver_pid]
//(native_heapdump_viewer是在development/scripts里面,这个操作需要复现问题手机的symbols)
11. python native_heapdump_viewer.py --verbose --html backtrace_heap.2225.txt --symbols ***/out/target/product/***/symbols > backtrace_heap.html
4、用chrome打开相应的html文件,此处可以发现SurfaceFlinger.cpp:3476
会导致大概2.6M的内存占用,这里就需要继续看模块代码才能处理了。(ps:此处可以发现20多M只导出5M,这个工具有部分缺陷,很多内容没有解析出来,有兴趣的同学可以自己研究一下如何解析出更多的内容,需要修改代码)
0 0.00% 0 app
5227414 100.00% 61363 zygote
5088172 97.34% 60778 libc++.so operator new(unsigned int) external/libcxxabi/src/stdlib_new_delete.cpp:32
2668176 51.04% 29258 libsurfaceflinger.so std::__1::__libcpp_allocate(unsigned int, unsigned int) external/libcxx/include/new:239
2635536 50.42% 28643 libsurfaceflinger.so android::SurfaceFlinger::commitTransaction() frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp:3476
2635536 50.42% 28643 libutils.so android::Looper::pollInner(int) system/core/libutils/Looper.cpp:323
2635536 50.42% 28643 libutils.so android::Looper::pollOnce(int, int*, int*, void**) system/core/libutils/Looper.cpp:205
2635536 50.42% 28643 libsurfaceflinger.so android::Looper::pollOnce(int) system/core/libutils/include/utils/Looper.h:267
2635536 50.42% 28643 libsurfaceflinger.so android::SurfaceFlinger::waitForEvent() frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp:1532
2635536 50.42% 28643 surfaceflinger main frameworks/native/services/surfaceflinger/main_surfaceflinger.cpp:120
2635536 50.42% 28643 libc.so __libc_init bionic/libc/bionic/libc_init_dynamic.cpp:136
2635536 50.42% 28643 surfaceflinger _start_main bionic/libc/arch-common/bionic/crtbegin.c:45
32640 0.62% 615 libsurfaceflinger.so operator() frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp:2333
32640 0.62% 615 libsurfaceflinger.so std::__1::__function::__value_func<void (android::Layer*)>::operator()(android::Layer*&&) const external/libcxx/include/functional:1799
5、该问题修改的地方,从对应行号,其实可以知道SurfaceFlinger的recordBufferingStats
导致的内存用量增加,至于修改方法,可以设置一个保存的数量或者干脆就不保存了,这个历史数据对用户影响不大。(这个现象目前在Google default android Q/R上应该还是存在的)
void SurfaceFlinger::commitTransaction()
{
if (!mLayersPendingRemoval.isEmpty()) {
for (const auto& l : mLayersPendingRemoval) {
//主要是这段逻辑引入的内存占用
recordBufferingStats(l->getName().string(),
l->getOccupancyHistory(true));
3. 显示端的内存调试
mtk的自己做的节点
adb shell cat /sys/kernel/debug/ion/ion_mm_heap
高通的
adb shell cat /sys/kernel/debug/ion/heaps/system
上述主要看的是gpu显示相关或者surfacefinger的内存用量问题
=> 如android 7.0 mtk的6737出现surfacefinger普遍偏大,原因是surfacefinger的buffer设置较大,缓存了10个,后面是改成3个
//6737平台上验证,进入播放视频,退出播放,重复几十次,
//没有该问题,surfaceflinger用量一直维持在20多M,该6.0平台稳定性比7.0要好很多(指的是内存用量这块)。
tmp/log$ adb shell cat /sys/kernel/debug/ion/ion_mm_heap | grep "280"
surfaceflinger( DpIonHandler) 280 1966080 0xd52a7a00
surfaceflinger( gralloc) 280 21319680 0xd757aa00 0xc35f4c80 4898816 0 2 1 0 1200000 0 0 280
surfaceflinger 0x0 0x0 0x0 0x0 p:1254 c:280 sf_info(-18771955 47 96 720 1280 0 0 720 1280 67045377 0 0 0 0 0 0 ) 0xc5ff1000 278528 0 3 2 0 6700000 0 0 280
surfaceflinger 0x0 0x0 0x0 0x0 p:1254 c:280 sf_info(-18771955 0 0 720 93 0 1187 720 93 134154241 0 0 0 0 1 0 ) 0xc7b78b40 139264 0 3 2 0 1e00000 0 0 280
surfaceflinger 0x0 0x0 0x0 0x0 p:1254 c:280 sf_info(-18771955 0 0 720 48 0 0 720 48 134154241 0 0 0 0 0 0 ) 0xc8733280 278528 0 3 2 0 2700000 0 0 280
…
=> Lost RAM比较大的问题
//初步定位是media模块导致的内存泄漏
client pid size
----------------------------------------------------
omx@1.0-service 823 134221824 //134M
omx@1.0-service 823 134221824
这个节点很有作用
4 Java process导致的内存泄漏
Java process内存分析手段目前比以前完善多了,以前使用的是hprof+MAT
分析(大部分只能分析Dalvik虚拟机部分,Native信息较少),而Android studio
可以使用profile进行memory的分析
4.1 MAT分析
先介绍mat工具吧(仅仅能分析java虚拟机的用量,一般对应下面2列内容)
Dalvik Heap 21302 21268 0 316 28662 16379 12283
Dalvik Other 2779 2776 0 100
=> 使用ddms或者monitor,点击dump hprof
文件,即可导出java虚拟机的内存分布
=> 接着用MAT工具打开,选择Dominator Tree
,查看java虚拟机树形的内存用量
=> 进去从最大的内存用量开始看,如此处是严格模式里面的数据,接下来需要看代码,最后发现这里大部分只在userdebug出现,在user版本也可能出现,概率会低一点,最终在项目上考虑还是给最大记录(循环记录)个数限定成1000(209,084个数的时候有4M, 可以找实际用户手机看一下这个是有多少量级,在用一个循环记录最新的数据即可)
Ps:这个一般还会使用list object
或者show object by class
查看一下incoming
和outgoing
的引用(篇幅有限,工具较旧,不多讲,后续有问题可以问)
4.2 Android Studio的Profile分析
android studio的Profile工具,这个比较强大(可以去除java虚拟机的内存占用,和部分跟虚拟机引用相关的native内存占用)
=> View –> tools window -> profile
,然后选择+
号(如红色部分),现在需要调试的手机和进程,后面会自动导出相关profile
日志
=>如Android Q的system server
内存用量高会出现PendingIntend
和RemoteCallback
的内存占用比较高的问题,可以从hprof里面看到,然后就是找代码调试,此处也稍微介绍一下调试过程
=>使用断点,调试PendingIntend
导致的RemoteCallback
中mCallbacks
比较多,如下是102374
个(10多万条)数据,查看代码此处也可以使用adb shell dumpsys activity intents
查看里面的PendingIntend$1
的个数,找出地方,后面就是看代码逻辑看看如何修复这个问题,这个问题主要是alarm设置闹钟时会register远端的回调RemoteCallback,但是移除闹钟时由于部分逻辑binder跨进程调用会有问题,导致泄漏,后面就需要比较枯燥的看代码找方案的时间。
=> RemoteCallback
里面的问题,打印register和unregister等的调用栈时发现,只有注册没有接触注册的操作,注册前mCallbacks.size()是1,注册后还是1,可能存在问题,后面就是断点,发现这部分逻辑存在问题,虽然new的一个新的对象,但是binder的对象是一致的,这就导致了jni的内存泄漏,这个问题比较隐蔽。
Line 12032: 01-01 02:15:35.202 1350 1786 E RemoteCallbackList: yun_hen 21_1 register mCallbacks.size() = 1, callback = android.app.IWallpaperManagerCallback$Stub$Proxy@9aa3b84, cookie = null, binder = android.os.BinderProxy@4eef6fb, cb = android.os.RemoteCallbackList$Callback@7b5bf6d, getCallingUid=10058, getCallingPid=1706, this = android.os.RemoteCallbackList@848eba2
Line 14398: 01-01 02:16:37.922 1350 1041 E RemoteCallbackList: yun_hen 21_1 register mCallbacks.size() = 1, callback = android.app.IWallpaperManagerCallback$Stub$Proxy@c0c092b, cookie = null, binder = android.os.BinderProxy@4eef6fb, cb = android.os.RemoteCallbackList$Callback@e2d3188, getCallingUid=10058, getCallingPid=1706, this = android.os.RemoteCallbackList@848eba2
Ps:此类内存分析还有很多需要做,一般挑选比较大的做,如果是持续做的话,每个小点也需要优化(主要是投入产出比)。
RemoteCallback在Android R和比较新的Android Q Google有提供patch,内部修改方案和Google类似,如果需要查看Google的修改,可以查看:2019-8 Remove cancel listeners from pending intent alarms
至于PendingIntend的问题,分析过程也是类似,Google 2019-11也有修复方案Remove cancel listeners from pending intent alarms
,
内部在Google的方案出来之前就已经修复这几个问题。
// google工程师在2019.08.01的时候合入android主线(看时间算早了,好几个类似的问题都是在11月才传入主线),
// 不过就算是2019.08.01也已经是正式版本是否之后好久了,而且一般厂家在pre-release的时候就会开始开发
Author: Dan Zhang <danielzhang@google.com> 2019-08-01 13:19:30
Committer: android-build-merger <android-build-merger@google.com> 2019-08-01 13:19:30
Parent: 24ec321bf3bd094c5975439bd8285272f91dd655 (Add @UnsupportedAppUsage annotations)
Parent: d40b5cecfefc7193732f17ca915ad2d48b4fc30b (Fix the reference leak on RemoteCallbackList)
Child: afdb23ab6f909c5438fa69aad458a11497cff216 (Use new UnsupportedAppUsage annotation.)
Merge "Fix the reference leak on RemoteCallbackList"
am: be30d27a8a
Change-Id: Ibf79be3f125ab58c8048c15f3f6f3a3e693295e7
// sony工程师在2019.04.22的修复了这个问题
commit d40b5cecfefc7193732f17ca915ad2d48b4fc30b
Author: Kyeongkab.Nam <Kyeongkab.Nam@sony.com>
Date: Mon Apr 22 18:14:45 2019 +0900
Fix the reference leak on RemoteCallbackList
In case that register is invoked consecutively with same callback,
reference leack could be happened since unlinkToDeath for all
callbacks is not being called.
Test: call register multiple times with same callback and unregister
callback same times. confirm the global reference table information.
Change-Id: I7dbf108ea87b3ee7ce1e1a0ff75e05e8c4478f67
diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java
index b13e68d..0c3f291 100644
--- a/core/java/android/os/RemoteCallbackList.java
+++ b/core/java/android/os/RemoteCallbackList.java
@@ -123,6 +123,7 @@ public class RemoteCallbackList<E extends IInterface> {
IBinder binder = callback.asBinder();
try {
Callback cb = new Callback(callback, cookie);
+ unregister(callback);
binder.linkToDeath(cb, 0);
mCallbacks.put(binder, cb);
return true;
一般大的厂家这类问题都会有自己的修复方案(但从各个厂家的做法看,他们不一定会将类似问题反馈给Google),Google其实也会给在后面版本修复(不过这个时候比较晚了,大部分项目都已经出货了,如果厂家不进行优化,类似问题就会流入用户手中),这里其实就是技术壁垒的一部分,我有你没有,那不就是有优化的就有优势。
4.3 java native分析
最后再来看一下java native用量的调试方式,这个我们也是使用的是上面native进程用量的调试方式,注意此处也只能找出show maps中libc_malloc的用量
Native Heap 4155 4136 0 9521 15540 13099 2440
=>使用方法和上面类似,只是调试的进程都换成了app_process或者app_process64,这个是由于java进程都是zygote/zygote64 fork出来的。
Ps: 根据手机卡顿程度可以自己调整adb shell setprop libc.debug.malloc.options backtrace=64堆栈个数大小,如go的版本(adb shell setprop libc.debug.malloc.options backtrace=6 //go手机改小一点)
adb root
adb shell setenforce 0
adb shell
cd /data/local/
chmod 777 tmp
//如果是64位后面app_process换成app_process64
setprop libc.debug.malloc.program app_process
setprop libc.debug.malloc.options "backtrace_enable_on_signal backtrace leak_track"
stop
start
ps -A | grep system_server kill -45 <pid of system_server>
[Now you can reproduce the memory leak issue, after issue appeared, you can go to the next step
to generate the memory leak backtrace. ]
kill -47 <pid of system_server>
[After a 'malloc/new/free' function triggered in 'system_server' at this time, you'll find memory dump
logs under the path: /data/local/tmp/ now. ]
=> 这里也提供一个systemui的native用量的例子吧,我们知道是bitmap,可以在代码中打印调用bitmap的java堆栈(此处native的堆栈只到c++,java的堆栈需要自己加代码),后面发现是壁纸的bitmap,接下去就是找方案让bitmap不用的时候recycle
//使用浏览器打开,看到:这个10MB是allocateHeapBitmap申请的,需要释放,但是释放时机太慢,开机5分钟内都可能没有释放
17495249 100.00% 21898 app
1. 11322709 64.72% 85 libhwui.so android::allocateHeapBitmap(unsigned int, SkImageInfo const&, unsigned int) frameworks/base/libs/hwui/hwui/Bitmap.cpp:80
1. 11294997 64.56% 80 libhwui.so android::allocateBitmap(SkBitmap*, sk_sp<android::Bitmap> (*)(unsigned int, SkImageInfo const&, unsigned int)) frameworks/base/libs/hwui/hwui/Bitmap.cpp:68
1. 11294997 64.56% 80 libhwui.so android::Bitmap::allocateHeapBitmap(SkBitmap*) frameworks/base/libs/hwui/hwui/Bitmap.cpp:92
1. 11212052 64.09% 78 libandroid_runtime.so HeapAllocator::allocPixelRef(SkBitmap*) frameworks/base/core/jni/android/graphics/Graphics.cpp:611
1. 11212052 64.09% 78 libskia.so SkBitmap::tryAllocPixels(SkBitmap::Allocator*) external/skia/src/core/SkBitmap.cpp:239
1. 11212052 64.09% 78 libandroid_runtime.so doDecode(_JNIEnv*, SkStreamRewindable*, _jobject*, _jobject*) frameworks/base/core/jni/android/graphics/BitmapFactory.cpp:407
10368000 59.26% 1 libandroid_runtime.so nativeDecodeFileDescriptor(_JNIEnv*, _jobject*, _jobject*, _jobject*, _jobject*)
4.4 binder分析
1、之前项目有个比较疑难的问题,看到system的Dalvik Heap
用量增加,大概有55M左右
** MEMINFO in pid 1302 [system] **
Pss Private Private SwapPss Heap Heap Heap
Total Dirty Clean Dirty Size Alloc Free
------ ------ ------ ------ ------ ------ ------
Native Heap 5389 5352 0 16772 23612 21571 2040
Dalvik Heap 19638 19612 0 36407 62336 37760 24576
TOTAL 133272 40872 16004 69510 85948 59331 26616
2、用MAT打开,这里是5个线程在不停的内存泄漏,5+3+3+3+1 = 15M到16M左右(1个int数组,4个float数组)
这里的第一个难点是怎么知道thread是谁?最初的想法是在所有thread new的地方都打印堆栈(如果是native的thread或者基类的thread或者线程池,这样就比较麻烦)
3、接下去
例如要找MAT的java.lang.Thread @ 0x1757ba58
Class Name | Shallow Heap | Retained Heap | Percentage
--------------------------------------------------------------------------------
java.lang.Thread @ 0x1757ba58 Thread| 120 | 5,259,016 | 10.66%
--------------------------------------------------------------------------------
=>可以在profile中打开,找对thread这个类(这里只有62个),接下去根据地址排序,找到0x1757ba58
地址的Thread
,然后看到名字是HwBinder:1302_4
4、我们知道是HwBinder了,接下去需要看一下Binder传递的数据是什么先,可以使用MAT的Winow->Inspector
=> 可以看到这个线程里面的部分元素,如下,从mat里面的4个元素对应的数据是1.57542..0E9
(E9是10的9次方),float[10]第四个都是0,
(注意这个float[10]在mat上是56个字节,但是在android studio float[10]是 40个字节,所以总量可能稍有差异,android studio上看到的是float 8.2+ int 4.7M大概13M左右)
5、那么需要查看binder对端,由于这个是android Q go项目没有system trace,那么可以查看“/sys/kernel/debug/binder”这个节点的日志,每个1s打印出来,过滤与system_server通信的binder
adb shell cat "/sys/kernel/debug/binder/failed_transaction_log"
adb shell cat "/sys/kernel/debug/binder/transaction_log"
adb shell cat "/sys/kernel/debug/binder/transactions"
adb shell cat "/sys/kernel/debug/binder/stats"
adb shell cat "/sys/kernel/debug/binder/state"
=>可以看到gnss的gps模块和suspend这个存在与HwBinder通信,接下去就看代码,如查看“frameworks/base/services/core/jni”、“frameworks/base/core/jni”两个jni文件中的代码,这个数据应该是有往上传的(binder通信数据一般都2端都有)
//
716: outgoing transaction 98649452: c2fa4380 from 577:691 to 1393:2153 code e flags 10 pri 0:120 r1
adb shell ps | grep "577"
gps 577 1 38212 2160 binder_thread_read 0 S android.hardware.gnss@2.0-service-qti
19040:99854269: reply from 571:571 to 1393:2573 context hwbinder node 0 handle -1 size 28:8 ret 0/0 l=0
system 571 1 16512 1760 binder_thread_read 0 S android.system.suspend@1.0-service
=>接着查到com_android_server_location_GnssLocationProvider.cpp的gnssSvStatusCbImpl比较可疑(数据结构和类型惊人的相似)
//frameworks/base/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
675 Return<void> GnssCallback::gnssSvStatusCbImpl(const T& svStatus) {
676 JNIEnv* env = getJniEnv();
677
678 uint32_t listSize = getGnssSvInfoListSize(svStatus);
679 if (listSize > static_cast<uint32_t>(
680 android::hardware::gnss::V1_0::GnssMax::SVS_COUNT)) {
681 ALOGD("Too many satellites %u. Clamps to %u.", listSize,
682 static_cast<uint32_t>(android::hardware::gnss::V1_0::GnssMax::SVS_COUNT));
683 listSize = static_cast<uint32_t>(android::hardware::gnss::V1_0::GnssMax::SVS_COUNT);
684 }
685 //1个int数组,4个float数组
686 jintArray svidWithFlagArray = env->NewIntArray(listSize);
687 jfloatArray cn0Array = env->NewFloatArray(listSize);
688 jfloatArray elevArray = env->NewFloatArray(listSize);
689 jfloatArray azimArray = env->NewFloatArray(listSize);
690 jfloatArray carrierFreqArray = env->NewFloatArray(listSize);
691
692 jint* svidWithFlags = env->GetIntArrayElements(svidWithFlagArray, 0);
693 jfloat* cn0s = env->GetFloatArrayElements(cn0Array, 0);
694 jfloat* elev = env->GetFloatArrayElements(elevArray, 0);
695 jfloat* azim = env->GetFloatArrayElements(azimArray, 0);
696 jfloat* carrierFreq = env->GetFloatArrayElements(carrierFreqArray, 0);
6、=>对端是GnssLocationProvider.java的reportSvStatus,此处可以设置断点
//frameworks/base/services/core/java/com/android/server/location/GnssLocationProvider.java
private void reportSvStatus(int svCount, int[] svidWithFlags, float[] cn0s,
float[] svElevations, float[] svAzimuths, float[] svCarrierFreqs) {
SvStatusInfo svStatusInfo = new SvStatusInfo();
svStatusInfo.mSvCount = svCount;
svStatusInfo.mSvidWithFlags = svidWithFlags;
svStatusInfo.mCn0s = cn0s;
svStatusInfo.mSvElevations = svElevations;
svStatusInfo.mSvAzimuths = svAzimuths;
svStatusInfo.mSvCarrierFreqs = svCarrierFreqs;
sendMessage(REPORT_SV_STATUS, 0, svStatusInfo);
}
=>发现传递的值中一个也是很大的值1.5*10的9次方
,
可以基本确定这类型的数据就是gps的模块产生的
=>当然也可以用lldb断点c++的地方,此处可以看到虽然这个是c++代码,但是调用地方是javaThreadShell这个是java的thread,所以算在java上了
//frameworks/base/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
(lldb) bt
* thread #25, name = 'HwBinder:1276_1', stop reason = breakpoint 1.1
* frame #0: 0x7e41f1b4 libandroid_servers.so`android::hardware::Return<void> android::GnssCallback::gnssSvStatusCbImpl<android::hardware::hidl_vec<android::hardware::gnss::V2_0::IGnssCallback::GnssSvInfo> >(android::hardware::hidl_vec<android::hardware::gnss::V2_0::IGnssCallback::GnssSvInfo> const&)
frame #1: 0x7e418dba libandroid_servers.so
frame #2: 0x7dce4328 android.hardware.gnss@2.0.so`android::hardware::gnss::V2_0::BnHwGnssCallback::_hidl_gnssSvStatusCb_2_0(android::hidl::base::V1_0::BnHwBase*, android::hardware::Parcel const&, android::hardware::Parcel*, std::__1::function<void (android::hardware::Parcel&)>) + 172
frame #3: 0x7dce48de android.hardware.gnss@2.0.so`android::hardware::gnss::V2_0::BnHwGnssCallback::-OnTransact(unsigned int, android::hardware::Parcel const&, android::hardware::Parcel*, unsigned int, std::__1::function<void (android::hardware::Parcel&)>) + 814
frame #4: 0xa3d744fc libhidlbase.so`android::hardware::BHwBinder::transact(unsigned int, android::hardware::Parcel const&, android::hardware::Parcel*, unsigned int, std::__1::function<void (android::hardware::Parcel&)>) + 48
frame #5: 0xa3d76b2c libhidlbase.so`android::hardware::IPCThreadState::getAndExecuteCommand() + 968
frame #6: 0xa3d77a48 libhidlbase.so`android::hardware::IPCThreadState::joinThreadPool(bool) + 68
frame #7: 0xa3d82cfc libhidlbase.so
frame #8: 0xa44508c4 libutils.so`android::Thread::_threadLoop(void*) + 216
frame #9: 0xa5b10d16 libandroid_runtime.so`android::AndroidRuntime::javaThreadShell(void*) + 86
frame #10: 0xa54708d8 libc.so`__pthread_start(void*) + 22
frame #11: 0xa542a130 libc.so`__start_thread + 32
7、最后就是看代码,确实是jni中new了没有delete
//frameworks/base/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
jintArray svidWithFlagArray = env->NewIntArray(listSize);
jfloatArray cn0Array = env->NewFloatArray(listSize);
jfloatArray elevArray = env->NewFloatArray(listSize);
jfloatArray azimArray = env->NewFloatArray(listSize);
jfloatArray carrierFreqArray = env->NewFloatArray(listSize);
jint* svidWithFlags = env->GetIntArrayElements(svidWithFlagArray, 0);
jfloat* cn0s = env->GetFloatArrayElements(cn0Array, 0);
jfloat* elev = env->GetFloatArrayElements(elevArray, 0);
jfloat* azim = env->GetFloatArrayElements(azimArray, 0);
jfloat* carrierFreq = env->GetFloatArrayElements(carrierFreqArray, 0);
env->ReleaseIntArrayElements(svidWithFlagArray, svidWithFlags, 0);
env->ReleaseFloatArrayElements(cn0Array, cn0s, 0);
env->ReleaseFloatArrayElements(elevArray, elev, 0);
env->ReleaseFloatArrayElements(azimArray, azim, 0);
env->ReleaseFloatArrayElements(carrierFreqArray, carrierFreq, 0);
//Fix memory leakage
env->DeleteLocalRef(svidWithFlagArray);
env->DeleteLocalRef(cn0Array);
env->DeleteLocalRef(elevArray);
env->DeleteLocalRef(azimArray);
env->DeleteLocalRef(carrierFreqArray);
//NewIntArray/NewFloatArray
需要和DeleteLocalRef
配套使用,GetIntArrayElements/GetFloatArrayElements
和ReleaseIntArrayElements/ReleaseFloatArrayElements
也需要配套使用,c++不像java会自动释放本地变量,所以这里需要非常注意
4.5 其它调试方法
1、 我们发现除了native和Dalvik的用量,还有so、apk、EGL/GL等的用量,这部分我们怎么看呢
** MEMINFO in pid 1393 [system] **
Pss Private Private SwapPss Heap Heap Heap
Total Dirty Clean Dirty Size Alloc Free
------ ------ ------ ------ ------ ------ ------
Native Heap 6813 6784 0 4316 12992 10725 2266
Dalvik Heap 16036 15996 0 204 26694 15254 11440
Dalvik Other 2450 2448 0 60
Stack 24 24 0 16
Ashmem 30 24 0 0
Gfx dev 852 204 648 0
Other dev 39 20 12 0
.so mmap 2436 308 892 835
.jar mmap 7861 0 6708 0
.apk mmap 10159 0 6752 0
.ttf mmap 44 0 0 0
.dex mmap 5417 116 5300 128
.oat mmap 2426 0 1388 0
.art mmap 8365 7568 16 2976
Other mmap 1621 1428 12 0
EGL mtrack 1944 1944 0 0
GL mtrack 1428 1428 0 0
Unknown 2102 2100 0 1500
TOTAL 80082 40392 21728 10035 39686 25979 13706
2、先看一堆mmap的用量,这个可以使用showmap查找一下,如下是system server的map堆栈信息,可以看到services.odex、Velvet.apk、boot-framework.art
其实都是载入system server
里面了.(这里部分是有优化空间的,如之前项目针对systemui等优化过调用camera的context进程上下文导致camera apk载入了systemui)
3、EGL/GL的占用
上面有介绍过,一般用的也是下面2个节点去查看,这部分一般都是平台商gpu的部分,不一定看得到代码.
// mtk的自己做的节点
adb shell cat /sys/kernel/debug/ion/ion_mm_heap
// 高通的
adb shell cat /sys/kernel/debug/ion/heaps/system
adb shell cat /sys/kernel/debug/dma_buf/dmaprocs // kernel 4.14以后的版本使用这个
这个节点可以定位大部分的问题,如Android R的TaskSnapshot
会导致ion
增加较大,可以通过这个节点,辅助systrace
分析载入逻辑,找到TaskSnapshot
进行优化