
- 提 高
- 内存的使用效率
- 优 化
- 内存的管理
- 中文名
- 内存优化
- 目 的
- 主要是为了保持虚拟内存的连续性
-
安卓内存优化
2020-05-11 18:12:39文章目录为什么要进行内存优化优化步骤1,查看当前的应用内存使用情况,发现是否有内存泄漏2,内存泄漏点查找,并解决问题3,其它一些日常项目中编码的注意点 为什么要进行内存优化 安卓应用如果不进行内存优化,...为什么要进行内存优化
安卓应用如果不进行内存优化,一些不良的代码习惯或编码可能有内存泄漏,甚至导致OOM。这样的应用随着长时间的使用,经常会出现越来越卡的现象,严重的甚至会直接闪退,进而给用户带来劣质感,严重影响用户体验和使用,最终导致用户流失。
对于你的手机,都有自己的app内存配置参数,可以adb查看adb shell getprop | findstr heap 显示 [dalvik.vm.heapgrowthlimit]: [384m] //一个应用正常时的最大堆(Dalvik的)内存,会触发oom [dalvik.vm.heapmaxfree]: [8m] //单次最大的free内存 [dalvik.vm.heapminfree]: [2m] //单次最小的free内存 [dalvik.vm.heapsize]: [512m] //application标签下加android:largeHeap="true"时的最大堆内存,会触发oom [dalvik.vm.heapstartsize]: [8m] //应用起始分配的堆内存 [dalvik.vm.heaptargetutilization]: [0.75] //堆内存利用率参考值
项目功能完后内存如果有泄漏或抖动等,轻则出现卡顿,严重的长期不退出进程最终出现OOM。
**哟哟,似乎上面的解释很合理,以上说的都是不内存优化的危害,所以要内存优化。
但是,但是,但是安卓的java部分的内存不是自动回收的吗?,是不是这部分就不用优化啦?理论上是这样的(现实是残酷的),当创建的对象在不使用的时候会被系统在某个时间自动回收,但是系统认为可以被回收的不再被使用的对象是指那些 不在GC Root的引用链上,但是实际上很多时候我们认为已经不需要使用了的对象因为编码或系统本身的原因,还是在这条引用链上的,导致无法回收。优化步骤
1,查看当前的应用内存使用情况,发现是否有内存泄漏
- 可以使用adb查看
- 可以使用Android studio的Profile查看
- 也可以使用DDMS查看
这里主要讲的是第一种,后两种也比较简单。
adb shell dumpsys meminfo [applicationId(一般都是包名)]
这里主要看Java Heap、Native Heap(C、C++占用的内存) 、Graphics(GPU占用的内存,主要是纹理、帧缓存等占用的,手机没有独立显存)
进入某个界面进行一些操作后返回,检查(记得gc下下,内存稳定下来看,防止误差):- 内存是否增长明显
- Activities、Views等是否数量增加
无论出现1或者2都是代表内存并没有被释放
2,内存泄漏点查找,并解决问题
一般可以使用DDMS或者Profile导出上面步骤1操作前和操作后的hprof文件,如下图:
点击上面的红圈后生成了hprof文件,一般需要使用platform-tools里面的hprof-conv.exe转换一下,如:hprof-conv.exe input.hprof output.hprof
再使用MAT打开output.hprof,如图:
可以直接看Leak Suspects里面的分析,如果有具体的分析结果并且指明了具体的未释放类和对象,可以直接修复(一般只是参考,不依靠这个修改)
然后也可以创建Histogram进行类对象数量和大小等的比对(这个可以精确定位内存增长在哪个类的对象上),或者直接在Histogram里面输入自己的包名或者类名查询,然后可以通过菜单选项选择,看到具体对象的被引用情况。比如下图搜索多出来的Activity,最终确定的引用关系:
这个可以看到:LiveActivity类的context对象被MaterialDialog的静态内部类Builder持有了,而Builder的对象builder被MaterialDialog的实例持有,MaterialDialog的实例又被Message的实例持有,而Message的对象又被HandlerThread持有。
通过这个引用链可以发现根本原因,我们一般是: 1,先看引用链上有木有我们自己包下面的类和对象,有的话就在对应的类上修改(比如使用弱引用等)来断开这个强引用链,如果没有则 2,再看系统引用为什么还保持着这个对象,要么是调用系统api的问题,要么是系统本身API就有问题(比如安卓早期版本系统层面就有很多内存泄漏的地方,还有可能国内各种手机厂商的Rom定制导致的),我们这里是HandlerThread里的Looper没有清掉MessageQueue里面的Message导致的,HandlerThread这个类已经有quit()释放资源的方法,那么这里肯定是我们自己调用问题导致quit()没有被执行,属于2的情况。
最终查看LiveActivity类里的dialog dismiss代码,发现是在 activity finish()之后执行的dialog dismiss()方法导致这个问题。调整先后位置后重新走步骤1 adb查看activity数量不再增加,验证OK。3,其它
实际开发中都是集成LeakCanary到开发项目中,这样只需要操作应用,自动检查出一部分内存列漏点,并提示引用链。解决还是按照步骤2的解决问题部分来的。
一些日常项目中编码的注意点
编码时,总的原则是:
对象的生命周期不能超过它的使用周期,不使用了及时手动删掉一些内部的持有的生命周期比较长的或者在其它GC Root链上的对象(比如赋值null,弱引用等)项目实践中注意一下情况:
1. 首先还是需要强调一下,实现模块内具体功能时,尽量避免以应用内常驻后台的Service方式实现,android官方也认为这种方式过于贪婪。对这类后台Service,合理处理Service的生命周期,保证流程结束时,Service能够被正常
停止。通过bindService绑定的Service,用完后要及时解除绑定,否则也会造成被绑定的Service一直无法被释放。2. 在应用的内部流程上,保证对象的生命周期不会超过相应的逻辑流程。比如Apllication中初始化的对象生命周期一定要是整个应用进程的。
3. 静态对象直接或间接引用到Activity,静态对象的生命周期和进程一致。正常情况下,在AMS调度完Acitity的onDestroy之后,ActivityThread会删除对Activity的引用,之后不存在从GC roots到该Activity的引用路径,该Activity的java对象也会被回收。但是在Activity被静态对象引用后,即使前面的引用被删除后,依然存在到该Activity的引用,进程不退出,静态对象不会释放,被其引用的Activity也不会释放。这种情况,也是Monkey测试中碰到最多的内存泄露情形。比较常见的情形,把Activity作为Context直接传给静态对象,把Activity的非静态内部类对象传给静态对象,把Activity的某个view传给静态对象,这些情形都会导致静态对象持有Activity的引用。第1种情况比较明显,后面2种情况中,非静态内部类会持有其外部类的引用,而view对象会通过其mContext持有Activity的引用。所以,对静态对象的赋值,除非是简单的java类型,否则一定要有相对应的清理操作。
4. 需要使用Context作为参数传递时,尽量使用ApplicationContext,使用Activity作为Context的参数传递时,可能会把Activty的引用传递给一个生命周期不同的对象,从而导致Activity在应该被释放时而没有被释放。在静态对象被构造时,如果有Context的参数,要主动转换成ApplicationContext,防止传入的参数是Activity。以上,对Service也是一样。
5. 如非必要,不要使用SingleInstance。SingleInstane的对象一旦被创建,在应用退出之前,其对象不会被释放。所以,这点要根据具体流程进行评估,仅在确定需要时使用SingleInstance。同样,对于静态对象,也是需要确保其生命周期和相应的逻辑流程一致,在不需要时进行清理。
6. 对于Bitmap类的对象要及时主动释放,不仅对于代码中主动构造的Bitmap对
象,要有主动的释放操作,对于通过view. setBackgroundResource(@DrawableRes int resid)接口设置的Bitmap资源,以及通过xml布局文件配置的Bitmap资源,都要在不需要时进行主动释放。主动释放的这部分主要是Native Heap和Graphics,前者是为图片数据分配native层的内存,后者是描画该图片GPU占用的内存。因此,在Activity的onDestroy中,要主动释放通过各种方式加载的Bitmap对象。首先,通过View的getBackground()方法获取到BitmapDrawable对象,再通过BitmapDrawable得到Bitmap对象,最后调用Bitmap的recycle方法进行回收。另外有些图片用的是.9格式的png图片,这种图片生成的是NinePatchDrawable,也就是说,这种情况下,通过getBackground()方法获取到的是NinePatchDrawable对象。NinePatchDrawable中取得Bitmap的方法与BitmapDrawable不同,首先通过成员函数getConstantState()获取到内部的mNinePatchState对象,再通过NinePatchState的getBitmap()获取到Bitmap对象。7. 重写OnTrimMemory(int level)方法,在应用的所有界面不可见时,系统会回调该方法供应用释放部分内存,其中level的值TRIM_MEMORY_UI_HIDDEN(如想了解level的其他取值及作用,请自行搜索,其他值可以暂不理会)。这时候可以释放掉Bitmap或其他不再使用的对象,在onRestart或其他合适的时间点,再重新恢复以上资源,保证应用在用户不可见的情况下,占用最少的内存。
8. 注册某个对象后,要及时进行取消。比如Activity实现了某些Listener接口,在被作为Listener对象注册后,在Activity生命周期结束后,没有进行取消注册的话,就会造成Activity的对象无法被释放。类似的,注册广播接收器、注册观察者、EventBus等等,都需要在适当的时间取消注册。
9. 使用的资源对象要及时关闭,比如File、Cursor等。集合中的对象,在不使用时要及时进行清理,特别是Static类型的集合对象,或者是被Static对象持有的集合对象。
10. 使用优化过的数据集合,这也是android官方推荐的。Android API当中提供了一些优化过后的数据集合工具类,如SparseArray,SparseBooleanArray,以及LongSparseArray等,使用这些API可以让我们的程序更加高效。传统Java API中提供的HashMap工具类会相对比较低效,因为它需要为每一个键值对都提供一个对象入口,而SparseArray就避免掉了基本数据类型转换成对象数据类型的时间。
11. 图片分辨率相关,对于xxhdpi的设备,在使用资源图片时,如果xxhdpi文件夹中存在相应的资源图片时,图片加载时,不会被进行缩放,如果xxhdpi不存在,xhdpi中存在,那么不管该图片的分辨率是多少,系统会对其进行放大。也就是说,对于同一张图片,仅仅放在xxhdpi或者xhdpi中时,对于xxhdpi的设备,获取到的Bitmap对象的大小是不一样的。对于一个1280×720的图片,大小可能是3.68M和8.29M,差别超过一倍。
12. ListView复用,getView里尽量复用conertView,同时因为getView会频繁调用,要避免频繁地生成对象。
13. Handler导致的内存泄露,Handler一般以内部类的形式实现,从而Handler会持有外部类的引用。如果通过Handler发送一个延迟处理的消息到线程的消息队列,会存在一个消息队列-〉消息-〉Handler-〉外部类(Activity或Servie)的引用,如果外部类退出,这时候,因为存在从消息队列的引用,相应的Activity或Service就不会被释放。对于这种情况,可以考虑Handler不以内部类形式实现,或以静态内部类实现,或者在外部类退出时,从消息队列中删除相应的延迟消息。
14. 线程导致内存泄露,如果以内部类形式实现了一个子线程,线程开始运行并执行一个耗时的操作,在线程没有结束之前,外部类退出,此时,外部类不会被释放,因为作为内部类的子线程持有外部类的引用,解决方法和前一条类似。
15. 如果在Activity中播放一个无限循环的动画,在退出Activity时没有在 onDestroy中去停止动画,那么动画会一直播放下去,尽管已经无法在界面上看见动画效果了,并且这个时候 Activity的 View会被动画持有,而View又持有了Activty,最终Activity无法释放。解决方法是在Activity的onDestroy中调用animator.cancel()来停止动画。
-
RocketMQ 内存优化
2018-12-14 10:40:19RocketMQ 内存优化1.启动脚本的内存调整1.mqbroker和mqnamesrv的内存调整 RocketMQ 的默认内存占用非常高,是4×4g的,普通人是消耗不起的,所以第一件事情就是调整RocketMQ的占用内存。 调整RocketMQ的内存目前我所...
RocketMQ 的默认内存占用非常高,是4×4g的,普通人是消耗不起的,所以第一件事情就是调整RocketMQ的占用内存。调整RocketMQ的内存目前我所知道的有两个方面
- MQ的启动脚本可以调整内存
- mqbroker和mqnamesrv的配置可以调整内存
1.启动脚本的内存调整
启动脚本可以配置MQ整体内存大小,有两个启动脚本,runbroker.sh 和runserver.sh。
脚本里面的默认配置信息是JAVA_OPT="${JAVA_OPT} -server -Xms4g -Xmx4g -Xmn2g -XX:PermSize=128m -XX:MaxPermSize=320m"
以上配置可以根据自己情况进行调整,记得runbroker.sh 和runserver.sh都要修改。
1.mqbroker和mqnamesrv的内存调整
这两项配置也很重要,默认是500m到1g。具体文件配置是mqbroker.xml和mqnamesrv.xml。
文件里面的配置信息是<options> <-Xms200m></-Xms200m> <-Xmx200m></-Xmx200m> <-XX:NewSize>50M</-XX:NewSize> <-XX:MaxNewSize>50M</-XX:MaxNewSize> <-XX:PermSize>30M</-XX:PermSize> <-XX:MaxPermSize>30M</-XX:MaxPermSize> </options>
以上可以根据自己情况进行配置。
要注意的事情是mqbroker.xml和mqnamesrv.xml的内存不要超过runbroker.sh 和runserver.sh的内存,不然会引起内存不够导致奔溃。
Docker 部署RocketMQ
如果有使用docker,那么部署RocketMQ是个轻松愉快的事情,内存控制更加方便,主要部署脚本以下
前提部署环境能够上网,docker会自动拉取MQ镜像
1.rmqserver部署
docker run -d -p 9876:9876 --name rmqserver -e "JAVA_OPT_EXT=-server -Xms128m -Xmx128m -Xmn128m" foxiswho/rocketmq:server
2.rmqbroker 部署
docker run -d -p 10911:10911 -p 10909:10909 --name rmqbroker --link rmqserver:namesrv -e "NAMESRV_ADDR=namesrv:9876" -e "JAVA_OPTS=-Duser.home=/opt" -e "JAVA_OPT_EXT=-server -Xms128m -Xmx128m -Xmn128m" -v /data/rocketmq2/broker.conf:/etc/rocketmq/broker.conf foxiswho/rocketmq:broker
3.broker.conf配置文件内容
brokerClusterName = DefaultCluster brokerName = broker-a brokerId = 0 deleteWhen = 04 fileReservedTime = 48 brokerRole = ASYNC_MASTER flushDiskType = ASYNC_FLUSH autoCreateTopicEnable = true autoCreateSubscriptionGroup = true
以上内容保存为broker.conf即可
接下来更精彩,请勿走开(以下是私事,不喜欢别往下看了)
好用的HTTP模拟请求工具
市面上有很多HTTP模拟请求工具,有yapi,有ApiPost,好不好用在此不做评价。这里推荐使用
BB-API
,功能全面,免费使用、资源无限制,自动生成接口文档,界面简洁大气。支持局域网部署,打造属于你、公司的HTTP 协议管理工具。
官网地址http://api.app-yun.com/bbapi/index.html
-
内存优化(一)浅谈内存优化
2018-12-20 16:39:39本系列文章,主要是总结我对Android开发过程中内存优化的理解,很多东西都是平常的习惯和一些细节问题,重在剖析优化的原理,养成一种良好的代码习惯。 概述 既然谈优化,就绕不开Android三个内存相关的经典问题...本系列文章,主要是总结我对Android开发过程中内存优化的理解,很多东西都是平常的习惯和一些细节问题,重在剖析优化的原理,养成一种良好的代码习惯。
概述
既然谈优化,就绕不开Android三个内存相关的经典问题:
- OOM
- 内存泄漏
- 频繁GC卡顿
导致这三个问题的原因:
OOM
App在启动时会从系统分配一个默认的堆内存,同时拥有一个堆内存最大值(可以动态申请这个大小),这个Max Heap Size的大小,决定了软件运行时可以申请的最大运行内存。App软件内存分配是个不断创建和GC回收的过程,就像一个水池拥有注入和排出水的通道,当注入过快,排出不足时,水池满了溢出,Out of Memory,即我们常说的OOM。
内存泄漏
当我们在代码中创建对象,会申请内存空间,同时包含一个对象的引用,当我们长时间不使用该引用时,JVM GC操作时会根据这个引用去释放内存。但是,对象的回收可能有点差错,如果这个对象A被另一个线程B所引用,当我们不再使用A,可A却处于B的hold状态,那么我们每次创建的A都得不到回收,这个时候就会发生内存泄漏了。
频繁GC卡顿
上面说了,App的堆内存有最大值,是有限的,那么如果我们频繁的创建,当运行内存不断上升,为了维持App的运行,GC回收也会频繁操作,软件运行资源有些,必然导致卡顿问题。
JAVA的GC机制,非常的复杂和精辟,不可一言概论之,在看过许多blog之后,给出一点自己的总结。
简述JVM GC
我们都知道Java语言非常的方便,不像C语言,申请和释放内存都是自己操作,java有虚拟机帮忙。Android 的每个应用程序都会使用一个专有的Dalvik虚拟机实例来运行,即使内存泄漏也只是kill当前App.
Java虚拟机有一套完整的GC方案,只是简单理解的话就是,它维持着一个对象关系树,当开始GC操作时,它会从GC Roots开始扫描整个Object Tree,当发现某个无法从Tree中引用到的对象时,便将其回收。
GC Roots分类举例:
- Class类
- Alive Thread
- 线程stack上的对象,如方法或者局部变量
- JNI活动对象
- System Class Loader
Java中的引用关系
java中有四种对象引用关系,分别是:强引用StrongRefernce、软引用SoftReference、弱引用WeakReference、虚引用PhantomReference,这四种引用关系分别对应的效果:
StrongRefernce
通过new创建的对象,如Object obj = new Object();,强引用不会被垃圾回收器回收和销毁,即是OOM,所以这也容易造成我们接下来会分析的**《非静态内部类持有对象导致的内存泄漏问题》**
SoftReference
软引用可以被垃圾回收器回收,但它的生命周期要强于弱引用,但GC回收发生时,只有在内存空间不足时才会回收它
WeakReference
弱引用的生命周期短,可以被GC回收,但GC回收发生时,扫描到弱引用便会被垃圾回收和销毁掉
PhantomReference
虚引用任何时候都可以被GC回收,它不会影响对象的垃圾回收机制,它只有一个构造函数,因此只能配合ReferenceQueue一起使用,用于记录对象回收的过程
PhantomReference(T referent, ReferenceQueue<? super T> q)
关于ReferenceQueue
他的作用主要用于记录引用是否被回收,除了强引用其他的引用方式得构造函数中都包含了ReferenceQueue参数。当调用引用的get()方法返回null时,我们的对象不一定已经回收掉了,可能正在进入回收流程中,而当对象被确认回收后,它的引用会被添加到ReferenceQueue中。
Felix obj = new Felix(); ReferenceQueue<Felix> rQueue = new ReferenceQueue<Felix>(); WeakReference<Felix> weakR = new WeakReference<Felix>(obj,rQueue);
总结
看完Android引用和回收机制,我们对于代码中内存问题的原因也有一定认识,当时现实中内存泄漏或者溢出的问题,总是不经意间,在我之后一些列的文章中,会对不同场景的代码问题进行分析和解决,一起来关注吧!
-
App性能优化:内存优化
2020-01-03 14:22:41App性能优化:内存优化 本篇主要探讨app性能优化相关的知识点,预计20年2月底前完成 内存优化工具 内存管理机制 内存都懂解决 内存泄漏解决 MAT详解 小结 ...App性能优化:内存优化
目录:
- 内存优化工具
- 内存管理机制
- 内存抖动解决
- 内存泄漏解决
- MAT详解
- 小结
背景介绍
- 内存是大问题但缺乏关注
- 压死骆驼的最后一根稻草
表现形式
- 内存抖动:锯齿状,GC导致卡顿
- 内存泄漏:可用内存减少、频繁GC
- 内存溢出:OOM、程序异常
1、工具选择
- Memory Profiler
- Memory Analyzer
- LeakCanary
Memory Profiler
- 实时图标展示内存使用量
- 识别内存泄漏、抖动
- 提供捕获堆转储、强制GC以及跟踪内存分配的能力
Memory Profiler的使用
- 非常直观
- 线下平时使用
Memory Analyzer(MAT)
- 强大的Java heap 分析工具,帮助查找内存泄漏和内存占用
- 生成整体报告,分析问题
- 线下深入使用
LeakCanary
- 自动内存泄漏检测
- 线下集成
2、android内存管理机制
Java内存管理机制
Java内存回收算法-标记清除算法
- 标记出所有需要回收的对象
- 统一回收所有被标记的对象
标记回收算法存在的问题
- 标记和清除的效率不高
- 产生大量的内存碎片
Java内存回收算法之 复制算法
- 将内存分成两份,只将数据存储在其中一块上。
- 当需要垃圾回收时,也是标记出废弃的数据,然后将有用数据复制到另一块内存上,最后将第一块内存全部清除
复制算法的问题
- 实现简单,运行高效
- 浪费一半空间,代价大
Java内存回收算法之 标记整理算法
- 标记过程与“标记-清除算法”一样
- 存活对象往一端移动
- 清理其余内存
标记整理算法的特点
- 避免标记-清理导致的内存碎片
- 避免复制算法的空间浪费
Java内存回收算法之 分代收集算法
- 结合多种收集算法优势
- 新生带对象存活率低:复制算法
- 老年代对象存活率高:标记-整理算法
Android内存管理机制
- 内存弹性分配,分配值和最大值受具体设备影响
- OOM场景:内存真正不足,可用内存不足
Dalvik 与Art区别
- Dalvik仅固定一种回收算法
- Art回收算法可运行期选择
- Art具备内存整理能力,减少内存空洞
Low Memory killer
- 进程分类:前端、可见、服务、后台、空进程,优先级依次降低
- 回收收益
内存抖动案例
######制造内存抖动
companion object { var datas: ArrayList<TestClass>? = null val handler = object : Handler() { override fun handleMessage(msg: Message) { //创造内存抖动 for (i in 0..100) { datas = ArrayList<TestClass>(100000) } sendEmptyMessageDelayed(0, 1000) } } }
内存抖动解决技巧
- 找循环或者频繁调用的地方
- 通过Profiler定位,锯齿状或者频繁的GC
内存泄漏
- 定义:内存中存在没有用的对象,又无法回收
- 表现:内存抖动、可用内存逐渐减少
内存泄漏案例
- 静态变量持有Activity引用导致Activity无法被回收
//定义一个接口 public interface Callback { void doOperation(); } //定义持有Activity引用的类 public class CallbackMananger { public static ArrayList<Callback> sCallbacks = new ArrayList(); public static void addCallback(Callback callback) { sCallbacks.add(callback); } public static void removeCallBack(Callback callback) { sCallbacks.remove(callback); } }
- 在生成LeakCanaryActivity,显示一张图片
class MemoryLeakActivity : AppCompatActivity(), Callback { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_memory_leak) val bitmap = BitmapFactory.decodeResource(resources, R.drawable.icon_leaktest) iv.setImageBitmap(bitmap) //保存Activity引用 CallbackMananger.addCallback(this) } override fun doOperation() { } }
-
重复打开关闭 页面,即可在Profiler中看到内存成阶梯状上升,如下图所示:
显然:这里每次GC的的时候并没有导致MemoryLeakActivity被回收,内存图形呈现阶梯状显示。 -
使用MAT分析内存泄漏
1. 进入官网下载MAT工具:MAT下载地址
2. 下载对应系统版本
-
安装MAT工具,双击zip文件解压后得到mat.app
-
获取堆文件 Heap Dump
-
将文件存到本地
-
注意这里是Android的hprof文件,需要使用android 的platform-tool里面的hprof-conv工具对文件进行一次格式转化,转化命令如下:
./hprof-conv /Users/hudingpeng/Desktop/memory-20200221T114049.hprof 2.hprof
-
这样在platform-tools目录下会生成一个2.hprof文件,如下图所示:
-
打开MAT工具:
- 直接点击mat.app会报错,按照以下步骤运行
- 右键mat.app => 显示包内容 =>进入 mat.app/Contents/MacOS ,此目录下就有工具MemoryAnalyzer =>双击直接打开工具就行了
-
10.点击open a Heap Dump选择我们的转化后的文件2.hprof 文件即可,下面分析MAT工具的一些常用用法
MAT工具分析内存泄漏
-
打开hprot文件,选择Histogram
-
搜索我们的泄漏的MemoryLeakActivity,可以看到有4个MemoryLeakActivity对象,表示已经泄漏了
-
右键选择查看with incoming references
-
选择一个,点击查看到GC Root的路径,看到具体引用的地方
这样我们就看到了具体泄漏的地方就是,CallbackManager类里面的静态变量sCallbacks -
点击Group by package 可以按包名排序
这样也可以很方便看到我们的包名下的类对象生成的情况,这里也可以看到MemoryLeakActivity在内存里有4个对象 -
第三个菜单项:dominator_tree 视图,显示对象占用内存的百分比
可以看到对象占用内存的百分比,给我们的内存优化提供一定的参考 -
第四个菜单项:OQL,相当于一种数据查询语言,我们直接搜索我们的类,点击感叹号查询,如下图所示
-
Top Consumer条目:通过图表的形式显示占用内存最多的对象
这对我们的内存占用优化也是一个不错的参考
关于BitMap的优化
Btimap的内存模型
-
获取Bitmap占用的内存
- getByteCount()方法
- 宽 x 高 x 一像素占用的内存
-
常规方式:
背景:图片对内存大小至关重要、图片宽高大于控件宽高 -
通过Epic ARTHook优雅检测不合理的图片
Epic:Epic 是一个在虚拟机层面、以 Java Method 为粒度的 运行时 AOP Hook 框架。简单来说,Epic 就是 ART 上的 Dexposed(支持 Android 4.0 ~ 10.0)。它可以拦截本进程内部几乎任意的 Java 方法调用,可用于实现 AOP 编程、运行时插桩、性能分析、安全审计等。
Epic 被 VirtualXposed 以及 太极 使用,用来实现非 Root 场景下的 Xposed 功能,已经经过了相当广泛的验证。
非常厉害的AOP框架,参考连接:Epic AOP框架
这里我们Hook setImageBitmap方法:
import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.util.Log; import android.view.View; import android.view.ViewTreeObserver; import android.widget.ImageView; import com.optimize.performance.utils.LogUtils; import com.taobao.android.dexposed.XC_MethodHook; public class ImageHook extends XC_MethodHook { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { super.afterHookedMethod(param); // 实现我们的逻辑 ImageView imageView = (ImageView) param.thisObject; checkBitmap(imageView,((ImageView) param.thisObject).getDrawable()); } private static void checkBitmap(Object thiz, Drawable drawable) { if (drawable instanceof BitmapDrawable && thiz instanceof View) { final Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap(); if (bitmap != null) { final View view = (View) thiz; int width = view.getWidth(); int height = view.getHeight(); if (width > 0 && height > 0) { // 图标宽高都大于view带下的2倍以上,则警告 if (bitmap.getWidth() >= (width << 1) && bitmap.getHeight() >= (height << 1)) { warn(bitmap.getWidth(), bitmap.getHeight(), width, height, new RuntimeException("Bitmap size too large")); } } else { final Throwable stackTrace = new RuntimeException(); view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { int w = view.getWidth(); int h = view.getHeight(); if (w > 0 && h > 0) { if (bitmap.getWidth() >= (w << 1) && bitmap.getHeight() >= (h << 1)) { warn(bitmap.getWidth(), bitmap.getHeight(), w, h, stackTrace); } view.getViewTreeObserver().removeOnPreDrawListener(this); } return true; } }); } } } } private static void warn(int bitmapWidth, int bitmapHeight, int viewWidth, int viewHeight, Throwable t) { String warnInfo = new StringBuilder("Bitmap size too large: ") .append("\n real size: (").append(bitmapWidth).append(',').append(bitmapHeight).append(')') .append("\n desired size: (").append(viewWidth).append(',').append(viewHeight).append(')') .append("\n call stack trace: \n").append(Log.getStackTraceString(t)).append('\n') .toString(); LogUtils.i(warnInfo); } }
在App onCreate方法里注册要Hook的方法,传入ImageHook
DexposedBridge.hookAllConstructors(ImageView.class, new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { super.afterHookedMethod(param); DexposedBridge.findAndHookMethod(ImageView.class, "setImageBitmap", Bitmap.class, new ImageHook()); } });
more:LeakCanary 监控内存泄漏
内存优化总结
优化大方向
- 内存泄漏
- 内存抖动
- Btimap
优化细节
- LargeHeap属性 (建议开启,大概率能申请到更多内存)
- onTrimMemory (系统块干掉app了,可以手动清理界面,跳转到主界面)
- 使用系统优化过的集合:SparseArray
- 谨慎使用SharedPreferences
- 谨慎使用外部库
- 业务架构设计合理
-
Android性能优化(二)内存优化
2020-03-07 22:40:45这篇文章,总结一下内存优化相关的一些知识。内存优化,相比于启动优化,可能没那么明显。为什么这么说呢?启动快慢,我们体验一次就能体验出来。而内存增长,我们操作一次,两次,三次......如果不借助什么工具或者... -
Android内存优化
2018-05-23 14:42:38参考Android性能优化(四)之内存优化实战 内存优化的套路: 解决所有的内存泄漏 集成LeakCanary,可以方便的定位出90%的内存泄漏问题; 通过反复进出可疑界面,观察内存增减的情况,Dump Java Heap获取当前堆栈... -
Android 性能优化之内存泄漏检测以及内存优化(中)
2017-04-10 20:49:32上篇博客我们写到了 Java/Android 内存的分配以及相关 GC 的详细分析,这篇博客我们会继续分析 Android 中内存泄漏的检测以及相关案例,和 Android 的内存优化相关内容。 Android 内存泄漏案例和检测 常见... -
服务器内存优化
2019-06-24 18:06:45在对内存优化之前,需要先确定程序每个模块的内存分配。程序的性能有perf、gprof等分析工具,但内存没有较好的分析工具,因此需要自行统计。在linux下/proc/self/statm有当前进程的内存占用情况,共有七项:指标... -
camera内存优化
2020-03-10 10:15:43总有些项目的内存优化落到Camera头上,从Hal1到Hal3,永不停歇...以下适用于HAL3(Android P). 众所周知,内存与Performance在某些条件下,是无法调和的矛盾,请大家根据各项目状态酌情选用. [SOLUTION] ... -
Android性能优化:这是一份全面&详细的内存优化指南
2018-03-14 08:45:09本文主要讲解性能优化中的内存优化,希望你们会喜欢 目录 1. 定义 优化处理 应用程序的内存使用、空间占用 2. 作用 避免因不正确使用内存 & 缺乏管理,从而出现 内存泄露(ML)、内存... -
【redis】redis内存管理、淘汰机制、内存优化
2020-08-25 12:44:21redis内存管理、淘汰机制、内存优化 -
Android 性能优化之内存泄漏检测以及内存优化(下)
2017-04-10 20:50:06上篇博客我们写到了 Android 中内存泄漏的检测以及相关案例,这篇我们继续来分析一下 Android 内存优化的相关内容。 上篇:[Android 性能优化之内存泄漏检测以及内存优化(上)]... -
iOS性能优化-内存优化
2018-09-10 14:25:31一、为什么需要内存优化 二、内存管理 三、常见问题 四、内存占用 五、检测工具 摘要 一、为什么需要内存优化 The easy answer is users have a better experience. Not only will your app launch faster.... -
sap 内存优化
2015-06-24 17:01:25sap 内存优化 -
Android性能优化系列之内存优化
2017-01-21 21:00:45GC)完成的,程序员不需要通过调用函数来释放内存,但也随之带来了内存泄漏的可能,上篇博客,我介绍了 Android性能优化系列之布局优化,本篇博客,我将介绍内存优化的相关知识。内存的分配策略概述程序运行时的内存... -
Android-性能优化-内存优化
2017-09-22 11:16:56Android-性能优化-内存优化 概述 JVM 内存分配机制 详见:JVM 内存分配机制 JVM 垃圾回收机制 详见:JVM 垃圾回收机制 DVM 与 JVM 的区别 虚拟机区别 Dalvik 虚拟机(DVM)是 Android 系统在 ... -
App性能优化之内存优化
2017-04-16 21:52:58App性能优化之内存优化本文为慕课网《App性能优化之内存优化》课程的学习笔记,视频地址 (http://www.imooc.com/video/13670)安卓系统内存分配与回收方式# 如何查看一个app在安卓系统中的内存分配情况? 1.启动... -
android 内存优化
2016-05-25 17:08:38最近在研究一个安卓项目内存优化的问题,确实这是一个比较系统的工程,和个人的代码习惯以及对jvm原理的掌握有很大关系,下面提示一些注意点 1. 内存优化 Android系统对每个软件所能使用的RAM空间... -
[Unity 优化]内存优化
2017-11-07 22:21:40内存优化可以使用Unity profiler或者对应平台的内存分析工具进行分析,从 Unity3D内部的内存+Mono的托管内存 进行分析,避免内存泄漏。1、内存测试一般使用手机测试,Unity profiler在Editor测试下的结果不准确。... -
Redis 内存优化方式
2018-09-28 09:46:19Redis 内存优化是对数据类型做一些参数上的优化 内存优化方式与参数 关闭 Redis 的虚拟内存[VM]功能,即 redis.conf 中 vm-enabled = no 设置 redis.conf 中 maxmemory ,用于告知 Redis 当使用了多少物理内存后... -
【内存优化】(MDCC技术大咖秀)Android内存优化之OOM
2015-09-21 17:34:16原文地址: 【MDCC技术大咖秀】Android内存优化之OOM -
c++的内存优化
2019-01-18 17:43:332.内存优化有哪些方式 3.怎样做内存优化 概述: 我们常常在开发场景下提及的内存是指程序内存. 程序内存可以分为以下五种: 1、 栈区(stack):栈的空间是连续的, 先进后出能保证不会产生内存碎片, 由高地址向低... -
Android App性能优化之内存优化
2017-03-09 10:45:27为什么要进行内存优化?1.App运行内存限制,OOM导致App崩溃2.App性能:流畅性、响应速度和用户体验Android的内存管理方式Android系统内存分配与回收方式● 一个App通常就是一个进程对应一个虚拟机可以通过下面的命令... -
Unity 内存优化
2018-09-25 16:22:11内存优化的几个方面 1.压缩自带类库; 2.运用对象池; 3.释放AssetBundle占用的资源; 4.降低模型的片面数,降低模型的骨骼数量,降低贴图的大小; 5.使用光照贴图,使用多层次细节(LOD),使用着色器(Shader),... -
cocos creator内存优化
2019-12-10 11:49:34一、内存优化 因为 iOS小游戏和微信共用同一个进程,而微信在连续两次收到系统内存警告的时候会关闭小游戏并释放小游戏占用的内存。如果你的小游戏有外网用户反馈“闪退”,或者你自己测试的时候频繁出现“该小... -
【Android 性能优化】—— 详解内存优化的来龙去脉
2017-03-19 16:43:06本文是【Android 性能优化】系列的第二篇文章,我们在第一篇【Android 性能优化】—— UI篇中主要介绍了Android界面的优化的原理以及方法,这一篇中我们将着重介绍Android的内存优化。本文的篇幅很长,但是请不要嫌... -
Android 性能优化之内存优化
2016-12-18 20:33:11Android 性能优化之内存优化 Android 应用程序在开发的过程中内存的准确控制是判断一个程序好坏的重要标准之一: 一、假如我们开发的程序内存溢出、泄漏了会引发那些实质性的问题呢? 1、程序卡顿、响应速度变慢。... -
Redis内存优化
2020-04-16 14:50:06使用maxmemory参数限制最大可用内存,当超出内存上限maxmemory时使用LRU等删除策略释放空间以及防止所用内存超过服务器物理内存。 2.配置内存回收策略 Redis所用内存达到maxmemory上限时会触发相应的溢出控制策略... -
Android性能优化之内存优化
2016-05-08 18:02:401、内存溢出的原因 1.1、内存泄漏 内存泄漏和内存溢出的区别: 内存泄漏:程序中存在对无用对象的引用,导致GC无法回收。内存泄漏最终会导致oom。 内存溢出:程序在申请内存时,没有足够的内存...2、内存优化 2 -
mysql内存优化总结
2018-05-09 20:36:211.mysql内存优化原则 a. 将尽量多的内存分配给mysql做缓存,但是也要给操作系统和其他程序预留足够内存。 b. MyISAM的数据文件读取依赖于操作系统的io,因此如果有MyISAM表,就要预留更多的内粗给操作系统做io缓存...