1.什么叫OOM,out of memory的简称,称之为内存溢出;
2.JVM内存模型
按照JVM规范,JAVA虚拟机在运行时会管理以下的内存区域:
程序计数器:当前线程执行的字节码的行号指示器,线程私有;
JAVA虚拟机栈:Java方法执行的内存模型,每个Java方法的执行对应着一个栈帧的进栈和出栈的操作。
本地方法栈:类似“ JAVA虚拟机栈 ”,但是为native方法的运行提供内存环境。
JAVA堆:对象内存分配的地方,内存垃圾回收的主要区域,所有线程共享。可分为新生代,老生代。
方法区:用于存储已经被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。Hotspot中的“永久代”。
运行时常量池:方法区的一部分,存储常量信息,如各种字面量、符号引用等。
直接内存:并不是JVM运行时数据区的一部分, 可直接访问的内存, 比如NIO会用到这部分。
按照JVM规范,除了程序计数器不会抛出OOM外,其他各个内存区域都可能会抛出OOM。
3. 最常见的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来设置栈的大小。
4.OOM问题,原因不外乎以下3类
1、线程池不管理式滥用
2、本地缓存滥用(如只需要使用Node的id,但将整个Node所有信息都缓存)
3、特殊场景考虑不足(如采用单线程处理复杂业务,环境震荡+多设备下积压任务暴增)
5.而造成OOM的情况大致可以归为以下几类:
A. 资源对象没关闭造成的内存泄露,try catch finally中将资源回收放到finally语句可以有效避免OOM。资源性对象比如:
A01,Cursor
A02,调用registerReceiver后未调用unregisterReceiver()
A03,未关闭InputStream/OutputStream
AO4,Bitmap使用后未调用recycle()
B. 作用域不一样,导致对象不能被垃圾回收器回收,比如:
B01,非静态内部类会隐式地持有外部类的引用,
B02,Context泄露,概括一下,避免Context相关的内存泄露,记住以下事情:
1、 不要保留对Context-Activity长时间的引用(对Activity的引用的时候,必须确保拥有和Activity一样的生命周期)
2、尝试使用Context-Application来替代Context-Activity;
3、如果你不想控制内部类的生命周期,应避免在Activity中使用非静态的内部类,而应该使用静态的内部类,
并在其中创建一个对Activity的弱引用。
这种情况的解决办法是使用一个静态的内部类,其中拥有对外部类的WeakReference。
B03,Thread 引用其他对象也容易出现对象泄露。
B04,onReceive方法里执行了太多的操作
C.内存压力过大
C01,图片资源加载过多,超过内存使用空间,例如Bitmap 的使用
C02,重复创建view,listview应该使用convertview和viewholder
6. 处理方法
1.使用缓存技术,比如LruCache、DiskLruCache、对象重复并且频繁调用可以考虑对象池
2.对于引用生命周期不一样的对象,可以用软引用或弱引用SoftReferner WeakReferner
3.对于资源对象 使用finally 强制关闭
4.内存压力过大就要统一的管理内存