转:Tomcat 内存溢出 "OutOfMemoryError" 问题总结 (JVM参数说明)

问题说明:
公司内网环境中部署的jenkins代码发版平台突然不能访问了,查看tomcat的catalina.out日志发现报错如下:

?


1

2

3

4

5

6

[[email protected] logs]# tail -f /srv/apache-tomcat-7.0.67/logs/catalina.out

......

Exception in thread "http-bio-8080-exec-5" java.lang.OutOfMemoryError: PermGen space

Exception in thread "http-bio-8080-exec-5" java.lang.OutOfMemoryError: PermGen space

Exception in thread "http-bio-8080-exec-5" java.lang.OutOfMemoryError: PermGen space

......

上面报错是由于tomcat内存溢出引起的:

?


1

2

3

[[email protected] logs]# ps -ef|grep tomcat

root     23615     1 14 15:15 ?        00:04:45 /usr/java/jdk1.7.0_79/bin/java -Djava.util.logging.config.file=/srv/apache-tomcat-7.0.67/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.endorsed.dirs=/srv/apache-tomcat-7.0.67/endorsed -classpath /srv/apache-tomcat-7.0.67/bin/bootstrap.jar:/srv/apache-tomcat-7.0.67/bin/tomcat-juli.jar -Dcatalina.base=/srv/apache-tomcat-7.0.67 -Dcatalina.home=/srv/apache-tomcat-7.0.67 -Djava.io.tmpdir=/srv/apache-tomcat-7.0.67/temp org.apache.catalina.startup.Bootstrap start

root     24191 24013  0 15:49 pts/6    00:00:00 grep tomcat

Tomcat默认可以使用的内存为128MB,在较大型的应用项目中,这点内存显然是不够的,从而有可能导致系统无法运行!
其中常见的内存问题是报Tomcat内存溢出错误,Out of Memory(系统内存不足)的异常,从而导致客户端显示500错误。
在生产环境中,tomcat内存设置不好很容易出现JVM内存溢,解决方法就是:修改Tomcat中的catalina.sh文件(windows系统下修改的文件时catalina.bat)。在catalina.sh文件中,找到cygwin=false,在这一行的前面加入参数,具体如下:

?


1

2

3

4

5

6

7

[[email protected] bin]# pwd

/srv/apache-tomcat-7.0.67/bin

[[email protected] bin]# vim catalina.sh     //在cygwin=false这一行的上面添加下面内容

......

JAVA_OPTS=‘-Xms1024m -Xmx1024m -XX:PermSize=256M -XX:MaxNewSize=512m -XX:MaxPermSize=512m‘

cygwin=false

......

其中,-Xms设置初始化内存大小,-Xmx设置可以使用的最大内存。一般把-Xms和-Xmx设为一样大

接着重启tomcat即可,重启后查看tomcat服务进程,就能看到内存信息了:

?


1

2

3

4

5

6

7

[[email protected] bin]# ps -ef|grep tomcat|grep -v grep|awk -F" " ‘{print $2}‘|xargs kill -9

[[email protected] bin]# /srv/apache-tomcat-7.0.67/bin/startup.sh

[[email protected] bin]# ps -ef|grep tomcat

root     24547     1  3 15:53 pts/6    00:01:02 /usr/java/jdk1.7.0_79/bin/java -Djava.util.logging.config.file=/srv/apache-tomcat-7.0.67/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Xms1024m -Xmx1024m -XX:PermSize=256M -XX:MaxNewSize=512m -XX:MaxPermSize=512m -Djava.endorsed.dirs=/srv/apache-tomcat-7.0.67/endorsed -classpath /srv/apache-tomcat-7.0.67/bin/bootstrap.jar:/srv/apache-tomcat-7.0.67/bin/tomcat-juli.jar -Dcatalina.base=/srv/apache-tomcat-7.0.67 -Dcatalina.home=/srv/apache-tomcat-7.0.67 -Djava.io.tmpdir=/srv/apache-tomcat-7.0.67/temp org.apache.catalina.startup.Bootstrap start

root     24982 24013  0 16:22 pts/6    00:00:00 grep tomcat

Tomcat连接数设置:在tomcat配置文件server.xml中的<Connector ... />配置中,和连接数相关的参数有:
minProcessors:        最小空闲连接线程数,用于提高系统处理性能,默认值为10
maxProcessors:       最大连接线程数,即:并发处理的最大请求数,默认值为75
maxThreads               最大并发线程数,即同时处理的任务个数,默认值是200
acceptCount:           允许的最大连接数,应大于等于maxProcessors,默认值为100
enableLookups:       是否反查域名,取值为:true或false。为了提高处理能力,应设置为false
connectionTimeout: 网络连接超时,单位:毫秒。设置为0表示永不超时,这样设置有隐患的。通常可设置为30000毫秒。

