百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

Android优化———内存优化 安卓 内存优化

lipiwang 2024-10-27 13:29 8 浏览 0 评论

Java虚拟机

Java内存模型

  • 虚拟机栈( 线程私有 ):局部变量表、操作数栈、动态链接、方法出口等信息
  • 堆( 线程共享 ):实例对象
  • 方法区( 线程共享 ):类信息,常量,即时编译器编译后的代码
  • 程序计数器( 线程私有 ):字节码行号指示器,记录当前线程执行到多少行
  • 本地方法栈( 线程私有 ):和虚拟机栈类似,两者的区别就是虚拟机栈是为虚拟机执行java方法服务,本地方法栈为虚拟机执行native方法服务 。

程序计数器

线程计数器中如果正在执行java方法,计数器记录的是当前指令的地址,

如果是Native方法,计数器记录为空

堆内存 = 新生代(1) + 老年代(2)

  • 新生代:复制算法
  • 老年代:标记整理算法

方法区

也叫“永久代”,1.8以后将方法区去除了,将方法区移动到直接内存

内存回收主要考虑 堆区方法区 的回收,其他部分会根据线程的产生和消亡

个版本区别

  • 1.6:运行常量池在方法区
  • 1.7:运行常量池在堆中
  • 1.8:删除方法区,引入直接内存,元空间概念,方法区中的静态变量被转移到堆中,只有class元数据在元空间。

堆中的老年代和方法区(永久代)是绑定的,无论哪一方满了,都会触发双方的GC回收

问题:

  1. 堆和栈的区别:栈:基本数据类型变量(int、short、long、byte、float、double、boolean、char)以及对象的引用变量堆:存储java对象堆中的对象对所有线程可见,栈内存只属于一个线程堆的内存空间远远大于栈
  2. 为什么删除方法区?启动大小固定,很难调优,容易发生OOM元空间在本地内存中分配,本地内存足够就不会溢出

GC垃圾回收

判断对象是否存活

  1. 引用计数算法(缺点:循环引用,技数永远不为0)
  2. 可达性算法(二叉树中向下搜索,不存在引用链则对象不可用)

回收算法

  1. 标记清除算法:标记完后对对象进行回收,使用在 老年代缺点:效率不高,标记和清除效率不高差生大量碎片空间,导致空间浪费
  2. 复制算法:将可用对象复制到新的连续空间,删除之前的空间缺点:浪费50%的内存,复制长生存期的对象效率低下,所以该算法使用在 新生代
  3. 标记整理算法:前期使用标记清除算法,后续使用整理算法,使对象排列称联系空间,使用在 老年代
  4. 分代收集算法:对数据进行分代,每一代执行不同的回收算法

年轻代分为eden、s0、s1区,分别为8:1:1,年轻代和老年代为1:2

  1. 元空间的gc:元空间中的类加载器存活,则元空间中元数据也存活

Minor GC : 清理年轻代
Major GC : 清理老年代
Full GC : 清理整个堆空间,包括年轻代和永久代

四大引用介绍

简述

  • 强引用:Strong Reference,通常使用的对象方式,gc不会回收
  • 软引用:SoftReference,当内存不足时进行回收
  • 弱引用:WeakReference,下一次gc时回收
  • 虚引用:PhantomReference,任何时候可回收

在内存泄露问题处理上,使用最多的是弱引用,许多源码、框架都是用

eg:

  1. ThreadLocalMap中存储以ThreadLocal的弱引用为键,具体内容为value
  2. Glide中缓存使用activeResource,存储的是图片的弱引用
  3. 解决Handler的内存泄漏使用弱引用

Reference理解

所有的引用都是继承自Reference,以下以WeakReference为例:

public class WeakReference<T> extends Reference<T> {

    /**
     * Creates a new weak reference that refers to the given object.  The new
     * reference is not registered with any queue.
     *
     * @param referent object the new weak reference will refer to
     */
    public WeakReference(T referent) {
        super(referent);
    }

    /**
     * Creates a new weak reference that refers to the given object and is
     * registered with the given queue.
     *
     * @param referent object the new weak reference will refer to
     * @param q the queue with which the reference is to be registered,
     *          or <tt>null</tt> if registration is not required
     */
    public WeakReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q);
    }

}

