在采用Tocmat布署华丹快速开发平台时,对应一些负载比较大的业务系统,需要优化一下内存参数。本文我们详细介绍一下有关Tomcat中内存参数含义以及如何设置。

先来解释一下jvm内存主要参数:

堆区相关参数:

-Xms:java Heap初始大小。默认是物理内存的1/64。

-Xmx:java heap最大值。

-XX:NewSize: 新生成的池的初始大小。缺省值为2M。

-XX:MaxNewSize: 新生成的池的最大大小。缺省值为32M。

一般-Xmx建议单实例Tomcat设为物理内存一半,多实例总和为物理内存一半,不可超过物理内存。

生产环镜建议将-Xms与-Xmx两个参数的配置相同的值,其目的是为了能够在java垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小而浪费资源。(当初始堆占满后,会尝试进行GC,如果GC之后还不能得到足够的内存,那么就会扩展堆,如果-Xmx设置的太小,扩展堆就会失败,导致OutOfMemoryError错误提示)

最大内存可以设多大? 首先JVM内存限制于实际的最大物理内存,假设物理内存无限大的话,JVM内存的最大值跟操作系统有很大的关系。简单的说就32位处理器虽然可控内存空间有4GB,但是具体的操作系统会给一个限制, 这个限制一般是2GB-3GB(一般来说Windows系统下为1.5G-2G,Linux系统下为2G-3G),而64bit以上的处理器就不会有限制了。

其实即使对于64位操作系统,我们也不要无限增大最大内存值,最好不要超过4G,如果负载多的话,可以考虑采用Tomcat集群方式,即使在同一台服务器上布多个Tomcat实例构成集群也会大幅度增大负载支持量。

-XX:NewSize及-XX:MaxNewSize两参数有关域区域新生代内存,指JAVA堆区域新生代内存的初始大小及最大可分配大小。一般堆区会分为3个区域,新生代、中生代和老年代。java中每新new一个对象所占用的内存空间就是新生代的空间,当java垃圾回收机制对堆区进行资源回收后,那些新生代中没有被回收的资源将被转移到中生代,中生代的被转移到老生代。

我们在程序中通过new关键字新建出来的对象,一般都会分配在新生代中。当新生代满了后,JVM会通过GC来进行一次小规模的垃圾回收,此时新生代中存活的对象会被移动至中生代。新生代内存的大小也间接决定了JVM进行monior gc的频率。

这两个参数也可以通过-Xmn来设置(jdk1.4版本以后),是对 -XX:newSize、-XX:MaxnewSize两个参数的同时配置,也就是说如果通过-Xmn来配置新生代的内存大小,那么-XX:newSize = -XX:MaxnewSize = -Xmn。

非堆区(内存的永久保存区)相关参数:

-XX:PermSize:设定内存的永久保存区初始大小,缺省值为64M。

-XX:MaxPermSize:设定内存的永久保存区最大大小,缺省值为64M。

内存的永久保存区,PermGen space的全称是Permanent Generation space,是指内存的永久保存区域,不足时会报OutOfMemoryError: PermGen space。从文字上看就是内存溢出,解决方法是加大内存。为什么会内存溢出,这是由于这块内存主要是被JVM存放Class和Meta信息的,Class在被Load的时候被放入PermGen space区域,它和存放Instance的Heap区域不同,GC(Garbage Collection)不会在主程序运行期对PermGen space进行清理,所以如果你的APP会LOAD很多CLASS的话,就很可能出现PermGen space错误。这种错误常见在web服务器对JSP进行pre compile的时候。如果你的WEB APP下都用了大量的第三方jar, 其大小超过了设定大小那么就会产生此错误信息了。

注意:以上jvm内存参数默认值是把jdk的默认值,tomcat中可以更改相关值,如tomcat9的service.bat中就明确通过:

if "%JvmMs%" == "" set JvmMs=128

if "%JvmMx%" == "" set JvmMx=256

设定最小、最大内存分别为128M、256M。

在遇到内存溢出错误时,需要区分是什么类型内存溢出,比如OutOfMemoryError: PermGen space之种错是需要修改-XX:MaxPermSize的值,而去修改-Xmx无效果。java.lang.OutOfMemoryError: Java heap space则是需要增加-Xmx的值。

