内存分配与回收
无论是在移动设备还是传统PC,内存的使用都是需要关注的一块。特别是在Android上,内存使用不当容易导致内存泄漏,影响App的性能。
内存分配
在Java中,内存是分代管理的,比如新创建的对象,他被放在了Young Generation。JVM的书籍中一般会都会介绍内存模型,分配等,下面我们简单回顾一下。
- 年轻代( Young Generation): 包含Eden,S0,S1
- 老年代(Old Generation): Minor GC之后迁入
- 永久代(Permenent Generation ): ClassLoader,Class等
不同代之间的差异主要是体现在对象的存活时间上:
- Eden,新创建的对象存放;
- S0/S1,幸存对象,Eden满后,触发Minor GC,幸存的对象已入S0中,S0满后,触发Minor GC,幸存对象放入S1中,任何时候S0、S1有一个是空的;
- 老年代,多次Minor GC后依然存活的对象(比如15轮GC),放入老年代;
- 老年代满后,触发Major GC,存活的放入永久代
- Major GC相比Minor GC,更加耗时;
- Major GC发生在老年代和永久代
// TODO
了解内存模型有助于我们更好的理解Java代码的执行,比如我们应该避免在循环体内大量创建临时对象,因为这会导致年轻代触发大量GC。
垃圾回收
现在我们知道,内存模型是分代管理,分配的,那么问题来了;
- 为甚么要分代管理内存,搞一个大块不是更简单么?
- 每一块内存满了之后触发的垃圾回收是怎么样的呢?
第一个问题,笔者是这么认为的:内存分代,是因为在程序中对象的存活时间是不一致的,很多对象创建完了很快就不需要使用了,比如说一次性手套,用完就丢了(回收了)。但是有些对象却存活很久,有可能伴随整个程序的生命周期。所以为了更高效的管理内存,更快回收未被使用的内存,分代是一个较好的策略。
第二个问题,每一块内存满了之后,根据不同代的特性,可以采用不同的垃圾回收算法, 下面我们简单了解下几个经典的回收算法。
- 标记清除法/标记压缩法 (Mark and Sweep)
- 复制收集算法 (Copy and Collection)
- 引用计数法 (Reference Count)
在Android开发中,做Java后端的开发者可能会需要根据业务场景进行JVM调优,但是做Android开发实际上是不需要做参数调优的。Andorid使用的不是JVM,而是DVM,而且官方也没有公开提供接口给开发者来设置每个app的虚拟机参数,我们了解虚拟机的垃圾回收只是作为理解。
在执行app的时候,日志终端经常会出现一些GC的日志,这些就是Android平台下发生GC的体现,我们更多的是根据日志来推断当前App是否频繁回收垃圾,是否内存不足。
TODO
- 共享、私有内存
- GC
查看某个java进程的内存与gc情况
- ps -eaf |grep java
- jstat -gc
[pid]
[ 1000]
- 内存使用量
adb shell dumpsys meminfo 10821