1.内存管理
1.垃圾回收机制
(1)什么是垃圾回收机制?
(a)垃圾回收器负责回收程序中已经不再使用,但是仍然被各种对象占用的内存。
(b)无需手动管理内存,Android系统会自动跟踪所有的对象,并释放那些不再被使用的对象。
缺点:垃圾回收机制是一把双刃剑,在提高程序员工作效率的同时,会对应用程序的性能造成影响,严重的会是应用程序卡顿等。
2.Android中的垃圾回收机制
(1)Young Generation
- 大多数新建的对象都位于Eden区
- 当Eden区被对象填满时,就会执行Minor GC。并把说有存活下来的对象转移到其中一个survivor区。
- Survivor Space:S0、S1有两个,存放每次垃圾回收后存活的对象。
- Minor GC同样检测Survivor区存活的对象,并把它们转移到另一个survivor中,这样的话总有一个空的survivor区。
(2)Old Generation
- 存放长期存活的对象和经过多次Minor GC后依然存活下来的对象
- 满了进行Major GC
(3)permanent Generation
- 存放方法区,方法区中要加载的类的信息、静态变量、final类型的常量、属性和方法信息
3.垃圾回收
内存占用过多,需要为新对象分配空间,不同的虚拟机在发生GC时采用的策略不同,可能会暂停当前程序的执行。如上图,峰值后占用内存减少,是系统进行了一次垃圾回收。
4.垃圾回收机制&FPS
- Android没间隔16ms发出VSYNC信号,触发对UI进行渲染,那么整个过程如果保证在16ms以内能达到一个流畅的画面。60FPS。
- 如果某一帧的操作超过了16ms就会让用户感觉到卡顿。
- UI渲染过程中发生GC,导致某一帧绘制超过16ms。
综上,系统在垃圾回收时可能会暂停当前程序的执行,这样有可能会导致某一帧的绘制超过了16ms,用户就会感觉卡顿。
5.内存泄漏
- 应用程序分配了大量不能被回收的对象
- 系统可分配内存越来越小
- 新对象的创建需要的内存不足
- GC之后再分配
- 60FPS
综上,内存泄露指的是应用程序在运行时分配了一些的对象,这些对象所占用的内存并不能被垃圾回收程序所回收——导致系统可分配内存越来越少——新对象的创建需要的内存不够——调用垃圾回收程序执行一次垃圾回收——垃圾回收可能会暂停当前程序的执行——可能会导致某一帧的绘制超过16ms,影响到当前的帧率——帧率达不到60fps——用户感到卡顿。
6.内存检测工具
1.Memory Monitor
2.Allocation Tracker
主要用来追踪应用程序在运行时,所有已创建的对象、对象的数量、占用内存大小、以及这些对象是在哪些方法中被创建出来的信息。 点击图上2处,再次点击Allocation Tracker,AS会打开Allocation Tracker统计结果。
7.内存抖动
内存抖动是因为短时间内大量的对象被创建又马上被释放。瞬间产生大量的对象会严重占用Young Generation的内存区域,当达到阈值,剩余空间不够的时候,会触发GC从而导致刚产生的对象很多又被回收。即使每次分配的对象占用了很少的内存,但是它们叠加在一起回增加Heap的压力,从而触发更多其他类型的GC。这个过程可能会影响到帧率,并让用户感知到性能问题。
8.Heap Viewer
该工具可实时展示应用程序在运行时所有已分配对象的数量、大小、类型等信息。
上面三点的总结,对于Memory Monitor:
方便显示内存使用和GC情况;
快速定位卡顿是否和GC有关;
快速定位Crash是否和内存占用过高有关;
快速定位潜在的内存泄露问题;
缺点:不能准确定位问题,只能提供大概的方向。
对于Allocation Tracker :
定位代码中分配的对象的类型、大小、时间、线程、堆栈等信息;
定位内存抖动问题;
配合Heap Viewer一起定位内存泄露问题
缺点:使用复杂。
对于Heap Viewer:
内存快照信息
每次GC之后收集一次信息
查找内存泄露利器
缺点:使用复杂。
9.常见的内存泄露问题
1.单例造成的内存泄露
静态变量在整个程序的运行期间是一直存在的,不会被垃圾回收;静态变量本身会持有一个activity对象的引用的,activity对象即使被销毁了也不会被垃圾回收,从而造成了内存泄露。
2.非静态内部类的静态实例造成的泄露
TestResource是在MainActivity定义的非静态内部类,
其对象sResource对象会间接的持有MainActivity实例的引用,又把sResource定义为静态变量,所以在程序运行期间sResource对象不会被垃圾回收,并且一直持有MainActivity的引用,导致MainActivity也不会被垃圾回收。
3.Handler造成的内存泄露
创建一个匿名内部类的对象,它会间接持有外部类实例的引用;Handler经常用来做耗时的操作,可能存活时间比MainActivity实例时间长,所以可能会导致MainActivity销毁后并不能被垃圾回收,必须要等Handler被垃圾回收之后,MainActivity对象才会被垃圾回收。
避免内存泄露的方法:
a) 尽量不要让静态变量引用Activity;如果要用,尽量使用WeakReference;
b) 使用静态内部类来代替内部类(静态内部类不会持有它外部类的引用);
c) 静态内部类使用弱引用来引用外部类
10、减少内存的使用
- 使用更轻量的数据结构
- 避免在ondraw方法中创建对象
- 对象池(Message.obtain())
- LRUCache
- bitmap内存复用,压缩(imSampleSize、inBitmap)
- StringBuilder