一 什么是内存
RAM(random access memory)随机存取存储器。
二 内存分配时涉及的区域
寄存器(Registers):速度最快的存储场所,因为寄存器位于处理器内部,我们在程序中无法控制
栈(Stack):存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中
堆(Heap):堆内存用来存放由new创建的对象和数组。在堆中分配的内存,由Java虚拟机的自动垃圾回收器(GC)来管理。
静态域(static field): 静态存储区域就是指在固定的位置存放应用程序运行时一直存在的数据,Java在内存中专门划分了一个静态存储区域来管理一些特殊的数据变量如静态的数据变量
常量池(constant pool):虚拟机必须为每个被装载的类型维护一个常量池。常量池就是该类型所用到常量的一个有序集和,包括直接常量(string,integer和floating point常量)和对其他类型,字段和方法的符号引用。
非RAM存储:硬盘等永久存储空间
三 有关内存的几种概念
内存泄漏
- 定义:无用对象持续占有内存,使得内存得不到释放,使得可用内存变小。
- 影响:内存泄漏会是可用内存减少,造成内存溢出的情况。
内存抖动
- 定义:短时间内有大量对象进行销毁和重建,它伴随着频繁的GC
- 影响:内存抖动可能造成瞬间的内存溢出,造成OOM。
内存溢出
- 定义:当前占用内存+申请使用的内存大于系统为程序分配的内存。俗称:OOM
- 影响:程序卡顿,莫名消失,直接崩溃。
四 优化内存 避免OOM的方案
内存优化的目的:
是为了减少程序占用的内存,避免出现OOM的情况,让我们的程序减小系统销毁的机会,在系统中存活更久的时间,提高用户的体验。
内存优化的方案: 5R
- Reckon(计算)
- Reduce(减少)
- Reuse(重用)
- Recycle(回收)
- Review(检查)
五 内存优化详细方案
1 Reckon 计算
如果我们想优化我们的程序内存,首先我们需要知道程序内存的使用情况。
1. 使用工具检测内存使用情况
- process state: 手机系统自带的检测工具 在运行时的使用情况
- meminfo:手机系统自带的检测工具 在后台时的使用情况
- Android Profiler(Memory Monitor): Android Studio 自带工具 跟踪整个app的内存变化情况
- Allocation Tracker 追踪内存对象的来源。
- Heap Viewer 查看当前内存快照,便于对比分析哪些对象有可能发生了泄漏
- LeakCanary 当发生内存泄漏时,会生成leak 报告, 报告中会详细写明具体发现内存泄漏的语句。
- Memory Analysis Tool: 傻瓜式“的堆转储文件分析工具
2. 借助API获取内存使用情况
- ActivityManager#getMemoryClass() 查询可用堆内存的限制
- ActivityManager#getMemoryInfo(ActivityManager.MemoryInfo)
- android.os.Debug#getMemoryInfo(Debug.MemoryInfo memoryInfo)
- android.os.Debug#getNativeHeapSize(): 返回的是当前进程navtive堆本身总的内存大小
- android.os.Debug#getNativeHeapAllocatedSize()
返回的是当前进程navtive堆中已使用的内存大小
- android.os.Debug#getNativeHeapFreeSize()
返回的是当前进程navtive堆中已经剩余的内存大小
2 Reduce 减少
减少内存使用情况是最直接的内存优化方式
1. Bitmap的使用注意要点
- 图片显示:我们需要根据需求去加载图片的大小。
例如在列表中仅用于预览时加载缩略图(thumbnails )。
只有当用户点击具体条目想看详细信息的时候,去显示整个图片
- 图片大小:使用BitmapFactory.Options设置inSampleSize, 这样做可以减少对系统资源的要求。属性值inSampleSize表示缩略图大小为原始图片大小的几分之一。
- 图片像素:
- ALPHA_8:每个像素占用1byte内存
- ARGB_4444:每个像素占用2byte内存
- ARGB_8888:每个像素占用4byte内存 (默认)
- RGB_565:每个像素占用2byte内存
Android默认的颜色模式为ARGB_8888,这个颜色模式色彩最细腻,显示质量最高。但同样的,占用的内存也最大。 所以在对图片效果不是特别高的情况下使用RGB_565
- 图片回收:使用Bitmap过后,就需要及时的调用Bitmap.recycle()方法来释放Bitmap占用的内存空间
- 捕获异常:使用Bitmap时,加上try-catch 进行捕获可出现的OOM
2. 修改对象引用类型
引用分为四种级别,这四种级别由高到低依次为:强引用>软引用>弱引用>虚引用。
- 在Android应用的开发中,为了防止内存溢出,在处理一些占用内存大而且声明周期较长的对象时候,可以尽量应用软引用和弱引用技术。
3. 其他方式
- 对常量使用static final修饰符
- 静态方法代替虚拟方法
- 减少不必要的全局变量
- 避免创建不必要的对象
- 避免内部Getters/Setters
- 避免使用浮点数
- 使用实体类比接口好
- 避免使用枚举
3 Reuse 复用
Reuse重用,减少内存消耗的重要手段之一。
核心思路就是将已经存在的内存资源重新使用而避免去创建新的,最典型的使用就是缓存(Cache)和池(Pool)
1. Bitmap缓存:
- 内存缓存(LruCache)
- 硬盘缓存(DiskLruCache)
一般使用开源项目进行图片缓存
- Android-Universal-Image-Loader
- picasso
- ImageCache
- Volley
Adapter适配器
使用convertView和ViewHolder来进行缓存处理池(PooL)
对象池:
对象池使用的基本思路是:将用过的对象保存起来,等下一次需要这种对象的时候,再拿出来重复使用,从而在一定程度上减少频繁创建对象所造成的开销。
线程池:
线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。
4 Recycel 回收
垃圾回收
Java垃圾回收器
- 定义:
Java技术提供了一个系统级的线程,即垃圾收集器线程(Garbage Collection Thread),来跟踪每一块分配出去的内存空间,当Java 虚拟机(Java Virtual Machine)处于空闲循环时,垃圾收集器线程会自动检查每一快分配出去的内存空间,然后自动回收每一快可以回收的无用的内存块。
- 作用
清除不用的对象来释放内存
采用一种动态存储管理技术,它自动地释放不再被程序引用的对象,按照特定的垃圾收集算法来实现资源自动回收的功能。当一个对象不再被引用的时候,内存回收它占领的空间,以便空间被后来的新对象使用。
消除堆内存空间的碎片
由于创建对象和垃圾收集器释放丢弃对象所占的内存空间,内存会出现碎片。碎片是分配给对象的内存块之间的空闲内存洞。碎片整理将所占用的堆内存移到堆的一端,JVM将整理出的内存分配给新的对象。
优点
减轻编程的负担,提高效率
使程序员从手工回收内存空间的繁重工作中解脱了出来,因为在没有垃圾收集机制的时候,可能要花许多时间来解决一个难懂的存储器问题。在用Java语言编程的时候,靠垃圾收集机制可大大缩短时间。
它保护程序的完整性:
因此垃圾收集是Java语言安全性策略的一个重要部份
- 缺点
占用资源时间:
Java虚拟机必须追踪运行程序中有用的对象, 而且最终释放没用的对象。这一个过程需要花费处理器的时间。
不可预知:
垃圾收集器线程虽然是作为低优先级的线程运行,但在系统可用内存量过低的时候,它可能会突发地执行来挽救内存资源。当然其执行与否也是不可预知的。
不确定性
不能保证一个无用的对象一定会被垃圾收集器收集,也不能保证垃圾收集器在一段Java语言代码中一定会执行。
同样也没有办法预知在一组均符合垃圾收集器收集标准的对象中,哪一个会被首先收集。不可操作
垃圾收集器不可以被强制执行,但程序员可以通过调用System. gc方法来建议执行垃圾收集器。
System.gc()
使用此方法,建议垃圾回收器回收。
- 总结
1.给对象赋予了空值null,以下再没有调用过。
2.给对象赋予了新值,既重新分配了内存空间。
- 总结
资源回收
Thread(线程)回收
线程中涉及的任何东西GC都不能回收(Anything reachable by a thread cannot be GC’d ),所以线程很容易造成内存泄露。
Cursor(游标)回收
Cursor是Android查询数据后得到的一个管理数据集合的类,在使用结束以后。应该保证Cursor占用的内存被及时的释放掉,而不是等待GC来处理。并且Android明显是倾向于编程者手动的将Cursor close掉
- Stream/File(流/文件)回收
主要针对各种流,文件资源等等如:
InputStream/OutputStream,SQLiteOpenHelper,SQLiteDatabase,Cursor,文件,I/O,Bitmap图片等操作等都应该记得显示关闭。 - Receiver(接收器)回收
调用registerReceiver()后未调用unregisterReceiver().
当我们Activity中使用了registerReceiver()方法注册了BroadcastReceiver,一定要在Activity的生命周期内调用unregisterReceiver()方法取消注册
也就是说registerReceiver()和unregisterReceiver()方法一定要成对出现,通常我们可以重写Activity的onDestory()方法
5 ReView 检查
- Code Review(代码检查)
- Code Review主要检查代码中存在的一些不合理或可以改进优化的地方
UI Review(视图检查)
Android对于视图中控件的布局渲染等会消耗很多的资源和内存,所以这部分也是我们需要注意的
- 减少视图层级
- 重用系统资源
- Desgin Review(设计检查)
- 框架设计
- 界面设计
- 逻辑设计
感谢
文中部分内容参考了 大苞米的博客(http://blog.csdn.net/a396901990)