其中和最大连接数相关的参数为maxProcessors和acceptCount。如果要加大并发连接数,应同时加大这两个参数。
web server允许的最大连接数还受制于操作系统的内核参数设置,通常Windows是2000个左右,Linux是1000个左右。Unix中如何设置这些参数,请参阅Unix常用监控和管理命令

例如:

?


1

2

3

4

<Connector port="8888" protocol="HTTP/1.1"

maxThreads="500" minSpareThreads="50" maxSpareThreads="100" enableLookups="false" acceptCount="2000"

connectionTimeout="20000"

redirectPort="8443" />

JVM参数优化:Xmn、Xms、Xmx、Xss都是JVM对内存的配置参数,我们可以根据不同需要区修改这些参数,以达到运行程序的最好效果。
-Xms:堆内存的最小大小,默认为物理内存的1/64
-Xmx:堆内存的最大大小,默认为物理内存的1/4
-Xmn:堆内新生代的大小。通过这个值也可以得到老生代的大小:-Xmx减去-Xmn
-Xss:设置每个线程可使用的内存大小,即栈的大小。在相同物理内存下,减小这个值能生成更多的线程,当然操作系统对一个进程内的线程数还是有限制的,不能无限生成。线程栈的大小是个双刃剑,如果设置过小,可能会出现栈溢出,特别是在该线程内有递归、大的循环时出现溢出的可能性更大,如果该值设置过大,就有影响到创建栈的数量,如果是多线程的应用,就会出现内存溢出的错误。

除了这些配置,JVM还有非常多的配置,常用的梳理如下:
一、堆设置
-Xms:  初始堆大小
-Xmx:  最大堆大小
-Xmn:  新生代大小
-XX:NewRatio:  设置新生代和老年代的比值。如:为3,表示年轻代与老年代比值为1:3
-XX:SurvivorRatio:  新生代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:为3,表示Eden:Survivor=3:2,一个Survivor区占整个新生代的1/5  
-XX:MaxTenuringThreshold:  设置转入老年代的存活次数。如果是0,则直接跳过新生代进入老年代
-XX:PermSize、-XX:MaxPermSize:   分别设置永久代最小大小与最大大小(Java8以前)
-XX:MetaspaceSize、-XX:MaxMetaspaceSize:  分别设置元空间最小大小与最大大小(Java8以后)

二、收集器设置
-XX:+UseSerialGC:  设置串行收集器
-XX:+UseParallelGC:  设置并行收集器
-XX:+UseParalledlOldGC:  设置并行老年代收集器
-XX:+UseConcMarkSweepGC:  设置并发收集器

三、垃圾回收统计信息
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:filename

四、并行收集器设置
-XX:ParallelGCThreads=n:  设置并行收集器收集时使用的CPU数。并行收集线程数。
-XX:MaxGCPauseMillis=n:  设置并行收集最大暂停时间
-XX:GCTimeRatio=n:  设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)

五、并发收集器设置
-XX:+CMSIncrementalMode:  设置为增量模式。适用于单CPU情况。
-XX:ParallelGCThreads=n:  设置并发收集器新生代收集方式为并行收集时,使用的CPU数。并行收集线程数。

如下面是一个jvm进程是查询出来(ps -ef)的参数设置情况:
-Xmx256m -Xms256m -Xmn64m -XX:PermSize=64m -Xss256k -XX:+PrintGCDetails -XX:+PrintGCTimeStamps

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

TOMCAT组件一律使用平滑启停模式,请确保所有应用系统可以平滑启停。

启动命令:

$ CATALINA_HOME/bin/startup.sh

停止命令:

$ CATALINA_HOME/bin/shutdown.sh

TOMCAT配置:

TOMCAT组件使用标准化部署,对于jvm基本配置如下:

[[email protected] bin]$ vim ../bin/catalina.sh

#!/bin/sh

#

export JAVA_OPTS="

-server

-Xms2048M

-Xmx2048M

-Xmn512M

-Xss1024K

-XX:MetaspaceSize=256m

-XX:MaxMetaspaceSize=256m

-XX:SurvivorRatio=8

-XX:TargetSurvivorRatio=90

-XX:MaxTenuringThreshold=7

-XX:GCTimeRatio=19

-XX:+UseParNewGC

-XX:+UseConcMarkSweepGC

