5 垃圾回收算法(比较简单,可以去看Java性能调优指南,不想写)

jvm并行与并发:

并行:指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。
并发:指用户线程与垃圾收集线程同时执行(但不一定是并行,可能会交替执行),用户程序在继续运行,而垃圾收集器运行在另一个 CPU 上。

2.1. 标记-清除算法(Mark-Sweep)

最基础的垃圾回收算法,分为两个阶段,标注和清除。标记阶段标记出所有需要回收的对象,清除阶段回收被标记的对象所占用的空间。该算法最大的问题是内存碎片化严重,后续可能发生大对象不能找到可利用空间的问题。

2.2. 复制算法(Copying)

为了解决Mark-Sweep算法内存碎片化的缺陷而被提出的算法。按内存容量将内存划分为等大小的两块。每次只使用其中一块,当这一块内存满后将尚存活的对象复制到另一块上去,把已使用的内存清掉
,这种算法虽然实现简单,内存效率高,不易产生碎片,但是最大的问题是可用内存被压缩到了原本的一半。且存活对象增多的话,Copying算法的效率会大大降低。

2.3. 标记-整理算法(Mark-Compact)

结合了以上两个算法,为了避免缺陷而提出。标记阶段和Mark-Sweep算法相同,标记后不是清理对象,而是将存活对象移向内存的一端。然后清除端边界外的对象。

2.4. 分代收集算法(Generational Collection)

分代收集法是目前大部分JVM所采用的方法,其核心思想是根据对象存活的不同生命周期将内存划分为不同的域,一般情况下将GC堆划分为老生代(Tenured/Old Generation)和新生代(Young Generation)。老生代的特点是每次垃圾回收时只有少量对象需要被回收,新生代的特点是每次垃圾回收时都有大量垃圾需要被回收,因此可以根据不同区域选择不同的算法。

目前大部分JVM的GC对于新生代都采取Copying算法,因为新生代中每次垃圾回收都要回收大部分对象,即要复制的操作比较少,但通常并不是按照1:1来划分新生代。一般将新生代划分为一块较大的Eden空间和两个较小的Survivor空间(From Space, To Space),每次使用Eden空间和其中的一块Survivor空间,当进行回收时,将该两块空间中还存活的对象复制到另一块Survivor空间中。

而老生代因为每次只回收少量对象,因而采用Mark-Compact算法。

另外,不要忘记在Java基础:Java虚拟机(JVM)中提到过的处于方法区的永生代(Permanet Generation)。它用来存储class类,常量,方法描述等。对永生代的回收主要包括废弃常量和无用的类。

对象的内存分配主要在新生代的Eden Space和Survivor Space的From Space(Survivor目前存放对象的那一块),少数情况会直接分配到老生代。当新生代的Eden Space和From Space空间不足时就会发生一次GC,进行GC后,Eden Space和From Space区的存活对象会被挪到To Space,然后将Eden Space和From Space进行清理。如果To Space无法足够存储某个对象,则将这个对象存储到老生代。在进行GC后,使用的便是Eden Space和To Space了,如此反复循环。当对象在Survivor区躲过一次GC后,其年龄就会+1。默认情况下年龄到达15的对象会被移到老生代中。

3 垃圾收集器

gcroots(枚举根节点)等一系列知识点

在可达性分析中,我们知道常说的GC(Garbage Collector) roots,特指的是垃圾收集器(Garbage Collector)的对象,GC会收集那些不是GC roots且没有被GC roots引用的对象。在Java语言里,可作为GC Roots对象的包括如下几种:

a.虚拟机栈(栈桢中的本地变量表)中的引用的对象

b.方法区中的类静态属性引用的对象

c.方法区中的常量引用的对象

d.本地方法栈中JNI的引用的对象

OopMap:https://blog.csdn.net/ifleetingtime/article/details/78934379?from=timeline&isappinstalled=0

安全点和安全区域:https://zhuanlan.zhihu.com/p/55303309

七种垃圾收集器:

垃圾收集算法是垃圾收集器的理论基础,而垃圾收集器就是其具体实现。

Serial(串行GC)-复制
ParNew(并行GC)-复制
Parallel Scavenge(并行回收GC)-复制
Serial Old(MSC)(串行GC)-标记-整理
CMS(并发GC)-标记-清除
Parallel Old(并行GC)–标记-整理
G1(JDK1.7update14才可以正式商用)

说明:

1~3用于年轻代垃圾回收:年轻代的垃圾回收称为minor GC

4~6用于年老代垃圾回收(当然也可以用于方法区的回收):年老代的垃圾回收称为full GC

G1独立完成"分代垃圾回收"
注意:并行与并发

并行:多条垃圾回收线程同时操作
并发:垃圾回收线程与用户线程一起操作

