当前位置: 首页 > 科技观察

Tomcat性能调优JVM调优

时间:2023-03-12 16:17:44 科技观察

Tomcat、Jetty、GlassFish等系列的web容器/应用服务器,虽然作为容器,它们提供了JavaWeb运行环境来支持Servlet/JSP等内容的运行,但是我们都知道它本质上是一个Java应用程序。每次容器启动运行时,都会运行这个Java程序来实现Web容器的能力。作为一类“特殊”的Java应用,和其他Java应用一样,需要用到JVM,就会有堆,会用到垃圾回收,还会涉及到不同的堆分区比例……所以在Web中容器(应用服务器)的调优,JVM的调优必不可少。对于JVM调优,主要考虑两点:内存大小配置垃圾回收算法选择当然,准确地说,以上两点并不是相互独立的,内存大小配置也会影响垃圾回收的执行效率。其中,内存大小配置,最重要的是确定内存占用的总大小,确定内存中每一代(Gen)的大小,划分内存大小配置,配置所谓的内存尺寸。到内存使用情况。以HotSpot虚拟机为例,Java堆主要有新生代、老年代和永久代三个空间。根据不同应用的特点,观察应用的内存使用情况。如果有大量不会被重用的临时对象,可以调整NewGen,让这些临时对象在新生代创建,在MinorGC产生时回收,这样寿命短的对象就不会被提升到老年代,从而避免垃圾回收产生FullGC。理想情况下,短期存活的对象只在新生代完成生命周期,被耗时较少的MinorGC回收,而长期存活的对象会被多次使用,经过多次提升到老年代回收,最终通过FullGC完成生命周期。这里与内存大小相关的调整参数有:-Xms-Xmx这两个参数用来配置堆的初始大小和最大值。这里需要观察,找到一个合适的值。设置太大会导致内存浪费,也会导致垃圾回收时间过长。对于Tomcat,初始值和最大值一般设置为相同的值,避免初始内存不足时触发FullGC扩充内存。设置好堆大小后,根据对象生命周期的特点调整新生代和老年代的大小比例。涉及的参数有:-XX:NewSize-XX:NewRatio-XX:MaxNewSize-Xmn第一个是直接设置新生代的初始大小,第二个是设置比例(Ratio)。太高或太低都会导致GC无法高效工作。毕竟MinorGC也是比较耗时的。最后一个设置新生代的初值和最大值相同,堆空间的变化不影响它的值。对于大量使用第三方类库的应用,会加载很多依赖框架的类,在使用过程中可能会因为PermGen不足而出现OOM。在这种情况下,您可以在稳定状态下观察Perm区域的占用情况。然后设置参数。-XX:PermSize-XX:MaxPermSize-XX:MaxMetaspaceSize第一个设置Perm区域的初始大小,第二个用于设置Perm区域的最大值。在Java8中,Perm区域被移除,改为Metaspace,但如果遇到类似的OOM,仍然可以调整其大小。此外,对于使用大量线程的应用,还可以配置-Xss,主要用于设置单个线程的堆栈大小。注意是单一尺寸,所以设置值越大,占用越大,可用线程越少。这里的配置一般以-X开头,可以直接用数字加单位,-XX需要等号后面的数字加单位,例如:java-Xms100m-Xmx200m-XX:PermSize=300m的这里数字后面的单位可以是m,g,k来表示计算机中不同的单位。然后我们一直在说,根据不同的应用,观察分析堆的大小和每一代堆的大小。我们应该观察什么?我们一般会在JVM配置中添加一些打印GC日志的选项。配置方法和上面的类似,这样在产生GC的时候,会打印出每次产生占用的大小,具体的触发时间等等。推荐配置如下:-XX:+PrintGCTimeStamps-XX:+PrintGCDetails-Xloggc:<文件名>-XX:PrintGCDateStamps第一个和第四个选项可以任选其一,第一个打印自JVM启动以来的时间,一般也叫uptime,第四个打印的是系统当前的日期和时间。根据GC日志产生的内容,观察具体大小,开始使用上面的配置参数进行调整。当然也可以借助JConsole、JVisualVM等工具进行直观的理解和调整。工具的使用可以参考历史文章Java七兵器系列激情指环——多功能Profiling工具JVisualVM垃圾收集算法不同的垃圾收集算法对应用程序的影响很大。一方面,可能在服务器上使用了单线程的回收算法,或者应用对响应的要求很高,却使用了吞吐量优先的算法,导致响应缓慢。所以,垃圾回收算法的选择一般是根据应用的特点,是低延迟还是高吞吐,选择合适的算法。前面我们也提到,垃圾回收算法和内存大小配置并不是独立的。如果内存设置大,收集的频率会降低,但每次执行的时间也会变长。所以这里也有一个权衡。延迟和吞吐量调整其他JVM配置垃圾收集算法对应不同的垃圾收集器。具体来说,JVM中的配置就是使用不同的收集器如-XX:+UseParallelOldGC或-XX:+UseConcMarkSweepGC来达到选择算法的目的。其中,ParallelGC又被称为吞吐量优先收集器,可以提高应用程序的吞吐量,但在调整老年代大小并进行多次垃圾收集后,无法满足应用程序的低延迟要求。一般比较常用的是ConcMarkSweepGC,也叫CMSGC,可以实现老年代的垃圾回收和应用程序的纯并行执行,所以可以实现低延迟。这里要注意,由于CMSGC和其他GC回收算法使用不同的框架,不能混用。使用CMS进行老年代回收时,新生代默认使用单线程恢复算法。这时候可以配置-XX:+UseParNewGC,使用年轻代进行并行收集。由于CMS是垃圾收集和应用线程并行,它需要额外的CPU处理资源。如果只有一台CPU机器,或者有多个繁忙的CPU,想使用低延迟的收集器,可以配置CMS收集处理器的增量模式进行回收,增量模式通过指定-XX:+CMSIncrementalMode。此时垃圾收集器应用程序线程交替运行。通过配置-XX:CMSIncrementalSafetyFactor=X-XX:CMSIncrementalDutyCycleMin=Y-XX:CMSIncrementalPacing您可以控制垃圾收集后台线程为应用程序线程放弃多少CPU周期。参数-XX:+CMSParallelRemarkEnabled用于减少标记暂停。另外,由于CMS回收后老年代的内存空间不连续,所以在FullGC时使用参数-XX:+UseCMSCompactAtFullCollection来压缩老年代。G1收集器是在JDK1.7中引入的,可以通过配置-XX:+UseG1GC来启用。这方面实践经验不多,欢迎有相关经验的朋友分享。此外,还可以对新生代进行更详细的配置,比如设置Eden区和Survivor区的比例等,类似于Newxx,可以通过SurvivorRation来设置比例。其他JVM配置可以使用-XX:+DisableExplicitGC选项来禁用显式System.gc调用。这需要在使用前进行评估。所谓调优,就是一个不断调整优化的过程,需要观察、配置、测试、反复。毕竟,上面的选项在哪里配置呢?前面我们提到Tomcat本质上是一个普通的Java应用程序,所以它类似于一般的Java启动方式,也类似于java-Xms100m-XX:+UseParallelOldGC应用程序主类方式来启动,不同的是,Tomcat将上述命令放入文件中,对应不同的操作系统,Windows下使用bat文件,Linux下使用sh文件。所以我们的配置项也添加到这些文件中。我们看一下catalina.sh中实际执行的命令:所以我们的选项可以添加到optionsJAVA_OPTSCATALINA_OPTS中。配置比较简单,例如如下:配置的时候需要特别注意不要flush掉之前的配置。比如在配置JAVA_OPTS的时候,必须加上前面的配置。是这样写的:JAVA_OPTS="$JAVA_OPTS的新增内容"【本文为专栏作者“侯树成”原创稿件,转载请通过作者微信获取授权公众号“Tomcat那些事儿”】点此查看更多这个作者的好文章