android弱引用有哪些(android性能优化和内存优化)

1、Android内存管理机制 1.1Java内存分配模型 先上一张JVM将内存划分区域的图 程序计数器:存储当前线程执行目标方法执行到第几行。 栈内存:Java栈中存放的是一个个栈帧,每个栈帧对应一个被调用的方法。栈帧包括局部标量表, 操作数栈。 本地方法栈:本地方法栈主要是为执行本地方法服务的。而Java栈是为执行Java方法服务的。 方法区:该区域被线程共享。主要存储每个类的信息(类名,方法…

1、Android内存管理机制

1.1 Java内存分配模型

先上一张JVM将内存划分区域的图

Android 性能优化——内存优化

程序计数器:存储当前线程执行目标方法执行到第几行。

栈内存:Java栈中存放的是一个个栈帧,每个栈帧对应一个被调用的方法。栈帧包括局部标量表,

操作数栈。

本地方法栈:本地方法栈主要是为执行本地方法服务的。而Java栈是为执行Java方法服务的。

方法区:该区域被线程共享。主要存储每个类的信息(类名,方法信息,字段信息等)、静态变量,常量,以及编译器编译后的代码等。

堆:Java中的堆是被线程共享的,且JVM中只有一个堆内存,主要存储对象本身及数组

1.2 Dalvik和ART介绍

Dalvik:Dalvik是Google公司自己设计用于Android平台的Java虚拟机。它可以支持已转换为.dex格式的Java应用程序的运行,.dex格式是专门为Dalvik应用设计的一种压缩格式,适合内存和处理器速度有限的系统,Dalvik经过优化,允许在有限的内存中同时运行多个虚拟机实例,并且每一个Dalvik应用做为独立的Linux进程执行,独立的进程可以防止在虚拟机崩溃的时候所有程序都被关闭。

ART:ART表示Android Runtime,Dalvik是依靠一个just-In -Time编译器去解释字节码,运行时编译后的应用都需要通过一个解释器在用户的设备上运行,这一机制并不是特别高效,但是能让应用更容易在不同的硬件和架构上运行。ART则是完全改变了这种做法,在安装应用的时候就预编译字节码到机器语言,这一机制叫预编译。在移除解释代码这一过程,应用程序执行将更有效率,启动速度更快。

ART优点:

1.系统性能更高

2.应用启动速度,运行更快,体验更好,触感反馈更加及时。

3.更长的电池续航能力

4.支持更低的硬件

ART缺点:

1.储存空间占用更大。

2.应用安装时间更长。

Dalvik与ART区别

1.Dalvik每次都要编译在运行,art只会安装时启动编译

2.art占用的空间比Dalvik要大,就是用空间换时间

3.art减少编译,减少CPU使用频率,使用明显改善电池续航

4.art启动,运行更快,体验更好,触感反馈更及时。

1.3 为什么要进行内存优化

1.减少oom,提高应用的稳定性

2.减少卡顿,体验更好

3.减少内存占用,应用存活率更高

4.提前处理掉一些异常的隐患

2、Java内存回收算法

2.1判断Java中对象是否存活的算法

2.1.1 引用计数算法

堆内存的每个对象都有一个引用计数器,当对象被引用的时候,计数器+1,当引用失效时计数器-1,当计数器的值为0时,说明该对象没有被引用,就会被认为是垃圾对象,系统将会将其回收内存重新分配。

优点:引用计数器执行简单,判定效率高。

缺点:对于循环引用的对象难以判断出来,同时引用计数器增加了程序执行的开销,在jdk1.1后,就不在使用了。

2.1.1 根搜索法

GC Roots的对象做为起点,然后向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,则该对象不可达,也就是说该对象为为垃圾对象,可以被回收。

在Java中,可以做为GC Roots的对象包括一下四种:

1.虚拟机栈中引用的对象

2.方法区中的类静态属性引用的对象

3.方法区中常量引用的对象

4.本地方法栈中JNI的引用对象

2.2 JVM垃圾回收算法

2.2.1 标记清除法

最基础的垃圾收集算法,算法分为标记和清除两个阶段:首先标记出所有需要回收的对象,在标记完成之后统一回收掉所有被标记的对象。

缺点:效率低,其次会产生大量的不连续的内存碎片,导致提前触发另一次垃圾收集动作。

Android 性能优化——内存优化

2.2.2 复制回收算法

复制回收算法是将可用内存按容量分成大小相等的两块,每次只使用其中的一块,当这块内存使用完了,就将存活的对象复制到另一块内存上去,然后把使用过的内存空间一次清理掉,这样使得每都次都是对其中一块内存进行回收,内存分配时不用考虑内存碎片等复杂情况。

缺点:可使用内存降为原来的一半。

Android 性能优化——内存优化

2.2.3 标记整理法

标记-整理算法在标记-清除算法的基础上做了改进,标记阶段将可回收的对象标记出来,标记完成后不是直接对可回收的对象进行清理,而是让所有存活的对象都向一端移动,在移动的过程中清理掉可回收的对象。

优点:相比于标记清除法来说,标记整理法不会大量产生不连续内存碎片问题。

缺点:如果是在对象存活率较高的情况下会执行较多的复制操作,效率将会降低很多,而在存活率较低的情况下,效率会大大提高。

Android 性能优化——内存优化

2.2.4 分代收集回收算法

当前商业虚拟机都是采用的是分代收集算法,根据对象存活的周期不同将内存划分为几块,一般是将java堆分为年轻代,老年代和永久代。然后根据各个年代的特点来采取不同收集算法,年轻代存活率较低,采用复制回收算法,老年代对象存活率较高,采用标记清除法或者是标记整理法来进行回收。

Android 性能优化——内存优化

3、内存问题表现形式

3.1 内存抖动

内存波动图呈锯齿状,gc频繁导致卡顿。

Android 性能优化——内存优化

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文件

Android 性能优化——内存优化

导出来的Dump是没法直接使用mat打开的,Android SDK自带了一个转换工具在SDK的platform-tools下,其中转换语句为:

cd D:aasdkplatform-tools
hprof-conv aaa.hprof  bbb.hprof

注:aaa.hprof表示从profiler中导出来的dump文件,bbb.hprof 表示转化出来的dump文件

使用mat打开转化出来的dump

MAT视图

Android 性能优化——内存优化

在MAT窗口上,OverView是一个总体概览,显示总体的内存消耗情况和疑似问题。

1、Histogram:列出内存中的所有实例对象和个数以及大小,在顶部regex区域支撑正则表达式查找

2、Dominator Tree:列出最大的对象及其依赖存活的Object,相比于Histogram,能更方便的看出引用关系。

3、Top Consumers:通过图像列出最大的Object

4、Leak Suspects:通过MAT自动分析内存泄露的原因和泄露的一份总体报告

其中分析内存情况,我们基本用到的就是Histogram和Dominator Tree

Android 性能优化——内存优化

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 弱软虚引用。

Android 性能优化——内存优化

图标的左下角出现这个,则表示出现了内存泄露。然后回调代码中分析即可。

4.3 LeakCanary

使用

implementation \'com.squareup.leakcanary:leakcanary-android:1.5.4\'

application中

public class App extends Application {

    private RefWatcher mRefWatcher;

    @Override
    public 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 {

    @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);
    }

}