(2)常用五种组合:

Serial/Serial Old

ParNew/Serial Old:与上边相比,只是比年轻代多了多线程垃圾回收而已

ParNew/CMS:当下比较高效的组合

Parallel Scavenge/Parallel Old:自动管理的组合
G1:最先进的收集器,但是需要JDK1.7update14以上

(3.1)Serial/Serial Old:

https://files.jb51.net/file_images/article/201702/2017021415541227.png

特点:

年轻代Serial收集器采用单个GC线程实现"复制"算法(包括扫描、复制)
年老代Serial Old收集器采用单个GC线程实现"标记-整理"算法
Serial与Serial Old都会暂停所有用户线程(即STW)
说明:

STW(stop the world):编译代码时为每一个方法注入safepoint(方法中循环结束的点、方法执行结束的点),在暂停应用时,需要等待所有的用户线程进入safepoint,之后暂停所有线程,然后进行垃圾回收。

适用场合:

CPU核数<2,物理内存<2G的机器(简单来讲,单CPU,新生代空间较小且对STW时间要求不高的情况下使用)
-XX:UseSerialGC:强制使用该GC组合
-XX:PrintGCApplicationStoppedTime:查看STW时间
由于它实现相对简单,没有线程相关的额外开销(主要指线程切换与同步),因此非常适合运行于客户端PC的小型应用程序,或者桌面应用程序(比如swing编写的用户界面程序),以及我们平时的开发、调试、测试等。

(3.2)ParNew/Serial Old:

https://files.jb51.net/file_images/article/201702/2017021415541328.png

说明:

ParNew除了采用多GC线程并行来实现复制算法以外,其他都与Serial一样,但是此组合中的Serial Old又是一个单GC线程,所以该组合是一个比较尴尬的组合,在单CPU情况下没有Serial/Serial Old速度快(因为ParNew多线程需要切换),在多CPU情况下又没有之后的三种组合快(因为Serial Old是单GC线程),所以使用其实不多。

-XX:ParallelGCThreads:指定ParNew GC线程的数量,默认与CPU核数相同,该参数在于CMS GC组合时,也可能会用到

(3.3)Parallel Scavenge/Parallel Old:

https://files.jb51.net/file_images/article/201702/2017021415541529.png

特点:

年轻代Parallel Scavenge收集器采用多个GC线程实现"复制"算法(包括扫描、复制)
年老代Parallel Old收集器采用多个GC线程实现"标记-整理"算法
Parallel Scavenge与Parallel Old都会暂停所有用户线程(即STW)
说明:

吞吐量:CPU运行代码时间/(CPU运行代码时间+GC时间)
CMS主要注重STW的缩短(该时间越短,用户体验越好,所以主要用于处理很多的交互任务的情况)
Parallel Scavenge/Parallel Old主要注重吞吐量(吞吐量越大,说明CPU利用率越高,所以主要用于处理很多的CPU计算任务而用户交互任务较少的情况)
参数设置:

-XX:+UseParallelOldGC:使用该GC组合
-XX:GCTimeRatio:直接设置吞吐量大小,假设设为19,则允许的最大GC时间占总时间的1/(1 +19),默认值为99,即1/(1+99)
-XX:MaxGCPauseMillis:最大GC停顿时间,该参数并非越小越好
-XX:+UseAdaptiveSizePolicy:开启该参数,-Xmn/-XX:SurvivorRatio/-XX:PretenureSizeThreshold这些参数就不起作用了,虚拟机会自动收集监控信息,动态调整这些参数以提供最合适的的停顿时间或者最大的吞吐量(GC自适应调节策略),而我们需要设置的就是-Xmx,-XX:+UseParallelOldGC或-XX:GCTimeRatio两个参数就好(当然-Xms也指定上与-Xmx相同就好)
适用场合:

很多的CPU计算任务而用户交互任务较少的情况
不想自己去过多的关注GC参数,想让虚拟机自己进行调优工作
对吞吐量要求较高,或需要达到一定的量。

(2.4)ParNew/CMS:

https://files.jb51.net/file_images/article/201702/2017021415541630.png

说明:

CMS(Concurrent Mark Sweep)收集器:老年代收集器,致力于获取最短回收停顿时间(然后就达到缩短垃圾回收的时间)

以上只是年老代CMS收集的过程,年轻代ParNew看"2.2、ParNew/Serial Old"就好
CMS是多回收线程的,不要被上图误导,默认的线程数:(CPU数量+3)/4

CMS主要注重STW的缩短(该时间越短,用户体验越好,所以主要用于处理很多的交互任务的情况)
特点:

1.年轻代ParNew收集器采用多个GC线程实现"复制"算法(包括扫描、复制)

