Android面试(四)内存优化一

来源:互联网 发布:钟无艳国语网络歌手 编辑:程序博客网 时间:2024/05/22 00:27

内存优化

什么是OOM,OOM是如何造成的?

oom-out of memory(内存溢出), 当自身占用的内存加上要求分配的内存超过了系统给你的内存时, 系统就会抛出out of memory的异常(每个Android能用的内存是限的)比如: 当前应用只剩下4M的空间可用, 但你却加载得到一个需要占用5M空间的图片Bitmap对象, 就会抛出oom的异常

什么是内存泄漏,内存泄漏是如何造成的?

当一个对象已经不需要再使用了,本该被回收时,而有另外一个正在使用的对象持有它的引用从而导致它不能被回收,这导致本该被回收的对象不能被回收而停留在堆内存中,这就产生了内存泄漏

内存泄漏对程序的影响?

内存泄漏是造成应用程序OOM的主要原因之一!我们知道Android系统为每个应用程序分配的内存有限,
而当一个应用中产生的内存泄漏比较多时,这就难免会导致应用所需要的内存超过这个系统分配的内存限额,这就造成了内存溢出而导致应用Crash

什么是内存抖动?

内存抖动可以看到内存的占用量短时间内大量被占用,短时间内又被大量释放,造成的原因变量短暂大量产生,又被大量释放,就会造成内存抖动,

什么是overDraw?

过度重绘:导致延迟绘制时间,做了很多无用功,消耗了内存

举例:在背景上加了背景颜色 在布局上又加了背景颜色
最后Button上又设置了颜色 那么重叠的部分就叫过度重绘

UI卡顿的原因?

一 首先我们要了解16ms黄金准则 : 一般来说,Android设备的屏幕刷新率为60帧每秒,要保持流畅就遇要每一帧不超过16.6MS每帧,如果超过就会发生跳帧的现象。

二 原因

  • 1 人为在UI线程中做轻微耗时操作,导致UI线程卡顿
  • 2 布局Layout过于复杂,无法在16ms内完成常渲染,view嵌套不建议超过8层
  • 3 同一时间动画执行的次数过多,导致CPU或GPU负载过重
  • 4 view过度绘制,导致某些像素在同一帧时间内被绘制多次,CPU或GPU负载过重
  • 5 view频繁的触发measure layout 导致measure layout累计耗时过多及整个view频繁的重新渲染
  • 6 内存频繁触发GC过多,导致暂时阻塞渲染操作
  • 7 冗余资源及逻辑等导致加载和执行缓慢
  • 8 频繁GC,GC所有线程会暂停
  • 9 overDraw 重叠部分 绘制多次

如何内存优化?

  • 1 布局优化 不要嵌套布局 不需要显示的布局要gone这样就不会绘制
  • 2 列表及adapter优化 滑动停止再加载图片
  • 3 背景和图片等内存分配优化
  • 4 避免ANR
  • 5 当service完成后,尽量关闭它
  • 6 在UI不可见的时候,释放掉一些只有UI使用的资源
  • 7 在系统内存紧张的时候,尽可能多的释放掉一些非重要资源
  • 8 避免滥用Bitmap导致的内存泄漏
  • 9 使用针对内存优化的数据容器
  • 10 避免使用依赖注入的框架

布局优化

选用相对布局还是线性布局?

RelativeLayout : 在进行布局的时候会进行多次测量
嵌套越多测量次数越多

LinearLayout: 在设置权重的时候才会测量两次
相对布局可以让布局嵌套变少,而相对布局则测量少
所以最好的方式是结合使用

Merge和ViewStub的使用

  • 1 尽量使用view自身的参数
  • 2 减少一个布局的不必要节点(textView换行符替换多个textview,textView左右加图片属性等)
  • 3 尽量重用一个布局文件(include包含重复的布局)

<include>标签
include标签常用于将布局中的公共部分提取出来供其他layout共用,以实现布局模块化,这在布局编写方便提供了大大的便利

<merge/>标签在UI的结构优化中起着非常重要的作用,它可以删减多余的层级,优化UI。多用于替换FrameLayout或者当一个布局包含另一个时,标签消除视图层次结构中多余的视图组。例如你的主布局文件是垂直布局,引入了一个垂直布局的include,这是如果include布局使用的LinearLayout就没意义了,使用的话反而减慢你的UI表现。这时可以使用标签优化。

