<>新生代GC案例

若系统不停运行,然后把Eden给搞满:

此时必然触发Minor
GC,有专门GC线程执行GC,且对不同内存区域有不同垃圾回收器,这相当于GC线程和垃圾回收器配合,使用自己的GC算法对指定内存区域执GC:

垃圾回收一定会通过一个后台运行的GC线程,如针对新生代用ParNew垃圾回收器,其针对新生代采用复制算法:标记Eden区中的存活对象,然后全部转移到Survivor1,然后一次性清空Eden中垃圾对象:

接着系统继续运行,新对象继续分配在Eden:

当Eden再满,又触发Minor GC,此时已然是GC线程运行垃圾回收器中的算法逻辑:采用复制算法逻辑,标记Eden和Survivor1中的存活对象。

然后一次性把存活对象转移到Survivor2,接着把Eden和Survivor1中的垃圾对象都回收:

<>GC过程中,还能创建新对象吗?

我们假设允许:

如上所示,若一边垃圾回收器在想法把Eden和Survivor2里存活对象标记出来转移到Survivor1,然后还得想法把Eden
和Survivor2里的垃圾对象都清掉,结果这时系统程还在不停往Eden创建新对象。这些新对象有的又很快成垃圾对象,现在咋办?乱套了,对程序新创建的这些对象,你怎么让垃圾回收器去持续追踪这些新对象的状态呢?

如何才能在这次GC过程中:

*
把新对象中的那些存活对象转移到Survivor2?

*
如何把新创建的对象中的垃圾都给回收?

有的同学说,那是垃圾回收器的事啊!你得好好设计!

有些事情想着很简单,但是一旦你要在JVM实现必然很复杂。所以说,在GC过程中,同时还允许我们写的Java系统继续不停的运行在Eden里持续创建新的对象,目前看很不合适。

<>JVM的痛点:Stop the World

因为GC时,尽可能要让垃圾回收器专心工作,不能随便让我们写的Java系统继续新建对象了,所以此时JVM会在后台直接进入“Stop the
World”:停止Java系统所有工作线程,让我们写的代码无法再运行!然后让GC线程能安心执行GC:

这就能让我们的系统暂停运行,不再创建新对象,同时让GC线程尽快完成GC工作:标记和转移Eden及Survivor2的存活对象到Survivor1,然后快速地一次性回收掉Eden和Survivor2中的垃圾对象:

GC完毕,即可继续恢复Java系统的工作线程运行,继续在Eden创建新对象:

<>Stop the World造成的系统停顿

假设Minor
GC要运行100ms,则可能导致系统直接停顿100ms,不能处理任何请求。这100ms期间用户发起的所有请求都会出现短暂卡顿,因为系统工作线程不在运行,不能处理请求。假设是Web系统,可能导致你的用户从网页或APP点击一个按钮,平时只要几十ms就可以返回响应,现在因为Web系统JVM正在执行Minor
GC,暂停所有工作线程,导致你的请求过来到响应返回,这次需等待几百ms。

因为内存分配不合理,导致对象频繁进入老年代,平均7、8min一次Full GC,而Full GC最慢,有时一次回收要进行几s甚至几十s。此时一旦频繁Full
GC,你的系统每隔几min就会卡死几十s。这让用户体验极差。所以无论新生代GC还是老年代GC,都尽量不能频繁,也要避免持续时间过长。

<>不同的垃圾回收器的不同的影响

新生代的回收,Serial垃圾回收器就是用1个线程进行垃圾回收,然后此时暂停系统工作线程,所以一般服务器程序中很少用。平时常用的新生代垃圾回收器是ParNew,其针对服务器一般都是多核CPU有优化,支持多线程GC,可大幅提升回收性能,缩短GC时间。

大致原理:

CMS也是基于多线程的,且可使用一套独特的机制尽可能的在垃圾回收的过程中减少“Stop the World”的时间,避免长时间系统卡死。

G1更是将采用复杂的回收机制将回收性能优化到机制,尽可能更多的降低“Stop the
World”的时间。JVM本身迭代演进,就是不断优化垃圾回收器机制和算法,尽可能降低垃圾回收的过程对系统运行的影响。

对于我们这些 crud
boy,就得合理对线程系统优化内存分配和垃圾回收,尽可能减少垃圾回收的频率,降低垃圾回收的时间,减少垃圾回收对系统运行的影响。JVM优化也就是这样。

技术
©2019-2020 Toolsou All rights reserved,
程序员的520,送给女友的几行漂亮的代码(python版)基于stm32控制四轮小车电机驱动(一)linux查看磁盘空间命令实验四 自动化测试工具-软件测试axios拦截器封装与使用C语言——qsort函数opencv-python傅里叶变换以及逆变换在算法研究过程中如何进行算法创新nc的安装和简单操作C语言做一个简易的登陆验证(功能)界面