记一次Debian 环境中tomcat java.lang.OutOfMemoryError: Java heap space问题处理

目录

1、问题表现

2、排查过程

free -h 查看服务器内存情况

jinfo -flags PID 查询JVM进程情况

JVM 参数之 初始堆大小、最大堆大小

Default Heap Size

Client JVM Default Initial and Maximum Heap Sizes 

Server JVM Default Initial and Maximum Heap Sizes

Specifying Initial and Maximum Heap Sizes

jmap -heap PID 查询JVM堆使用情况

3、tomcat JVM 最大堆大小设定方法


1、问题表现

使用的是tomcat 9.0.41版本,使用应用时,出现内容溢出,报错信息:java.lang.OutOfMemoryError: Java heap space。

后经过跟踪发现,每次tomcat的access日志出现这种非法访问:

catalina.out日志出现:

org.apache.coyote.http11.Http11Processor.service 解析 HTTP 请求 header 错误注意:HTTP请求解析错误的进一步发生将记录在DEBUG级别。
    java.lang.IllegalArgumentException: 在方法名称中发现无效的字符串, HTTP 方法名必须是有效的符号.
        at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:417)
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:261)
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:888)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1597)
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:748)

之后,原本部署的应用在使用时就很容易出现

java.lang.OutOfMemoryError: Java heap space的问题。 通过jvisualvm工具监控确实可以看到堆是满的,没有剩余空间了。 并且通过top 命令查看当前发现tomcat对应进程的CPU占用率极高,四核的CPU占用率直接飙升到380%:

2、排查过程

free -h 查看服务器内存情况

free -h 查询到的就是以G为单位的内存与交换内存的大小,如下图所示 内存15GB

通过jvisualvm工具查看到当前tomcat进程堆大小: 换算之后 大致为  堆最大大小为 3.47G

通过ps -ef |grep tomcat 查询当前tomcat对应的进程号:

jinfo -flags PID 查询JVM进程情况

通过 命令jinfo -flags PID 即可查询进程对应的 java进程具体的状态。查询到的内容大致如下:

$ jinfo -flags 1540532
Attaching to process ID 1540532, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.281-b09
Non-default VM flags: -XX:CICompilerCount=3 -XX:InitialHeapSize=262144000 -XX:+ManagementServer -XX:MaxHeapSize=4194304000 -XX:MaxNewSize=1397751808 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=87031808 -XX:OldSize=175112192 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC 
Command line:  -Djava.util.logging.config.file=/opt/tomcat/apache-tomcat-9.0/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djdk.tls.ephemeralDHKeySize=2048 -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -Djava.rmi.server.hostname=192.168.0.216 -Dcom.sun.management.jmxremote.port=18999 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Dorg.apache.catalina.security.SecurityListener.UMASK=0027 -Dignore.endorsed.dirs= -Dcatalina.base=/opt/tomcat/apache-tomcat-9.0 -Dcatalina.home=/opt/tomcat/apache-tomcat-9.0 -Djava.io.tmpdir=/opt/tomcat/apache-tomcat-9.0/temp
 

JVM 参数之 初始堆大小、最大堆大小

高亮部分内容就是 当前java 进程使用的java 虚拟机设定:

-XX:CICompilerCount=3    指定了JVM同时可以运行的编译线程数 通常情况下,这个参数不需要进行特殊配置,使用大于1的值可能会略微提升编译速度,但效果并不显著。同时,这样做可能会对系统的稳定性造成影响,增加JVM崩溃的风险,尤其是在JDK 1.4和1.5版本中.在JVM调优中并不常见。常规的JVM调优主要涉及内存设置,如 -Xms(初始堆大小)和 -Xmx(最大堆大小),以及分代垃圾回收相关的设置。 这里我搜索了一下catalina.sh里面没有设定,我估计是tomcat9 默认就是这个数据。

-XX:InitialHeapSize=262144000    JVM启动参数,用于指定JVM初始化时堆内存的大小,也就是 -Xms(初始堆大小)。单位字节 

-XX:MaxHeapSize=4194304000       即-Xmx(最大堆大小)

堆大小heap size  包括初始堆大小-XX:InitialHeapSize  以及最大堆大小 -XX:MaxHeapSize   如果没有通过命令指定,默认是通过物理内存为依据计算的。 

这个地方,官方资料:https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/parallel.html#default_heap_size

内容是这样的: 

Default Heap Size
Unless the initial and maximum heap sizes are specified on the command line, they are calculated based on the amount of memory on the machine.  除非通过命令指定初始以及最大堆大小,不然他们就根据物理内存为计算依据。


Client JVM Default Initial and Maximum Heap Sizes 
The default maximum heap size is half of the physical memory up to a physical memory size of 192 megabytes (MB) and otherwise one fourth of the physical memory up to a physical memory size of 1 gigabyte (GB).最大物理内存不超过192MB时默认最大堆大小就物理内存的一般,超过192到1GB物理内存时,最大堆大小就占物理内存的四分之一。

For example, if your computer has 128 MB of physical memory, then the maximum heap size is 64 MB, and greater than or equal to 1 GB of physical memory results in a maximum heap size of 256 MB.  如果你的电脑是128M的内存,那么最大堆大小就是物理内存的一半即64M;如果是更大的或等于1GB,那么默认的最大堆大小就是1G的四分之一即256M。