一般地,我们给出如下内存参数建议:

服务器内存2G:

JAVA_OPTS="-server -Xms1024m -Xmx1024m -XX:PermSize=64M -XX:MaxNewSize=256m -XX:MaxPermSize=128m -Djava.awt.headless=true "

服务器内存超过4G:

JAVA_OPTS="-server -Xms2048m -Xmx2048m -XX:PermSize=64M -XX:MaxNewSize=256m -XX:MaxPermSize=128m -Djava.awt.headless=true "

上述的设置一般的应用足够了,比如我们华丹快速开发平台,一般的业务应用,-Xmx256m就足够了。

如果业务负载量很大的情况下,且服务器内存超过8G,可以采用在一台服务器上配置多Tomcat实例组成集群的方式可获得更好的效果。

下面我们就介绍一下在tomcat中如何设置内存参数:

1、linux操作系统:

修改%tomcat%/bin/catalina.sh文件,打开该文件,在setlocal下一行(如果没有setlocal,就在注释结束后)增加

set "JAVA_OPTS=%JAVA_OPTS% -Xms1024m -Xmx1024m -XX:PermSize=64M -XX:MaxNewSize=256m -XX:MaxPermSize=128m",保存即可。

注意,如果将tocmat设置成了自启动服务,还需要到/etc/rc.d/init.d目录下,找到tomcat文件,同样修改。

2、windows操作系统:

修改%tomcat%\bin\catalina.bat文件,打开该文件,与linux一样,在setlocal下一行(如果没有setlocal,就在注释结束后)增加

set "JAVA_OPTS=%JAVA_OPTS% -Xms1024m -Xmx1024m -XX:PermSize=64M -XX:MaxNewSize=256m -XX:MaxPermSize=128m",保存即可。

通过上述修改,只对通过startup.bat启运tomcat才有效。将tomcat注册成windows服务,通过服务启动tomcat,上述方法无效,这时你查看tomcat日志,发现日志里显示,jvm参数还是128 - 256 没变化:

31-Jan-2021 11:06:19.515 信息 [main] org.apache.catalina.startup.VersionLoggerListener.log 命令行参数:       -Xms128m

31-Jan-2021 11:06:19.515 信息 [main] org.apache.catalina.startup.VersionLoggerListener.log 命令行参数:       -Xmx256m

这时我们可以通过%tomcat%\bin\下的tomcatw.exe文件中设置参数,比如安装Tomcat9后对应的昌tomcat9w.exe。

打开程序界面,可以方便设置最小及最大内存。

但是用这个方法的前提是已将Tomcat注册成服务,且服务名称必须是tomcat9,否则这个tomcat9w.exe运行会报错,服务tomcat9未安装。

在%tomcat%/bin目录下,通过运行命令service.bat install,安装的服务名,默认就是tomcat9,但是有情况下我们要改服务名,比如一台服务器上注册多个tomcat服务,服务名称必须得改,通过tomcat9w.exe就无法更改内存。

这种情况下,按如下方法处理:

打开%tomcat%\bin目录下的service.bat文件,搜索"%JvmMs%",找到如下段:

if "%JvmMs%" == "" set JvmMs=128

if "%JvmMx%" == "" set JvmMx=256

修改为:

if "%JvmMs%" == "" set JvmMs=1024

if "%JvmMx%" == "" set JvmMx=1024

保存后,重新注册Tocmat服务,必须重新注册,否则修改无效。

%tomcat%\bin\service remove去除原服务,

%tomcat%\bin\service install重新注册服务。

另外,我们也可以通过修改注册表的方式修改内存参数(注册成windows服务的情况):

打开注册表,找到目录HKEY_LOCAL_MACHINE\SOFTWARE\Apache Software Foundation\Procrun 2.0\对应注册服务名\Parameters\Java,可以看到JvmMs和JvmMx项,其中JvmMs设置最小的内存使用参数,JvmMx设置最大的内存使用参数。设置好JvmMs和JvmMx项的值,重启tomcat服务器即可生效。