-XX:+CMSClassUnloadingEnabled

-XX:-CMSParallelRemarkEnabled

-XX:+DisableExplicitGC

-XX:CMSInitiatingOccupancyFraction=70

-XX:SoftRefLRUPolicyMSPerMB=0

-XX:+PrintClassHistogram

-XX:+PrintGCDetails

-XX:+PrintGCTimeStamps

-XX:+PrintGCApplicationConcurrentTime

-XX:+PrintGCApplicationStoppedTime

-Xloggc:/var/log/bsp/tomcat_8082_servicefront/gc.log"

..............

..............

Tomcat常见内存溢出的几种情况:
OutOfMemoryError:Java heap space
OutOfMemoryError:GC overhead limit exceeded
OutOfMemoryError:Permgen space
OutOfMemoryError:Metaspace

1)OutOfMemoryError:Java heap space
每个Java程序都只能使用一定量的内存, 这种限制是由JVM的启动参数决定的。而更复杂的情况在于, Java程序的内存分为两部分: 堆内存(Heap space)和 永久代(Permanent Generation, 简称 Permgen):

这两个区域的最大内存大小, 由JVM启动参数 -Xmx 和 -XX: MaxPermSize 指定. 如果没有明确指定, 则根据平台类型(比如OS版本+ JVM版本)和物理内存的大小来确定。假如在创建新的对象时, 堆内存中的空间不足以存放新创建的对象, 就会引发java.lang.OutOfMemoryError: Java heap space 错误。不管机器上还有没有空闲的物理内存, 只要堆内存使用量达到最大内存限制, 就会抛出 java.lang.OutOfMemoryError: Java heap space 错误。

原因分析
产生 java.lang.OutOfMemoryError: Java heap space 错误的原因, 很多时候, 就类似于将 XXL 号的对象,往 S 号的 Java heap space 里面塞。其实清楚了原因, 就很容易解决。 只要增加堆内存的大小, 程序就能正常运行. 另外还有一些比较复杂的情况, 主要是由代码问题导致的:
a)超出预期的访问量/数据量。 应用系统设计时,一般是有 “容量” 定义的, 部署这么多机器, 用来处理一定量的数据/业务。 如果访问量突然飙升, 超过预期的阈值, 类似于时间坐标系中针尖形状的图谱, 那么在峰值所在的时间段, 程序很可能就会卡死、并触发 java.lang.OutOfMemoryError: Java heap space 错误。

b)内存泄露(Memory leak). 这也是一种经常出现的情形。由于代码中的某些错误, 导致系统占用的内存越来越多. 如果某个方法/某段代码存在内存泄漏的, 每执行一次, 就会(有更多的垃圾对象)占用更多的内存. 随着运行时间的推移, 泄漏的对象耗光了堆中的所有内存, 那么 java.lang.OutOfMemoryError: Java heap space 错误就爆发了。

解决方案
如果设置的最大内存不满足程序的正常运行, 只需要增大堆内存即可。但很多情况下, 增加堆内存空间并不能解决问题。比如存在内存泄漏, 增加堆内存只会推迟 java.lang.OutOfMemoryError: Java heap space 错误的触发时间。当然, 增大堆内存, 可能会增加 GC pauses 的时间, 从而影响程序的 吞吐量或延迟。

2)OutOfMemoryErro: GC overhead limit exceeded
Java运行时环境内置了垃圾收集(GC) 模块. 上一代的很多编程语言中并没有自动内存回收机制, 需要程序员手工编写代码来进行内存分配和释放, 以重复利用堆内存。在Java程序中, 只需要关心内存分配就行。如果某块内存不再使用, 垃圾收集(Garbage Collection) 模块会自动执行清理。一般来说, JVM内置的垃圾收集算法就能够应对绝大多数的业务场景。java.lang.OutOfMemoryError: GC overhead limit exceeded 这种情况发生的原因是, 程序基本上耗尽了所有的可用内存, GC也清理不了。暂时解决这个问题de 方法: 1)增加参数,-XX:-UseGCOverheadLimit,关闭这个特性;2)同时增加heap大小:-Xmx1024m。

原因分析
JVM抛出 java.lang.OutOfMemoryError: GC overhead limit exceeded 错误就是发出了这样的信号: 执行垃圾收集的时间比例太大, 有效的运算量太小. 默认情况下, 如果GC花费的时间超过 98%, 并且GC回收的内存少于 2%, JVM就会抛出这个错误。