其中存在两种构造方法,区别在于是否传入引用队列,如果不传入引用队列,说明只存在一种引用,不需要引用队列成链存储

public abstract class Reference<T> {
    private static boolean disableIntrinsic = false;
    private static boolean slowPathEnabled = false;

    //引用的对象,由垃圾回收器控制其引用
    volatile T referent;         /* Treated specially by GC */
    final ReferenceQueue<? super T> queue;
    Reference queueNext;
    Reference<?> pendingNext;

    public T get() {
        return getReferent();
    }

    @FastNative
    private final native T getReferent();

    public void clear() {
        clearReferent();
    }

    @FastNative
    native void clearReferent();

    public boolean isEnqueued() {
        // Contrary to what the documentation says, this method returns false
        // after this reference object has been removed from its queue
        // (b/26647823). ReferenceQueue.isEnqueued preserves this historically
        // incorrect behavior.
        return queue != null && queue.isEnqueued(this);
    }

    public boolean enqueue() {
       return queue != null && queue.enqueue(this);
    }

    /* -- Constructors -- */

    Reference(T referent) {
        this(referent, null);
    }

    Reference(T referent, ReferenceQueue<? super T> queue) {
        this.referent = referent;
        this.queue = queue;
    }
}

抽象类很简短,可以看出一个关键点,Reference是一个节点,保存next的引用,方法调用都是使用ReferenceQueue方法,直接进入:

private Reference<? extends T> head = null;
  private Reference<? extends T> tail = null;

 boolean enqueue(Reference<? extends T> reference) {
        synchronized (lock) {
            if (enqueueLocked(reference)) {
                lock.notifyAll();
                return true;
            }
            return false;
        }
    }

  private boolean enqueueLocked(Reference<? extends T> r) {
        ...

         if (r instanceof Cleaner) {
            Cleaner cl = (sun.misc.Cleaner) r;
            cl.clean();
            r.queueNext = sQueueNextUnenqueued;
            return true;
        }

        if (tail == null) {
            head = r;
        } else {
            tail.queueNext = r;
        }
        tail = r;
        tail.queueNext = r;
        return true;
    }

入队方法中,

  1. 使用synchronized添加锁,入队结束后释放锁,在ReferenceQueue中并不是标准的队列,使用的是Reference节点成链,行成单链表,类似于MessageQueue.
  2. 如果是Cleaner类,创建一个虚引用节点,即不如队。Cleaner是用来释放非堆内存,所以做特殊处理

SoftReference

>public class SoftReference<T> extends Reference<T> {
    //时间戳,由gc更新
    static private long clock;
    private long timestamp;

    public SoftReference(T referent) {
        super(referent);
        this.timestamp = clock;
    }

    /**
     * Creates a new soft reference that refers to the given object and is
     * registered with the given queue.
     *
     * @param referent object the new soft reference will refer to
     * @param q the queue with which the reference is to be registered,
     *          or <tt>null</tt> if registration is not required
     *
     */
    public SoftReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q);
        this.timestamp = clock;
    }

    public T get() {
        T o = super.get();
        if (o != null && this.timestamp != clock)
            this.timestamp = clock;
        return o;
    }

}

由gc管理时间戳

  • clock:上一次gc时间
  • timestamp:访问get时最近一次的gc时间

回收条件为:clock - timestamp <= free_heap * ms_per_mb

  • free_heep为堆空间空闲大小
  • ms_per_mb是保留软引用时间/MB

PhantomReference

public class PhantomReference<T> extends Reference<T> {

    public T get() {
        return null;
    }

    public PhantomReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q);
    }

}

虚引用的get方法返回null,不做gc保留

虚引用通过构造方法可以查看是 持有对象引用

总结:所有引用都是继承自Reference基类的,该类是一个链表节点,ReferenceQueue通过这点形成单链表,称之为队列,进行引用管理,所有引用都可以通过Reference的isEnqueue方法判断引用是否存在。

FinalizerReference理解

java堆中创建对象时,如果java类定义了finalize方法,就会新建一个FinalizerReference类,指向这个新建的对象

