接 App优化之内存优化(序) , 作为App优化系列中内存优化的一个小部分.
由于内存相关知识比较生涩, 内存优化中使用到的相关工具, 也有很多专有名词. 对Java内存管理, GC, Android内存管理, Dalvik/ART等知识有一个理论的认识, 可以让我们更好的使用这些工具, 分析内存问题.
据此, 我们就先从理论入手, 聊聊GC那些事儿.
GC 是 garbage collection 的缩写, 垃圾回收的意思. 也可以是 Garbage Collector, 也就是垃圾回收器.
我们先来解释下Garbage Collector(垃圾回收器).
内存管理, 一直是编程中的一个大的问题. 在较老的语言中, 例如C++语言中, 内存管理是显式的, 也就是说使用者自己申请内存使用, 自己释放内存. 这就是为什么C++语言中除了构造函数, 还有析构函数. 我们在创建对象的时候调用构造函数创建, 系统会在对象结束其作用域的时候调用析构函数, 我们需要做的就是在析构函数中释放掉我们申请的相关资源, 以便释放内存地址.
显然, 这种显式的由编程人员自己控制释放内存的方式很容易出问题, 忘了, 漏了, 都可能导致内存问题. 也不符合程序员要懒的特征.
故而, Java语言中引入了自动内存管理的机制, 也就是垃圾回收器. 大部分的现代面向对象语言, 也都是采用自动内存管理机制.
内存自动管理回收机制可以解决大部分, 但不是所有的内存问题, 这也是为什么我们要讨论内存泄露.
垃圾回收器有三大职责:
gc process
垃圾回收器中有一个进程来做上面的这些事情, 这个进程查找我们的对象引用的关系并释放其内存, 这个进程就是garbage collection(垃圾回收), 也就是我们常说的GC.
简单说下:
heap&stack
直译GC根, 我们姑且不译了吧.
所谓GC Root我们可以理解为是一个Heap内存之外的对象, 通常包括但不仅限于如下几种:
如果这个对象是 引用可达 的, 则称之为 活的(live) , 反之, 如果这个对象 引用不可达 , 则称之为 死的(dead) , 也可以称之为 垃圾(garbage) .
这个 引用可达与不可达 就是相对于GC Root来说的:
gc-roots
我们平常在查看我们的java版本时, 你会发现:
$ java -version java version "1.8.0_74" Java(TM) SE Runtime Environment (build 1.8.0_74-b02) Java HotSpot(TM) 64-Bit Server VM (build 25.74-b02, mixed mode) 复制代码
其中有个HotSpot VM的东西, 那么这个是什么呢? 和JVM有什么关系呢?
在此简单说下, 以便行文:
我们当前使用的sun(oracle)的java版本(应该是1.3以上)都是内置的HotSpot VM实现. 所以接下来的分析也都是基于HotSpot VM的, 但是还是简称JVM.
JVM使用分代式的内存管理方式, 将Heap分成三代 --- 新生代, 老一代, 持久代.
Hotspot heap structure
Young Generation
Old Generation
Permanent Generation
小GC 执行非常频繁, 而且速度特别快.
大GC 一般会比小GC慢十倍以上.
大小GC都会发出"Stop the World"事件, 也就是说中断程序运行, 直至GC完成. 这也是我们在App优化之消除卡顿中为什么说频繁GC会造成用户感知卡顿.
了解了内存Heap的几个区域, 我们再来看下垃圾收集器是怎么利用这几个区域来管理内存和回收垃圾的.
1. 创建新的对象
每当我们使用new创建一个对象时, 这个对象会被分配到 新生代 的 Eden 区域:
object allocation
2. 当Eden区域满时
当Eden区域内存被分配完时, 小GC 程序被触发:
Eden filling
引用可达的对象 会移到Survivor(幸存者)区域-- S0 , 然后清空Eden区域, 此时 引用不可达的对象 会直接删除, 内存回收, 如下:
aged
3. Eden再次满时
当Eden区域再次分配完后, 小GC 执行, 引用可达的对象 会移到Survivor(幸存者)区域, 而 引用不可达的对象 会跟随Eden的清空而删除回收.
需要注意的是, 这次 引用可达的对象 移动到的是 S1 的幸存者区.
而且, S0区域也会执行 小GC , 将其中还 引用可达的对象 移动到S1区, 且年龄+1. 然后清空S0, 回收其中 引用不可达的对象 .
此时, 所有 引用可达的对象 都在S1区, 且S1区的对象存在不同的年龄. 如下:
next filling
当Eden第三次满时, S0和S1的角色互换了:
s0s1
依此循环.
4. 当Survivor区的对象年龄达到"老年线"时
上面1~3循环, Survivor区的对象年龄也会持续增长, 当其中某些对象年龄达到"老年线", 例如8岁时, 它们会"晋升"到 老年区 .
old aged
如此1~4步重复, 大体流程是这样的
gc flow