2.年老代CMS收集器采用多线程实现"标记-清除"算法

初始标记:标记与根集合节点直接关联的节点。时间非常短,需要STW
并发标记:遍历之前标记到的关联节点,继续向下标记所有存活节点。时间较长。
重新标记:重新遍历trace并发期间修改过的引用关系对象。时间介于初始标记与并发标记之间,通常不会很长。需要STW
并发清理:直接清除非存活对象,清理之后,将该线程占用的CPU切换给用户线程
3.初始标记与重新标记都会暂停所有用户线程(即STW),但是时间较短;并发标记与并发清理时间较长,但是不需要STW

关于并发标记期间怎样记录发生变动的引用关系对象,在重新标记期间怎样扫描这些对象

缺点:

并发标记与并发清理:按照说明的第二点来讲,假设有2个CPU,那么其中有一个CPU会用于垃圾回收,而另一个用于用户线程,这样的话,之前是两CPU运行用户线程,现在是一个,那么效率就会急剧下降。也就是说,降低了吞吐量(即降低了CPU使用率)。
并发清理:在这一过程中,产生的垃圾无法被清理(因为发生在重新标记之后)
并发标记与并发清理:由于是与用户线程并发的,所以用户线程可能会分配对象,这样既可能对象直接进入年老代(例如,大对象),也可能进入年轻代后,年轻代发生minor GC,这样的话,实际上要求我们的年老代需要预留一定空间,也就是说要在年老代还有一定空间的情况下就要进行垃圾回收,留出一定内存空间来供其他线程使用,而不能等到年老代快爆满了才进行垃圾回收,通过-XX:CMSInitiatingOccupancyFraction来指定当年老代空间满了多少后进行垃圾回收
标记-清理算法:会产生内存碎片,由于是在老年代,可能会提前触发Full GC(这正是我们要尽量减少的)
参数设置:

-XX:+UseConcMarkSweepGC:使用该GC组合
-XX:CMSInitiatingOccupancyFraction:指定当年老代空间满了多少后进行垃圾回收
-XX:+UseCMSCompactAtFullCollection:(默认是开启的)在CMS收集器顶不住要进行FullGC时开启内存碎片整理过程,该过程需要STW
-XX:CMSFullGCsBeforeCompaction:指定多少次FullGC后才进行整理
-XX:ParallelCMSThreads:指定CMS回收线程的数量,默认为:(CPU数量+3)/4
适用场合:

用于处理很多的交互任务的情况

方法区的回收一般使用CMS,配置两个参数:-XX:+CMSPermGenSweepingEnabled与-XX:+CMSClassUnloadingEnabled

适用于一些需要长期运行且对相应时间有一定要求的后台程序

(2.5)G1

https://files.jb51.net/file_images/article/201702/2017021415541731.png

说明:

从上图来看,G1与CMS相比,仅在最后的"筛选回收"部分不同(CMS是并发清除),实际上G1回收器的整个堆内存的划分都与其他收集器不同。
CMS需要配合ParNew,G1可单独回收整个空间
原理:

G1收集器将整个堆划分为多个大小相等的Region
G1跟踪各个region里面的垃圾堆积的价值(回收后所获得的空间大小以及回收所需时间长短的经验值),在后台维护一张优先列表,每次根据允许的收集时间,优先回收价值最大的region,这种思路:在指定的时间内,扫描部分最有价值的region(而不是扫描整个堆内存),并回收,做到尽可能的在有限的时间内获取尽可能高的收集效率。
运作流程:

初始标记:标记出所有与根节点直接关联引用对象。需要STW
并发标记:遍历之前标记到的关联节点,继续向下标记所有存活节点。在此期间所有变化引用关系的对象,都会被记录在Remember Set Logs中
最终标记:标记在并发标记期间,新产生的垃圾。需要STW
筛选回收:根据用户指定的期望回收时间回收价值较大的对象(看"原理"第二条)。需要STW
优点:

停顿时间可以预测:我们指定时间,在指定时间内只回收部分价值最大的空间,而CMS需要扫描整个年老代,无法预测停顿时间
无内存碎片:垃圾回收后会整合空间,CMS采用"标记-清理"算法,存在内存碎片
筛选回收阶段:
由于只回收部分region,所以STW时间我们可控,所以不需要与用户线程并发争抢CPU资源,而CMS并发清理需要占据一部分的CPU,会降低吞吐量。
由于STW,所以不会产生"浮动垃圾"(即CMS在并发清理阶段产生的无法回收的垃圾)
适用范围:

追求STW短:若ParNew/CMS用的挺好,就用这个;若不符合,用G1
追求吞吐量:用Parallel Scavenge/Parallel Old,而G1在吞吐量方面没有优势

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值