需要注意:java.lang.OutOfMemoryError: GC overhead limit exceeded 错误只在连续多次 GC 都只回收了不到2%的极端情况下才会抛出。假如不抛出 GC overhead limit 错误会发生什么情况呢? 那就是GC清理的这么点内存很快会再次填满, 迫使GC再次执行. 这样就形成恶性循环, CPU使用率一直是100%, 而GC却没有任何成果. 系统用户就会看到系统卡死 - 以前只需要几毫秒的操作, 现在需要好几分钟才能完成。

解决方案
有一种应付了事的解决方案, 就是不想抛出 “java.lang.OutOfMemoryError: GC overhead limit exceeded” 错误信息, 则添加下面启动参数:

?


1

2

#不推荐

-XX:-UseGCOverheadLimit

强烈建议不要指定该选项: 因为这不能真正地解决问题,只能推迟一点 out of memory 错误发生的时间,到最后还得进行其他处理。指定这个选项, 会将原来的 java.lang.OutOfMemoryError: GC overhead limit exceeded 错误掩盖,变成更常见的 java.lang.OutOfMemoryError: Java heap space 错误消息。

需要注意: 有时候触发 GC overhead limit 错误的原因, 是因为分配给JVM的堆内存不足。这种情况下只需要增加堆内存大小即可。在大多数情况下, 增加堆内存并不能解决问题。例如程序中存在内存泄漏, 增加堆内存只能推迟产生 java.lang.OutOfMemoryError: Java heap space 错误的时间。当然, 增大堆内存, 还有可能会增加 GC pauses 的时间, 从而影响程序的 吞吐量或延迟。如果想从根本上解决问题, 则需要排查内存分配相关的代码. 简单来说, 需要回答以下问题:

可以根据内存分析的结果, 以及Plumbr生成的报告(下面会提到), 如果发现对象占用的内存很合理, 也不需要修改源代码的话, 那就增大堆内存吧。在这种情况下,修改JVM启动参数, (按比例)增加下面的值:

?


1

-Xmx1024m

这里配置了最大堆内存为 1024MB。可以根据实际情况修改这个值. 如果JVM还是会抛出 OutOfMemoryError, 那么可能还需要查询手册, 或者借助工具再次进行分析和诊断。也可以使用 g/G 表示 GB, m/M 代表 MB, k/K 表示 KB.下面的这些形式都是等价的, 设置Java堆的最大空间为 1GB:

?


1

2

3

4

5

# 等价形式: 最大1GB内存

java -Xmx1073741824 com.mycompany.MyClass

java -Xmx1048576k com.mycompany.MyClass

java -Xmx1024m com.mycompany.MyClass

java -Xmx1g com.mycompany.MyClass

3)OutOfMemoryError: Permgen space
PermGen space是指内存的永久保存区域。OutOfMemoryError: PermGen space从表面上看就是内存益出,解决方法也一定是加大内存。说说为什么会内存益出:这一部分用于存放Class和Meta的信息,Class在被 Load的时候被放入PermGen space区域,它和和存放Instance的Heap区域不同,GC(Garbage Collection)不会在主程序运行期对PermGen space进行清理,所以如果你的APP会LOAD很多CLASS的话,就很可能出现PermGen space错误。这种错误常见在web服务器对JSP进行pre compile的时候。

JVM限制了Java程序的最大内存使用量, 可以通过启动参数来配置。而Java的堆内存被划分为多个区域, 如下图所示:

这些区域的最大值, 由JVM启动参数 -Xmx 和 -XX:MaxPermSize 指定. 如果没有明确指定, 则根据操作系统平台和物理内存的大小来确定。java.lang.OutOfMemoryError: PermGen space 错误信息所表达的意思是: 永久代(Permanent Generation) 内存区域已满。

原因分析:我们先看看 PermGen 是用来干什么的?
在JDK1.7及之前的版本, 永久代(permanent generation) 主要用于存储加载/缓存到内存中的 class 定义, 包括 class 的 名称(name), 字段(fields), 方法(methods)和字节码(method bytecode); 以及常量池(constant pool information); 对象数组(object arrays)/类型数组(type arrays)所关联的 class, 还有 JIT 编译器优化后的class信息等。很容易看出, PermGen 的使用量和JVM加载到内存中的 class 数量/大小有关。可以说 java.lang.OutOfMemoryError: PermGen space 的主要原因, 是加载到内存中的 class 数量太多或体积太大。

解决方案
a)解决程序启动时产生的 OutOfMemoryError
在程序启动时, 如果 PermGen 耗尽而产生 OutOfMemoryError 错误, 那很容易解决. 增加 PermGen 的大小, 让程序拥有更多的内存来加载 class 即可. 修改 -XX:MaxPermSize 启动参数, 类似下面这样:

