您现在的位置是:首页 > 网络趣梗网络趣梗
android软引用和弱引用(android强引用和弱引用)
2022-08-30 12:36:50网络趣梗0人已围观
简介 android软引用和弱引用(android强引用和弱引用),新营销网红网本栏目通过数据整理汇集了android软引用和弱引用(android强引用和弱引用)相关信息,下面一起看看
android软引用和弱引用(android强引用和弱引用),新营销网红网本栏目通过数据整理汇集了android软引用和弱引用(android强引用和弱引用)相关信息,下面一起看看。
1.Android内存管理机制1.1 Java内存分配模型。首先是JVM划分内存区域的示意图。
程序计数器:存储当前线程执行目标执行到的行。
堆栈内存:Java stack存储堆栈帧,每个堆栈帧对应一个被调用的。该堆栈包括本地标签表,
操作数堆栈。
本地堆栈:本地堆栈主要用于执行本地服务。Java栈用于执行Java服务。
区域:该区域由线程共享。它主要存储每个类的信息(类名,信息,字段信息等。)、静态变量、常量以及编译器编译的代码。
堆:Java中的堆是线程共享的,JVM中只有一个堆内存,主要存储对象本身和数组。
1.2 dal vik和ART简介Dalvik:Dalvik是Google为Android平台设计的Java虚拟机。它可以支持已经转换为。dex格式。dex格式是专门为Dalvik应用设计的压缩格式,适用于内存和处理器速度有限的系统。Dalvik经过优化,允许多个虚拟机实例在有限的内存中同时运行,每个Dalvik应用都作为独立的Linux进程执行,可以防止虚拟机崩溃时所有程序关闭。
ART:ART代表Android运行时。Dalvik依靠实时编译器来解释字节码。所有在运行时编译的应用程序都需要通过解释器在用户的设备上运行。这种机制不是特别有效,但它可以使应用程序更容易在不同的硬件和架构上运行。ART完全改变了这种做法,在安装应用程序时将字节码预编译成机器语言。这种机制称为预编译。在移除解释代码的过程中,应用的执行效率会更高,启动速度也会更快。
艺术的优势:
1.系统性能更高
2.应用启动速度,操作更快,体验更好,触觉反馈更及时。
3.更长的电池寿命
4.支持下层硬件
艺术缺点:
1.存储空间占用更多的空间。
2.应用程序安装时间较长。
达尔维克和艺术的区别
1.Dalvik每次都会编译运行,art只在安装的时候才开始编译。
2 .艺术比达尔维克占用更多的空间,也就是空间换时间。
3.art减少编译,降低CPU使用频率,明显提高续航。
4 .美术启动,运行速度更快,体验更好,触觉反馈更及时。
1.3为什么要优化内存1。减少oom,提高应用程序的稳定性。
2.减少停滞,获得更好的体验
3.内存占用减少,应用存活率更高。
4.提前排除一些异常隐患。
2.Java内存回收算法2.1 Java中判断对象是否存活的算法2.1.1引用计数算法堆内存中的每个对象都有一个引用计数器,引用对象时为1,引用无效时为-1。当计数器的值为0时,表示该对象没有被引用,所以会被认为是垃圾对象,系统会重新分配其回收的内存。
优点:参考计数器执行简单,判断效率高。
缺点:循环引用对象判断困难,引用计数器增加了程序执行的开销。jdk1.1以后就不再用了。
2.1.1根搜索方法GC根的对象作为起点,然后向下搜索。搜索的路径称为引用链。当一个对象在没有任何引用链的情况下连接到GC根时,该对象是不可达的,也就是说,它是一个垃圾对象,可以回收。
在Java中,有四种对象可以用作GC根:
1.虚拟机堆栈中引用的对象
2.区域中类的静态属性引用的对象
3.区域中常数引用的对象
4.本地堆栈中JNI的引用对象
2.2 JVM垃圾收集算法2.2.1标记法最基本的垃圾收集算法分为两个阶段:首先标记所有需要收集的对象,标记后统一收集所有被标记的对象。
缺点:效率低。其次,会产生大量不连续的内存碎片,提前触发另一次垃圾收集动作。
2.2.2副本恢复算法副本恢复算法根据容量将可用内存分成大小相等的两块,每次只使用一块。当这块内存用完时,它将幸存的对象复制到另一块内存中,然后一次性清理已用的内存空间,这样每次回收一块内存,内存分配不需要考虑内存碎片等复杂情况。
缺点:可用内存减少到原来的一半。
2.2.3标记整理方法标记整理算法是在标记清除算法的基础上改进的。在标记阶段,可回收的物体被标记出来。不是在做好标记后直接清理可回收物,而是把所有活体都搬到一端,在移动的过程中把可回收物清理干净。
优点:与标记清除法相比,标记排序法不会造成大量不连续的内存碎片。
缺点:如果在对象存活率高的时候进行更多的复制操作,效率会大大降低,而存活率低的时候效率会大大提高。
2.2.4代收集回收算法目前所有商用虚拟机都采用代收集算法,根据对象生命周期的不同,将内存分成若干块。一般java堆分为年轻一代,老一代,永久一代。然后根据每个年龄段的特点,采用不同的采集算法。年轻一代的存活率低,而采用繁殖恢复算法,年龄较大的对象存活率高。采用标记清除法或标记排序法。
来进行回收。
3、内存问题表现形式3.1 内存抖动
内存波动图呈锯齿状,gc频繁导致卡顿。
3.2 内存泄漏
内存泄露简单来说就是系统分配出去的内存由于某种原因导致没法释放,内存会越来越小,最终导致oom。
3.3 内存溢出
即OOM,OOM时会导致程序异常。Android设备出厂以后,java虚拟机对单个应用的最大内存分配就确定下来了,超出这个值就会OOM。
4、内存优化常用工具4.1 Memory Profiler
Memory Profiler是Android studio自带的工具,实时图表形式展示应用内存使用的情况,可以用来识别内存泄露,抖动等
注意:如果在控制台中没有找到Profiler,可View —– Tool Windows — Profiler 进行打开
优点:方便直观,便于线下使用
4.2 Memory Analyzer(MAT)
1、强大的java heap分析工具,查找内存泄露及内存占用
2、生成整体报告,便于分析问题
3、可以在线下深入使用
MAT使用:
MAT下载地址: https://www.eclipse.org/mat/downloads.php
获取hprof文件
导出来的Dump是没法直接使用mat打开的,Android SDK自带了一个转换工具在SDK的platform-tools下,其中转换语句为:
cd D:aasdkplatform-toolshprof-conv aaa.hprof bbb.hprof
注:aaa.hprof表示从profiler中导出来的dump文件,bbb.hprof 表示转化出来的dump文件
使用mat打开转化出来的dump
MAT视图
在MAT窗口上,OverView是一个总体概览,显示总体的内存消耗情况和疑似问题。
1、Histogram:列出内存中的所有实例对象和个数以及大小,在顶部regex区域支撑正则表达式查找
2、Dominator Tree:列出最大的对象及其依赖存活的Object,相比于Histogram,能更方便的看出引用关系。
3、Top Consumers:通过图像列出最大的Object
4、Leak Suspects:通过MAT自动分析内存泄露的原因和泄露的一份总体报告
其中分析内存情况,我们基本用到的就是Histogram和Dominator Tree
Class Name:类名。
Objects:对象实例个数。
Shallow Heap:对象自身占用内存大小,不包括它引用的对象
Retained Heap:是当前对象大小和直接或者间接引用到的对象大小总和,包括递归释放的。、
查找内存泄露方式
步骤1:在Regex通过包名进行匹配,当然也可以通过其他方式进行匹配
步骤二:右键选中怀疑对象,List objects – with incoming references
注 with outgoing references 他引用了那些对象
with incoming references 那些对象引用了他
步骤三:选择当前的一个 Path to GC Roots/Merge to GC Roots 的 exclude All 弱软虚引用。
图标的左下角出现这个,则表示出现了内存泄露。然后回调代码中分析即可。
4.3 LeakCanary
使用
implementation com.squareup.leakcanary:leakcanary-android:1.5.4
application中
public class App extends Application {private RefWatcher mRefWatcher;@Overridepublic void onCreate() {super.onCreate();mRefWatcher = LeakCanary.install(this);}public static RefWatcher getRefWatcher(Context context) {App application = (App) context.getApplicationContext();return application.mRefWatcher;}}
在activity或者fragment中的onDestory() 调用
RefWatcher refWatcher = App.getRefWatcher(getActivity());refWatcher.watch(this);
原理
主要是通过WeakReference + ReferenceQueue来判断对象是否被系统GC回收,WeakReference创建时,传入一个ReferenceQueue对象,当WeakReference引用的对象生命周期结束后,会被添加到ReferenceQueue中,当GC过后,对象一直没有被添加进入到ReferenceQueue,可能就会存在内存泄露,再次触发GC,二次确认。
5、常见的内存泄露
1、资源性对象未关闭
对于资源性对象不再使用时,应该立即调用它的close()函数,将其关闭,然后再置为null。例如Bitmap等资源未关闭会造成内存泄漏,此时我们应该在Activity销毁时及时关闭。
2、注册对象未注销
例如BraodcastReceiver、EventBus未注销造成的内存泄漏,我们应该在Activity销毁时及时注销。
3、类的静态变量持有大数据对象
尽量避免使用静态变量存储数据,特别是大数据对象,建议使用数据库存储。
4、单例造成的内存泄漏
优先使用Application的Context,如需使用Activity的Context,可以在传入Context时使用弱引用进行封装,然后,在使用到的地方从弱引用中获取Context,如果获取不到,则直接return即可。
5、非静态内部类的静态实例
该实例的生命周期和应用一样长,这就导致该静态实例一直持有该Activity的引用,Activity的内存资源不能正常回收。此时,我们可以将该内部类设为静态内部类或将该内部类抽取出来封装成一个单例,如果需要使用Context,尽量使用Application Context,如果需要使用Activity Context,就记得用完后置空让GC可以回收,否则还是会内存泄漏。
6、Handler临时性内存泄漏
Message发出之后存储在MessageQueue中,在Message中存在一个target,它是Handler的一个引用,Message在Queue中存在的时间过长,就会导致Handler无法被回收。如果Handler是非静态的,则会导致Activity或者Service不会被回收。并且消息队列是在一个Looper线程中不断地轮询处理消息,当这个Activity退出时,消息队列中还有未处理的消息或者正在处理的消息,并且消息队列中的Message持有Handler实例的引用,Handler又持有Activity的引用,所以导致该Activity的内存资源无法及时回收,引发内存泄漏。解决方案如下所示:
1、使用一个静态Handler内部类,然后对Handler持有的对象(一般是Activity)使用弱引用,这样在回收时,也可以回收Handler持有的对象。
2、在Activity的Destroy或者Stop时,应该移除消息队列中的消息,避免Looper线程的消息队列中有待处理的消息需要处理。
需要注意的是,AsyncTask内部也是Handler机制,同样存在内存泄漏风险,但其一般是临时性的。对于类似AsyncTask或是线程造成的内存泄漏,我们也可以将AsyncTask和Runnable类独立出来或者使用静态内部类。
7、容器中的对象没清理造成的内存泄漏
在退出程序之前,将 里的东西clear,然后置为null,再退出程序
8、WebView
WebView都存在内存泄漏的问题,在应用中只要使用一次WebView,内存就不会被释放掉。我们可以为WebView开启一个独立的进程,使用AIDL与应用的主进程进行通信,WebView所在的进程可以根据业务的需要选择合适的时机进行销毁,达到正常释放内存的目的。
9、使用ListView时造成的内存泄漏
在构造Adapter时,使用缓存的convertView。
6、优化内存空间的方式6.1、java对象的引用
强引用:我们平时开发写的代码,基本百分之九十九的都是强引用。
软引用:如果一个对象具有软引用,那么当内存不足时,就会回收它。
弱引用:GC时,只要发现有弱引用,那么就会回收它,当然,有可能存在GC多次才发现
虚引用:虚引用必须要和引用队列关联起来使用。任何时候都有可能被垃圾回收器回收。一般可以用来判断GC的频率,GC频率过高,那么说明内存出了问题。同时也可以监听某个重要的对象是否被回收。
所以,在平时我们编写代码的时候,适当的使用软引用,弱引用,对我们的内存优化也能起到重要的作用。
6.2、减少不必要的内存开销
1、AutoBoxing
自动装箱的核心是吧基础数据类型转换成对应的包装类,比如int 类型只是占用4字节,但是Integer对象占用16字节。
2、内存复用
资源复用:通用的字符串,颜色定义,简单页面布局的复用
视图复用: 进行布局复用
3、使用优化过的数据类型
如 SparseArray、SparseBooleanArray、LongSparseArray,使用这些API可以让我们的程序更加高效。HashMap 工具类会相对比较 低效,因为它 需要为每一个键值对都提供一个对象入口,而 SparseArray 就 避免 掉了 基本数据类型转换成对象数据类型的时间。
4、项目中少用枚举,枚举占用内存是常量三倍。
5、在应用可以内存过低时主动释放内存
在application中的 onTrimMemory/onLowMemory,内存紧张时会回调该 ,我们可以在这个 中释放掉图片缓存,静态缓存来避免被kill。
6、避免创建一些不必要的对象
如在字符串拼接时不要用“+”来进行拼接,而是使用StringBuffer,StringBuilder来替代。因为String 内部是被final修饰的,不可继承,使用+进行拼接是会产生一个新的对象,而占用内存。
7、尽量不要在一些循环的地方创建对象。如自定义的时候在onDraw() 。
7、优雅的检测大图
项目中会经常遇到这样的情况,我们的布局中,控件的宽高可能只是50 * 50 但是从服务器给过来的图片或者是UI给过来的图片往往会大很多,而如果图片在资源文件下还好,可以直接查看宽高,但是如果从服务器上获取到的呢,这是我们经常会忽略的。而图片过大,占用的内存就更多,这是没有必要的。那么我们怎么检测出服务器给过来的图片过大的呢?
7.1、继承ImageView 重新实现onDraw();
这种 我们可以重新测量图片的宽高,超过一定的范围,我们就可以输出警告。但是这种 对代码侵入性很强。如果是有新同学加入,容易造成代码混乱。
7.2、ARTHook
Hook的意思是钩子,也就是在消息过去之前可以把消息勾住,不让其传递,能够针对不同的消息或者api在执行之前,先执行我们自己的操作。
这里推荐使用Epic 框架: https://github.com/tiann/epic
添加依赖
implementation me.weishu:epic:0.3.6
创建一个ImageHook类
package com.optimize.performance.memory;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 {@Overrideprotected 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() {@Overridepublic 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);}}
在application中
DexposedBridge.hookAllConstructors(ImageView.class, new XC_MethodHook() {@Overrideprotected void afterHookedMethod(MethodHookParam param) throws Throwable {super.afterHookedMethod(param);DexposedBridge.findAndHookMethod(ImageView.class, "setImageBitmap", Bitmap.class, new ImageHook());}});
这样在开发者调用setImageBitmap 来设置图片的时候,都会进行对图片的宽高进行比如,如果超出一定的范围则进行提示。
相关文章
粤语歌曲网(经典粤语歌曲汇总)
北京奥特莱斯(来北京必逛的12个商场)
豪杰超级解霸(还记得豪杰超级解霸吗?)
龚自珍的资料(清代诗人龚自珍一生功绩简介)
象牙塔是什么意思(“象牙塔”里怎么了?)
水色风信子(水培风信子容易养)
婧字怎么读
虞姬是哪里人(古代著名美人虞姬到底是哪里人?)
公办三本院校(我们还有哪些“三本”?)
电子酒柜(酒柜最全选购指南)
德州景点(德州市景区景点62个)
玉兰油官方网(OLAY发布高端臻粹系列)
更多android软引用和弱引用(android强引用和弱引用)相关信息请关注本文章,本文仅仅做为展示!
Tags: 网络趣事
很赞哦! ()
相关文章
随机图文
留言与评论 (共有 条评论) |