目录
Parallel Scavenge收集器如何控制程序的吞吐量
前言
前面的技术文章我主要介绍了JVM垃圾收集的几种算法的核心思想,算法本身是有逻辑性的,那么如果我们要真正应用到程序中,需要一个把收集算法集中到一个管理工具上,这个管理工具就是本篇文章我们要讲的几种经典的垃圾收集器。
1.Serial收集器
Serial收集器,名字已经展示出了一个核心信息,它是一个以单线程为模式的垃圾收集器,即收集器使用单一处理器或收集单一线程去工作,当使用这个线程工作时,收集器会暂停其他所有程序运行中的线程,在工作结束时,暂停的工作线程才会继续工作。也许会有程序员听过"Stop The World"这个词,其意思就是在进行垃圾回收时,对于要处理的应用程序,程序中的所有线程会停止,只让待垃圾收集的线程运行,当然,垃圾回收也有一个重要原则,即尽量减少"Stop The World"的时间,便于使程序运行效率尽量高,并且考虑到用户体验,不可想象当我们在使用一款应用程序时,系统提示必须要暂停5分钟为了收集垃圾,这肯定不现实。
Serial收集器已经存在了很长一段时间,早在JDK1.3.1之前,Serial收集器就是HopSpot虚拟机新生代收集器的唯一选择。
可以看到,Serial收集器分为新生代Serial收集器和老年代Serial收集器,前者使用复制算法,后者采用整理算法,原因在于新生代的内存区域中会有大批对象“死去”,少量对象“存活”,采用复制算法所消耗的复制成本不高就可以完成工作;老年代采用整理算法的原因在于老年代内存区域中会有大量对象存活,如果采用复制算法,成本会相对较高,采用整理算法更加高效。
Serial收集器采用串行的原因
串行和并行是两种不同的工作方式,Serial采用串行,是为了提高单一线程垃圾收集的效率,省去了多行线程切换的开销,更加注重垃圾收集本身。所以对于单核CPU和内存较小的系统环境,更适合Serial收集器的使用。
2.ParNew收集器
之前介绍到了典型的串行收集器:Serial收集器,那么复杂多样的世界里,肯定也需要并行收集器来发挥作用,所以这里要讲的ParNew收集器,其实质就是Serial收集器的多线程并行版本,专门用来处理多核CPU应用到的场景。从名字也能看出来,ParNew中的Par是Paralle的缩写,New代表这种收集器只能处理新生代区域的对象。
其实严格来讲,这里说的ParNew收集器指的是新生代ParNew收集器,后面我会介绍到Parallel Old收集器,这就是能多线程处理老年代区域的收集器。
ParNew收集器是在JDK1.4.2同步发布的,在JDK6之后成为了HotSpot默认的收集器作为对比来学习,ParNew收集器和Serial收集器的区别就在于ParNew收集器可以对多个线程进行垃圾收集,而他们的共同之处有很多:
ParNew收集器的回收效率
我们知道,ParNew收集器是Serial在新生代区域的多线程版本,因此它在多核CPU的系统环境下,能更好的发挥自己的优势,更好的进行垃圾收集,提升程序的吞吐量。而在单个CPU的环境下,ParNew收集器相比Serial收集器,就没什么优势了。
这里提到了吞吐量,这是一个覆盖范围很广的词语,在JVM垃圾收集的体系下,吞吐量指的是单位时间内,虚拟机可以执行的用户代码总时长和虚拟机总时间之比(虚拟机总时间就是运行用户代码时间和垃圾收集时间之和)。吞吐量的大小可以人为调节,我们通过设置-XX:MaxGCPauseMillis参数,控制的是垃圾收集的最大停顿时间,因为如果设置的最大停顿时间比较小,就表示收集器进行垃圾回收更加频繁,一般来讲这会导致吞吐量下降,反之则会提高程序的吞吐量。
3.Parallel Scavenge收集器
Parallel Scavenge收集器也是一个处理新生代区域的收集器,从基本特性上来看,这种收集器和ParNew收集器高度相似,但它的一大特点就在于:Parallel Scavenge收集器可以实现对吞吐量的精准控制。
Parallel Scavenge收集器如何控制程序的吞吐量
在刚才介绍ParNew收集器时,读者已经知道了垃圾收集中的吞吐量,那么它是如何控制吞吐量的呢?这里我们重点探讨三个参数:
- -XX:MaxGCPauseMillis:控制最大垃圾收集停顿时间
- -XX:GCTimeRatio:设置吞吐量大小
- -XX:+UseAdaptiveSizePolicy:Java虚拟机将自动调整堆大小和其他参数,以最大限度地减少垃圾回收的时间
-XX:MaxGCPauseMillis
和之前的ParNew收集器的-XX:MaxGCPauseMillis参数一致,垃圾收集停顿时间越长,程序的吞吐量会越小,反之越高。这里我举一个具体的实例:
如果我们要应用程序处理一个有大量数据的工作,大量数据往往也会带来大量垃圾需要收集,所以需要垃圾收集停顿时间尽量长,这样才能收集更多的垃圾,常见的值是可以把-XX:MaxGCPauseMillis设置为500毫秒;而当我们还要处理有大量的交互式请求的工作,比如Web应用程序中的HTTP请求,很显然,这种请求需要程序尽快地给出一个响应,这样用户体验才会不受影响。所以对于这类工作,我们可以把-XX:MaxGCPauseMillis参数设置小一些,比如设为100毫秒。具体的实现,我们可以在java的命令行中添加以下这段命令:
java -XX:MaxGCPauseMillis=100 YourProgram
-XX:GCTimeRatio
这个参数是指垃圾回收时间和应用程序运行总时间的比率,也就是吞吐量。如果我们把-XX:GCTimeRatio设置为4,就意味着这个比率是四分之一。也因为Parallel Scanvenge收集器具备直接调节程序吞吐量的能力,所以这种收集器有时也被称为吞吐量优先收集器,
-XX:+UseAdaptiveSizePolicy
这个参数其实质就是一个开关参数,当我们打开这个参数后,虚拟机会根据系统的运行情况和收集到的监控信息,自动动态调整许多内存中的细节指标:
总结
Serial收集器,ParNew收集器和Parallel Scavenge收集器都是Java虚拟机的垃圾回收器。这三种垃圾回收器的主要区别在于它们的工作方式和性能。
Serial收集器是Java虚拟机最古老的垃圾回收器之一。它是一种单线程垃圾回收器,只使用一个线程来执行垃圾回收操作。这使得Serial收集器非常适合于小型应用程序,但对于大型应用程序来说可能会导致性能问题。
ParNew收集器是Serial收集器的多线程版本。它使用多个线程来执行垃圾回收操作,从而提高了性能。ParNew收集器通常与CMS垃圾回收器一起使用。
Parallel Scavenge收集器是一种并行垃圾回收器,它使用多个线程来执行垃圾回收操作。Parallel Scavenge收集器通常用于需要高吞吐量的应用程序,例如数据仓库或Web服务器。