The maximum heap size is not actually used by the JVM unless your program creates enough objects to require it. A much smaller amount, called the initial heap size, is allocated during JVM initialization. This amount is at least 8 MB and otherwise 1/64th of physical memory up to a physical memory size of 1 GB.最大堆大小并不是实际JVM使用的,除非你的程序创建了足够多的对象并需要这些堆。一个相对小的大小就是初始堆大小,初始堆大小在JVM初始化时候就已经分配,初始堆大小至少8MB,否则就是物理内存(最大1GB)的64分一即初始堆大小为16M。

The maximum amount of space allocated to the young generation is one third of the total heap size.分配给新生代的最大容量是总堆内存的三分之一。


Server JVM Default Initial and Maximum Heap Sizes
The default initial and maximum heap sizes work similarly on the server JVM as it does on the client JVM, except that the default values can go higher. On 32-bit JVMs, the default maximum heap size can be up to 1 GB if there is 4 GB or more of physical memory. On 64-bit JVMs, the default maximum heap size can be up to 32 GB if there is 128 GB or more of physical memory. You can always set a higher or lower initial and maximum heap by specifying those values directly; see the next section.服务器上默认的JVM 初始以及最大堆大小与客户端JVM一致,除非这些默认数据可以更高。

在32位JVM环境里,如果有4GB甚至更多物理内存时,默认最大堆大小可以提升到1GB。

在64位JVM环境里,如果有128GB甚至更多物理内存时,默认最大堆大小可以提升到32GB。 (这里基本上可以理解为服务器 JVM环境里,默认的最大堆大小就是物理内存的四分之一)您可以通过直接指明这些值的方式来设定更高或者更低的初始化堆大小以及最大堆大小。详见下个章节。


Specifying Initial and Maximum Heap Sizes
You can specify the initial and maximum heap sizes using the flags -Xms (initial heap size) and -Xmx (maximum heap size). If you know how much heap your application needs to work well, you can set -Xms and -Xmx to the same value. If not, the JVM will start by using the initial heap size and will then grow the Java heap until it finds a balance between heap usage and performance. 你可以使用标志-Xms(初始堆大小)以及Xmx(最大堆大小)来明确指定初始以及最大堆大小。如果你知道你的应用需要多少的堆才能很好的运行,也可以将-Xms以及-Xmx设定成相同的值,否则JVM在启动时将使用初始堆大小,并且会自动增加java堆大小直到寻找到堆使用与性能之间的平衡点。

Other parameters and options can affect these defaults. To verify your default values, use the -XX:+PrintFlagsFinal option and look for MaxHeapSize in the output. For example, on Linux or Solaris, you can run the following:

java -XX:+PrintFlagsFinal <GC options> -version | grep MaxHeapSize
其他的参数与选项也可以影响这些默认值。为了确认默认在指,可以使用-XX:+PrintFlagsFinal选项同时在输出内容中查找MaxHeapSise。例如,在linux或者solaris下,可以通过如下命令查看:java -XX:+PrintFlagsFinal <GC options> -version | grep MaxHeapSize

-XX:MaxNewSize=1397751808    新生代可被分配的内存的最大上限。

-XX:MinHeapDeltaBytes=524288

-XX:NewSize=87031808   新生代初始内存的大小

-XX:OldSize=175112192   

-XX:+UseCompressedClassPointers

-XX:+UseCompressedOops

-XX:+UseParallelGC 

tomcat java.lang.OutOfMemoryError: Java heap space 报错实际就标识:当前使用的JVM 堆大小设置就不合适,设置的数据小了,因此排除应用本身的问题,就需要设定tomcat使用的JVM 堆大小。将最大堆大小适当调大。 按照JVM不同条件下,最大堆大小与物理内存的关系 以及当前设定的最大堆大小去修改。

jmap -heap PID 查询JVM堆使用情况

除了前面提到的jinfo -flags PID可以查询JVM堆情况 ,jmap -heap PID命令更加直观:

按照查询到的信息,当下这个服务器物理内存,默认的最大堆大小为物理内存的四分之一。 

3、tomcat JVM 最大堆大小设定方法

通过使用标志-Xms(初始堆大小)以及Xmx(最大堆大小)来明确指定初始以及最大堆大小。

java.lang.OutOfMemoryError: Java heap space问题的原因实际就是: 默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制;
空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制。因此服务器一般设置-Xms、-Xmx 相等以避免在每次GC 后调整堆的大小。
说明:如果-Xmx 不指定或者指定偏小,应用可能会导致java.lang.OutOfMemory错误,此错误来自JVM,不是Throwable的,无法用try…catch捕捉。

tomcat的JVM配置可以通过设定 catalina.sh中的JAVA_OPTSCATALINA_OPTS。 

这两者区别在于:通过 JAVA_OPTS 设定的参数会被其他使用java虚拟机的应用使用,而通过CATALINA_OPTS 设定的jvm参数只适用于tomcat本身。 如果有其他程序使用使用当下设定的JVM参数 则可以使用JAVA_OPTS设置,否则建议设置 CATALINA_OPTS。

编辑catalina.sh,找到 cygwin=false ,在此之前添加: CATALINA_OPTS="-server -Xms256m -Xmx5120m" 
 

之后 重启tomcat。

重启之后 可以通过tomcat日志 查看到当前设定的堆大小情况:

也可以通过命令 jmap -heap PID查看:

可以看到 MaxHeapSize已变更,同时 年轻代的空间也跟随发生了变化。 

后续再观察程序运行情况了。。。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值