?


1

java -XX:MaxPermSize=512m com.yourcompany.YourClass

以上配置允许JVM使用的最大 PermGen 空间为 512MB, 如果还不够, 就会抛出 OutOfMemoryError

b)解决运行时产生的 OutOfMemoryError
如果在运行的过程中发生 OutOfMemoryError, 首先需要确认 GC是否能从PermGen中卸载class。 官方的JVM在这方面是相当的保守(在加载class之后,就一直让其驻留在内存中,即使这个类不再被使用). 但是, 现代的应用程序在运行过程中, 会动态创建大量的class, 而这些class的生命周期基本上都很短暂, 旧版本的JVM 不能很好地处理这些问题。那么我们就需要允许JVM卸载class。使用下面的启动参数:

?


1

-XX:+CMSClassUnloadingEnabled

默认情况下 CMSClassUnloadingEnabled 的值为false, 所以需要明确指定。 启用以后, GC 将会清理 PermGen, 卸载无用的 class. 当然, 这个选项只有在设置 UseConcMarkSweepGC 时生效。 如果使用了 ParallelGC, 或者 Serial GC 时, 那么需要切换为CMS:

?


1

-XX:+UseConcMarkSweepGC

通过来说,java.lang.OutOfMemoryError: PermGen space有效解决方法:手动设置MaxPermSize大小修改TOMCAT_HOME/bin/catalina.sh

?


1

2

在 catalina.sh 文件 echo "Using CATALINA_BASE:   $CATALINA_BASE"上面或者第一行加入以下行:

set JAVA_OPTS=%JAVA_OPTS% -server -XX:PermSize=256M -XX:MaxPermSize=512m

4)OutOfMemoryError: Metaspace
Metaspace可以简单理解为为存放的类元信息和方法信息的一个JVM 块,Metaspace是JDK1.8的新特性,可以把它理解为JDK1.6的PermGen区域(但是两者也有很大区别的)。JVM限制了Java程序的最大内存, 修改/指定启动参数可以改变这种限制。Java将堆内存划分为多个部分, 如下图所示:

[Java8及以上版本]这些内存池的最大值, 由 -Xmx 和 -XX:MaxMetaspaceSize 等JVM启动参数指定. 如果没有明确指定, 则根据平台类型(OS版本+JVM版本)和物理内存的大小来确定。java.lang.OutOfMemoryError: Metaspace 错误所表达的信息是: 元数据区(Metaspace) 已被用满

原因分析
如果你是Java老司机, 应该对 PermGen 比较熟悉. 但从Java 8开始,内存结构发生重大改变, 不再使用Permgen, 而是引入一个新的空间: Metaspace. 这种改变基于多方面的考虑, 部分原因列举如下:Permgen空间的具体多大很难预测。指定小了会造成 java.lang.OutOfMemoryError: Permgen size 错误, 设置多了又造成浪费。
a)为了 GC 性能 的提升, 使得垃圾收集过程中的并发阶段不再 停顿, 另外对 metadata 进行特定的遍历(specific iterators)。
b)对 G1垃圾收集器 的并发 class unloading 进行深度优化。
c)在Java8中,将之前 PermGen 中的所有内容, 都移到了 Metaspace 空间。例如: class 名称, 字段, 方法, 字节码, 常量池, JIT优化代码, 等等。

Metaspace 使用量与JVM加载到内存中的 class 数量/大小有关。可以说java.lang.OutOfMemoryError: Metaspace 错误的主要原因,是加载到内存中的 class 数量太多或者体积太大。

解决方案
如果抛出与 Metaspace 有关的 OutOfMemoryError , 第一解决方案是增加 Metaspace 的大小. 使用下面这样的启动参数:

?


1

-XX:MaxMetaspaceSize=512m

这里将 Metaspace 的最大值设置为 512MB, 如果没有用完, 就不会抛出 OutOfMemoryError

有一种看起来很简单的方案:直接去掉 Metaspace 的大小限制。 但需要注意:不限制Metaspace内存的大小, 假若物理内存不足, 有可能会引起内存交换(swapping), 严重拖累系统性能。 此外,还可能造成native内存分配失败等问题。在现代应用集群中,宁可让应用节点挂掉, 也不希望其响应缓慢。如果不想收到报警, 可以像鸵鸟一样, 把 java.lang.OutOfMemoryError: Metaspace 错误信息隐藏起来。 但这不能真正解决问题, 只会推迟问题爆发的时间。 如果确实存在内存泄露, 请参考前面的文章, 认真寻找解决方案。

