一、背景

Android由于是以Java语言为主要开发语言,所以它的内存管理并不像C语言那样由开发者去管理内存的分配以及回收等,而是交由JVM虚拟机的内存回收机制去处理。这就导致我们在开发过程中难免会遇到内存相关的各种问题带来的困扰。而这些问题的根源就在于我们对Android的内存管理机制的不够了解,以及开发过程中缺少对内存的关注导致。所以这里借这篇文章我们再回顾一下Android的内存管理机制,以及如何在开发过程中去尽量避免这些内存问题。

二、内存管理—内存划分

Android中的内存是由谁来管理?答案是JVM虚拟机——内存大管家。

通常我们Android都得基于Java语言做的开发。(这里要排除新的Flutter开发,它是基于Dart语言开发。)而Java语言从API的角度来讲,就是由我们的JDK组成的。同时JRE又是JDK的一部分,而我们这里要说的JVM呢又是JRE的组成部分,简称Java虚拟机。它的主要作用就是实现Java语言的跨平台。它能够将我们的Java语言编译成二进制机器码,然后运行在各平台上。除了帮助跨平台之外,它还承担着内存管理的任务。Android开发中常说的内存基本都是指运行时的内存。

从线程的角度来看,JVM在执行Java程序的过程中会将所管理的内存划分为线程私有和线程共有两大类。

2.1、线程私有

(1)程序计数器:Java是多线程的,既然是多线程就需要线程间切换、通信等操作。如何保证线程间操作时每个线程的执行顺序能按代码的步骤正常执行呢?这时候程序计数器就是关键了,它会帮我们记录当前线程执行到的位置(在字节码中记录的这些位置统称为指令地址)。这个内存区不会出现OOM

2)虚拟机栈:我们常提在嘴上的堆和栈,其中栈指的就是Java虚拟机栈。每个线程在创建时都是创建一个对应的虚拟机栈,而虚拟机栈又有一个个的栈帧组成。每一个栈帧对应着一次方法调用。当前正在执行的方法对应的是当前栈帧。栈只会执行两种操作:压栈和出栈。栈帧中存储着对应方法的局部变量表,操作数栈,动态链接和方法的返回地址。即虚拟机栈中存储着栈帧,而每个栈帧里面存储着当前方法所需的数据、指令和返回等信息。这个内存区会出现栈溢出、OOM异常

(3)本地方法栈:这个和虚拟机栈作用类似,区别就是虚拟机栈为执行的Java服务,本地方法栈为Native服务

2.2、线程共有

(1)堆内存:Java虚拟机中最大的一块内存,主要目的就是存放对象实例和数组。也是Java虚拟机进行垃圾回收的主要工作区域。

(2)方法区:我们都知道Java中每个类都对应一个Class对象保存这个类的信息,方法区就是用来保存这个类信息的区域。同时它还会保存常量(包括运行时常量池)、静态变量以及编译器编译后的代码等。在方法区中有一个非常重要的部分就是运行时常量池,它是每一个类或接口的常量池的运行时表示形式,在类和接口被加载到JVM后,对应的运行时常量池就被创建出来

三、内存管理—内存分配

Java中的内存时如何分配的?

3.1、堆内存区划分

我们这里所说的内存分配主要说的是堆内存块的分配。而要了解堆内存块的分配规则,我们要先了解JVM对堆内存块的区域划分。

JVM将堆内存进行了进一步的划分,如下:

●新生代(PSYoungGen):MinorGC。

新生代又细分成如下三个空间

1)、Eden空间

2)、FromSurvivor空间

3)、ToSurvivor空间

Eden空间用来优先分配对象、数组。From和To空间是个交换区。默认情况下这三个空间的内存占比是8:1:1。当然这个比例也是可以通过虚拟机调的。

●老年代(ParOldGen):Full GC

3.2、堆内存区的分配方式