<ViewStub />标签最大的优点是当你需要时才会加载,使用他并不会影响UI初始化时的性能。各种不常用的布局想进度条、显示错误消息等可以使用<ViewStub />标签,以减少内存使用量,加快渲染速度。<ViewStub />是一个不可见的,大小为0的View。<ViewStub />标签使用如下

<ViewStub    android:layout="@layout/progress_overlay"      android:layout_width="fill_parent"      android:layout_height="wrap_content"      android:layout_gravity="bottom" />  

内存泄漏

android常见造成内存泄漏的问题是什么?

1 内存泄漏 是指无用对象(不再使用的对象)持续占有内存或无用对象的内存得不到及时释放,
从而造成的内存空间的浪费称为内存泄漏

2上下文导致的内存泄漏:举例 单例中持有的上下文是从activity传过来的,一旦activity不再需要,而被销毁的时候,因为单例的工具类还持有activity的引用,那么activity便不会被GC回收

3非静态内部类造成的内存泄漏 : 举例:(Handler造成的内存泄漏,AsyncTast内存泄漏等)当一个非静态内部类里做着一件耗时的任务,那么包含这个内部类的activity就算销毁也不回被回收。原因 非静态内部会持有外部类的引用(为什么会持有看下面)

4资源没有关闭造成的内存泄漏:如游标没有关闭 流没有关闭 bitmap没有释放等等

上下文导致内存泄漏的原因?

主要是GC原理,垃圾回收机制:Java采用根搜索算法,当GC Roots不可达时,并且对象finalize没有自救的情况下,才会回收。

回收对象:GC会收集那些不是GC roots且没有被GC roots引用的对象。

非静态内部类造成内存泄漏的原因?

1 静态内部类对外部类会存在一个隐式引用(因为非静态内部类可以调用外部类的方法,即然可以调用外部类的方法那么必须持有外部类的引用)

**注意:内部类不一定会导致内存泄漏,除非执行了耗时任务外部类销毁的时候,
内部类的任务还在执行那么,执行的任务会持有内部类的引用,内部类又持有外部类的引用**

public class BitmapDemo {    public void say(){        MyDemo demo = new MyDemo();    }    public void setName(){    }    class MyDemo{        public void say(){            setName();        }    }}

2 静态内部类中存在异步任务,可能会导致其对应的外部类内存资源无法正常释放

3 静态内部类中创建了一个静态实例,会导致内存泄漏

解决方法

1 通过静态内部类来去除隐式引用

static class MyDemo{    public void say(){        //setName();    }}

2 修改静态内部类的构造方式,手动引入其外部类引用(看handler内存泄漏解决)

3 Android可以WeakReference包裹外部类实例

handler如何造成内存泄漏?如何解决?

内存泄漏的原因:非静态内部类持有外部类的引用,当activity退出的时候有可能发送的message
还在message队列里,这样就造成外部引用被持有而不能被销毁。

解决办法

public class Main2Activity extends AppCompatActivity {private  MyHandler handler = new MyHandler(this);@Overrideprotected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main2);    handler.sendEmptyMessage(1000);}static class MyHandler extends Handler{    private final SoftReference<Activity> activitySoftReference;    public MyHandler(Activity activity){        activitySoftReference = new SoftReference<>(activity);    }    @Override    public void handleMessage(Message msg) {        super.handleMessage(msg);        if (activitySoftReference != null){        }    }}}

什么是冷启动什么是热启动?

冷启动 :就是启动应用前,系统中没有该应用的任何进程信息(第一次启动应用,或者应用被杀死再次启动)

热启动 :用户使用返回键退出应用,然后马上又重新启动应用(进程是保留在后台的)

冷启动优化

  • 1 减少onCreate方法的工作量 第三方在onCreate初始化懒加载()
  • 2 不要让application参与业务的操作
  • 3 不要在Application进行耗时操作
  • 4 不要以静态变量的方式在application中保存数据
  • 5 布局减少嵌套

sp问题

  • 1 不能跨进程同步 每个进程都有一个副本
  • 2 存储sp的文件过大问题

内存对象序列化

  • serializeble 会产生大量的临时内存
  • Parcelable 不能序列化硬盘上的文件而且使用繁琐

    总结:

    • 1 Serializeble是java的序列化方式,Parcelable是android特有的序列化方式
    • 2 在使用内存的时候,Parcelable比Serializable性能高
    • 3 Serializable在序列化的时候会产生大量的临时变量,从而引起频繁的GC
    • 4 parcelable不能使用在要将数据将存储在磁盘上的情况