对Tomcat 8.0进行JVM层面的优化(基于Oracle JDK 8)
目录
tomcat容器是运行在jvm上的, 其默认内存一般都很小(物理内存的1/64), 在实际生产环境中, 若不配置则会极大浪费服务器资源, 影像系统的性能. 可以通过调整jvm启动参数, 使得tomcat拥有更好的性能.
对于jvm的优化主要有两个方面: 内存调优 和 垃圾收集策略调优.
1 tomcat的内存调优
1.1 tomcat的内存占用
tomcat 的运行内存 = xmx(初始内存大小) + perm generation(jdk 7中的永久代大小) + java应用创建的线程数 * 1mb.
java 应用每创建一个线程, jvm 进程的内存中就会创建一个 thread 对象, 同时也会在操作系统中创建一个真正的物理线程(参考jvm规范), 操作系统会在 tomcat 的空闲内存中创建这个物理线程, 而不是在 jvm 的 xmx 堆内存中创建.
在 jdk 1.4中, 默认的栈大小是256kb/线程, 但自 jdk 5(为了推广的方便, jdk 后续版本不再是1.x命名)开始, 默认的栈大小变为1m/线程. 举例: 如果系统剩余内存为400m, 则java应用最多能创建400个可用线程.
结论: 要想创建更多的线程, 必须减少分配给jvm的最大内存.
1.2 内存配置相关参数
-server # jvm的server模式, 在多cpu服务器中性能可以得到更好地发挥. 默认为client. 配置server模式时要将其作为第一个参数. -xmx4g # java heap的最大可用内存, 默认为物理内存的1/4(已在jdk 7下验证, 最大值为30638mb, 总内存126gb的23.75%). # 在只运行tomcar容器的服务器中, 建议设置为物理内存的50%~80%. -xms4g # java heap的初始大小, 默认值为物理内存的1/64(已在jdk 7下验证, 内存126gb, 初始值为2gb). -xss128k # 每个线程的stack大小. 在相同物理内存下, 减小这个值能生成更多的线程, # 但是操作系统对一个进程内的线程数是有限制的, 经验范围是3000~5000. -xx:newratio=4 # 设置新生代(包括eden和两个survivor区)与老年代的比值(除去持久代), 默认为2, 即新生代与老年代所占比值为1:2, 新生代占整个堆栈的1/3. -xx:survivorratio=4 # 设置新生代中eden区与1个survivor区的大小比值. 默认为8, 即eden区占新生代的80%, 2个survivor分别占新生代的10%. # 设置为4, 则两个survivor区与一个eden区的比值为2:4, 一个survivor区占整个新生代的1/6. -xmn1024m # 设置young generation所占用的java heap大小为1g. 此值对系统性能影响较大, sun官方推荐配置为整个堆的3/8(或xmx的1/4~1/3左右). # 也可使用-xx:newsize和-xx:maxnewsize设置新生代的初始值和最大值. # 注意: -xmn 与 -xx:newsize、-xx:maxnewsize 的优先级: -xx:newratio的值会被忽略. # 1. 高优先级: -xx:newsize/-xx:maxnewsize # 2. 中优先级: -xmn, 等效于同时设置 -xmn = -xx:newsize = -xx:maxnewsize 三者的值 # 3. 低优先级: -xx:newratio # -xmn参数是在jdk 1.4 开始支持, 推荐使用之. -xx:newsize=1g # 设置新生代的大小, 默认为1.25mb(已在jdk 7下验证). 若显示设置此值, 将使得newratio选项失效. -xx:oldsize=2g # 设置老年代的大小, 默认为5.1875mb(已在jdk 7下验证). -xx:permsize=128m # jdk 7及以下版本适用: 设置java heap中永久代的初始大小, 默认为20.75mb(已在jdk 7下验证, client、server模式下均相同). -xx:maxpermsize=256m # jdk 7及以下版本适用: 设置java heap中永久代的最大值. 默认为82.0mb(已在jdk 7下验证, client、server模式下均相同). -xx:metaspacesize=128m # jdk 8及以上版本适用: 初始元空间的大小, 默认为21mb(实际为20.79mb左右, 已验证). -xx:maxmetaspacesize=256m # jdk 8及以上版本适用: 最大元空间的大小. 默认无上限(在126gb物理内存的服务器中, 默认值为(2^44-1)mb, 已验证).
总结:
- 情形一: 如果不指定xmx、xms和newsize、oldsize, 则系统将基于xms=1/64总内存大小, 对各个space按照newratio=2进行空间的分配, 此时newsize与oldsize的默认大小将失效.
- 情形二: 如果指定了xmx、xms, 未指定newsize、oldsize, 则系统将优先满足 newratio=2, 且oldsize+newsize=xms, 此时newsize与oldsize的默认大小将失效.
- 结论: 除非显式指定newsize与oldsize的值, 否则它们的默认配置一般都不会得到满足. 显式指定其中任一个, 另一个就会基于默认值, 并根据应用程序的消耗动态分配空间大小.
1.3 内存调优实践
jvm内存方面的调优, 需要在 ${tomcat_home}/bin/catalina.sh
文件中调整, 配置 java_opts 变量即可. 在启动tomcat时, 会执行catalina.sh中的脚本, 将 java_opts 作为jvm的启动参数进行处理.
具体可参考如下配置(示例服务器配置: 126g的物理内存, 2个10核心20线程的物理cpu):
# 在文件最前面(即cygwin=false之前)设置, $java_opts 的作用是保留原有的设置, 防止此次修改覆盖之前的设置 java_opts="$java_opts -xmx96g -xms96g -xx:metaspacesize=256m -xx:maxmetaspacesize=512m"
-
说明:
- 若不指定xmx, 即java heap的最大值, 程序启动时, jvm会调整其大小以满足程序运行的需要. 每次调整时, 都会对堆进行一次完全垃圾收集(即full gc), 比较影响性能. 因此推荐明确指定xmx的大小.
- 令
xmx=xms
会有更好地性能表现: 能避免jvm在每次垃圾收集后重新动态调节堆空间, 因为频繁伸缩堆大小将带来额外的性能消耗. - jvm有2中模式: client客户端模式 和 server服务端模式 , 平时开发中使用的多是默认的client模式. 可通过命令行参数
-server
强制开启server模式. 两者之间的最大区别是, jvm对server模式做了大量优化: 虽然server模式下应用程序启动较慢, 但在长时间运行下, 程序运行效率会明显高于client模式, 即 client模式不适合需要长时间运行的项目 .
-
元空间的调优:
元空间的大小将受限于机器的内存的限制. 限制类的元数据的内存大小, 以避免出现虚拟内存切换以及本地内存分配失败.
如果可能出现类加载器泄漏, 应当配置此参数指定大小. 32位机器上, 如果地址空间可能会被耗尽, 也应当配置此参数.
元空间的初始大小是21m——这是gc的初始高水位线, 超过这个大小会进行full gc来进行类的收集.
如果启动后gc过于频繁, 请将该值设置得大一些, 以便推迟gc的执行时间.
1.4 验证配置效果
# 由于配置了环境变量, 在任意目录下使用jps及jmap命令, 即可调用相关工具: # 使用 jps 查看 java 的所有进程 [root@localhost ~]# jps 14308 bootstrap 15620 jps # 其中 bootstrap 进程就是 tomcat, 其进程号为14308. # 通过 jmap 工具查看其内存相关配置 [root@localhost ~]# jmap -heap 14308 # 显示以下结果 attaching to process id 24980, please wait... debugger attached successfully. server compiler detected. jvm version is 25.151-b12 using thread-local object allocation. parallel gc with 28 thread(s) # 默认使用并发gc策略 heap configuration: # 堆的配置参数 minheapfreeratio = 0 maxheapfreeratio = 100 maxheapsize = 103079215104 (98304.0mb) # 最大堆内存 newsize = 34359738368 (32768.0mb) # 新生代大小, 由于默认的newratio=2, 所以是1/3的堆大小 maxnewsize = 34359738368 (32768.0mb) oldsize = 68719476736 (65536.0mb) # 老年代大小, 由于默认的newratio=2, 所以是2/3的堆大小 newratio = 2 survivorratio = 8 metaspacesize = 21807104 (20.796875mb) # 元空间大小 compressedclassspacesize = 1073741824 (1024.0mb) maxmetaspacesize = 17592186044415 mb # 最大元空间大小 g1heapregionsize = 0 (0.0mb) heap usage: # 堆的使用情况 ps young generation eden space: # 新生代的伊甸区, 由于默认的survivorratio=8, 所以是6/8的newsize capacity = 25769803776 (24576.0mb) used = 9742123824 (9290.813278198242mb) free = 16027679952 (15285.186721801758mb) 37.80441600829363% used from space: # 新生代的from区, 由于默认的survivorratio=8, 所以是1/8的newsize capacity = 4294967296 (4096.0mb) used = 0 (0.0mb) free = 4294967296 (4096.0mb) 0.0% used to space: # 新生代的to区, 由于默认的survivorratio=8, 所以是1/8的newsize capacity = 4294967296 (4096.0mb) used = 0 (0.0mb) free = 4294967296 (4096.0mb) 0.0% used ps old generation # 老年代, 由于默认的newratio=2, 所以是2/3的堆大小 capacity = 68719476736 (65536.0mb) used = 254298704 (242.5181427001953mb) free = 68465178032 (65293.481857299805mb) 0.37005331832915545% used 23928 interned strings occupying 3198312 bytes.
2 gc策略调优实践
tomcat的gc策略一般都是与其内存参数一起配置的, 与应用复杂度相匹配的gc策略、与服务器性能相适应的内存比例, 都将使得系统性能得到大幅提升.
gc策略方面的调优, 也是在 ${tomcat_home}/bin/catalina.sh
文件中调整 -- 同样是配置 java_opts 变量即可.
以jdk 8、tomcat 8为例, 服务器内存为126gb, 作出如下配置:
通过solr集群大批量导入数据的应用中, parallel gc策略的暂停时间太长, 所以选择cms收集器.
# 下述配置各自独占一行, 要置于"cygwin=false"之前. # 配置内存 java_opts="-server -xmx96g -xms96g -xmn35g -xx:oldsize=55g -xss128k -xx:metaspacesize=128m -xx:maxmetaspacesize=256m" # 配置gc策略 java_opts="$java_opts -xx:+useconcmarksweepgc -xx:maxtenuringthreshold=15 -xx:cmsinitiatingoccupancyfraction=40 -xx:cmsfullgcsbeforecompaction=0 -xx:+explicitgcinvokesconcurrent -xx:softreflrupolicymspermb=0 -xx:maxgcpausemillis=100 -xnoclassgc "
其中java heap的初始大小和最大大小均设置为96g, 76%的物理内存(以不超过80%为宜).
参考资料:
版权声明
作者: ma_shoufeng(马瘦风)
出处: 博客园
您的支持是对博主的极大鼓励, 感谢您的阅读.
本文版权归博主所有, 欢迎转载, 但未经博主同意必须保留此段声明, 且在文章页面明显位置给出原文链接, 否则博主保留追究法律责任的权利.