移动开发最新讲一讲 Android 内存优化的小秘密,2024年最新面试文件

最后

针对于上面的问题,我总结出了互联网公司Android程序员面试涉及到的绝大部分面试题及答案,并整理做成了文档,以及系统的进阶学习视频资料。
(包括Java在Android开发中应用、APP框架知识体系、高级UI、全方位性能调优,NDK开发,音视频技术,人工智能技术,跨平台技术等技术资料),希望能帮助到你面试前的复习,且找到一个好的工作,也节省大家在网上搜索资料的时间来学习。

image

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • merge shortest paths to gc roots - 从GC roots到一个或一组对象的公共路径

  • exclude all phantom/weak/soft etc. references - 排除一些类型的引用(如软引用、弱引用、虚引用),留下强引用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K7NkNezK-1570627368280)(https://upload-images.jianshu.io/upload_images/15679108-52ec3eeee36e24f1?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]

为了避免查看太多并不是强相关的对象,直接从本应用的java 类入手,MAT 也提供正则式过滤,直接输入.com.vd.(本应用 packageName)去过滤,结果就非常明显,整个应用自己写的对象占用的内存都在这里。从大的对象下手,是否这个对象有存在的意义,是否需要占这么大的一个内存。是否可以对其做相应的处理。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z7ziCoev-1570627368281)(https://upload-images.jianshu.io/upload_images/15679108-bfa2c4e80eef4416?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]

MAT提供了更加方便的OQL查询,可以找到指定一个名字的对象,包括可以根据本身java对象的成员属性来做条件语句。譬如上图我找长宽都大于100px的图片都有哪些。可以把大图片揪出来。

小结
可先用LeakCanary跑出明显的内存泄漏,再用MAT检查整个应用的内存分布状况,去优化该优化的Java堆内存。

/   Native内存优化   /

| native 内存优化 | malloc_debug | heapsnap | DDMS |

| — | — | — | — |

| root权限 | 需要 | 需要 | 不需要 |

| 环境 | python | jni | 需要使用sdk18 的 tools/ddms.bat(sdk 18之后就被剔除了) |

  • malloc_debug是官方推荐的一种方法,目前效果还不错

  • heapsnap 是一个可以跑在Adnroid的C库github开源库 ,目前只能查询内存泄漏。而且编译不过,原因是缺少了一些库。在它基础上我整合了一份编译成功,有兴趣点击这里

  • DDMS目前被遗弃,在android 9.0没整成功,放弃。

malloc_debug步骤开启malloc debug模式,打开cmd窗口输入。

//查询所有内存

adb shell setprop wrap.packagename ‘“LIBC_DEBUG_MALLOC_OPTIONS=backtrace logwrapper”’

//查询内存泄漏

adb shell setprop wrap.packagename ‘“LIBC_DEBUG_MALLOC_OPTIONS=leak_track logwrapper”’

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2tPExSMw-1570627368281)(https://upload-images.jianshu.io/upload_images/15679108-f5d409f398e4dcac?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]

  • 关掉自身应用,再打开,monkey跑起来

  • 通过adb shell dumpsys meminfo com.all.videodownloader.videodownload查到pid为2968

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6a3AOG3W-1570627368282)(https://upload-images.jianshu.io/upload_images/15679108-19ce414d5576efc1?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]

通过adb shell am dumpheap -n <PID_TO_DUMP> /data/local/tmp/heap.txt把文件抓取出来到/data/local/tmp/heap.txt。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cppaMKmb-1570627368283)(https://upload-images.jianshu.io/upload_images/15679108-8af573ffa144daa9?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]

把native内存文件拷贝出来,等下分析。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yzopNc1r-1570627368284)(https://upload-images.jianshu.io/upload_images/15679108-3ba25d79d4e2e464?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]

使用python分析
搭建环境
  • 下载native_heapdump_viewer.py
  • python编译器我选择了PyCharm
  • 新建项目,把native_heapdump_viewer.py和 heap.txt,放到同一个目录,如下图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PAOF2nrK-1570627368284)(https://upload-images.jianshu.io/upload_images/15679108-412db8e3bf660ae6?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]

修改python代码修改native_heapdump_viewer.py 代码中NDK配置地方:

resByte = subprocess.check_output([“G:/AndroidNDK/android-ndk-r17/toolchains/aarch64-linux-android-4.9/prebuilt/windows-x86_64/bin/aarch64-linux-android-objdump”, “-w”, “-j”, “.text”, “-h”, sofile])

p = subprocess.Popen([“G:/AndroidNDK/android-ndk-r17/toolchains/aarch64-linux-android-4.9/prebuilt/windows-x86_64/bin/aarch64-linux-android-addr2line”, “-C”, “-j”, “.text”, “-e”, sofile, “-f”], stdout=subprocess.PIPE, stdin=subprocess.PIPE)

替换def init(self):函数中的部分代码,把下面代码:

if len(extra_args) != 1:

print(self._usage)

sys.exit(1)

替换为:

self.symboldir = “C:/Users/chaojiong.zhang/Documents/AndroidStudio/DeviceExplorer/xiaomi-mi_8-4b429b4”

extra_args.append(“dump.txt”)

self.symboldir - 就是dump.txt 里面的内存地址都需要 通过so库来查找对应的是哪一个函数。而so存放的父路径地址就是self.symboldir,那么也就是说需要把 手机上的/system/lib64、/vendor/lib64/整个 文件夹pull 下来到电脑上,譬如这里是pull到C:/Users/chaojiong.zhang/Documents/AndroidStudio/DeviceExplorer/xiaomi-mi_8-4b429b4。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N8RIzq5h-1570627368285)(https://upload-images.jianshu.io/upload_images/15679108-686f6f681f7f4286?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]

在def main():函数插入部分代码在函数第一行插入和最后一行插入以下代码,目的是直接把结果log输出到test.txt可以直接查看。

def main(): sys.stdout= open(“test.txt”, “w”)

//…

sys.stdout.close()

跑起来看看。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ROftfSIu-1570627368285)(https://upload-images.jianshu.io/upload_images/15679108-73b3fa847c3d23e1?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]

malloc_debug内存文件分析
字段解读
  • BYTES- 占用的内存大小单位byte
  • %TOTAL - 占总 native 内存百分比
  • %PARENT - 占父帧内存百分比
  • COUNT - 调用了多少次
  • ADDR- 内存地址
  • LIBRARY - 占用的内存所属哪一个 so 库
  • FUNCTION- 占用的内存所属哪一个方法
  • LOCATION - 占用的内存所属哪一行
内存信息分析一

10285756  58.29%  99.95%       49     eac0b276 /system/lib/libhwui.so android::Bitmap::allocateHeapBitmap(SkBitmap*)

可以看得出来 allocateHeapBitmap方法占用了,10M左右的内存,占总native内存 58.29%,占父帧 99.95% (意思是:A-> B ,A方法调用B方法,A方法总共占用了10M,其中9.9M是在B方法中申请的,那么%PARENT 就是99%),调用了49次,动作发生在libhwui.so中的 android::Bitmap::allocateHeapBitmap方法。下面是allocateHeapBitmap被调用的流程:

BitmapFactory.decodeResource -> BitmapFactory.nativeDecodeStream ->BitmapFactory.cpp 中 nativeDecodeStream() -> doDecode() -> SkBitmap.tryAllocPixels() -> … -> android::Bitmap::allocateHeapBitmap()

Bitmap.createBitmap -> nativeCreate() -> Bitmap.cpp 中的 nativeCreate() -> GraphicsJNI.cpp zhong de allocateJavaPixelRef() -> … -> android::Bitmap::allocateHeapBitmap()

也就是说java层的bitmap 创建都会跑到allocateHeapBitmap这个函数。那么上面这个占用了10M的 allocateHeapBitmap,究竟是java层哪个类调用下来的,这个目前是无解(包括最近华为的方舟环境平台DevEco也不行),只能在java层去全盘查询了,哪些图片使用了较多的内存。内存信息分析二[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BzuCln4b-1570627368286)(https://upload-images.jianshu.io/upload_images/15679108-05cef497ef1eb068?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]

  • WebViewGoogle.apk占用了10M的内存,WebViewGoogle.apk就是应用使用的WebView,android 5.0之前作为模块存在于frameworks/base目录下,并提供接口。android 5.0之后变成了编译为一个独立的apk,包名是 com.android.webview。检查所有的WebView使用情况,譬如:如果场景允许,使用完毕是否有 调用WebView.clearCache()
  • boot-framework.oat 占了5M ,Android framework代码通过dex2oat转成的oat二进制文件(机器码),无需优化
  • libandroid_runtime.so 占了 5M,虚拟机内存,属于按比例划分共享库,无需优化
小结
native内存目前无法很清晰的定位到对应的java层代码,无解。只能看个大概,然后有目的性去排查某个类,或者某个模块。

/   graphics内存优化   /若应用没有自己接入OpenGL/ GL surfaces/ GL textures开源库,来绘制图形,可不必理会。毕竟已经超出android应用工程师的范围了。/   stack内存优化   /

解决栈溢出
死循环问题
JDK 1.8之前的HaskMap,避免使用多线程造成死循环问题。
递归问题
避免深层次的递归问题,较深层次的递归可采用尾递归的方法。递归的退出,最好用标识位退出。或者通过线程interrupt(),isInterrupted()去退出递归,确保递归正确退出。递归中如果有Thread.sleep ,要注意中断被消费问题。
Intenet问题
对于Intent传递大对象,或者ArrayList,Intent的上限是505K 。解决方案:
  • 一般通过 static 持有需要传递的对象解决。
  • 把跳转的页面写成 fragment ,数据可以不需要传递也可获取
  • 通过EventBus RxBus(原理都是通过全局单例来传递)
  • 通过ObjectCache把对象转成json串,保存到本地,获取时候序列化为对象。
解决重复生成局部变量
避免在循环内重复生成局部变量

private void memoryShake() {

ArrayList shakes = new ArrayList<>();

for (int i = 0; i < 100; i++) {

Integer shake = new Integer(i);

shakes.add(shake);

}

}

private void memoryShake1() {

ArrayList shakes = new ArrayList<>();

Integer shake;

for (int i = 0; i < 100; i++) {

shake = new Integer(i);

shakes.add(shake);

}

}

memoryShake()会在循环内生成100个shake局部变量+100个局部变量的引用,memoryShake1()会在循环内生成100个shake局部变量+1个局部变量的引用,一个对象引用在64bit的环境是8byte 。100*8 = 800 byte = 0.8KB。

String使用问题

循环内字符的拼接不要使用+符号,(使用+符号,编译成字节码后,循环内会生成StringBuilder对象去拼接)。正确应该使用StringBuffer(线程安全)或者StringBuilder(线程不安全)。

/   code内存优化   /

最后

下面是有几位Android行业大佬对应上方技术点整理的一些进阶资料。希望能够帮助到大家提升技术

高级UI,自定义View

UI这块知识是现今使用者最多的。当年火爆一时的Android入门培训,学会这小块知识就能随便找到不错的工作了。

不过很显然现在远远不够了,拒绝无休止的CV,亲自去项目实战,读源码,研究原理吧!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

能够帮助到大家提升技术

[外链图片转存中…(img-FRNM64uo-1715465671088)]

高级UI,自定义View

UI这块知识是现今使用者最多的。当年火爆一时的Android入门培训,学会这小块知识就能随便找到不错的工作了。

不过很显然现在远远不够了,拒绝无休止的CV,亲自去项目实战,读源码,研究原理吧!

[外链图片转存中…(img-WiT8Am1M-1715465671088)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值