我们创建一个对象时,JVM会去判断当前堆内存是否是规整状态。通过当前堆内存的规整状态来选择指针碰撞还是空闲列表方式进行分配。

指针碰撞:如果Java堆中内存是绝对规整的,所有用过的内存都放在一边,空闲的内存放在另一边,中间放着一个指针作为分界点的指示器,那所分配内存就仅仅是把那个指针向空闲空间那边挪动一段与对象大小相等的距离,这种分配方式称为“指针碰撞”

空闲列表:如果Java堆中的内存并不是规整的,已使用的内存和空闲的内存相互交错,那就没有办法简单地进行指针碰撞了,虚拟机就必须维护一个列表,记录上哪些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录,这种分配方式称为“空闲列表”。选择哪种分配方式由Java堆是否规整决定,而Java堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定。

四、内存分配—内存回收

前面我们也讲了,Java中的内存回收不由开发者来控制,而是由JVM虚拟机的垃圾回收器(GC)来进行回收管理。Java中GC是如何回收一个对象的?

4.1、GC如何判断一个对象可回收

1、引用计数法

顾名思义当一个对象被另一个对象引用时,将该对象的引用计数+1。相反当该对象被引用对象释放时则-1。当这个对象的引用计数为0时,则认为这个对象是垃圾对象可回收。但是这种方法是不靠谱的,因为这种方法是无法判断一个对象是否真实无用可回收的。比如对象A引用了对象B,同时对象B也引用了A。虽然这两个对象在各自的引用者中都没有被用到,但是这个时候引用计数法就无法判断这个对象是否是可回收对象了。

2、可达性分析算法

Java中可达性分析首先要确定Gc Roots对象。然后将所有与这个GCRoot对象有直接或间接引用立链的对象都统计为不可回收对象。

这个Gc Roots对象是个啥对象?所谓Gc Roots对象就是如下这些对象实列:

●方法区中类静态属性引用的对象,以及方法区中常量引用的对象

●虚拟机栈(本地变量表)中引用的对象

●本地方法栈(JNI开发Native方法)中引用的对象

4.2、GC如何回收内存

在GC经过上述算法确定完那些对象可回收之后就是进行对象的回收操作了。但是回收操作是要根据不同的GC回收器的能力来完成的。而这个能力就是回收器的回收算法能力。

●复制回收算法:它的主要工作区域是年轻代

它会将年代年轻的内存分出两块同样大小的区域(其实就是前面说的From、To两块内存区)。一块用来正常使用,一块空闲备用等待复制。判断完对象存活状态后,进行回收时,将存活对象复制到备用空间,然后清空之前的那块内存空间等待备用。复制回收算法的缺点就是内存只有50%利用率。这里就可以解释一下为什么前面的年轻代内存区占比是8:1:1了,在Java中我们通常认为有90%的对象是不需要回收的,只有10%的对象需要被回收,而复制回收算法需要预留一份与这个10%一样大小的内存区来备用。所以就得出了8:1:1的空间占比。复制回收算法就是在From和To之间进行内存Copy

标记-清除算法:主要作用域是老年代

判断完内存区对象的存活状态后,会对垃圾对象做一个可回收的标记。等垃圾回收器扫描完所有内存后,一次性清除被标记为可回收的对象。标记算法的缺点是垃圾回收后内存空间是不连续的,存在内存碎片(也就是前面提到的内存不规整状态)。

标记-整理算法:主要作用域是老年代

标记整理算法就是在标记清除算法的基础上解决了垃圾回收后内存碎片的问题,即清除垃圾对象后,会对内存区进行整理,使其成为规整状态。可是既然要整理内存区,就必然要进行内存移动,就会降低效率。所以它的效率比标记清除算法要低。

五、内存管理—内存优化

首先为啥会出现内存优化一说?这是因为我们在开发中经常忽视内存相关的问题,导致功能开发完成后经过测试或者上线后发现经常由于内存的问题带来一连串的恶性循环的后果。比如:内存抖动、内存泄漏等等容易在开发中被忽视的问题最终都有可能引发严重的线上问题。