要从根本上解决上面说到的这四种Tomcat 内存溢出 "OutOfMemoryError" 问题, 则需排查分配内存的代码. 简单来说, 需要解决这些问题:
a)哪类对象占用了最多内存?
b)这些对象是在哪部分代码中分配的。

要搞清出这一点, 可能需要好几天时间。下面是大致的流程:
a)获得在生产服务器上执行堆转储(heap dump)的权限。"转储"(Dump)是堆内存的快照, 稍后可以用于内存分析. 这些快照中可能含有机密信息, 例如密码、信用卡账号等, 所以有时候, 由于企业的安全限制, 要获得生产环境的堆转储并不容易。
b)在适当的时间执行堆转储。一般来说,内存分析需要比对多个堆转储文件, 假如获取的时机不对, 那就可能是一个"废"的快照. 另外, 每次执行堆转储, 都会对JVM进行"冻结", 所以生产环境中,也不能执行太多的Dump操作,否则系统缓慢或者卡死,你的麻烦就大了。
c)用另一台机器来加载Dump文件。一般来说, 如果出问题的JVM内存是8GB, 那么分析 Heap Dump 的机器内存需要大于 8GB. 打开转储分析软件(我们推荐Eclipse MAT , 当然你也可以使用其他工具)。
d)检测快照中占用内存最大的 GC roots。

接下来, 找出可能会分配大量对象的代码. 如果对整个系统非常熟悉, 可能很快就能定位了。这里推荐一款检查内存泄露工具 plumbr 。 Plumbr 能捕获所有java.lang.OutOfMemoryError , 并找出其他的性能问题, 例如最消耗内存的数据结构等等。Plumbr 在后台负责收集数据 —— 包括堆内存使用情况(只统计对象分布图, 不涉及实际数据),以及在堆转储中不容易发现的各种问题。 如果发生 java.lang.OutOfMemoryError , 还能在不停机的情况下, 做必要的数据处理. 下面是Plumbr 对一个 java.lang.OutOfMemoryError 的提醒:

以上能直接看到:
a) 哪类对象占用了最多的内存(此处是 271 个 com.example.map.impl.PartitionContainer 实例, 消耗了 173MB 内存, 而堆内存只有 248MB)
b)这些对象在何处创建(大部分是在 MetricManagerImpl 类中,第304行处)
c)当前是谁在引用这些对象(从 GC root 开始的完整引用链)
得知这些信息, 就可以定位到问题的根源, 例如是当地精简数据结构/模型, 只占用必要的内存即可。

来看一个案例:公司的jenkins在使用过程中出现报错 "java.lang.OutOfMemoryError: Metaspace"

解决办法:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

[[email protected] ~]# ps -ef|grep tomcat_jenkins

root      59446   4761 12 May27 ?        5-12:10:08 /root/base/jdk8/bin/java -Djava.util.logging.config.file=/root/tomcat_jenkins/conf/logging.properties

-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -server -Xms5120M -Xmx5120M -Xmn1500M -Xss1024K -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=512m

-XX:SurvivorRatio=8 -XX:TargetSurvivorRatio=90 -XX:MaxTenuringThreshold=7 -XX:GCTimeRatio=19 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled

-XX:-CMSParallelRemarkEnabled -XX:+DisableExplicitGC -XX:CMSInitiatingOccupancyFraction=70 -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+PrintClassHistogram -XX:+PrintGCDetails

-XX:+PrintGCTimeStamps -XX:+PrintGCApplicationConcurrentTime -XX:+PrintGCApplicationStoppedTime -Xloggc:/var/log/tomcat_19090_jenkins/gc.log -Dhudson.model.ParametersAction.keepUndefinedParameters=true

-DJENKINS_HOME=/mnt/jenkins_config -Djava.awt.headless=true -Djdk.tls.ephemeralDHKeySize=2048 -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -classpath

/root/tomcat_jenkins/bin/bootstrap.jar:/root/tomcat_jenkins/bin/tomcat-juli.jar -Dcatalina.base=/root/tomcat_jenkins -Dcatalina.home=/root/tomcat_jenkins -Djava.io.tmpdir=/root/tomcat_jenkins/temp org.apache.catalina.startup.Bootstrap start

发现当前Metaspace元数据区设置为512M,查看配置:

[[email protected] ~]# cat /root/tomcat_jenkins/bin/setenv.sh

JAVA_HOME=/root/base/jdk8

.......

.......

-XX:MetaspaceSize=512m

-XX:MaxMetaspaceSize=512m