内存问题

  • 内存泄漏:内存没有按照预期在gc时回收
  • 内存溢出:内存大小超出指定大小,导致OOM
  • 内存抖动:短时间创建大量内存对象,然后回收,导致内存发生锯齿形抖动,内存空间不连续加上碎片会导致更大的空间,最终OOM

内存优化意义

  • 减少OOM,提高系统稳定性
  • 减少卡顿,提高流畅度
  • 减少内存占用,提高应用存活率
  • 减少异常发生和代码逻辑隐患

Android内存泄漏

常见内存泄漏

  1. 匿名内部类持有外部类引用,导致外部类内存泄漏(Handler)
  2. 单例传入Context导致调用单例方无法被回收。
  3. 非静态内部类创建静态实例
  4. 注册与反注册
  5. 资源对象关闭
  6. 集和及时清理

内存泄漏检测

  1. Profiler,Memory Analyzer(MAT)

Android studio自带内存、cpu、网络的变化,可以根据内存变化做具体分析

  1. LeakCanary

框架集成,自动检测内存泄漏,生成app,提供内存泄漏栈堆情况

原理:绑定生命周期,对Activity和Fragment来说,在onDestory时将对象放入弱引用队列进行存储,触发gc后,如果还存在,则发生内存泄漏

  1. StrictMode(很少用)

一款比较老的工具,ThreadPolicy可以检测主线程是否网络访问,是否读写。VMPolicy检测内存,Activity,Fragment是否泄漏,资源是否正确关闭

内存优化空间

  1. 不必要的自动装箱

自动装箱就是将基础数据类型转化为对应的复杂类型,在HashMap的增删改查中充满了自动装箱问题,所以尽量避免这中问题,如将HashMap替换为SparseArray和ArrayMap

  1. 内存复用
  • 资源复用:通用字符串,颜色,布局
  • 视图复用:类似于RecyclerView的优化复用
  • 对象池:创建对象池,不用重复创建对象,类似于线程池,messae享元模式
  • Bitmap对象复用:使用inBitmap属性可以告知Bitmap解码器尝试使用已经存在的内存区域,新解码的bitmap会尝试使用之前那张bitmap在heap中占据的pixel data内存区域。
  1. 在App可用内存过低时主动释放内存在App退到后台内存紧张即将被Kill掉时选择重写Application中 onTrimMemory/onLowMemory 方法去释放掉图片缓存、静态缓存来自保。
  2. 其他场景优化item被回收不可见时释放掉对图片的引用ListView :因此每次item被回收后再次利用都会重新绑定数据,只需在ImageView onDetachFromWindow的时候释放掉图片引用即可。RecyclerView :因为被回收不可见时第一选择是放进mCacheView中,这里item被复用并不会只需bindViewHolder来重新绑定数据,只有被回收进mRecyclePool中后拿出来复用才会重新绑定数据,因此重写Recycler.Adapter中的onViewRecycled()方法来使item被回收进RecyclePool的时候去释放图片引用。如果使用字符串拼接,尽量使用StringBuilder、StringBuffer(内存抖动)自定义view减少onDraw的耗时和执行次数尽量使用静态内部类尽量使用基础数据类型合适的时候使用软/弱引用

线上监控方案

  1. 常规监测当内存使用超过80%,使用 Debug.dumpHprofData(String fileName)
    获取dump文件回传至服务器,而后手动分析LeakCanary集成并带到线上
  2. Probe线上监测工具
  3. LeakInspector
  4. ResourceCanary

最后

小编在网上收集了一些Android 开发相关的学习文档、面试题、Android 核心笔记等等文档,希望能帮助到大家学习提升,如有需要参考的可以直接私信我领取哦

相关推荐

《每日电讯报》研发数字工具,教你更有效率地报道新闻

为鼓励新闻编辑部持续创新,《每日电讯报》正在尝试有战略地研发数字工具。网站的数字媒体主任马尔科姆o科尔斯(MalcolmColes)表示,《每日电讯报》正试图去“创建一些可持续资产”,以便于让记者们...

html5学得好不好,看掌握多少标签

