`

Tomcat有关JVM的部分优化及原因

阅读更多

      之前在网上有看到过很多关于Tomcat启动参数的设置,有不少类似于标准的设置参考,但是很多解释比较模糊,在阅读过《深入理解java虚拟机》一书之后,其中主要涉及的JVM优化想跟大家分享一下,如有错误还请指正。

      一般优化就是在catalina.bat或者catalina.sh的首行之前加入JAVA_OPTS="",其中catalina.bat可以不用引号,catalina.sh必须加上引号,否则不生效也没有任何提示。目前我的环境主要是是4G内存的windows server2008和CentOS5.9的64系统,然后是JDK1.7_45的64位虚拟机(也就是HotSpot),tomcat版本为7.0.42。大概是为了实现每秒2000次响应,其中算法和数据库时间都在100ms以下,而且都能够承受如此频率的响应。我的配置开始也是照抄的网上大神们的,后面了解之后稍微做了一些修改如下:

set JAVA_OPTS="-server -Xms2048M -Xmx2048M -Xss256k -XX:+AggressiveOpts -XX:+UseBiasedLocking -XX:PermSize=128M -XX:MaxPermSize=256M -XX:+DisableExplicitGC -XX:MaxTenuringThreshold=31 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=96m -XX:-UseFastAccessorMethods -XX:CMSInitiatingOccupancyFraction=70 -XX:+UseCMSInitiatingOccupancyOnly -verbose:gc -Xloggc:../logs/gc.log -XX:+PrintGCDetails -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true"

 下面介绍以下每一项的意思,可能不是特别全面而且用词可能不是特别准确,主要是根据书中所说进行的简单总结。

 1."-server":启动虚拟机运行在server模式下,默认是在client模式下。这两个模式大致有两个方面的区别默认垃圾收集器和编译行为。垃圾收集器,client模式下默认为新生代Serial收集器+老年代Serial Old收集器,首先这两个收集器都是单线程的,既是只会用一个CPU或一条收集线程去完成垃圾收集工作。而且这两个垃圾收集器都会有"Stop The World"的不良用户体验,既是在用户不可见的情况下吧用户正常工作的线程全部停掉,当然新生代还是采取的复制算法,老年代为标记整理算法,如下图

 
    而在server模式下新生代默认采用的Parallel Scavenge收集器,老年代采用的Serial Old收集器(因为没有其他老年代收集器能与Parallel Scavenge收集器配合)。其中Parallel Scavenge收集器是可以达到一个可控的吞吐量的收集器,可以使用配置"-XX:MaxGCPauseMillis"参数和"-XX:GCTimeRatio"参数来指定最长停顿时间和吞吐量大小,吞吐量即是GC时间占总CPU时间的比值,比如将GCTimeRatio配置为19,那么允许的最大GC时间就占总时间的5%(1/(1+19))。

    关于编译,HotSpot虚拟机中内置了两个即时编译器,分别为Client Compiler(C1编译器)和Server Compiler(C2编译器),他们都是虚拟机的后端运行期编译器(JIT编译器,Just In Time Compiler)把字节码转变成机器码的过程。因为最初Java程序是通过解释器进行解释执行的,当虚拟机发现某个方法或代码块的运行特别频繁时,就会把这些代码认作”热点代码“(Hot Spot Code),为了提高执行效率,运行时虚拟机会将这些代码编译为与本地平台有关的机器码,并进行各种优化,完成这项任务的编译器就是JIT编译器。大致可以简单的认为C1的编译速度较快,C2的编译质量较高(优化更多),但其实无论在server还是client模式下都可以采用分层编译(Tiered Compilation),其中jdk7的server模式分层编译默认开启。也可以通过"-XX:+TieredCompilation"手动开启。这个参数有助于在程序启动响应速度和运行效率之间达到平衡。server模式和client模式在Hot Spot Code的判定上面也有所不同,判定方法是否为Hot Spot Code见后文关于"-XX: CompileThreshold"参数介绍,在判定代码块(循环语句等)时,server的默认值会比client更宽松,也就是server会做更多的编译工作。在其他方面server模式会进行一些更复杂和激进的编译优化,本人才疏学浅不作介绍,可以参见Sun官方Wiki百科。

 

2."-Xms2048m":设置JVM堆的最小大小为2048m,一般跟最大大小设置为一样,避免动态分配影响性能。

 

3."-Xmx2048m":设置JVM堆的最大大小为2048m,需要注意的是本地方法栈、方法区和直接内存都不在这个范围之中,所以需要考虑到这部分内存加上堆内存才是整个应用所使用的内存。而且32位虚拟机实际支持的内存为1.5~2G,64位无限制。

 

4."-Xss256k":设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。

 

5."-XX:+AggressiveOpts":使用激进的优化特性,主要是在编译期间,jdk6就已经默认开启了,可以不指定,但需要测试效果,因为可能激进优化的假设不成立,优化后导致比如加载新类后类型继承结构变化、出现罕见陷阱等情况时(此处优化是由C2编译器进行),又会通过逆优化退回到解释转台继续执行(或者是C1编译器编译),反而降低了性能。

 

6."-XX:+UseBiasedLocking":使用偏向锁,也是jdk6的默认值,他的目的是消除数据在无竞争的情况下的同步原语,就是说把不需要同步的同步语句去掉。实现主要是偏向第一个使用该同步资源的线程,所以叫偏向锁。但这也是一个带有效益权衡性质的优化,如果代码中大多数同步语句都是数据有竞争的情况下,禁止("-XX:-UseBiasedLocking")反而可以提升性能。

 

7."-XX:PermSize=128M -XX:MaxPermSize=256M":分别设置永久代初始大小,和永久代最大大小。永久代也就是方法区,虽然是永久代但其实也并不是永远不变,因为会涉及到类型的卸载,不过卸载类的要求相当严格。

 

8."-XX:+DisableExplicitGC":忽略用户代码中手动调用GC(system.gc())。

 

 9."-XX:MaxTenuringThreshold=31":新生代中对象的年龄到达多少岁时晋升到老年代(一次minorGC年龄加1),该默认值为15。需要注意的是JVM为了适应不同程序的内存状况并不是一定要求只有年龄达到MaxTenuringThreshold才能晋升到老年代,如果在Survicor空间中相同年龄所有对象大小的和大于Survivor空间的一半,年龄大于或等于该年龄的对象就会直接进入老年代,这称为动态对象年龄判定。例如,在上述配置条件下,新生代默认占对内存的1/3(其余为老年代,可以通过"-Xmn768m"来指定新生代大小为768m),则默认的Survivor区占新生代的1/10大致为68m,如果此时执行minorGC时,Eden区域存活的对象中10岁的对象的大小的和大于68M,那么这次minorGC之后,Eden区域中存活的大于或等于10岁的对象就都会直接进入老年代,而不是等到31岁。

    注:因为新生代的对象大多具备朝生夕死的特点,新生代一般采取复制算法,这样效率会很快,具体为将新生代分为三块区域,Eden区和两块Survivor区比例为8:1(可以通过配置"-XX:SurvivorRatio=8"指定新生代中Eden:Survivor为8:1,默认比例),每次只使用Eden区和一块Survivor区(即使用新生代的90%,只会浪费10%的空间,而且据IBM公司的研究,新生代中98%的对象都是朝生夕死),剩下一块Survivor区用作minorGC时将原来Eden区和另一块Survivor区存活的对象复制过去,而此时如果这块Survivor区不够用就需要向老年代分配(比如上述的动态对象年龄判定条件满足时)。

 

10."XX:+UseConcMarkSweepGC":使用Concurrent Mark Sweep收集器(CMS收集器)。其主要特点是获取最短回收停顿时间的收集器,其收集步骤分为4步:初始标记(CMS initial mark),并发标记(CMS concurrent mark),重新标记(CMS remark),并发清除(CMS concurrent sweep)。其中初始标记和重新标记仍需要Stop The World,但初始标记只是标记GC Roots能直接关联到的对象(采用的垃圾收集算法为根搜索算法),并发标记就是进行GC Roots搜索确定对象是否“已死”,而重新标记是为了修正并发标记时程序继续运作造成的改变,重新标记需要的时间远低于并发标记,所以整个收集过程停顿时间会很短,如下图:

 

 11."-XX:+UseParNewGC":新生代采用ParNew收集器。ParNew收集器除了多线程收集之外其他和Serial收集器类似,以致在单cpu情况下效果不如Serial,甚至双cpu情况下由于线程交互开销也不能完全保证比Serial更优秀。但是,采用它的原因是只有它能与CMS收集器配合工作。其收集过程如下图:


 

12."-XX:+CMSParallelRemarkEnabled":使CMS垃圾收集时,第二次标记(前文描述CMS中的重新标记)采用多线程方式,本人没有测试默认值是否开启。

 

13."-XX:+UseCMSCompactAtFullCollection":设置CMS收集器在完成垃圾收集后是否要进行一次内存碎片整理,默认开启,关闭可能会导致老年代碎片过多分配大对象失败。

 

14."-XX:LargePageSizeInBytes=96m":设置java进程大内存页每页大小,需要操作系统支持。详细请参见博客:http://kenwublog.com/tune-large-page-for-jvm-optimization

 

15."-XX:-UseFastAccessorMethods":设置关闭快速调用成员方法,这里表述可能不是太准确。首先说明一下什么方法叫做AccessorMethods,①必须是成员方法,静态方法不行,②返回值类型必须是引用类型或者int,其它都不算,③方法体的代码必须满足aload_0; getfield #index; areturn或ireturn这样的模式,方法名是什么都没关系,是不是get、is、has开头都不重要。 因为此类方法方法体很简单,而且没有方法计数器,开启此设置后可以跳过对该类方法的编译,详细参见http://mail.openjdk.java.net/pipermail/hotspot-compiler-dev/2011-March/005057.html该设置默认在jdk6中是开启的,但由于jdk7的server模式默认开启了多层编译,此时在这种多台方法调用时甚至会导致性能下降,所以设置关闭(jdk7是否默认关闭不太清楚),但仍需要根据自己项目测试。

 

16."-XX:CMSInitiatingOccupancyFraction=70":CMS垃圾收集老年代时触发条件,当老年代使用的比例占用到整个老年代的70%时,执行一次Full GC(major GC),默认值90%,如有错误还请留言指正。

注:本人没有自己编译过JDK,网上资料主要有两种说法,90%和68%,经本人在上述环境测试,没有设置改制时应用运行一段时间会自己挂掉,没有输出任何日志,gc日志显示正在执行minor GC时突然挂掉,配置为70%后没有出现过这种情况,所以我偏向认为默认值为90%。

 

17."-XX:+UseCMSInitiatingOccupancyOnly":CMS GC触发的条件为旧生代已使用的空间达到设定的CMSInitiatingOccupancyFraction百分比,例如默认CMSInitiatingOccupancyFraction为90%,如旧生代空间为1 000MB,那么当旧生代已使用的空间达到900MB时,CMS GC即开始执行;另外一种触发方式是JVM自动触发,JVM基于之前GC的频率及旧生代的增长趋势来评估决定什么时候执行CMS GC,如果不希望JVM自行触发,可设置该参数。

 

18."-verbose:gc":输出GC前后内存情况,但并不详细,如,

[Full GC 168K->97K(1984K), 0.0253873 secs]

 

19."-Xloggc:../logs/gc.log":将GC日志输出到gc.log文件中,此处是相对路径,Tomcat则是在TOMCAT_HOME/logs/gc.log。

 

20."-XX:+PrintGCDetails":输出GC的详细信息,如,

33.125: [GC [DefNew: 3324K->152K(3712K), 0.0025925 secs] 3324K->152K(11904K), 0.0031680 secs]
100.667: [Full GC [Tenured: 0K->210K(10240K), 0.0149142 secs] 4603K->210K(19456K), [Perm : 2999K->2999K(21248K)], 0.0150007 secs] [Times: user=0.01 sys=0.00, real=0.02 secs]

 其中33.125指的是JVM启动之后经过的时间,GC指的就是minor GC,DefNew指的是新生代(默认的收集器),3324K(GC前)->152K(GC后)(3712K(总大小)),0.0025925花费的时间3324K->152K(11904K)指的是整个JVM堆的大小变化,后面的Full GC中Tenured既是老年代,此处应该是新生代对象晋升到老年代所以导致老年代大小反而增加。Perm既是永久代,后面的user,sys,real和Linux的time命令输出时间含义一致,分别代表用户态消耗的CPU时间、内核态消耗的CPU时间以及操作从开始到结束所经过的真实时间。

 

21."-Djava.awt.headless=true": Headless模式是系统的一种配置模式。在该模式下,系统缺少了显示设备、键盘或鼠标。 Headless模式虽然不是我们愿意见到的,但事实上我们却常常需要在该模式下工作,尤其是服务器端程序开发者。因为服务器(如提供Web服务的主机)往往可能缺少前述设备,但又需要使用他们提供的功能,生成相应的数据,以提供给客户端(如浏览器所在的配有相关的显示设备、键盘和鼠标的主机)。 一般是在程序开始激活headless模式,告诉程序,现在你要工作在Headless mode下,如果服务程序并不需要图像处理可以不设置。

 

22."-Djava.net.preferIPv4Stack=true":禁用IPv6,因为本人应用中会用到JGroups作为分布式内存数据库,而在使用IPv6的地址会无法识别加入到集群,所以禁用掉了,大家可以根据自己应用情况决定。

  • 大小: 60.8 KB
  • 大小: 32.7 KB
  • 大小: 18.6 KB
  • 大小: 18.6 KB
分享到:
评论

相关推荐

    Web服务器三剑客运维配置实战 Nginx+JVM+Tomcat+HTTP协议.zip

    虽然在课程中还讲解了部分HTTP协议的技术,但是课程的重点还是NGINX、JVM、Tomcat三相运维与配置技术。课程内容包括了Nginx进阶基础,Nginx配置提升,JVM虚拟机尝试,JVM运维实用排障工具,JVM监控工具,Tomcat配置...

    深入理解_Java_虚拟机 JVM_高级特性与最佳实践

    / 112 5.2.5 服务器JVM进程崩溃 / 113 5.3 实战:Eclipse运行速度调优 / 114 5.3.1 调优前的程序运行状态 / 114 5.3.2 升级JDK 1.6的性能变化及兼容问题 / 117 5.3.3 编译时间和类加载时间的优化 / 122 5.3.4 ...

    整理的多家公司常见面试题库350道Java面试题手册

    分享下整理的Java架构面试专题及答案,其中大部分都是大企业面试常问的面试题,可以对照这查漏补缺,希望能对即将找工作的朋友起到一些帮助,主要内容如下: 一、性能优化面试专栏 1.1、tomcat性能优化整理 1.2、JVM...

    2021最新java面试合集pdf.rar

    JVM与性能优化知识点整理.pdf JVM面试专题.docx JVM面试专题及答案.pdf Linux系统Redis笔记.docx MongoDB学习笔记.docx mybatis原理.docx MyBatis面试专题.docx MyBatis面试专题及答案.pdf Mybatis面试题(含答案)....

    Java虚拟机

    第二部分讲解了JVM的自动内存管理,包括虚拟机内存区域的划分原理以及各种内存溢出异常产生的原因;常见的垃圾收集算法以及垃圾收集器的特点和工作原理;常见虚拟机监控与故障处理工具的原理和使用方法。第三部分...

    Java-review-gudie:【Java快速面试指南】Java基础、异常、集合、并发编程、JVM、Spring全家桶、MyBatis、Redis、数据库、中间件MQ、Dubbo、Linux、Tomcat、ZooKeeper、Netty等等

    之前有分享过这份知识点笔记的初稿,现在又对知识点笔记进行了一定的优化。于是有了现在的V2.0版本的面试手册。当然除了在线版还有本地文档版本 部分内容收集整理于网络,在此也再次感谢所有内容产出者的贡献! 如果...

    java面试题库2021.pdf

    ⑤JVM ⑥GC ⑦ IO 和 NIO, AIO 二、 JavaEE 部分 1、 Spring ①IoC 与 Bean 配置、 管理 ②AOP 与事务、 权限控制 ③S2SH 整合开发 ④Spring, JPA 整合 2、 Hibernate ①ORM 与持久化映射 ②延迟加载、 性能优化 ...

    高级java笔试题-Java-mianshi-note:【一线互联网大厂Java核心面试题库】Java基础、异常、集合、并发编程、JVM、Sp

    【一线互联网大厂Java核心面试题库】Java基础、异常、集合、并发编程、JVM、Spring全家桶、MyBatis、Redis、数据库、中间件MQ、Dubbo、Linux、Tomcat、ZooKeeper、Netty等等... 2020年初开始,陆陆续续一直在收集...

    达内java培训目录

    JavaSE核心 异常处理、多线程基础、IO系统、网络编程、Java反射机制、JVM性能调优(JVM内存结构剖析、GC分析及调优、JVM内存参数优化)、Java泛型、JDK新特性 熟练掌握JavaSE核心内容,特别是IO和多线程;...

    java面试题

    76.4. 在weblogic管理制台中对一个应用域(或者说是一个网站,Domain)进行jms及ejb或连接池等相关信息进行配置后,实际保存在什么文件中? 86 76.5. 在weblogic中发布ejb需涉及到哪些配置文件 87 76.6. 如何在weblogic中...

    Java面试宝典2010版

    10、在weblogic管理制台中对一个应用域(或者说是一个网站,Domain)进行jms及ejb或连接池等相关信息进行配置后,实际保存在什么文件中? 11、说说weblogic中一个Domain的缺省目录结构?比如要将一个简单的helloWorld.jsp...

    《MyEclipse 6 Java 开发中文教程》前10章

    7.5 MyEclipse Hibernate工具的高级部分 135 7.5.1 反向工程向导的完整说明 135 7.5.2 使用HQL编辑器 138 7.6 小结 140 7.7参考资料 141 第八章 开发Web应用 142 8.1介绍 142 8.2 Web项目和术语 142 8.2.1 Java EE ...

    最新Java面试宝典pdf版

    1、Tomcat的优化经验 85 2、HTTP请求的GET与POST方式的区别 85 3、解释一下什么是servlet; 85 4、说一说Servlet的生命周期? 86 5、Servlet的基本架构 86 6、SERVLET API中forward() 与redirect()的区别? 86 7、什么...

    Java面试笔试资料大全

    1、Tomcat的优化经验 85 2、HTTP请求的GET与POST方式的区别 85 3、解释一下什么是servlet; 85 4、说一说Servlet的生命周期? 86 5、Servlet的基本架构 86 6、SERVLET API中forward() 与redirect()的区别? 86 7、什么...

    Java面试宝典2012版

    1、Tomcat的优化经验 85 2、HTTP请求的GET与POST方式的区别 85 3、解释一下什么是servlet; 85 4、说一说Servlet的生命周期? 86 5、Servlet的基本架构 86 6、SERVLET API中forward() 与redirect()的区别? 86 7...

    Java面试宝典2012新版

    1、Tomcat的优化经验 85 2、HTTP请求的GET与POST方式的区别 85 3、解释一下什么是servlet; 85 4、说一说Servlet的生命周期? 86 5、Servlet的基本架构 86 6、SERVLET API中forward() 与redirect()的区别? 86 7、什么...

    JAVA面试宝典2010

    1、Tomcat的优化经验 85 2、HTTP请求的GET与POST方式的区别 85 3、解释一下什么是servlet; 85 4、说一说Servlet的生命周期? 86 5、Servlet的基本架构 86 6、SERVLET API中forward() 与redirect()的区别? 86 7、什么...

    Java面试宝典-经典

    1、Tomcat的优化经验 85 2、HTTP请求的GET与POST方式的区别 85 3、解释一下什么是servlet; 85 4、说一说Servlet的生命周期? 86 5、Servlet的基本架构 86 6、SERVLET API中forward() 与redirect()的区别? 86 7、什么...

Global site tag (gtag.js) - Google Analytics