2023Android大厂面试题详解之内存优化,内存抖动和内存泄漏。(附面试题汇总)

内存优化,内存抖动和内存泄漏。(东方头条)

详细讲解

性能优化《内存泄漏与内存抖动优化实战》

详细讲解

享学课堂移动互联网系统课程:性能优化《内存泄漏与内存抖动优化实战》

这道题想考察什么?

内存抖动与内存泄漏是什么,会对程序造成什么影响?为什么会产生这些影响?

考察的知识点

内存优化、JVM GC

考生如何回答

什么是内存抖动?

在Java中,每创建一个对象,就会申请一块内存,存储对象信息;每分配一块内存,程序的可用内存也就少一块;当程序被占用的内存达到一定临界程度,GC 也就是垃圾回收器(Garbage Collector)就会出动,来释放掉一部分不再被使用的内存。 这本身没有问题,但是当频繁创建对象就会造成内存不断地攀升,在回收了之后又迅速涨起来,接着又一次的回收。在短时间内反复地发生内存增长和回收,这就是内存抖动(Memory Churn)。

我们可以通过 Android Studio 的 Memory Profiler 来直观地观察到这种现象:

在这里插入图片描述

内存抖动的问题

内存抖动可能导致程序卡顿甚至OOM内存溢出。

卡顿

内存的回收在Java当中采用的是GC机制,无论是何种方式实现的GC在执行的时候都不可避免的需要 STW(Stop The World) 。STW意味着我们所有的工作线程都将会被暂停,虽然这个时间很短,但终究是有时间成本的。一两次内存回收不容易被用户察觉,但多次内存回收行为集中在短时间内爆发,这就造成了比较大的界面卡顿的风险。 例如当用户点击某个按钮,或者在界面中进行滑动时,此时虚拟机在运行GC线程,进行内存回收,那响应用户点击事件的线程就被GC暂停了,只能在恢复后才能响应,因此给到用户最直观的感受就是程序卡了。

OOM

内存抖动除了可能造成卡顿之外,也可能会造成内存溢出(OOM)。这是因为如果垃圾回收的实现采用的是标记-清除算法,那么此算法可能导致大量的内存碎片。

在这里插入图片描述

当我们程序频繁的创建与回收对象(内存抖动),那么可能就会导致程序中连续内存不足。比如上图中,我们需要创建一个占用10个格子大小内存的字节数组对象,此时就会出现OOM。因为虽然在内存回收后,拥有不止10个格子大小的可用内存,但是没有10个连续的白色格子(可用内存)。这就是内存碎片,空闲的连续空间比要申请的空间小,导致这些小内存块不能被利用。

Android 在官方文档和 Android Studio 里都建议我们尽量避免在 View的onDraw() 里创建对象,就是因为onDraw方法可能会被频繁的调用。因此我们应该避免在可能会频繁被执行的、循环体内创建一个新对象。

什么是内存泄露

内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。 在Java中,就是该释放的对象无法被释放,那这些对象将一直内存,最终导致程序可用内存越来越少,直至无内存可用(OOM)

为什么会出现这种情况?这就需要了解GC机制是如何判断一个对象是否可被回收的。垃圾对象检测主要有两种算法:引用计数法和可达性分析法

引用计数法

所谓的引用计数法就是给每个对象一个引用计数器,每当有一个地方引用它时,计数器就会加1;当引用失效时,计数器的值就会减1;任何时刻计数器的值为0的对象就是不可能再被使用的;但是当两个对象互相引用会导致无法回收。

这种方法没有被Java使用,Java中采用的是可达性分析法.

可达性分析法

通过一系列称为“GC Roots”的对象作为起始点,从这些节点向下搜索,搜索所有的引用链,当一个对象到GC Roots没有任何引用链(即GC Roots到对象不可达)时,则证明此对象是不可用的。
在这里插入图片描述

比如:当我们某个Activity在finish退出之后,我们希望这个Activity对象能及时被回收掉,但是因为此Activity对象被一个单例(GC Root)引用着,那就导致Activity无法被回收,出现内存泄露。

public class Manager {
    //GC ROOT
    private static final Manager ourInstance = new Manager();

    private Context mContext; //mContext是Activity则会导致此Activity被GC Root持有引用

    public static Manager getInstance() {
        return ourInstance;
    }

    private Manager() {
    }

    public void init(Context context){
       mContext = context;
    }
}

而要修改上面的代码,可以在允许传递Application的情况下,尽量传递Application,或者直接使用context.getApplicationContext()避免传递Activity。也可以采用非强引用的方式(见Java中有几种引用关系,它们的区别是什么?)

完整2023Android大厂面试题详解可以扫描下方二维码免费领取!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值