Tomcat容器是运行在JVM上的, 其默认内存一般都很小(物理内存的1/64), 在实际生产环境中, 若不配置则会极大浪费服务器资源, 影像系统的性能. 可以通过调整JVM启动参数, 使得Tomcat拥有更好的性能.

对于JVM的优化主要有两个方面: JVM内存调优垃圾收集策略调优, 本片博文是在Oracle JDK 8之上的测试, 特此说明.

1 JVM内存调优

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"

(1) 说明:

① 若不指定Xmx, 即Java Heap的最大值, 程序启动时, JVM会调整其大小以满足程序运行的需要. 每次调整时, 都会对堆进行一次完全垃圾收集(即Full GC), 比较影响性能. 因此推荐明确指定Xmx的大小.

② 令Xmx=Xms会有更好地性能表现: 能避免JVM在每次垃圾收集后重新动态调节堆空间, 因为频繁伸缩堆大小将带来额外的性能消耗.

③ JVM有2种模式: client客户端模式server服务端模式 , 平时开发中使用的多是默认的client模式. 可通过命令行参数-server强制开启server模式. 两者之间的最大区别是, JVM对server模式做了大量优化: 虽然server模式下应用程序启动较慢, 但在长时间运行下, 程序运行效率会明显高于client模式, 即 client模式不适合需要长时间运行的项目 .

(2) 元空间的调优:

元空间的大小将受限于机器的内存的限制. 限制类的元数据的内存大小, 以避免出现虚拟内存切换以及本地内存分配失败.

如果可能出现类加载器泄漏, 应当配置此参数指定大小. 32位机器上, 如果地址空间可能会被耗尽, 也应当配置此参数.

元空间的初始大小是21M——这是GC的初始高水位线, 超过这个大小会进行Full GC来进行类的收集.

如果启动后GC过于频繁, 请将该值设置得大一些, 以便推迟GC的执行时间.

1.4 验证配置效果

(1) jps工具查看Java进程:

# 由于配置了环境变量, 在任意目录下使用jps及jmap命令, 即可调用相关工具:
# 使用 jps 查看 Java 的所有进程, 其中 Bootstrap 进程就是 Tomcat, 其进程号为14308.
[root@localhost ~]# jps
14308 Bootstrap
15620 Jps

(2) jmap工具查看JVM内存配置和使用情况:

# 通过 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为例, 服务器内存为128GB, 作出如下配置:

通过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%为宜).

后续看到资料, 建议在使用Lucene的场合下要少为JVM分配内存、保留更多的物理内存用于Lucene对于索引文件的处理, 尚未探究此思路, 写在此处留待参考.

参考资料

Tomcat中Java垃圾收集调优

版权声明

