这里写目录标题
- 问题场景
- 问题分析与解决
- 1.优化项目代码
- 2.提升Java heap size
- 3.JVM参数配置
- 配置参考
- 堆区参数配置说明
- 非堆区参数配置说明
问题场景
最近客户反馈在生产环境导入操作时遇到任务一直执行中,并且入库的数据量一直不改变。通过日志查询,终于定位到报错信息如下:
java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Unknown Source) ~[na:1.8.0_221]
at java.io.ByteArrayOutputStream.grow(Unknown Source) ~[na:1.8.0_221]
at java.io.ByteArrayOutputStream.ensureCapacity(Unknown Source) ~[na:1.8.0_221]
at java.io.ByteArrayOutputStream.write(Unknown Source) ~[na:1.8.0_221]
at com.cxstar.common.utils.DESUtil.decryptFile(DESUtil.java:193) ~[cxstar-common-1.0.0.jar!/:1.0.0]
at com.cxstar.business.service.impl.ArchiveServiceImpl.upload(ArchiveServiceImpl.java:103) ~[cxstar-business-1.0.0.jar!/:1.0.0]
at com.cxstar.business.factory.AsyncFactory.importAttachment(AsyncFactory.java:1016) ~[cxstar-business-1.0.0.jar!/:1.0.0]
at com.cxstar.business.factory.AsyncFactory.access$800(AsyncFactory.java:62) ~[cxstar-business-1.0.0.jar!/:1.0.0]
at com.cxstar.business.factory.AsyncFactory$7.run(AsyncFactory.java:708) ~[cxstar-business-1.0.0.jar!/:1.0.0]
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source) ~[na:1.8.0_221]
at java.util.concurrent.FutureTask.run(Unknown Source) ~[na:1.8.0_221]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(Unknown Source) ~[na:1.8.0_221]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source) ~[na:1.8.0_221]
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) [na:1.8.0_221]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) [na:1.8.0_221]
at java.lang.Thread.run(Unknown Source) [na:1.8.0_221]
问题分析与解决
java.lang.OutOfMemoryError: Java heap space (JVM 堆空间溢出)简单来说就是在创建新的对象时, 堆内存中的空间不足以存放新创建的对象,导致此种问题的发生。
1.优化项目代码
根据报错信息定位到内存消耗较大的代码,然后对其进行重构或者优化算法。如果是在生产环境,务必要在内存消耗过大的代码出增加日志信息输出,否则容易像我定位一晚上才找到问题所在
2.提升Java heap size
增加堆内存空间设置,此种方式容易操作。可以较快解决当前问题,但是总体来说还是需要找到项目代码中的问题才是最优解,毕竟内存总是有限的
客户生产环境的服务器是8g内存,并且操作系统是比较老的windows server 2008服务器。在部署项目时使用winsw工具将jar包安装为服务。因此在项目启动时,增加内存参数,修改后配置如下:
<service>
<id>app-id</id>
<name>app-name service</name>
<description>application descriptions</description>
<!-- 配置的启动:使用jar -jar方式运行项目 -->
<executable>java</executable>
<arguments>-jar -server -Xms4096m -Xmx4096m -XX:PermSize=256M -XX:MaxNewSize=512m -XX:MaxPermSize=512m "D:\application\api-rest-1.0.0.jar"</arguments>
<startmode>Automatic</startmode>
<logpath>%BASE%\logs</logpath>
<logmode>rotate</logmode>
</service>
3.JVM参数配置
配置参考
服务器内存8G ,所以可以采取以下配置:
set JAVA_OPTS=-server -Xms4096m -Xmx4096m -XX:PermSize=256M -XX:MaxNewSize=512m -XX:MaxPermSize=512m
服务器内存4G ,所以可以采取以下配置:
set JAVA_OPTS=-server -Xms2048m -Xmx2048m -XX:PermSize=256M -XX:MaxNewSize=512m -XX:MaxPermSize=512m
服务器内存2G ,所以可以采取以下配置:
set JAVA_OPTS=-server -Xms1024m -Xmx1024m -XX:PermSize=256M -XX:MaxNewSize=512m -XX:MaxPermSize=512m
服务器内存1G ,所以可以采取以下配置:
set JAVA_OPTS=-server -Xms512m -Xmx512m -XX:PermSize=128M -XX:MaxPermSize=256M -XX:MaxPermSize=256m
服务器内存512M ,所以可以采取以下配置:
set JAVA_OPTS=-server -Xms256m -Xmx256m -XX:PermSize=256M -XX:MaxNewSize=128m -XX:MaxPermSize=128m
堆区参数配置说明
堆区:通过new的方式创建的对象(一个类的实例)、数组所占的空间。所有的线程共享该区域
堆区还细分为新生代(Eden空间、From Survivor空间、To Survivor空间)、老年代(Tenured Generation空间)
参数名 | 含义 |
---|---|
-Xms | java虚拟机初始化时的最小内存 |
-Xmx | java虚拟机可使用的最大内存 |
通常会将-Xms 与-Xmx两个参数的配置相同
的值,其目的是为了能够在java垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小而浪费资源
参数名 | 含义 |
---|---|
-XX:newSize | 表示对象创建初始内存的大小,应小于-Xms的值 |
-XX:MaxnewSize | 表示对象创建可被分配的内存的最大上限,应小于-Xmx的值 |
-Xmn | jdk1.4后对-XX:newSize、-XX:MaxnewSize两个参数同时进行配置 |
非堆区参数配置说明
非堆区:代码、常量、外部访问(比如流在传输数据时所占用的资源)等,java垃圾回收机制只作用于堆区,非堆区不会被java垃圾回收机制进行处理
参数名 | 含义 |
---|---|
-XX:PermSize | 表示非堆区初始内存分配大小(方法区) |
-XX:MaxPermSize | 表示对非堆区分配的内存的最大上限(方法区) |
在配置之前一定要慎重的考虑一下自身软件所需要的非堆区内存大小,因为此处内存是不会被java垃圾回收机制进行处理
的地方。并且更加要注意的是最大堆内存与最大非堆内存的和绝对不能够超出操作系统的可用内存。