概述
内存简介
RAM(Random Access Memory) 随机存取存储器:内存。
通常分为:
- 寄存器(Registers):速度最快的存储场所,因为寄存器位于处理器内部,我们的程序中无法控制;
- 栈(stack):存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中;
- 堆(heap):存放基本由 new 创建的对象和数组。在堆中分配的内存,由 Java JVM 的自动回收器(GC)来管理;
- 静态域(static field):静态存储区就是指在固定的位置存放应用程序运行时一直存在的数据,Java 在内存中专门划分了一个静态存储区域来管理一些特殊的数据变量如静态的数据变量;
- 常量池(constant pool):JVM 必须为每个被转载的类型维护一个常量池。常量池就是该类型所用到常量的一个有序集合,包括直接常量(String、Integer 等常量)和其他类型,字段和方法的符号引用。
外存储:SD 卡等外置存储区域。
内存优化常用方式
优化 Bitmap 的加载和显示
使用 BitmapFactory.Options 设置 inSampleSize, 这样做可以减少对系统资源的要求。
属性值 inSampleSize 表示缩略图大小为原始图片大小的几分之一,即如果这个值为 2,则取出的缩略图的宽和高都是原始图片的 1/2,图片大小就为原始大小的 1/4
降低图像的颜色属性
Android 中图片有四种属性,分别是:
ALPHA_8:每个像素占用 1byte 内存
ARGB_4444:每个像素占用 2byte 内存
RGB_565:每个像素占用 2byte 内存 (不包含图片的透明属性)
ARGB_8888:每个像素占用 4byte 内存 (默认)
示例代码:
1 | BitmapFactory.Options bitmapFactoryOptions = new BitmapFactory.Options(); |
及时回收不再使用的 Bitmap 资源
1 | // 先判断是否已经回收 |
修改对象的引用类型
引用类型
- 强引用(StrongReference):不会被 GC 回收
- 软引用(SoftReference):内存不足时被 GC 回收
- 弱引用(WeakReference):执行 GC 的时候被回收
- 虚引用(PhantomReference):随时可能被回收
通常使用软应用来引用对象,例如自定义继承的 Handler 类,引用的 Activity 类等;
垃圾回收器(GC)
** Java 垃圾回收器**
在 C、C++等其他一些程序设计语言中,资源或对象都必须由程序员自行声明和回收,否者其中的资源将消耗内存,造成资源的浪费甚至崩溃。但是手工回收内存是往往是一项复杂而艰巨的工作。于是,Java 技术提供了一个系统级的线程,即垃圾收集器线程(Garbage Collection Thread),来跟踪每一块分配出去的内存空间,当 Java JVM 处于空闲循环时,垃圾收集器线程会自动检查每一块分配出去的内存空间,然后自动回收每一块可以回收的无用的内存块。
作用
清除不用的对象来释放内存
采用一种动态存储管理技术,它自动地释放不在被程序引用的对象,按照特定的垃圾收集算法来实现资源自动回收的功能。当一个对象不在被引用的时候,内存回收它占领的空间,以便空间被后来的新对象使用。消除堆内存空间的碎片
由于创建对象和垃圾收集器释放丢弃对象所占的内存空间,内存会出现碎片。碎片是分配给对象的内存块之间的空闲内存洞。碎片整理将占用的堆内存移动到堆的一端,JVM 将整理出的内存分配给新的对象。
优点
- 减少编程的负担,提高效率。程序员可以专心编程,而不用花时间去管理内存。
- 保护程序的完整性,属于 Java 语言安全性策略的一个重要部分
缺点
占用资源时间
JVM 必须追踪运行程序中的所有对象,然后释放没有被引用的对象,这一个过程需要花费处理器的时间;不可预知性
GC 线程虽然作为一个低优先级的线程运行,但是系统可用内存量过低的时候,它可能会突发的执行来挽救内存资源。执行有不可预知性。不确定性
不能保证一个无用的对象一定会被 GC 掉,也不能保证 GC 在一段 JAVA 语言代码中一定执行。同样也没办法预知在一组均符合 GC 手机标准的对象中,哪一个会被首先收集。不可操作性
GC 不可以被强制执行,但是程序员可以通过调用System.gc()
方法来建议执行 GC;
垃圾回收算法
** TODOS**
- 引用计数
- 标记-清除
- 复制
- 标记整理
- 增量收集
- 分代
finalize()
继承自Object
,在 GC 删除掉该对象之前调用该方法;
System.gc()
调用该方法为建议 JVM 执行 GC 动作,而不是强制执行。
资源的关闭
- 显示的关闭游标
Cursor
; - 显示的反注册
Receiver
、Controller
等; - 显示的关闭各种 IO 流;
其他一些常用方法
- 对常量使用 static final 修饰符,省略 clinit 的初始化类方法;
- 尽量使用 Application 的 Context 取代 Activity 的 Context;
- 避免创建不必要的对象。如频繁操作字符串是,使用 StringBuffer 代替 String;
- 避免使用浮点数。通常经验,在 Android 设备中,浮点数会比整型慢俩倍;
- 避免使用枚举。枚举比常量消耗更多的内存;
- 不在 for 循环的第二个条件中使用方法调用以及尽量调用本地变量而不是成员变量。
如:
错误示例 1:错误示例 2:1
for(int i =0; i < this.mCount; i++) {}
优化后代码:1
for(int i =0; i < this.mCount; i++) {}
1
2int count = this.mCount; // int count = this.getCount();
for(int i =0; i < count; i++) {}