Java GC是在什么时候,对什么东西,做了什么事情?”
什么位置:大部分在堆中,还有方法区!!方法区的垃圾收集主要回收两部分内容:废弃常量和无用的类,当满了之后同样触发FullGC, HotSpot1.8之前由永久代实现,1.8已经移到元空间,元空间并不在虚拟机中,而是使用本地内存。
什么时候:程序员不能控制具体时间,系统在不可预测的时间调用System.gc()函数的时候;当然可以通过调优,用NewRatio控制newObject和oldObject的比例,用MaxTenuringThreshold 控制进入oldObject的次数,使得oldObject 存储空间延迟达到full gc,从而使得计时器引发gc时间延迟OOM的时间延迟,以延长对象生存期。
什么东西:超出了作用域或引用计数为空的对象;从gc root开始搜索找不到的对象,而且经过一次标记、清理,仍然没有复活的对象。
什么事情:删除不使用的对象,回收内存空间;运行默认的finalize,当然程序员想立刻调用就用dipose调用以释放资源如文件句柄,JVM用from survivor、to survivor对它进行标记清理,对象序列化后也可以使它复活。
引用计数法和可达性分析算法
引用计数法(ReferenceCounting):给对象中添加一个引用计数器,每当它被引用到一个地方时,计数器值就+1,;当引用失效时,计数器值就-1;任何时刻计数器为0的对象就是不可能在被使用。
1)、优点
判定效率很高
(2)、缺点
不会完全准确,因为如果出现两个对象相互引用的问题就不行了。
现在虚拟机都不采用引用计数法。
可达性分析法:该方法的基本思想是通过一系列的“GC Roots”对象(局部变量,栈等)作为起点进行搜索,如果在“GC Roots”和一个对象之间没有可达路径,则称该对象是不可达的,不过要注意的是被判定为不可达的对象不一定就会成为可回收对象;被判定为不可达的对象要成为可回收对象必须至少经历两次标记过程,如果在这两次标记过程中仍然没有逃脱成为可回收对象的可能性,则基本上就真的成为可回收对象了。
GC Roots的对象
- 虚拟机栈(帧栈中的本地变量表)中引用的对象。
- 方法区中静态属性引用的对象。
- 方法区中常量引用的对象。
- 本地方法栈中 JNI 引用的对象。
Stop The World
可达性分析期间需要保证整个执行系统的一致性,对象的引用关系不能发生变化,所以需要将用户的正常的工作线程全部停掉,避免对象的引用关系变化;
导致GC进行时必须停顿所有Java执行线程(称为"Stop The World");(几乎不会发生停顿的CMS收集器中,枚举GC ROOTS时也是必须要停顿的)
是JVM在后台自动发起和自动完成的;
在用户不可见的情况下,把用户正常的工作线程全部停掉;
垃圾回收算法:
标记-清除(Mark-Sweep)算法:两个阶段:标记阶段和清除阶段。标记阶段的任务是根据GC ROOTS标记出所有需要被回收的对象,清除阶段就是回收被标记的对象所占用的空间;标记-清除算法实现起来比较容易,但是有一个比较严重的问题就是容易产生内存碎片,碎片太多可能会导致后续过程中需要为大对象分配空间时无法找到足够的空间而提前触发新的一次垃圾收集动作。
复制算法:它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉,这样一来就不容易出现内存碎片的问题。
标记-整理算法:应用在老年代。该算法标记阶段和Mark-Sweep一样,但是在完成标记之后,它不是直接清理可回收对象,而是将存活对象都向一端移动,然后清理掉端边界以外的内存。在清理的时候,把所有 存活 对象扎堆到同一个地方,让它们待在一起,这样就没有内存碎片了。
分代收集算法(目前常用):应用在年轻代。根据对象存活的生命周期将内存划分为若干个不同的区域,一般来说是将新生代划分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden空间和其中的一块Survivor空间,当进行回收时,将Eden和Survivor中还存活的对象复制到另一块Survivor空间中,然后清理掉Eden和刚才使用过的Survivor空间。
分代收集算法总结:
(1)在年轻代中,Eden区提供堆内存如果满了,Eden进行MinorGC,将存活的对象→Survivor A中,Eden区清空;
(2)Eden区再次满, Eden 区和 Survivor A 区同时进行 Minor GC,把存活对象放入 Survivor B 区,Eden和Survivor A同时清空;
(3)重复(2)的操作,如果当某个 Survivor 区被填满,且仍有对象未被复制完毕时,或者某些对象在反复 Survive 15 次左右时,或者大对象,则把这部分剩余对象放到Old 区(老年代);
(4)当 Old 区也被填满时,进行 Full GC,对 Old 区进行垃圾回收。
[注意,在真实的 JVM 环境里,可以通过参数 SurvivorRatio 手动配置 Eden 区和单个 Survivor 区的比例,默认为 8。可以通过参数–XX:SurvivorRatio 来设定,即将堆内存中年轻代划分为8:1:1]
常见垃圾回收器:
CMS
CMS(Current Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,它是一种并发收集器,采用的是Mark-Sweep算法。
一种以获取最短回收停顿时间为目标的收集器 “标记-清除”,有 4 个过程:
初始标记(查找直接与 gc roots 链接的对象),需要“Stop The World”;
并发标记(GC Roots Tracing 过程:以GCRoots的对象作为起始点,从这个节点向下搜索,搜索走过的路径称为ReferenceChain,当一个对象到GCRoots没有任何ReferenceChain相连时,这个对象不可到达,则证明这个对象不可用);
重新标记(因 为并发标记时有用户线程在执行,标记结果可能有变化),需要“Stop The World” ;
并发清除(并发清除阶段会清除对象)。
其中初始标记和重新标记阶段,要“stop the world”(停止工作线程)。
优点:并发收集,低停顿
缺点:
1)不 能处理浮动垃圾
2)对 cpu 资源敏感,占用CPU资源较大。CMS默认启动的回收线程数是(CPU数量+3)/ 4,也就是当CPU在4个以上时,并发回收时垃圾收集线程不少于25%的CPU资源,并且随着CPU数量的增加而下降。但是当CPU不足4个(譬如2个)时,CMS对用户程序的影响就可能变得很大。
3)产生大量内存碎片
G1
G1收集器是面向服务端应用的收集器,它能充分利用多CPU、多核环境。
因此它是一款并行与并发收集器,并且它能建立可预测的停顿时间模型。
对垃圾回收进行了划分优先级的操作,这种有优先级的区域回收方式保证了它的高效率;最大的优点是结合了空间整合,不会产生大量的碎片,也降低了进行gc的频率,让使用者明确指定停顿时间。
初始标记(Initial Marking)
初始标记阶段仅仅只是标记一下GC Roots能直接关联到的对象,并且修改TAMS(Next Top at Mark Start)的值,让下一阶段用户程序并发运行时,能在正确可用的Region中创建新对象,这阶段需要停顿线程,但耗时很短。
并发标记(Concurrent Marking)
并发标记阶段是从GC Root开始对堆中对象进行可达性分析,找出存活的对象,这阶段耗时较长,但可与用户程序并发执行。
最终标记(Final Marking)
最终标记阶段是为了修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录,虚拟机将这段时间对象变化记录在线程Remembered Set Logs里面,最终标记阶段需要把Remembered Set Logs的数据合并到Remembered Set中,这阶段需要停顿线程,但是可并行执行。
筛选回收(Live Data Counting and Evacuation)
筛选回收阶段首先对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划,这个阶段其实也可以做到与用户程序一起并发执行,但是因为只回收一部分Region,时间是用户可控制的,而且停顿用户线程将大幅提高收集效率。
https://baijiahao.baidu.com/s?id=1583441733083989684&wfr=spider&for=pc
http://blog.csdn.net/cy609329119/article/details/51771953
https://blog.csdn.net/xiaomingdetianxia/article/details/77446762