JVM学习(三):垃圾收集
1、垃圾收集(Garbage Collection,GC),需要考虑以下3件事
(1)哪些内存需要回收?(What)
(2)什么时候回收?(When)
(3)如何回收?(How)
2、判断堆内存是否需要回收,主要是判断对象的引用是否还存在,主要有以下策略
(1)引用计数算法:每个对象含有一个引用计数器,有引用+1,引用失效则-1,任意时刻计数器为0则判定对象不会再被使用,算法优点是实现简单,执行效率高,缺点是无法解决对象之间循环引用造成的问题。
(2)可达性分析算法:以GC Roots为起点向下搜索,当一个对象和GC Roots之间没有任何可达路径时,即判定该对象是不可用的。主流Java虚拟机均采用可达性分析算法来管理内存。
(3)Java中可以作为GC Roots的对象有:虚拟机栈(栈帧中的局部变量表)中引用的对象、方法区中静态类属性引用的对象、方法区中常量引用的对象以及本地方法栈中JNI(即Native方法)引用的对象。
(4)判断一个对象是否存活,至少要经历两次标记过程:
第一次经过可达性分析后发现没有与GC ROOTS相连接的引用链,将进行第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法(任何一个对象的finalize()方法只会被系统自动调用一次)。
当对象被判定为有必要执行finalize()方法,将会被放置在一个叫做F-Queue的队列之中,GC将会对F-Queue中的对象进行第二次小规模的标记,finalize()方法是对象避免被回收的最后一次机会,如果对象在finalize()方法中重新建立了引用关系,则第二次标记时它将被移除“即将回收”的集合,否则就会被GC回收掉。
3、对象引用的分类自JDK1.2以后分为以下几种
(1)强引用:new对象
(2)软引用:SoftReference
(3)弱引用:WeakReference
(4)虚引用:PhantomReference
4、常见垃圾收集算法
(1)标记-清除算法(老年代)
(2)复制算法(新生代)
(3)标记-整理算法(老年代)
(4)分代收集算法(新生代和老年代)
5、HotSpot的GC算法实现
(1)枚举根节点(GC Roots):主要在全局性的引用(常量或静态属性)与执行上下文(栈帧中的本地变量表)中,进行时必须停顿所有的Java执行线程(Stop The World),搜索引用链是使用一组称为OopMap的数据结构来实现的。
(2)安全点(Safepoint):如果为每一条指令都生成对应的OopMap,将会需要大量的额外空间,所以只在特定的位置(如方法调用、循环跳转、异常跳转等)进行GC,即程序执行时并非在所有地方都停顿下来进行GC,只有在到达安全点时才能暂停。线程执行时通过主动式中断,即轮询安全点设置的中断标志来中断当前任务从而在安全点停顿下来进行GC。
(3)安全区域(Safe Region):当线程处于Sleep或者Blocked状态,无法响应JVM的中断请求时,这是就需要通过安全区域来判断是否进行GC,即在一段代码中,引用关系不会发生变化,在这个区域内的任意地方开始GC都是安全的,可以看做被扩展了的安全点。
6、垃圾收集器
(1)CMS
(2)G1
7、内存分配与回收策略
(1)大多数情况下,对象在新生代Eden区中分配,当Eden区没有足够空间进行分配时,虚拟机将发生一次Minor GC。由于大多数对象都是朝生夕死,所以Minor GC的发生会非常频繁,速度也比较快。
(2)大对象(如长字符串和数组)或者长期存活的对象(对象年龄判断)分配在老年代,老年代为新生代提供空间分配担保,当老年代空间不足时会发生一次Full GC,速度一般会比Minor GC慢10倍以上。
8、HotSpot虚拟机将新生代分为一个Eden区和两个Survivor区,默认Eden和Survivor的比例大小是8:1,即新生代可分配的内存大小为总内存大小的90%(一个Eden区+一个Survivor区)。