在application中

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());
            }
        });

这样在开发者调用setImageBitmap 来设置图片的时候,都会进行对图片的宽高进行比如,如果超出一定的范围则进行提示。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

(0)
上一篇 2022年5月12日 下午2:23
下一篇 2022年5月12日 下午5:58

相关推荐

  • 微信号怎么加好友,微信快速加好友软件分享

    朋友认识,都会留个联系方式,方便联络和交流,过去是写个地址,然后留个电话,到现在最常用的,“来,加个微信吧”。 今天我们来分享一下,添加微信好友的2种方式。 方式一:扫码加好友 我们常说扫码扫码,这个码说的就是二维码,每个人的微信号都有一个独立的二维码,大家可以把这个二维码理解为微信给每个人发的一个小身份证。 扫码加好友是两个人操作,老张必须把自己的二维码,就是这个小身份证调出来,老陈用“扫一扫”…

    2022年7月15日
    620
  • 公司怎样做好营销,了解公司营销思路和创新点

    体验式营销 随着社会文明的高度发展,消费者开始追求更高层次的消费体验,体验式营销作为现代企业营销方法的一部分,能更好地兼顾消费者在整个消费过程中的体验,体验式营销要求将“感官”、“情感”、“思考”、“行动”、“关联”统一起来,为消费者带来全新的体验,加深对产品的印象,促进消费者消费。 连锁营销 连锁营销通过复制企业的管理特点和模式,实现对门店的复制扩张,促进企业的连锁化发展。直线管理咨询、深圳十大…

    2022年7月8日
    630
  • 卢松松大鱼号收入859元

    卢松松大鱼号收入859元

    2022年9月3日
    570
  • app推广如何赚钱,靠谱挣钱的app软件

    任何一个app运营人员都无法回避一个老生常谈的问题,那就是“推广一个app需要多少钱?”一个app推广需要多少钱呢?app应用如何推广?方式又有哪些呢?是不是每种app推广方式的收费标准都一样呢?今天,活动盒子小编就以常见的app推广方式为例说说app软件推广需要花多少钱。 1、APP应用市场推广要多少钱 国内的很多APP应用市场都是免费上传APP软件的,但是想要使APP推广获得更好的效果的话,很…

    2022年8月10日
    630
  • ktv点歌系统安装(免费家庭k歌软件)

    关于家庭KTV(卡拉OK)系统,很多小伙伴在问,之前老蜗牛分享了《家庭KTV卡拉OK练歌房功能怎么搞(一)》、《家庭卡拉OK(KTV)设备推荐和选购注意事项》,但是更多的小伙伴表示不会用,要求介绍一下系统如何连接和调试,今天就来分享一下。 其实并不难,非新手小白可以只接跳过。 首先我们来认识一下接下来需要用到的线材: 红白双莲花头音频信号线,这种线大家应该都见过,老式电视机、VCD等经常会用到,我…

    2022年5月9日
    1130
  • seo营销概念及优势是什么,seo营销基本实施步骤介绍

    1、分析与定位 所谓分析与定位,指的就是我们要进行分析我们的竞争对手,然后对我们自己进行定位,包括关键词的选择之类的等等系列。 2、制定seo营销战略规划 在经过了第一步以后,第二部就需要对第一步中得到的数据进行一个整理和安排,主要是针对营销型网站的整体设计及seo的战略规划等。 3、实施 在经过了第二部以后,我们就开始实际的实施工作了,网站的内容策略的实施、外部信息的推广等等操作。 4、监控与反…

    2022年5月18日
    700
  • 深圳营销型网站怎么样,高端品牌网站建设设计制作

    品牌营销型网站在企业的互联网营销中发挥着巨大的作用,当然,企业网站能否成交光有功能也是不够的,一个好的营销系统首先得要有一个好的展现形式,好的内容载体,这就是我们通常所说的网络营销的好工具——企业营销型网站,企业必须具备一个具有强烈营销冲击力的营销型网站,加上好产品、好服务、好的盈利模式,完善的售后才能够驰骋在网络营销的战场。 先来聊聊:我们为什么要建设营销型网站? 营销型网站与普通网站本质区别:…

    2022年6月30日
    1550
  • 自制幕布的最佳材料是什么,幕布和白墙区别大吗

    买了投影仪要不要买幕布是很多人纠结的问题,幕布和白墙的区别是什么?怎样的幕布性价比最高。要了解这些问题,首先要确定,幕布的优势在哪里? 一投影仪幕布的优势 1画面更平整 投影仪利用的是漫反射的成像原理,虽然这样有利于保护眼睛,但由于墙面的凹凸不平导致反射光线不集中,亮度也会大大削弱。投影仪幕布相对于白墙来说,表面更平整,反射光线聚合度更高,有利于亮度的集中。 2黑色边框有利于集中注意力 就像我们平…

    2022年6月9日
    1580
  • 哪个网站招聘靠谱,最靠谱的招聘网站排名

    现在在网上找工作的人越来越多了,但是招聘网站众多,各个企业鱼龙混杂,所以我们在找工作的时候要提高警惕,选择一些比较靠谱的招聘网站。那么,小编为大家来盘点一下我们常用的几个网站,来看看哪个招聘网站靠谱!  一、综合类招聘网站 1、智联招聘 和他类似的,还有51job,BOSS直聘,拉勾网,中华英才网等,这几个应该算是最常用的全国性的综合性招聘网站。基本上输入求职地点和求职行业,就能出来一片片的职位信…

    2022年7月6日
    3550
  • 索尼z4详细参数(索尼z4平板测评)

    IT之家讯近几年来,虽然索尼在平板方面一直保持低调,但每年的进步大家也看在眼里。XperiaZ2平板和Z3Compact平板功能丰富,设计优秀,在2014年取得了丰富的战果。索尼在大量用户调查和Z2平板的基础上,对XperiaZ4平板进行了大量改进,该机非常纤薄,并且搭载了先进的高分辨率屏幕和强大的芯片。XperiaZ4平板的确足够梦幻,但梦幻是否代表实用、好用?请看以下分解。 XperiaZ4平…

    2022年5月4日
    1360

发表回复

登录后才能评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信