1.如何判定哪些对象需要回收?
在对堆进行对象回收之前,要先判断哪些是无效对象。我们知道,一个对象不被任何对象或变量引用的话,就是无效对象,就需要回收。一般有两种判别方法:
(1)引用计数法
每个对象都有一个计数器,当这个对象被一个变量或另一个对象引用一次,该计数器加一;若引用失效则计数器减一。当计数器为0时,就认为该对象是无效对象。
(2)可达性分析法
所有和GC Roots直接或间接关联的对象都是有效对象,和GC Roots没有关联的对象就是无效对象。
GC Roots是指:
a.Java虚拟机栈所引用的对象(栈帧中局部变量表中引用类型的变量所引用的对象)
b.方法区中静态的属性引用的对象。
c.方法区中常量所引用的对象
d.本地方法栈所引用的对象
2.回收无效对象的过程
当JVM筛选出失效的对象之后,并不立即清除,还是会再给对象一次重生的机会,具体过程如下:
(1)判断该对象是否覆盖finalize()方法。
若已覆盖该方法,并该对象的finalize()方法还没有被执行过,那么就会将finalize()扔到F-Queue队列中;
若覆盖该方法,直接释放对象内存。
(2)执行F-Queue队列中的finalize()方法,如果finalize()方法中出现耗时操作,虚拟机就直接停止执行,将该对象清除。
(3)对象重生或死亡
如果在执行finalize()方法时,将this赋给了某一个引用,那么该对象重生,如果没有,则被回收。
3.方法区的内存回收
方法区中主要清除两种垃圾:废弃常量和废弃类。
(1)如何判定废弃常量?
清除废弃的常量和清除对象类似,只要常量池中的常量不被任何变量引用,那么这些常量就会被清除。
(2)如何判定废弃类?
a.该类的所有对象已经被清除
b.该类的java.lang.class对象没有被任何对象或变量引用。
只要一个类被虚拟机加载在方法区,那么在堆中就会有一个代表该类的对象:java.lang.Class。这个对象在类被加载进方法区的时候创建,在方法区中该类被删除时清除。
c.加载该类的ClassLoader已经被回收。
垃圾回收算法
(复制算法)解决内存碎片问题
将内存分为等大的A区和B区,创建对象时,存放于A区,当A区空间用完之后,将A区存活的对象复制到B区,然后A区清空。然而只是用了一半的空间,研究发现,新生代对象大多都是朝生夕死,也就是存活的对象不多,那么我们就没必要分配一半的空间用于“粘贴”。所以虚拟机将新生代分为Eden和Surivior两个区,比例为8:1:1,一个存活区用于“粘贴”,也就是新生代可用内存空间为容量的90%,这样的话就提高了空间的利用率。如果一个存活区存放不下复制的对象,将直接进入另一个区域老年代。
(标记整理算法)确定回收对象,进行标记,让存活的对象向一端移动,然后清除端边界以外的内存。
根据新生代和老年代的特点,分别选用适用的算法。
新生代存活率低,可以选择复制算法。老年代存活率高,可以采用标记清理或标记整理算法。
垃圾回收器
(1,Serial收集器)
(2.ParNew/Serial Old收集器)
(3)用于老年代,关注停顿时间,希望获取最短回收停顿时间,基于标记清除算法,会产生内存碎片,总体来说和用户线程一起并发执行,对CPU资源敏感,也就是会占用较多CPU资源。无法处理浮动垃圾,由于是和用户线程并发执行,所以并发时用户线程产生的新垃圾无法回收。
(4)使用多个CPU来缩短STW的停顿时间,分代收集。不会产生内存空间碎片。可以建立可预测的停顿时间模型,能够让使用者指定M毫秒时间段内,垃圾收集的时间不超过N毫秒。
(5)
Java中的引用类型(Java中根据生命周期的长短,分为4类)
(1)强引用
我们平常所使用的引用是强引用,例如:A a = new A();也就是通过 关键字new创建所关联的引用就是强引用。只要强引用存在,该对象永远不会回收。
(2)软引用
只有当堆即将发生OOM异常的时候,JVM才会回收软引用所指的对象。软引用通过SoftReference类实现。
(3)弱引用
只要垃圾收集器运行,软引用所指向的对象就会被回收。弱引用通过WeakReference类实现。
(4)虚引用
它和没有引用没有区别,无法通过虚引用访问对象的任何属性或函数。一个对象关联虚引用唯一的作用是该对象被垃圾收集器回收之前会受到一条系统通知。虚引用通过PhantomReference类来实现。
(GC种类)(1)Minor GC,新生代GC,回收速度快
(2)Major GC/Full GC,收集整个堆,回收速度慢
(对象优先分配在新生代Eden)大多数情况下,对象分配在Eden区,如果Eden区空间不够,将发起一次MinorGC.
(大对象直接进入老年代)大对象指需要连续的内存空间对象,经常出现大对象,由于需要连续的 空间,容易导致内存还有不少空间就提前触发垃圾收集,通过PretenureSizeThreshold参数设置限制,超过这个限制的对象直接分配在老年代。
(长时间存活的对象存放在老年代)每个对象有一个对象年龄计数器,经过第一次MinorGC进入存活区,年龄为1,以后经过MinorGC还能继续在存活区的话,年龄加一,当通过限制(MaxTenuringThresshold)后会晋升到老年代。
(动态年龄进行判断)存活区空间相同年龄的所有对象大小的总和大于存活区的空间一半,大于等于该年龄的可以直接进入老年代。
(空间分配担当)Minor GC之前,虚拟机检查老年代最大可用的连续空间是否大于新生代所有对象的总空间,如果可以,可以安全进行,如果不行,看是否允许担保失败,不允许的话进行Full GC。允许的话,看老年代的最大可用连续空间是否大于历次晋升老年代对象的平均大小,如果大于就进行MinorGC,否则进行FullGC。