问题描述
CPython的内存管理是以引用计数为主、可达性分析为辅助,因此大部分对象在引用计数为0时就马上释放。 Java则是到达某个时间点时触发GC,虽然有CMS, G1,但在大内存下full GC的停顿时间还是个问题。最近在开发一个网站(JSP + Spring,服务端完全Java实现),如果将来要使用大内存,是不是只能部署多台用4GB以下堆的Tomcat这种方式来避免长时间的GC停顿?
我发现了Azul: Azul Systems
据说可以完全消灭STW
这个问题的答案要取决于很多因素,包括但不限于:
- 对这个特定应用来说,多大的内存叫做大。题主给的是(10GB以上)的补充说明,我见过许多有把1GB以上的-Xmx叫做大内存的(注意这说的是服务器端Java应用,不是移动设备上的),也有2GB、4GB、6GB、8GB…一直上到96GB、128GB、180GB之类,再继续往上到1TB的。大家对“大”的界定可以有数量级的差别。
- 对这个特定应用来说,多长时间的暂停时间是可接受/不可接受的分界线。这通常是由服务级别协议(Service Level Agreement,简称SLA)定义的——这个应用对客户承诺要达到怎样级别的性能。例如说有可能要支持的服务级别是99.999%的响应时间不能超过500ms,那么就可以根据这个要求根据应用具体要做的事情估算能容忍的GC暂停时间的长度,例如说99.999%的GC暂停不能超过200ms。定义一个清晰的目标非常重要,无论是要调优还是要换JVM啥的。如果SLA并不对GC暂停时间有任何要求的话,GC暂停又如何呢?只要能跑完就好了 ;-)
- 对这个特定应用来说,对象的分配速度和生命周期是怎样的模式。这个非常重要,有些GC实现对此会非常敏感(例如HotSpot VM的ParNew+CMS组合)。另外一个问题讨论了与此略为相关的话题,放个传送门:jvm gc遍历一次新生代所有对象是否可达需要多久? - RednaxelaFX 的回答
- 这个特定应用所运行的机器配置如何。以前在淘宝工作的时候有幸见过各种部署情况,有些应用被部署在配置不同的若干服务器上,然后会发现CPU比较慢或核数比较少的机器更容易出现GC收集速度跟不上应用的分配速度的状况。CPU、应用与GC三者间的关系还是得看应用的具体行为,不过这是一个需要关注的点。
- 对这个特定应用来说,停顿(pause)的主要来源是哪里。当然对“大内存”的Java应用而言,GC pause time常是停顿的主要来源,但其实还可能有许多别的问题。
- 用的是哪个JVM…(咳咳
既然题主在问题说明里提到了Azul Systems,下面就多说几句相关的介绍。
Azul Systems当前版本的Zing JVM支持Linux/x86-64平台,不需要特殊硬件支持。目前可以支持最多1TB的Java heap,并且在这个规模上仍然可以维持GC暂停时间在10ms以下的级别。已经有实际用户在使用这种规模的部署,线上数据也确实达到了我们预期的水平。
年底将支持2TB的Java heap,开发工作在顺利进行中。之前在别的地方提过,我们之前最尴尬的瓶颈是手上没这么大内存的服务器,就算开发了支持2TB Java heap的JVM也没环境测试它。现在这个总算不是问题了…
之所以能达到这个暂停时间水平是因为Zing JVM采用的GC算法,C4(Continuously Concurrent Compacting Collector),是一个可以做到完全并发(fully concurrent)的GC算法,包括完全并发的标记(concurrent marking)以及完全并发的整理(concurrent compaction)。
这使得GC的“暂停时间”完全不受Java heap大小的影响,所以其实把Java heap开到多大GC暂停时间都还是会维持在相似的水平。“并发”的在这个语境中的具体意义就是“不同时暂停所有Java线程”,既然GC的主要操作都是并发的,Java heap大小自然就不影响暂停时间了。
C4的算法源自Azul Systems的上一代产品的GC算法,“Pauseless GC”。两个算法都是Azul Systems原创的。
C4算法的论文(2011,ISMM'11):
C4: The Continuously Concurrent Compacting Collector,官方网站介绍:
JVM Garbage Collectors and the Azul C4 (Continuously Concurrent Compacting Collector)Pauseless GC算法的论文(2005,VEE'05):
The Pauseless GC Algorithm实现了C4算法的Zing JVM大概是在2009-2010年投入生产的;
实现了Pauseless GC算法的Vega系统里的Azul VM(AVM)大概是在2004-2005年间以及成熟,最迟在2007年投入生产的。
Azul Systems的部分客户使用Zing的体验可以在这里看到:
Selected Azul Systems Customers。It's not vaporware ;-)
C4算法虽然可以是完全并发的,但目前Zing JVM里的实现还是采用了若干短暂的暂停,方便实现——只要有需要我们可以把这些暂停都去掉,达到算法上所描述的完全并发。之所以目前还没强烈的感受到去掉这些通常< 10ms的暂停的需要,是因为我们发现Linux自身就经常会带来一些> 10ms的停顿,也就是说Zing的GC暂停已经在许多实际部署的Linux环境的自身系统的噪音级别以下了。
许多别的并发GC主要做到的是并发标记,但后续动作要么是:
- 并发清理(sweeping),也就是不处理内存碎片问题,例如HotSpot VM的CMS GC以及Go 1.5开始的并发GC。就HotSpot CMS GC而言,最终难以避免迈向完全停顿的full GC来处理碎片;或者是
- 停顿的整理,也就是虽然处理碎片问题但是要通过stop-the-world方式来整理,例如G1。
以前Oracle也对G1 GC做过一些极限条件的内部测试,例如在1TB Java heap上跑测试。G1 GC坚持下来了——没crash,但也仅此而已;暂停时间已经上到分钟级了,在此规模上完全不实用。
在更“常规”的服务器端Java应用上,新版本的Oracle JDK/OpenJDK里的G1 GC越来越成熟,处理~16GB之类的Java heap可以比较得心应手了。
(来个小道消息:原本参与Oracle/Sun G1 GC研发的大部分主力现在都在Twitter,集中精力优化Twitter版定制OpenJDK里的HotSpot VM的G1 GC。这些定制优化还没提交回给OpenJDK上游。换句话说Twitter版G1比Oracle/OpenJDK版性能更好…)
利益相关:Azul Systems员工。关于公司及其产品的简单流水账,请跳传送门:
Azul Systems 是家什么样的公司? - RednaxelaFX 的回答今年10月Azul Systems的CTO,Gil Tene,将会到上海QCon做主题演讲,欢迎大家去多多交流 ^_^
Gil Tene | 全球软件开发大会上海站2015