5.1、内存抖动优化

首先要知道什么是内存抖动:短时间内有大量对象创建销毁,它伴随着频繁的GC。比较直观的现象就是如下图:

●内存抖动的危害

(1)频繁的GC会导致程序出现卡顿的现象,简而言之, 就是执行GC操作的时候,任何线程的任何操作都会需要暂停,挂起(stop the word),等待GC操作完成之后,其他操作才能够继续运行, 故而如果程序频繁GC, 自然会导致界面卡顿

(2)频繁GC会导致我们的内存不连续(内存碎片),从而导致OOM。

●内存抖动的预防

(1)避免在循环中创建对象

(2)避免在频繁调用的方法中创建对象,如View的onDraw方法

(3)允许复用的情况下,使用对象池进行缓存。

5.2、内存泄漏优化

为什么会出现内存泄漏?该释放的资源未能够及时释放,导致系统内存的浪费,可用内存逐渐减少。长生命周期对象持有短生命周期对象的强引用,从而导致短生命周期的对象无法被回收。这就是内存泄漏的主要原因。

●内存泄露的危害

(1)频繁的GC会导致程序出现卡顿的现象,简而言之, 就是执行GC操作的时候,任何线程的任何操作都会需要暂停,挂起(stop the word),等待GC操作完成之后,其他操作才能够继续运行, 故而如果程序频繁GC, 自然会导致界面卡顿

(2)频繁GC会导致我们的内存不连续(内存碎片),从而导致OOM。

●内存泄漏优化

(1)合理使用如下四种引用关系

强引用:new出来的对象都是强引用。被强引用的对象无法被垃圾回收。即使内存不足时也无法回收,就有可能造成OOM

软引用SoftReference:被软引用的对象,当内存不足时(即将OOM时),则被回收。

弱引用WeakReference:垃圾回收线程扫描内存区时,发现被弱引用的对象立即回收。

虚引用PhantomReference:任何时候都有可能被回收。

(2)合理使用内存泄漏检测工具

(1)LeakCanary是一个Android和Java的内存泄漏检测库,可以大幅可以大幅度减少了开发中遇到的OOM问题。

(2)Profile + MAT

Android Studio自带的Profile工具大部分时候都能够帮助我们定位内存的使用情况,但也会有出现泄漏时无法通过Profile工具来直观定位到泄漏点的情况,这时候就需要Profile工具配合MAT工具一起来排查泄漏点了。

这里简单列一下两个工具配合使用的步骤:

●导出profiler 的Heap Dump 文件

●找到Android SDK中platfrom-tools目录下的hprof-conv.exe工具(可以直接配置到环境变量里面)

●转换导出的Heap Dump文件: hprof-conv.exe -z 导出的HeapDump文件.hprof  转换后的目标文件.hprof

如果不转换,格式不兼容,MAT无法查看。

●使用MAT工具打开转换后的hprof文件。

六、总结

Android开发过程中内存一直是个容易引发各类问题的根源,而这个问题根源又常因为开发过程中的各类因素没有被及时的发现,所以在开发过程中重视内存的检测和优化是我们要时刻关注的一个点。

