垃圾回收机制是什么?
垃圾回收是一种自动的存储管理机制。 当一些被占用的内存不再需要时,就应该予以释放,以让出空间,这种存储资源管理,称为垃圾回收。
也就是说垃圾回收跟内存有关,那么都有哪些内存呢
JVM内存
JVM将内存划分为五个区间.
JVM分为五大内存空间,其中程序计数器、虚拟机栈、本地方法栈3个区域随线程而生、随线程而灭,因此这几个区域的内存分配和回收都具备确定性,就不需要过多考虑回收的问题,因为方法结束或者线程结束时,内存自然就跟随着回收了。而Java堆区和方法区则不一样,这部分内存的分配和回收是动态的,正是垃圾收集器所需关注的部分。
堆区的垃圾回收
什么是垃圾
如果一个对象已经没有任何一个地方引用它,它就是垃圾.
怎么确定是垃圾
引用计数法
给对象添加一个引用计数器,每当有一个地方引用它时,计数器加一。反之每当一个引用失效时,计数器减一。当计数器为0时,则表示对象不被引用。
可达性分析
设立若干根对象(GC Root),每个对象都是一个子节点,当一个对象找不到根时,就认为该对象不可达。
触发条件
Minor GC触发机制
当年轻代满时就会触发Minor GC,这里的年轻代满指的是Eden代满,Survivor满不会引发GC。
![](/qrcode.jpg)
FULL GC触发机制
- 老年代空间不足
- 方法区空间不足
- 通过Minor GC后进入老年代的平均大小大于老年代的可用内存。
- 由Eden区、From Survivor区向To Survivor区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小.
- System.gc()方法的调用,系统建议执行Full GC,但是不必然执行.
回收算法
1.标记-清除算法
该算法先标记,后清除,遍历所有的GC Root,分别标记处可达的对象和不可达的对象,然后将不可达的对象回收。
这种算法的不足主要体现在效率和空间
从效率的角度讲,标记和清除两个过程的效率都不高,因为需要遍历所有GC ROOT.
从空间的角度讲,标记清除后会产生大量不连续的内存碎片, 内存碎片太多可能会导致以后程序运行过程中在需要分配较大对象时,无法找到足够的连续内存而不得不提前触发一次垃圾收集动作。
2.复制算法
将内存分为两块,每次只使用一块。当这一块内存满了,就将还存活的对象复制到另一块上,并且严格按照内存地址排列,然后把已使用的那块内存统一回收。
优点是:解决了标记-清除算法的碎片问题,能够得到连续的内存空间
缺点是:浪费了一半内存
3.标记整理算法
复制算法在对象存活率较高时,持续复制效率非常低,老年代都是不易被回收的对象,针对老年代的特点,可以采用标记整理算法,标记整理算法在标记-清除算法基础上,它标记之后,不直接清理,是让所有存活对象都向一端移动,然后直接清理掉边界以外的内存.这样既避免了对象存活率较高时的持续复制,也避免了内存碎片的出现.适用于老年代.
4.分代收集算法
现代商用虚拟机基本都采用分代收集算法来进行垃圾回收。这种算法没什么特别之处,就是上面内容的结合.
它是对内存中的对象按照生命周期的长短,以及所在区域的不同进行划分.
堆区分为新生代,老年代.
方法区为持久代.
新生代就是活不了多久就死亡的对象,一般在堆内存.比如局部变量.
老年代是活的久但也会死亡的对象,一般在堆内存.比如一些生命周期长的对象.
持久代是不死的对象,一般在方法区.比如加载的class信息.
不同的年代使用不同的垃圾回收算法.
新生代使用复制算法.
老年代使用标记整理算法.
方法区的垃圾回收
持久代也就是方法区,Java虚拟机规范中说过可以不要求虚拟机在方法区实现垃圾收集,因为和堆区的垃圾回收效率相比,它的回收效率实在太低,但是此部分内存区域也是可以被回收的。
持久代和堆区的新生代和老年代不一样.方法区主要回收的内容是废弃常量和无用的类。
对于废弃常量也可通过引用的可达性来判断,但是对于无用的类则需要同时满足下面3个条件:
- 该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例;
- 加载该类的
ClassLoader
已经被回收; - 该类对应的
java.lang.Class
对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。