作者: 马瘦风(https://healchow.com)

出处: 博客园 马瘦风的博客(https://www.cnblogs.com/shoufeng)

感谢阅读, 如果文章有帮助或启发到你, 点个[好文要顶

对Tomcat 8.0进行JVM层面的优化(基于Oracle JDK 8)的更多相关文章

  1. jvm层面锁优化+一般锁的优化策略

    偏向锁: 首先了解对象头MARK指针(对象头标记,32位): 存储GC标记,对象年龄,对象Hash,锁信息(锁记录的指针,偏向锁线程的ID) 大部分情况是没有竞争的,所以可以通过偏向来提高性能 所谓的 ...

  2. Linux Tomcat 6.0安装配置实践总结

    系统环境: Red Hat Enterprise Linux Server release 5.7 (Tikanga)  64位 Tomcat下载 从官方网站 http://tomcat.apache ...

  3. Tomcat 部署安装及JVM调优~

    Tomcat 部署Tomcat环境 环境准备 linux: CentOS 7.3 tomcat: 9.0.0.M21 jdk: 1.8.0_131 ip: 192.168.1.5 tomcat官方下载 ...

  4. tomcat 9.0.4 性能调优

    参考了网上的一些优化参数,但是在启动中发现 有2个报错: 11-Feb-2018 15:57:23.293 警告 [main] org.apache.catalina.startup.SetAllPr ...

  5. Tomcat 调优及 JVM 参数优化

    Tomcat 本身与 JVM 优化 Tomcat:调整Server.xml JVM:bat启动服务方式的话修改catalina.bat 服务式启动的话参考:http://www.cnblogs.com ...

  6. Server Tomcat v8.0 Server at localhost failed to start 问题解决方法?

    bi编程jsp  servlet 第一个程序: HelloServlet 运行错误 404: 十月 28, 2017 11:25:14 上午 org.apache.tomcat.util.digest ...

  7. 服务器的tomcat调优和jvm调化

    下面讲述的是tomcat的优化,及jvm的优化 Tomcat 的缺省配置是不能稳定长期运行的,也就是不适合生产环境,它会死机,让你不断重新启动,甚至在午夜时分唤醒你.对于操作系统优化来说,是尽可能的增 ...

  8. Server Tomcat v7.0 Server at localhost was unable to start within 45 seconds

    错误:Server Tomcat v7.0 Server at localhost was unable to start within 45 seconds 错误提示就是我们限定了部署的时间导致的错 ...

  9. Could not publish server configuration for Tomcat v6.0 Server at localhost.

    经常在使用tomcat服务器的时候 总会发生一些莫名其妙的错误. 就像下面这个错误: 在配置文件中存在多个/MyWeb的配置,导致不能发布服务. 错误信息: Could not publish ser ...

随机推荐

  1. 大数据与Mapreduce

    第十五章 大数据与Maprudece 一.引言 实际生活中的数据量是非常庞大的,采用单机运行的方式可能需要若干天才能出结果,这显然不符合我们的预期,为了尽快的获得结果,我们将采用分布式的方式,将计算分 ...

  2. 前端应该了解的PWA

    一.传统web 应用 当前web应用在移动时代并没有达到其在桌面设备上流行的程度,下面有张图来对比与原生应用之间的差别. 究其原因,无外乎下面不可避免的几点: 移动设备网络限制-不可忽略的加载时间 w ...

  3. 安装RabbitMQ编译erlang时,checking for c compiler default output file name... configure:error:C compiler cannot create executables See 'config.log' for more details.

    checking for c compiler default output file name... configure:error:C compiler cannot create executa ...

  4. 44.1khz 16位比特双声道一分钟的音乐文件占多少硬盘空间?

    2*2*44.1*1000*60=10584000字节=10M2个声道*(16比特/8比特)字节*采样率(每秒采样44.1*1000次)*一分钟有60秒16比特是精度,描述振幅的,16比特等于2个字节 ...

  5. webpack学习之路01

    webpack是什么 1.模块化 能将css等静态文件模块化 2.借助于插件和加载器 webpack优势是什么 1.代码分离 各做各的 2.装载器(css,sass,jsx,es6等等) 3.智能解析 ...

  6. grub rescue 主引导修复

    使用windows 和 ubuntu 双系统的人,很有可能碰到重装某一个系统,或者另外添加分区,导致系统重启出现 : GRUB loading error:unknow filesystem grub ...

  7. linux几种定时函数的使用

    Linux定时函数介绍: 在程序开发过程中,我们时不时要用到一些定时器,通常如果时间精度要求不高,可以使用sleep,uslepp函数让进程睡眠一段时间来实现定时, 前者单位为秒(s),后者为微妙(u ...

  8. YII框架CGridView sql有条件分页实现

    $SQL="SELECT * FROM {{user}} WHERE `typeff`=2 order by create_time desc"; $SQL_count=" ...

  9. 自动化测试--protractor

    前戏 面向模型编程: 测试驱动开发: 先保障交互逻辑,再调整细节.---by 雪狼. 为什么要自动化测试? 1,提高产出质量. 2,减少重构时的痛.反正我最近重构多了,痛苦经历多了. 3,便于新人接手 ...

  10. spring security oauth2 jwt 认证和资源分离的配置文件(java类配置版)

    最近再学习spring security oauth2.下载了官方的例子sparklr2和tonr2进行学习.但是例子里包含的东西太多,不知道最简单最主要的配置有哪些.所以决定自己尝试搭建简单版本的例 ...