Java中的垃圾回收和GC

Java中垃圾回收主要在堆中,Java 内存运行时区域中的程序计数器、虚拟机栈、本地方法栈随线程而生灭;栈中的栈帧随着方法的进入和退出而有条不紊地执行着出栈和入栈操作。每一个栈帧中分配多少内存基本上是在类结构确定下来时就已知的(尽管在运行期会由 JIT 编译器进行一些优化),因此这几个区域的内存分配和回收都具备确定性,不需要过多考虑回收的问题,因为方法结束或者线程结束时,内存自然就跟随着回收了。而 Java 堆不一样,一个接口中的多个实现类需要的内存可能不一样,一个方法中的多个分支需要的内存也可能不一样,我们只有在程序处于运行期间时才能知道会创建哪些对象,这部分内存的分配和回收都是动态的,垃圾收集器所关注的是这部分内存。

判断可回收对象
Java中有两种方式可以判断一个对象是否可以被回收:
(1)引用计数法
给对象设置一个引用计数器,当对象被引用一次计数器值加1,取消引用计数器值就减1,当计数器值为0时该对象就是无引用对象。优点是简单高效,缺点是无法解决对象与对象之间的互相引用问题。
(2)可达性分析算法
把一系列被称为“GC ROOTS”的对象作为起始点,从这些起始点开始往下搜索,搜索走过的路径称之为引用链(Reference Chain),当一个对象无任何引用链相连是就是无引用对象。可达性分析算法解决了对象之间的循环引用问题。
GC ROOTS对象:在Java中线程栈变量、静态变量、常量池、JIN指针等都可以是GC ROOTS对象。

垃圾收集算法
Java中垃圾收集算法有如下四种:
(1)标记清除算法:标记清除算法(Mark Sweep)是最基础的垃圾收集算法,分为“标记”和“清除”两个阶段
,首先标记出所有可以被回收的对象,之后在统一清除垃圾对象。
标记清除算法主要有两个不足:
效率问题:标记和清除两个阶段效率都不高
空间问题:标记清除后会产生大量的不连续的内存碎片,空间碎片太多可能会导致以后程序在运行是需要分配较大的对象时找不到足够的连接内存空间而提前触发下一次GC。

(2)复制算法:
复制算法主要是为了解决效率问题,在复制算法中把内存分为相等的两块,每次只使用其中一块,当使用的那块内存满了后,就把还活着的对象复制到另外一块中,然后把已使用过的内存空间清理掉。
复制算法提高了效率,同时也不用考虑内存碎片等问题,但是因为把内存分为了两块,每次只使用其中一块,导致了内存变为了原来的一半。

(3)标记整理算法:
标记整理算法前期的标记阶段与标记清除算法一样,后面与之不同的是标记整理算法中会让存活对象移动到内存的一端,之后清除端边界以外的所有内存。

(4)分代收集算法:
分代收集算法是当前商业虚拟机采用的垃圾收集算法,根据对象存活的生命周期的不同,把内存分为新生代和老年代,新生代和老年代的比为1:2,其中新生代又分为一个Eden区和两个Survivor区(幸存区),新生代用的是复制算法,研究表明新生代中98%的对象都是“朝生夕死”的,所以也不用按1:1分配内存空间,新生代中Eden区和两个Survivor的比例为8:1:1,每次都只使用Eden区和其中一块Survivor区,当内存用完后把存活的对象复制到另外一个没有使用的Survivor区中,并且清除Eden区和刚刚使用过的Survivor区,这样就只有10%的内存浪费。如果遇到极端情况下Survivor区不够用时就会进行分配担保让对象进入老年代,除此之外,一个对象在新生代中经过了多次GC后(默认15次),依然存活,也会进入老年代中。

老年代中因为对象存活率高,也没有多余的空间进行分配担保,所以老年代一般是用标记清除算法或者是标记整理算法。

老年代中触发GC称为Full GC、Old GC或者Major GC,Full GC一般会同时触发新生代的GC(Minor GC) ,Full GC比较耗时,Major GC比较快。

不管是新生代的Minor GC还是老年代的Full GC都会触发一种现象STW(Stop The World),发生STW时会停止所有Java执行线程,看到的现象就是卡顿,新生代GC执行比较快卡顿几乎不明显,而老年代GC比较耗时,造成STW时间也比较长经常会有明显的卡顿现象,所以要尽量减少Full GC的发生。造成STW的一个主要原因是执行可达性分析算法,可达性分析算法执行过程中如果对象的引用关系还在发生变化,那么分析结果的准确性就得不到保证,所以在执行可达性分析算法枚举根节点时需要停止Java执行线程。

垃圾收集器
Serial收集器(串行收集器):
新生代收集器,Serial收集器是一个单线程收集器,它只会使用一个CPU或者一条收集线程去完成垃圾收集工作。当Serial收集器工作时会停止其他的工作线程,直至收集完成。

ParNew收集器:
新生代收集器,是Serial收集器的多线程版本,使用多条线程进行垃圾回收,当ParNew收集器工作时也会暂停其他用户线程。ParNew收集器在单CPU上并不比Serial收集强,只要在多CPU上才能体现它的效率。ParNew收集器是目前除了Serial收集器外唯一能和CMS收集器配合工作的收集器。

Parallel Scavenge收集器:
新生代收集器,多线程并行收集器。Parallel Scavenge收集器主要关注的不是尽可能的缩短收集器回收时用户线程的等待时间,而是关注达到一个可控制的吞吐量。所谓吞吐量就是 CPU 用于运行用户代码的时间与 CPU 总消耗时间的比值。

Serial Old收集器:
老年代收集器,单线程收集器。只用标记-整理算法。

Parallel Old收集器:
老年代收集器,和Parallel Scavenge配合使用,使用多线程和标记-整理算法。

CMS收集器:
老年代收集器,CMS收集器的目标是获取最短收集停顿时间,并发收集、低停顿,基于标记-清除算法实现。CMS收集器是和用户线程一起并发执行的。它的工作过程有如下四个步骤:
1、初始标记
2、并发标记
3、重新标记
4、并发清除

G1收集器(Garbage-First):
G1收集器工作在整个Java堆,可以采用不同的方式对新生代和老年代对象执行分代收集,同时G1收集器还可以利用多CUP执行并行收集的同时还可以和其他用户线程一起并发执行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值