大纲
- 内存抖动和内存泄漏
- 内存大户,Bitmap内存优化
- Profile内存检测工具
- Mat大对象与泄漏检测
【内存抖动和内存泄漏】
Out Of Memory(内存溢出)
翻译中文就是内存用完了,当JVM因为没有足够的内存来为对象分配空间并且垃圾回收器也已经没有空间可回收时,就会抛出这个error,此时称之为溢出。(注:非exception,因为这个问题已经严重到不足以被应用处理)。
为什么会OOM?
- 分配的少了:比如虚拟机本身可使用的内存(一般通过启动时的VM参数指定)太少。
- 应用用的太多:并且用完没释放,浪费了。此时就会造成内存泄露或者内存溢出。
在Java语言中,由于存在了垃圾自动回收机制,所以,我们一般不用去主动释放不用的对象所占的内存,也就是理论上来说,是不会存在“内存泄露”的。但是,如果编码不当,比如,将某个对象的引用放到了全局的Map中,虽然方法结束了,但是由于垃圾回收器会根据对象的引用情况来回收内存,导致该对象不能被及时的回收。如果该种情况出现次数多了,就会导致内存溢出,比如系统中经常使用的缓存机制。Java中的内存泄露,不同于C++中的忘了delete,往往是逻辑上的原因泄露。
OOM的类型?
JVM内存模型?
按照JVM规范,JAVA虚拟机在运行时会管理以下的内存区域:
- 程序计数器:当前线程执行的字节码的行号指示器,线程私有
- JAVA虚拟机栈:Java方法执行的内存模型,每个Java方法的执行对应着一个栈帧的进栈和出栈的操作。
- 本地方法栈:类似“ JAVA虚拟机栈 ”,但是为native方法的运行提供内存环境。
- JAVA堆:对象内存分配的地方,内存垃圾回收的主要区域,所有线程共享。可分为新生代,老生代。
- 方法区:用于存储已经被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。Hotspot中的“永久代”。
- 运行时常量池:方法区的一部分,存储常量信息,如各种字面量、符号引用等。
- 直接内存:并不是JVM运行时数据区的一部分, 可直接访问的内存, 比如NIO会用到这部分。
按照JVM规范,除了程序计数器不会抛出OOM外,其他各个内存区域都可能会抛出OOM。
最常见的OOM情况有以下三种:
- java.lang.OutOfMemoryError: Java heap space
java堆内存溢出,此种情况最常见,一般由于内存泄露或者堆的大小设置不当引起。对于内存泄露,需要通过内存监控软件查找程序中的泄露代码,而堆大小可以通过虚拟机参数-Xms,-Xmx等修改。 - java.lang.OutOfMemoryError: PermGen space
java永久代溢出,即方法区溢出了,一般出现于大量Class或者jsp页面,或者采用cglib等反射机制的情况,因为上述情况会产生大量的Class信息存储于方法区。此种情况可以通过更改方法区的大小来解决,使用类似-XX:PermSize=64m -XX:MaxPermSize=256m的形式修改。另外,过多的常量尤其是字符串也会导致方法区溢出。 - java.lang.StackOverflowError
不会抛OOM error,但也是比较常见的Java内存溢出。JAVA虚拟机栈溢出,一般是由于程序中存在死循环或者深度递归调用造成的,栈大小设置太小也会出现此种溢出。可以通过虚拟机参数-Xss来设置栈的大小。
内存抖动
短时间内大量的对象被创建,导致可用内存不足,从而引起频繁gc回收对象,这种已用内存忽高忽低的现象就叫内存抖动。由于gc的过程会 “stop the world” 停止其他的一切工作,gc太频繁无疑会造成界面卡顿,而且gc回收后可能会产生内存碎片,如果这时其他线程需要申请大块内存还有可能发生OOM,所以内存抖动的情况必须要避免。
什么情况会出现内存抖动呢?
for循环内使用了+进行字符串拼接
这是很常见的字符串拼接操作,通过查看字节码文件可以看出+的拼接字符串实际上是创建StringBuilder对象进行拼接,这样就会出现大量创建对象频繁GC,导致内存抖动。
内存抖动一定是锯齿状吗?
看下面一个实例,实现IOS小菊花的功能
public class IOSStyleLoadingView extends View {
private Context mContext;
private float mStrokeWidth;
private float northwestXStart = 264.57f;
private float northwestYStart = 264.71f;
private float northwestXEnd = 193.72f;
private float northwestYEnd = 194.14f;
private float northXStart = 300;
private float northYStart = 250;
private float northXEnd = 300;
private float northYEnd = 150;
private float notheastXStart = 335.25f;
private float notheastYStart = 264.54f;
private float notheastXEnd = 405.76f;
private float notheastYEnd = 193.63f;
private float eastXStart = 350;
private float eastYStart = 300f;
private float eastXEnd = 450;
private float eastYEnd = 300;
private float southeastXStart = 335.36f;
private float southeastYStart = 335.34f;
private float southeastXEnd = 406.10f;
private float southeastYEnd = 406.02f;
private float southXStart = 300.03f;
private float southYStart = 345f;
private float southXEnd = 300;
private float southYEnd = 450;
private float southwestXStart = 264.68f;
private float southwestYStart = 335.39f;
private float southwestXEnd = 194.06f;
private float southwestYEnd = 406.19f;
private float westXStart = 250