GC的思想早在java诞生以前就已经出现,垃圾回收是内存“自动化”的一部分。java
判断对象是否能够回收有两种策略。第一种是引用计数法,方法顾名思义,但它的问题是不能解决循环引用的状况,循环引用以下所示:算法
public class Test{ public Onject reference; public static void main(String[] args) { Test test1 = new Test(); Test test2 = new Test(); test1.reference = test2; test2.reference = test1; test1 = null; test2 = null; } }
主流jvm使用可达性分析算法,其核心思路是从一系列GC Root对象出发向下搜索,搜索的路径称为引用链。GC Root对象包括:安全
GC的区域是堆和方法区(永久代),这两个区域都是线程共享的。首先先说明清理方法区。方法区中主要存放的是类加载后的信息、常量、静态属性等。GC会清理没用的类和常量。咱们知道java中String是存放在常量池中的,那么若是没有引用指向常量池中的某个字符串,gc就会将其清理。清理类条件相对苛刻,须要知足才能够被回收,并非必定会回收,还跟jvm配置的相关参数有关:数据结构
Gc的主要区域是java堆,本文以及后面博文中会对堆的回收作详细说明。jvm
这里会记录GC算法的思想,算法实现超出能力范围编码
1.标记-清除算法spa
顾名思义,算法分为“标记”和“清除”两个阶段,它是GC中最基础的算法。但有明显缺陷:第一效率低;第二产生内存碎片,当程序运行须要分配大对象时,没法找到足够的连续内存,进而触发另外一次GC操做。线程
以前脑子故障一直想不通为何不边标记边删除,- -想出答案时我简直“佩服”个人智商,,由于只能按照引用链标记可达对象。。。另外看了不少概念性的东西,我在想什么样的对象是不可达的。不少例子中直接把引用指向null,这种我以为并无什么意义。后来我注意到做用域在是否可达中起很大做用。不一样大小的做用域,好比类、方法、甚至是某个代码块。当离开某个做用域时,做用域中对象变为不可达。那么gc就会回收这些内存。指针
2.复制算法code
复制算法产生的缘由是为了解决标记-清除算法效率低的问题。将可用内存分为两块(原理并不是实际),每次只使用其中一块,当这块用完,将存活对象复制到另外一块中,并清除原来的半区。内存分配再也不考虑碎片问题,仅仅是指针移动便可。不过算法的代价是内存利用率低。
Sun的HotSpot在新生代采用这种回收算法。新生代中大部分,有书上写98%的对象都是“朝生夕亡”的,多以并不须要按照1:1来划份内存。将较大的分给Eden,S0、S1则较小。默认是8:1:1的比例,不过也能够配置相关的jvm参数调整。使用时对象存放在Eden+某个Survivor中,并将存活的复制到另外一个Survivor上。这样仅仅浪费了10%的新生代空间。但也会出现存活对象多于10%的状况。此时须要依赖老年代进行分配担保,若是老年代提供担保,这些对象直接进入老年代。
3.标记-整理算法
新生代采用复制算法的一个重要缘由是新生代中对象存活率低。若是是老年代,对象存活率高,就须要大量的复制,内存利用率也不多是90%。因此老年代不采用复制算法。采用标记-整理算法。
标记-整理算法,在标记对象后,让存活对象向一端移动,最后清除边界之外的内存。
新生代使用复制算法、老年代使用标记-整理算法,这种方式称为“分代收集算法”。
为了快速完成标记过程,减小gc的停顿时间(目前的虚拟机是没法避免的,任何一种垃圾回收器都会产生停顿,STW Stop The World。产生停顿的缘由是垃圾回收须要确保一致性的快照中进行,保证标记清理时引用关系不变。其实CMS能够达到很短的停顿,但牺牲了一部分吞吐量,这个下一篇博文会说明),HotSpot中使用一组OopMap的数据结构来达到目的。ps:这里书上写的也不彻底明确。类加载完成的时候HotSpot就把对象内什么偏移量上是什么类型的数据计算出来,在JIT编译过程当中,也会在特定的位置记录下栈和寄存器中哪些位置是引用。这样GC扫描时就直接得知这些信息。这里我也不是彻底理解,之后慢慢理解。
安全点。安全点有两个做用,1.程序运行到安全点才生成OopMap 2.程序执行时只有全部线程都停顿在安全点才开始GC。方法调用、循环跳转、异常跳转这些指令会产生安全点。目前全部的虚拟机都采用主动中断的方式让全部线程停在最近的安全点。每一个线程在通过安全点时会检查一个终端标志,若是为真就停下来(挂起)。其实总结一下,安全点就是在编译的过程当中在特定的地方加入特定的指令,功能分别是生成OopMap和轮询中断标志、中断线程。若是在GC的过程当中有线程从sleep或挂起的状态变为运行状态,由于他是活动的,会对GC形成影响。虚拟机中还有成为安全区的机制。安全区域是不会改变引用关系的代码片断,若是醒来的线程在安全区中它能够继续运行至退出安全区,若是此时没有完成标记工做,该线程会停下来,等待能够离开安全区的信号。
jvm基础总归会有不少概念性的,很理论的东西。我认为这些算法思路是能够应用到平时编码中的。一些看似显而易见的结论或算法,在得出结论和应用时也许是不简单的。下一篇博文会说明各类垃圾回收器。