浅谈Metaspace内存溢出原因及JVM参数设置

浅谈Metaspace内存溢出原因及JVM参数设置

1.Metaspace内存溢出(oom)

日志

浅谈Metaspace内存溢出原因及JVM参数设置

原因分析

从Java8开始,Java中的内存模型引入了一个称为元空间(Metaspace)的新内存区域,并删除了Permgen。一般如 类的名称和字段、带有方法字节码的类的方法、常量池、访问限制等都会存在于元空间中。
metaspace空间增长大部分是由于反射类加载、动态代理生成的类加载等导致,也就是说Metaspace的大小和加载类的数据有关系,加载的类越多metaspace占用的内存也就越大那么引起Metaspace内存溢出就可以从以上几种数据源头进行分析。

常见的溢出情况:

  1. 迭代引入监控、分析类过多。比如项目上线时,经过评估设置MaxMetaspaceSize为128M,后续由于添加了各类监控、分析等功能,项目启动即接近了128M,后续自然会导致空间不足。
  2. 某个业务场景的访问量暴增,导致Metaspace内存溢出。
  3. JVM参数配置错误。这个就藏的有点深了,首先要判断反射对象是否被SoftReference软引用修饰的,如果是,则需要查看XX:SoftRefLRUPolicyMSPerMB的参数配置。SoftRefLRUPolicyMSPerMB参数大概意思是每1M空闲空间可保持的SoftReference对象的生存时长(单位是ms毫秒),JVM默认是1000ms,如果被设置为0,就会导致软引用对象马上被回收掉,进而会导致频繁的反射生成新的类,达不到复用的效果,而导致Metaspace激增。
  4. 类加载器泄漏。这个更头疼。首先要对堆转储的Dump文件进行分析,堆应该在至少一个ClassLoader实例泄漏后转储生成dump文件,这样才可以分析泄漏实例的引用,从而防止它被垃圾收集。可以添加-XX:+HeapDumpOnOutOfMemoryError JVM参数,然后运行并重新部署应用程序,直到它崩溃java.lang.OutOfMemoryError错误,崩溃前会自动生成堆转储dump文件。文件名类似于java_pid18148.hprof,.hprof文件位于应用程序服务器的启动目录中,也可以通过-XX:HeapDumpPath=/directory参数指定目录。解析.hprof文件最好使用工具吧,每个工具使用都不一样,最终通过查找引用并找到不需要的引用,可以找到导致泄漏的原因,从而防止类装入器被垃圾收集。此引用可能在业务代码、第三方库、应用程序服务器或JVM中。

解决方案

正常情况下的解决方案也很简单:

  1. 如果应用程序耗尽内存中的元空间区域,评估后重新加大元空间的配置即可。如将128M改为256M。
  2. 还可以删除MaxMetaspaceSizes配置参数,完全取消对元空间大小的限制,但这个不解决根本问题,只是临时取消了OOM的出现,留出一定的时间来检查和定位问题。
  3. 如果是-XX:SoftRefLRUPolicyMSPerMB导致的,则将其设为1000(1s)即可。

工具

  1. Arthas诊断工具(Java在线诊断利器之Arthas)
  2. 原始的 jmap和jhat
  3. MAT(内存分析器工具)

2.JVM参数设置

JVM各参数值大小的设置,根据应用程序的内存大小进行比例设置,针对个别具体的值,可微调,也可默认。

堆设置

-Xms 初始堆大小
-Xmx 最大堆大小
-Xmn 年轻代大小
-Xss 每个线程的堆栈大小
-XX:NewRatio=n 年轻代和年老代的比值。
-XX:SurvivorRatio=n 年轻代中Eden区与两个Survivor区的比值。
-XX:MaxPermSize=n 持久代大小
-XX:MaxTenuringThreshold=0 垃圾最大年龄

java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0

收集器设置

-XX:+UseSerialGC 串行收集器
-XX:+UseParallelGC 并行收集器
-XX:ParallelGCThreads 并行收集器收集时使用的CPU数。并行收集线程数
-XX:+UseParalledlOldGC 并行年老代收集器
-XX:MaxGCPauseMillis 每次年轻代垃圾回收的最长时间
-XX:+UseAdaptiveSizePolicy 并行收集器调优
-XX:+UseConcMarkSweepGC 年老代为并发收集,与-XX:NewRatio不同时设置
-XX:+UseParNewGC 年轻代为并行收集,JVM自行设置,无需配置。

-XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC -XX:MaxGCPauseMillis=100-XX:+UseAdaptiveSizePolicys -XX:+UseConcMarkSweepGC

垃圾回收统计信息

命令行参数,一些打印信息,调试时使用
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:filename

上一篇:metaspace概述(与compressedClassPointer的关系)


下一篇:Linux网络编程--socket