持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第14天,点击查看活动详情
面试官:JVM中都有哪些常见的垃圾回收器,各自的特点是什么?
垃圾回收器
在新生代和老年代进行垃圾回收的时候,都是要用垃圾回收器进行回收的,不同的区域用不同的垃圾回收器。
- Serial和Serial Old垃圾回收器:分别用来回收新生代和老年代的垃圾对象,工作原理就是单线程运行,垃圾回收的时候会停止我们自己写的系统的其他工作线程,让我们系统直接卡死不动,然后让他们垃圾回收,这个现在一般写后台Java系统几乎不用。
- ParNew和CMS垃圾回收器:ParNew现在一般都是用在新生代的垃圾回收器,CMS是用在老年代的垃圾回收器,他们都是多线程并发的机制,性能更好,现在一般是线上生产系统的标配组合。
- G1垃圾回收器:统一收集新生代和老年代,采用了更加优秀的算法和设计机制。
Stop the World
面试官:STW是什么?
我们平时使用JVM最大的痛点,其实就是在垃圾回收的这个过程。
因为在垃圾回收的时候,尽可能要让垃圾回收器专心致志的干工作,不能随便让我们写的Java系统继续对象了,所以此时JVM会在后台直接进入“Stop the World”状态。也就是说,他会直接停止我们写的Java系统的所有工作线程,让我们写的代码不再运行!然后让垃圾回收线程可以专心致志的进行垃圾回收的工作。
这样的话,就可以让我们的系统暂停运行,然后不再创建新的对象,同时让垃圾回收线程尽快完成垃圾回收的工作,就是标记和转移Eden以及Survivor2的存活对象到Survivor1中去,然后尽快一次性回收掉Eden和Survivor2中的垃圾对象。
接着一旦垃圾回收完毕,就可以继续恢复我们写的Java系统的工作线程的运行了,然后我们的那些代码就可以继续运行,继续在Eden中创建新的对象。
Stop the World造成的系统停顿
现在清楚“Stop the World”会对系统造成的影响了, 假设我们的Minor GC要运行100ms,那么可能就会导致我们的系统直接停顿100ms不能处理任何请求,在这100ms期间用户发起的所有请求都会出现短暂的卡顿,因为系统的工作线程不在运行,不能处理请求。
假设开发的是一个Web系统,那么可能导致用户从网页或者APP上点击一个按钮,然后平时只要几十ms就可以返回响应了,现在因为Web系统的JVM正在执行Minor GC,暂停了所有的工作线程,导致请求过来到响应返回,这次需要等待几百毫秒。那么大家可以思考一下,因为内存分配不合理,导致对象频繁进入老年代,可能平均七八分钟一次Full GC,而Full GC是最慢的,有的时候弄不好一次回收要进行几秒钟,甚至几十秒,有的极端场景几分钟都是有可能的。
那么此时一旦频繁的Full GC,难道希望系统每隔七八分钟就卡死个30秒吗?在30秒内任何用户的请求全部卡死无法处理,然后用户看到的都是系统超时之类的提示,这会让用户体验极差。所以说,无论是新生代GC还是老年代GC,都尽量不要让频率过高,也避免持续时间过长,避免影响系统正常运行,这也是使用JVM过程中一个最需要优化的地方,也是最大的一个痛点。
不同的垃圾回收器的不同的影响
比如对新生代的回收,Serial垃圾回收器就是用一个线程进行垃圾回收,然后此时暂停系统工作线程,所以一般我们在服务器程序中很少用这种方式。
但是我们平时常用的新生代垃圾回收器是ParNew,他针对服务器一般都是多核CPU做了优化,他是支持多线程个垃圾回收的,可以大幅度提升回收的性能,缩短回收的时间。
然后包括之前提到的CMS垃圾回收器,专门负责老年代的垃圾回收,他也有自己特殊的一套机制和原理,非常的复杂。
其实JVM本身的迭代演进,就是不断的在优化垃圾回收器的机制和算法,尽可能的降低垃圾回收的过程对我们的系统运行的影响。而我们作为一个合格的Java工程师,我们的责任就是尽可能搞懂这些垃圾回收器的运行机制和算法。然后合理的对线程系统优化内存分配和垃圾回收,尽可能减少垃圾回收的频率,降低垃圾回收的时间,减少垃圾回收对系统运行的影响。