不写废话,帮助你快速理解应对面试
目录
垃圾的定义
可达性分析
GC Root对象
回收的时机
如何进行垃圾回收
- 标记清除算法(Mark and Sweep GC)
- 复制算法(Copying)
- 标记整理算法(Mark-Compact)
JVM分代回收策略
- 新生代
- 老年代
JVM的引用关系
垃圾的定义
内存中没有用的对象。
怎么判断是否有用呢?——可达性分析
可达性分析
可达性分析算法(Reachability Analysis)的基本思路是,通过一些被称为引用链(GC Roots)的对象作为起点,从这些节点开始向下搜索,搜索走过的路径被称为(Reference Chain),当一个对象到 GC Roots 没有任何引用链相连时(即从 GC Roots 节点到该节点不可达),则证明该对象是不可用的。
GC Root对象
关于内存的区域分布,可以参考:面试笔记-Java内存分配
在Java中,有以下几种对象可以作为GCRoot:
- Java虚拟机栈(局部变量表)中的引用的对象。
- 方法区中静态引用指向的对象。
- 仍处于存活状态中的线程对象。
- Native方法中 JNI 引用的对象。
回收的时机
- AllocationFailure:在堆内存中分配时,如果因为可用剩余空间不足导致对象内存分配失败,这时系统会触发一次GC。
- System.gc():在应用层,可以主动调用此API来进行垃圾回收
如何进行垃圾回收
标记清除算法(Mark and Sweep GC)
标记清除算法(Mark-Sweep)是最基础的一种垃圾回收算法,它分为2部分,先把内存区域中的这些对象进行标记,哪些属于可回收标记出来,然后把这些垃圾拎出来清理掉。清理掉的垃圾就变成未使用的内存区域,等待被再次使用。
缺点:会产生碎片。
复制算法(Copying)
复制算法(Copying)是在标记清除算法上演化而来,解决标记清除算法的内存碎片问题。
它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。
当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。保证了内存的连续可用,内存分配时也就不用考虑内存碎片等复杂情况,逻辑清晰,运行高效。
优点:按顺序分配内存即可,实现简单、运行高效,不用考虑内存碎片。
缺点:可用的内存大小缩小为原来的一半,对象存活率高时会频繁进行复制。
标记整理算法(Mark-Compact)
标记整理算法(Mark-Compact)标记过程仍然与标记 --- 清除算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,再清理掉端边界以外的内存区域。
优点:这种方法既避免了碎片的产生,又不需要两块相同的内存空间,因此,其性价比比较高。
缺点:仍需要进行局部对象移动,所以一定程度上还是降低了效率。
JVM分代回收策略
分代回收的中心思想就是:对于新创建的对象会在新生代中分配内存,此区域的对象生命周期一般较短。如果经过多次回收仍然存活下来,则将它们转移到老年代中。
新生代
- Eden区满的时候会进行垃圾回收,将 Eden 区的垃圾对象回收清除,并将存活的对象复制到 From Survivor 区域,此时To Survivor是空的。
- 下一次 Eden 区满时,再执行一次垃圾回收。此次会将 Eden和From Survivor 区中所有垃圾对象清除,并将存活对象复制到To Survivor,此时From变为空。
- 如此反复在From和 To之间切换几次(默认 15 次)之后,如果还有存活对象。说明这些对象的生命周期较长,则将它们转移到老年代中。
老年代
一个对象如果在新生代存活了足够长的时间而没有被清理掉,则会被复制到老年代。
老年代的内存能存放更多的对象。如果对象比较大(比如长字符串或者大数组),并且新生代的剩余空间不足,则这个大对象会直接被分配到老年代上。
JVM的引用关系
引用 | GC回收时间 | 使用实例 |
---|---|---|
强引用 | GC不会回收 | Object obj = new Object(); |
软引用 | 内存不足,将其回收 | SoftReference<Object> sobj = new SoftReference(); |
弱引用 | 第一次GC,遍历到有弱引用,回收 | WeakReference<Object> sobj = new WeakReference(); |
虚引用 | 完全不对生存时间构成影响,无法通过虚引用获取一个对象的实例 | 不会使用 |