将setenv.sh文件中的Metaspace相关的jvm值大点

[[email protected] ~]# cat /root/tomcat_jenkins/bin/setenv.sh

.......

.......

-XX:MetaspaceSize=1024m

-XX:MaxMetaspaceSize=1024m

然后重启这个tomcat_jenkins服务即可

Java内存溢出(java.lang.OutOfMemoryError)常见问题运维总结

在解决java内存溢出问题之前,需要对jvm(java虚拟机)的内存管理有一定的认识。jvm管理的内存大致包括三种不同类型的内存区域:Permanent Generation space(永久保存区域)Heap space(堆区域)Java Stacks(Java栈)。其中:
- Permanent Generation space主要存放Class(类)和Meta信息,Class第一次被Load时被放入PermGen space区域,Class需要存储的内容主要包括方法和静态属性。
- Heap space用来存放Class实例(即对象), 对象需存储的内容主要是非静态属性。每次用new创建一个对象实例后,对象实例存储在堆区域中,这部分空间被jvm垃圾回收机制管理。
- Java StacksJava栈跟大多数编程语言包括汇编语言的栈功能相似,主要基本类型变量以及方法的输入输出参数。Java程序的每个线程中都有一个独立的堆栈。

注意:容易发生内存溢出问题的内存空间包括:Permanent Generation space和Heap space。

1)第一种 OutOfMemoryError: PermGen space (1.8后更新为Metaspace,不存在)
PermSpace主要是存放静态的类信息和方法信息,静态的方法和变量,final标注的常量信息等。发生这种问题的原意是程序中使用了大量的jar或class,使java虚拟机装载类的空间不够,与Permanent Generation space有关。解决这类问题有以下两种办法:
a)增加java虚拟机中的XX:PermSize和XX:MaxPermSize参数的大小,其中XX:PermSize是初始永久保存区域大小,XX:MaxPermSize是最大永久保存区域大小。如针对tomcat6.0,在catalina.sh 或catalina.bat文件中一系列环境变量名说明结束处(大约在70行左右) 增加一行:
JAVA_OPTS=" -XX:PermSize=64M -XX:MaxPermSize=128m"
b)清理应用程序中web-inf/lib下的jar,如果tomcat部署了多个应用,很多应用都使用了相同的jar,可以将共同的jar移到tomcat共同的lib下,减少类的重复加载。

第二种 OutOfMemoryError: Java heap space
发生这种问题的原因是java虚拟机创建的对象太多,在进行垃圾回收之间,虚拟机分配的到堆内存空间已经用满了,与Heap space有关。解决这类问题有两种思路:
a)检查代码中是否有死循环或递归调用;检查是否有大循环重复产生新对象实体;检查对数据库查询中是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内存溢出。这个问题比较隐蔽,在上线前数据库中数据较少,不容易出问题,上线后数据库中数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询;检查List、MAP等集合对象是否有使用完后未清除的问题。List、MAP等集合对象会始终存有对对象的引用,使得这些对象不能被GC回收。
b)增加Java虚拟机中Xms(初始堆大小)和Xmx(最大堆大小)参数的大小。如:set JAVA_OPTS= -Xms256m -Xmx1024m

第三种 OutOfMemoryError:unable to create new native thread  
在java应用中有时候会出现这样的错误:OutOfMemoryError: unable to create new native thread。这种怪事是因为JVM已经被系统分配了大量的内存(比如1.5G),并且它至少要占用可用内存的一半。有人发现,在线程个数很多的情况下,你分配给JVM的内存越多,那么,上述错误发生的可能性就越大。对于这个异常我们首先需要判断下,发生内存溢出时进程中到底都有什么样的线程,这些线程是否是应该存在的,是否可以通过优化来降低线程数; 另外一方面默认情况下java为每个线程分配的栈内存大小是1M,通常情况下,这1M的栈内存空间是足足够用了,因为在通常在栈上存放的只是基础类型的数据或者对象的引用,这些东西都不会占据太大的内存, 我们可以通过调整jvm参数,降低为每个线程分配的栈内存大小来解决问题,例如在jvm参数中添加-Xss128k将线程栈内存大小设置为128k。

原文地址:https://www.cnblogs.com/fxcoding/p/11605679.html

时间: 2024-10-01 10:29:15

转:Tomcat 内存溢出 "OutOfMemoryError" 问题总结 (JVM参数说明)的相关文章

SSH部署到Tomcat内存溢出-OutOfMemoryError 的解决办法