Android性能优化之内存优化浅析相关推荐

  1. Android 系统性能优化(55)---Android 性能优化之内存优化

    Android 性能优化之内存优化 前言 Android App优化这个问题,我相信是Android开发者一个永恒的话题.本篇文章也不例外,也是来讲解一下Android内存优化.那么本篇文章有什么不同 ...

  2. App性能优化(布局优化,线程优化,app瘦身优化,页面切换优化,App启动优化,内存优化)

    Android APP性能优化(最新总结) 在目前Android开发中,UI布局可以说是每个App使用频率很高的,随着UI越来越多,布局的重复性.复杂度也随之增长,这样使得UI布局的优化,显得至关重要 ...

  3. Lua性能优化—Lua内存优化

    原文链接https://blog.uwa4d.com/archives/usparkle_luaperformance.html 这是侑虎科技第236篇原创文章,感谢作者舒航供稿,欢迎转发分享,未经作 ...

  4. Android性能优化之内存优化 1

    导语 智能手机发展到今天已经有十几个年头,手机的软硬件都已经发生了翻天覆地的变化,特别是Android阵营,从一开始的一两百M到今天动辄4G,6G内存.然而大部分的开发者观看下自己的异常上报系统,还是 ...

  5. Android面试-Android性能优化和内存优化、APP启动速度一线大厂的实战案例解析

    一.Android 内存管理机制 二.优化内存的意义 三.避免内存泄漏 四.优化内存空间 五.图片管理模块的设计与实现 六.总结 深入探索Android内存优化 第一章.重识内存优化 第二章.常见工具 ...

  6. android—性能优化2—内存优化

    文章目录 性能优化: 工具: memory profiler LeakCanary arthook epic 库 java内存管理机制 java 内存回收机制 Android内存管理机制 Dalvik ...

  7. Android App性能优化之内存优化

    为什么要进行内存优化? 1.App运行内存限制,OOM导致App崩溃 2.App性能:流畅性.响应速度和用户体验 Android的内存管理方式 Android系统内存分配与回收方式 ●   一个App ...

  8. Android 性能监测工具,优化内存、卡顿、耗电、APK的方法

    导语     安卓大军浩浩荡荡,发展已近十个年头,技术优化月新日异,如今 Android 9.0 代号P  都发布了,Android系统性能已经非常流畅了.但是,到了各大厂商手里,改源码自定系统,使得 ...

  9. Android 系统性能优化(30)---Android性能全面分析与优化方案研究

    Android 性能优化 1.结合以下四个部分讲解: 性能问题分类 性能优化原则和方法 借助性能优化工具分析解决问题 性能优化指标 2性能问题分类 1.渲染问题:过度绘制.布局冗杂 2.内存问题:内存 ...

最新文章

  1. Linux下批量杀掉筛选进程
  2. python怎样定义一个数组_Python创建数组
  3. Office365-----Skype for business
  4. 《说服力——让你的PPT会说话》读书笔记02
  5. sql参数化还是被注入了_面试官问你 SQL 注入攻击了吗?
  6. 大数据WEB阶段Spring框架(一)IOC控制反转、DI注入依赖
  7. 大学学好高数的爆炸性意义!
  8. 解决VMware 7在Windows 7上无法上网的问题
  9. python下pymysql的问题
  10. LINUX CGROUP 概述
  11. android 多线程 handler使用方法
  12. Flexsim在固定资源类中没有分拣传送带?
  13. 批量文件转换:PDG转PDF并合并
  14. Springboot内置tomcat优化
  15. python提取pdf表格数据 无边框_Python使用Tabula提取PDF表格数据
  16. CodeForces - 3B Lorry【贪心】
  17. Elasticsearch顶尖高手系列:高手进阶篇(一)
  18. java小型计费系统设计_JAVA课程设计模拟电信计费系统
  19. 热拉登陆找不到服务器,在线服务器服务器路径.ppt
  20. ROS2极简总结-文件系统

热门文章

  1. Android高仿苹果计算器
  2. python乘车费用 青少年编程电子学会python编程等级考试二级真题解析2020年6月
  3. 谈谈K8S Pod Eviction 机制
  4. 民俗多种内涵-农业大健康·万祥军:牛首山谋定世外桃源
  5. 7-20 sdust-Java-字符串集合求并集
  6. 【笔记】H5跳转手机应用商店(指定应用页/第三方应用商店)
  7. 2021年,手机拼什么?
  8. Windows网络活跃点决定使用的优先权
  9. 爬取B站弹幕制作词云图
  10. CSR867x — 如何修改BLE的蓝牙地址