在JVM中,有一个垃圾回收线程,它是低优先级的,在正常情况下是不会执行的,只有在虚拟机空闲或者当前堆内存不足时,才会触发。扫描那些没有被引用的对象,加到要回收的集合中,进行回收。
Java的引用类型有四种:
强引用、软引用、弱引用、虚引用。
强引用:发生 gc 的时候不会被回收。
弱引用:GC的时候会被回收。
软引用:OOM的时候会被回收。
虚引用(幽灵引用):虚引用一般用来跟踪垃圾回收过程,可以通过它来观察对象是否已经被回收,从而进行相应的处理。虚引用没有生命周期,在任何时间都可能被回收掉。它和引用队列一起使用,如果存在虚引用,就会在垃圾回收前将这个对象的虚引用加入引用队列。
垃圾回收器知道哪些对象可以被回收有两种方法:
一个是引用计数,一个是可达性分析
引用计数:为每个对象创建一个引用计数,有对象引用时计数器 +1,引用被释放时计数 -1,当计数器为 0 时就可以被回收。
可达性分析:从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是可以被回收的。
对象回收的过程:
JVM现在是采用分代来管理内存,年轻代包括Eden和Survivor,Survivor又分为两个“From Survivor”和“To Survivor”、年老代、永久代(JDK1.8之后叫元数据区,主要存放.class对象和Meta元数据信息,1.8之前是加载到虚拟机内存的,1.8开始放到本地内存管理)。一般新new的对象先放到年轻代,如果年轻代对象快满了,会进行MinorGC,还被引用的对象全部复制到幸存者区,对象年龄为 1,“From Survivor”满了就放到“To Survivor”,对象在 Survivor 区中每过一次 Minor GC 年龄就加 1,当计数达到一定程度默认 15就会被放到老年代。
垃圾回收器有这几种:
新生代回收器有Serial(单线程收集器,适合单核CPU)、Parallel(多线程收集器)、ParNew(也是多线程收集器),都是采用标记-复制算法。
老年代回收器包括Serial Old(Serial老年代版本)、Parallel Old(Parallel老年代版本)、CMS(多线程的,GC的时候停顿时间比较短,但是是以牺牲吞吐量为代价的, JVM参数指定CMS回收器的话,默认新生代回收器使用ParNew),Serial Old和Parallel Old是采用标记-整理算法,CMS是采用标记-清除算法,优点是GC时间比较短,缺点是会产生内存碎片,内存不足时会采用 Serial Old 回收器进行垃圾清除,这段过程性能会降低。
还有G1回收器(JDK1.7出来的),G1是回收整个Java堆的。总体上采用的是标记-整理算法,局部region之间采用的是标记-复制算法,与CMS的标记-清除算法相比,不会产生内存碎片。缺点就是G1每个Region都要拥有一张卡表,高的情况下要占20%左右的堆空间,不推荐堆空间小于6G以下使用G1。