【开发者须知】
可能在搜索“: Java heap space”这个问题的时候,已经搜出了大量雷同的答案,说在配置中加JAVA_OPTS参数等等,如果能解决您的问题,那么恭喜,如果仍未解决,请参考我的文章。我是花了2天的时间才找到解决方案。
本文只在我所在的Windows+Tomcat+Java11环境中处理,其他环境未必有参考价值。
【故障现象】
Tomcat是8.5版,运行环境是Windows,Tomcat是以Windows服务形式启动和关闭。
原本在Eclipse开发环境中,使用Java读取MySQL数据,很正常,后来将class放到生产环境中,发现读大量数据时,Tomcat的HTTP链接直接终止,程序的try-catch并没有捕捉到错误。
于是在Tomcat的中发现这样一段日志:
......上面太多了,各种Error
: Java heap space
【排查过程】
1、这个错误是什么意思?
用“: Java heap space”做关键字查资料,意思就是Java虚拟机JVM的堆内存不够,溢出了。
2、那我的堆内存现在是多少?
由于我对Java并不是很深入研究,所以并不了解配置,当听说“堆内存”不够时,也是懵了,一头雾水的样子,这玩意儿不应该是系统自己本身就识别硬件并且在硬件基础上自动分配吗?查了一堆答案也是说自动分配“1/4物理内存”,而生产的机器内存是32G,理论上应该有8G,怎么不够呢?
那就查查吧:
在生产环境Java的代码(根据实际应用,找个管理员功能模块)中加入以下代码:
......
("最大可用内存,对应-Xmx:"+().maxMemory());
//("当前JVM空闲内存:"+().freeMemory());
//("当前JVM占用的内存总数:"+().totalMemory());
......
运行后输出结果为:
最大可用内存,对应-Xmx:268435456
非常奇怪,看上去才256M,难怪会溢出。
3、搜索现成答案(没解决)
发现大量文章都说在
的第一行增加:
set JAVA_OPTS=-Xms800m -Xmx800m -XX:PermSize=128M -XX:MaxNewSize=256m -XX:MaxPermSize=256m
或修改TOMCAT_HOME/bin/
在“echo "Using CATALINA_BASE: $CATALINA_BASE"”上面加入以下行:
JAVA_OPTS="-server -Xms800m -Xmx800m -XX:MaxNewSize=256m"
上面的方案并未解决我这个问题,无论怎么修改,Tomcat重启后仍然是识别256M的最大内存。
【意外转机】
经过1天半的各种修改,已经找不到办法了,最后随手打开文件,意外发现文件内有几行日志:
28-Dec-2022 13:15:22.133 信息 [main] 命令行参数: -Xms128m
28-Dec-2022 13:15:22.133 信息 [main] 命令行参数: -Xmx256m
咦?这里的256M哪里来的?会不会就是这货的值最直接生效?
于是再找答案,其实也没找到,于是想想翻一下“bin/”文件,意外意外地发现有这么三行:
if "%SERVICE_STARTUP_MODE%" == "" set SERVICE_STARTUP_MODE=manual
if "%JvmMs%" == "" set JvmMs=128
if "%JvmMx%" == "" set JvmMx=256
这时,才领悟到原来在服务的代码里面设置了这个JVM的最大内存值为256M。
↓答案及步骤就在这↓
找到问题好办了,就直接修改这里的数据,其中8192是8G:
if "%SERVICE_STARTUP_MODE%" == "" set SERVICE_STARTUP_MODE=manual
if "%JvmMs%" == "" set JvmMs=8192
if "%JvmMx%" == "" set JvmMx=8192
然后卸载一次Tomcat的windows服务:
D:\apache-tomcat-8.5.73\bin>service remove
再重新安装一遍windows服务:
D:\apache-tomcat-8.5.73\bin>service install
这时再回头看已经是8192了,().maxMemory()也返回8589934592的值了。再去执行生产代码,发现正常了。问题就这样解决了。
【总结】
很深入的原因我不打算知道,通过分析,大概估计Tomcat的Windows版本作为服务启动时,可能不加载或文件,所以网上流传的解决方案不能生效。
最后修改bin/里面的JvmMx和JvmMs解决问题。