使用ssh集成项目后,部署几次后会就会出现OutOfMemoryError PermGen space系统错误,通过在网上查阅资料,发现这个错误并不是Tomcat的问题,而JVM设计自身的一个缺陷,JVM把内存分了不同的区, PermGen space的全称是Permanent Generation space,是指内存的永久保存区域,类加载的时候就存放在这个区域中.本来SUN设计的时候认为这个区域在JVM启动的时候就固定了,但他没有想到现在动态会用得这么广泛.而且这个区域有特殊的垃圾收回机制,

JVM(三)-内存溢出OutOfMemoryError

一.内存溢出OutOfMemoryError (1)java堆溢出 ①Java堆用于存储对象实例,只要不断地创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,那么在对象数量到达最大堆的容量限制后就会产生内存溢出异常. 代码清单1中代码限制Java堆的大小为20MB,不可扩展(将堆的最小值-Xms参数与最大值-Xmx参数设置为一样即可避免堆自动扩展),通过参数-XX:+HeapDumpOnOutOfMemoryError可以让虚拟机在出现内存溢出异常时Dump出

tomcat 内存溢出

1.Tomcat内存溢出的原因 生产环境中Tomcat内存设置不好很容易出现内存溢出.造成内存溢出是不一样的,当然处理方式也不一样. 这里根据平时遇到的情况和相关资料进行一个总结.常见的一般会有下面三种情况: OutOfMemoryError: Java heap space OutOfMemoryError: PermGen space OutOfMemoryError: unable to create new native thread. Tomcat内存溢出解决方案 对于前两种情况,在应

Tomcat内存溢出的三种情况及解决办法分析

转自 http://cache.baiducontent.com/c?m=9f65cb4a8c8507ed4fece7631046893b4c4380147d8c8c4668d4e419ce3b4c413037bfa6663f405a8e906b6075aa1f0ce9f03176370123b5989b8908dcac925f75ce786a6459db0144dc4ee59a1167877bcd06feaf68bce6e732e2e88893880705dd53742bddaccd095b4

大神的---解决tomcat内存溢出问题----tomcat报错:This is very likely to create a memory leak问题解决

tomcat memory leak解决方案 这种问题在开发中经常会碰到的,看看前辈的总结经验 Tomcat内存溢出的原因  在生产环境中tomcat内存设置不好很容易出现内存溢出.造成内存溢出是不一样的,当然处理方式也不一样. 这里根据平时遇到的情况和相关资料进行一个总结.常见的一般会有下面三种情况:  1.OutOfMemoryError: Java heap space  2.OutOfMemoryError: PermGen space  3.OutOfMemoryError: unab

tomcat内存溢出,修改设置

问题描述: 1. java.lang.OutOfMemoryError: Java heap space JVM堆的设置是指java程序运行过程中JVM可以调配使用的内存空间的设置.JVM在启动的时候会自动设置Heap size的值,其初始空间(即-Xms)是物理内存的1/64,最大空间(-Xmx)是物理内存的1/4.可以利用JVM提供的-Xmn -Xms -Xmx等选项可进行设置.Heap size 的大小是Young Generation 和Tenured Generaion 之和. 2.j

linux中tomcat内存溢出解决办法

用命令 tail -f /root/apache-tomcat-6.0.20/logs/catalina.out(需要找到tomcat路径) 查看日志,查看是否有错误 linux中tomcat内存溢出解决办法 常见的一般会有下面三种情况:1.OutOfMemoryError: Java heap space2.OutOfMemoryError: PermGen space3.OutOfMemoryError: unable to create new native thread.前两种通常一起进

tomcat内存溢出处理

tomcat内存溢出设置JAVA_OPTS  答案1设置Tomcat启动的初始内存 其初始空间(即-Xms)是物理内存的1/64,最大空间(-Xmx)是物理内存的1/4.可以利用JVM提供的-Xmn -Xms -Xmx等选项可 进行设置 三.实例,以下给出1G内存环境下java jvm 的参数设置参考: JAVA_OPTS="-server -Xms800m -Xmx800m -XX:PermSize=64M -XX:MaxNewSize=256m -XX:MaxPermSize=128m -D

Tomcat 内存溢出对应解决方式

1.Tomcat内存溢出的原因 生产环境中Tomcat内存设置不好很容易出现内存溢出.造成内存溢出是不一样的,当然处理方式也不一样. 这里根据平时遇到的情况和相关资料进行一个总结.常见的一般会有下面三种情况: OutOfMemoryError: Java heap space OutOfMemoryError: PermGen space OutOfMemoryError: unable to create new native thread. Tomcat内存溢出解决方案 对于前两种情况,在应