垃圾回收(Garbage Collection,GC)是JAVA特有功能,垃圾收集意味着程序不再需要的对象是"无用信息",这些信息将被丢弃回收。
如何判断程序中哪些是垃圾,哪些是有用的对象?
当一个对象被引用,给它设置一个初值为0的引用计数器,被引用一次,计数器加1,当引用失效,计数器减1,当计数器为0时该对象可被当做垃圾回收。
存在问题:对象循环引用。
对象间的相互循环引用导致计数器不为0。
根对象(gc root)是肯定不会被当成垃圾的对象。
可达性分析算法就是扫描堆中的对象,看是否能沿着根对象为起点的引用链找到该对象,若找不到则表示可以回收。
可以被当做gc root的有:
①:虚拟机栈中引用的对象;
②:方法区中静态属性引用的对象;
③:方法区中常量引用的对象;
④:本地方法栈中JNI引用的对象。
强引用(StrongReference):A被强引用,那么垃圾回收器不会回收它;
软引用(SoftReference):在系统将要发生内存溢出异常前会把这些对象列进回收范围中进行第二次回收。
弱引用(WeakReference):不管内存够不够都会被垃圾回收器回收;
虚引用(PhantomReference):最弱的引用,不管有没有都对此对象的回收没有影响,设置它的唯一目的就是在这个对象被垃圾收集器回收时收到一个系统通知。
finalize方法?
当进行完可达性分析后发现没有与GC roots相连的引用链,那么它将会被第一次标记并且进行一次筛选,筛选的条件看这个对象是否有必要执行finalize方法。当finalize已经被虚拟机调用过或是没有覆写finalize方法,虚拟机将这两种情况视为“没有必要执行”。
当判定一个对象有执行finalize的机会,虚拟机会将它放在一个F-Queue队列中,并在稍后由一个虚拟机自动建立、低优先级的Finalizer线程去触发(不会等待它执行结束)它,当对象在finalize方法中将自己与引用链上的对象建立关联,那么在第二次标记阶段时它就会被移出“即将回收”的集合。如果对象没有逃脱那么它就真的被回收了。
①标记:将没有被gc root引用的对象标记出来;
②清除:将标记的对象的内存起始和结束地址置为空闲;
缺点:会产生小的内存碎片、效率慢。
①标记:将没有被gc root引用的对象标记出来;
②整理:将未标记的对象整理到一起;
优点:不会产生内存碎片;
缺点:耗时长。
先进行标记(将没有被gc root引用的对象标记出来),再将存活的对象复制到一块新的内存中。
优点:简单、高效;
缺点:消耗内存空间为原来的双倍。
分配担保机制:如果一块Survivor空间没有足够空间存放上一次新生代收集下来的存活对象,这些对象将直接通过分配担保机制进入老年代。
为什么老年代用标记整理和标记清除,新生代用复制?
因为新生代中垃圾占比90%以上,使用复制算法只需要很小的一块幸存区来充当额外的内存消耗。而老年代的存活对象占比很大,如果使用复制算法且没有分配担保的内存,就要将老年代分一半。所以老年代最好用标记清除和标记整理算法。
JVM将堆内存划分成两部分:新生代、老年代,新生代存放朝生夕死的对象,言外之意就是处于新生代的对象年龄小,新生代又划分为伊甸园、幸存区from和幸存区to;而老年代存放的都是存活率高、年龄大的对象。
过程:
①对象会首先被分配在伊甸园、当伊甸园空间不足时触发minor gc,将伊甸园和from中存活的对象的年龄加一,使用Copy算法将这些对象复制到to中,然后交换from和to,原先的to就变为from,然后下次回收重复这个操作;
注意:minor gc时会发生Stop The World(停止其他用户线程专注于垃圾回收)
②当from中对象年龄超过一定阈值(最大为15 4bits)时对象会晋升至老年代;
③当老年代空间不足时会发生full gc,full gc触发的STW时间会更长,因为full gc主要采取的回收算法是标记-清除和标记-整理。
注意:当新生代干净的情况下都装不下一个大对象时,直接将这个大对象放入老年代,且不触发gc;若老年代也放不下则先触发minor gc,再触发full gc。
Serial收集器是指使用单线程进行垃圾回收的回收器。每次回收只有一个工作线程,它在垃圾回收时必须停止其他所有的工作线程。
ParNew收集器是Serial的多线程版本,它最重要的特点是可以和CMS收集器(Concurrent Mark Sweep)配合使用。
Parallel Scavenge也是使用多线程进行垃圾回收的垃圾回收器,它使用复制算法,是吞吐量优先的垃圾收集器,所谓吞吐量就是代码运行时间占总虚拟机运行时间的比例:代码运行时间/(垃圾收集时间+代码运行时间)。
Serial的老年代版本,使用标记整理算法。
Parallel Scavenge的老年代版本,使用标记整理算法。
CMS收集器是一种以获取最短回收停顿时间为目标的收集器,他有四个阶段:
--初始标记:标记GC Roots能直接关联到的对象,速度很快;
--并发标记:进行GC Roots Tracing;
--重新标记:修正并发标记期间因用户程序继续运作导致标记变动的那一部分对象的标记记录,比初始标记稍长;
--并发清除:清除标记的垃圾。
三个缺点:
①CPU资源不足(少于4个)的情况下对用户程序的影响很大;
②并发清除会产生浮动垃圾;
③标记清除算法产生空间碎片。