html5你了解了多少?如果你还是入门阶段的话,或者还是一知半解的话,那么我们专门为你们收集的html5常用的标签大全对你就很有帮助了,你需要了解了html5有哪些标签你才能够更好的。驾驭html5...

前端分享-少年了解过iframe么(我想了解少年)

iframe就像是HTML的「内嵌画布」,允许在页面中加载独立网页,如同在画布上叠加另一幅动态画卷。核心特性包括:独立上下文:每个iframe都拥有独立的DOM/CSS/JS环境(类似浏...

做SEO要知道什么是AJAX(人能看到但搜索引擎看不到的内容)

一个明显的,人能看到但搜索引擎不能看到的内容是AJAX。那么什么是AJAX呢?其实,了解过的基本上也都清楚,AJAX不是新的编程语言,而是一种使用现有标准的新方法。AJAX最大的优点是在不重新加...

介绍最前沿的人工智能创新,‘无反向传播’神经网络训练方法?

图像由GoogleImageFX生成前言:本文整理自NoProp原始论文与实践代码,并结合多个公开实现细节进行了全流程复现。对神经网络训练机制的探索仍在不断演进,如果你也在研究反向传播之...

说说我们对HTML6的期许(对html的看法)

HTML5概述HTML5是HTML语言最受欢迎的版本之一,它支持音频和视频、离线存储、移动端、和标签属性等等。还提供了article,section,header这样的标签来帮助开发者更好...

浏览器中在线预览pdf文件,pdf.mjs插件实现web预览pdf

背景:本来只是淘宝上卖卖袜子,想着扩展一下业务,准备做同名“来家居”海外袜子馆外贸项目,碰到pdf在线预览的需求,就找了pdf.js插件进行实践后把此方法记录下来,可以通过多种方法来实现,每种方法都有...

SVG 在前端的7种使用方法,你还知道哪几种?

本文简介点赞+关注+收藏=学会了技术一直在演变,在网页中使用SVG的方法也层出不穷。每个时期都有对应的最优解。所以我打算把我知道的7种SVG的使用方法列举出来,有备无患~如果你还...

HTML5常用标签大全(html5em标签)

HTML前端开发最终取决于掌握标签的多少HTML大概有七八百个标签楼主这里给大家总结了下HTML常用标签标签描述<!--...-->定义注释。<!DOCTYPE>定义文档类型...

&quot;伪君子Snoop Dogg!&quot;... WHAT?| MetroDaily 24/7

TUE.01-新作品-虽说年纪大了会有点糊涂,但是最近SnoopDogg的这波操作实在是让粉丝们有点迷,甚至有人表示没想到他是这样的"伪君子"......而这一切都源于他近日在IG上Po出的一...

史努比snoopy卡通手机壁纸屏保(史努比壁纸无水印)

...

莎夏·班克斯盼望表哥Snoop Dogg为其作出场曲

NXT女子冠军莎夏·班克斯(SashaBanks)近日接受了迈阿密先驱报采访,访谈纪要如下:关于她出众的形象:“我一向喜欢与众不同。为了能让人眼前一亮,我的装束总是非常前卫、非常抢眼,这样才能让观众...

喜欢Snoop!全球第一间「史努比博物馆」海外分馆在东京!

1950年起,由美國漫畫家CharlesM.Schulz創作的作品《Snoopy》史努比,其鮮明的可愛角色與幽默的劇情內容,至今仍成為許多大朋友與小朋友心中的最愛。為了紀念作者所設立的全球首...

Vetements 推出 Snoop Dogg 肖像「天价」T-Shirt

Vetements的CEOGuramGvasalia早前才透露品牌经营策略的秘密–Vetements如何成为人人热议的话题品牌。但似乎他仍有更多需要解释的东西–这个法国奢侈品牌最新...

狗爷Snoop Dogg的《I Wanna Thank Me》巡回演唱会旧金山站

西海岸匪帮说唱歌手SnoopDogg在《IWannaThankMe》巡回演唱会旧金山站表演(图片来自ICphoto)西海岸匪帮说唱歌手SnoopDogg(图片来自ICphoto)西海...

取消回复欢迎 发表评论: