在了解内存泄漏和内存溢出之前 先了解一下 虚拟机栈 和 java堆
虚拟机栈:
虚拟机栈是线程私有的,也就是说每一个线程都有自己的虚拟机栈,一般用于存储局部变量,和方法。 每个方法从调用直至完成的过程,对应一个栈帧在虚拟机栈中入栈到出栈的过程。 也就是说,当这个方法执行的时候,这个方法就会去虚拟机栈中压栈,当方法执行完成后就会弹栈或者说出栈,而当在一个方法中嵌套调用其他方法,例如递归,当调用的方法过多超过了栈的深度便会内存泄漏,OutOfMemoryError
堆: Java 堆的唯一目的就是存放对象实例,几乎所有的对象实例(和数组)都在这里分配内存。
Java堆是线程共享的,类的对象从中分配空间,这些对象通过new、newarray、 anewarray 和 multianewarray 等指令建立,
它们不需要程序代码来显式的释放。
由于Java堆唯一目的就是用来存放对象实例,因此其也是垃圾收集器管理的主要区域,故也称为称为 GC堆。从内存回收的角度看,由于现在的垃圾收集器基本都采用分代收集算法,所以为了方便垃圾回收Java堆还可以分为 新生代 和 老年代 。新生代用于存放刚创建的对象以及年轻的对象,如果对象一直没有被回收,生存得足够长,对象就会被移入老年代。新生代又可进一步细分为 eden、survivorSpace0 和 survivorSpace1。刚创建的对象都放入 eden,s0 和 s1 都至少经过一次GC并幸存。如果幸存对象经过一定时间仍存在,则进入老年代。
内存泄漏
StackOverflowError(SOF)
内存泄漏表现为:该被回收的内存没有被回收,导致的内存浪费。
引起内存泄漏的根本原因是两个对象的生命周期不一样
也就是说 持有引用者的生命周期 > 被引用者的生命周期
发生内存泄漏的场景
在Handler中做延迟操作,及时Activity销毁了 因为Handler持有Activity该Activity也不会被销毁
非静态内部类,默认持有外部类的引用。
集合对象没有及时清理引起的内存溢出,将对象存入集合中以后,如果不对集合进行清理,那么集合便会一直持有这些对象的引用,导致GC不会回收内存,出现内存泄漏。
如果堆内存中的内存与对象的引用断了该对象才能被回收。
比如 创建一个Student类 类中有年龄、身高等属性
Student stu = new Student();
Student stu1 = new Student();
stu=null;
Student类是在堆内存中,而stu/stu1便是指向Student类在堆内存中的地址
这时该对象还是不会被GC回收 因为他还存在其他的引用 这个引用就相当于指针,我们将对象置空就相当于切断了引用。
如果该对象的引用被别人持有 该对象在堆中的内存就算使用完了也不会被回收,造成内存泄漏。
GC 会通过可达性分析算法 来判断该对象是否被回收。
可以利用LeakCanary来监视一个即将被销毁的对象
内存溢出
OutOfMemoryError(OOM)
分为 堆溢出 和 栈溢出
栈溢出指的是java虚拟机栈的栈溢出、java虚拟机栈是 方法运行时便会压栈(进栈) 当方法执行完成之后弹栈(出栈),所以当我们正常调用方法,就不会内存溢出,而有些特定的情况,比如方法中嵌套调用方法,比如递归,这个时候方法会不停的压栈,而栈的特性就是先进先出,所以当我们不停的压栈,直到超过java虚拟机的深度,便会出现内存溢出的异常。
堆溢出 因为系统给每个应用申请的内存是不一样的当我们使用RecyclerView 或者 ListView的时候,如果加载大量的图片 不进行优化缓存(删除最近最少使用) 便会超出系统为我们分配的内存从而导致溢出。