JAVA系列之JVM内存调优( 三 )


四、内存溢出排查一般来说内存溢出主要分为以下几类:

堆溢出(java.lang.OutOfMemoryError: Java heap space)栈深度不够( java.lang.StackOverflowError)栈线程数不够(java.lang.OutOfMemoryError: unable to create new native thread)元空间溢出(java.lang.OutOfMemoryError: Metaspace)
JAVA系列之JVM内存调优

文章插图
1、元空间溢出(java.lang.OutOfMemoryError: Metaspace)Metaspace元空间主要是存储类的元数据信息 , 各种类描述信息 , 比如类名、属性、方法、访问限制等 , 按照一定的结构存储在Metaspace里 。一般来说 , 元空间大小是固定不变的 。在出现溢出后 , 首先通过命令或监控工具(如下图)查看元空间大小 , 再检查是否-XX:MaxMetaspaceSize配置太小导致 。
JAVA系列之JVM内存调优

文章插图
JAVA系列之JVM内存调优

文章插图
如果发现元空间大小是持续上涨的 , 则需要检查代码是否存在大量的反射类加载、动态代理生成的类加载等导致 。可以通过-XX:+TraceClassLoading -XX:+TraceClassUnloading记录下类的加载和卸载情况 , 反推具体问题代码 。
JAVA系列之JVM内存调优

文章插图
2、栈深度不够(java.lang.StackOverflowError)引发StackOverFlowError的常见原因有:
  • 无限循环递归调用
  • 同一时间执行大量方法 , 资源耗尽
  • 方法中声明大量局部变量
  • 其它消耗栈资源的方法
  • xss配置太小导致
/** * VM Args: -Xss128k */public class JavaStackSOF {private int stackLength = 1;public void stackLeak() {stackLength++;stackLeak();}public static void main(String[] args) {JavaStackSOF oom = new JavaStackSOF();try{oom.stackLeak();}catch(Throwable e) {System.out.println("stack length:" + oom.stackLength);throw e;}}}stack length:2101Exception in thread "main" java.lang.StackOverflowErrorat com.sandy.jvm.chapter02.JavaStackSOF.stackLeak(JavaStackSOF.java:13)at com.sandy.jvm.chapter02.JavaStackSOF.stackLeak(JavaStackSOF.java:14)at com.sandy.jvm.chapter02.JavaStackSOF.stackLeak(JavaStackSOF.java:14)3、栈线程数不够(java.lang.OutOfMemoryError: unable to create new native thread)这类错误目前在生成系统只遇到过一次 , 原因是:linux系统中非root用户默认创建线程数最多是1024 。解决办法是修改文件:/etc/security/limits.d/90-nproc.conf还有一种情况是-xss配置太大 , 那么操作系统可创建的最大线程数太小导致 , 一般除非误操作是不会出现此问题的 。
4、堆溢出(java.lang.OutOfMemoryError: Java heap space)堆溢出是常见也是最复杂的一种情况 。导致堆溢出可能的情况有:
  • 堆内存配置太小
  • 超出预期的访问量:访问量飙升
  • 超出预期的数据量:系统中是否存在一次性提取大量数据到内存的代码
  • 内存泄漏
解决思路一般是:
一、堆dump文件获取1、通过参数配置自动获取dump文件(推荐)2、jmap -dump:format=b,file=filename.hprof pid二、MAT工具分析1、分析大对象、堆中存储信息、可能存在的内存泄漏地方 , 便于定位问题位置
五、JVM监控常用的监控工具或命令有:jstack、jstat、jConsole、jvisualvm 。监控指标主要是各内存区域大小是否合理、fullGC频率及耗时、youngGC耗时、线程数等 。
1、jstackjstack主要用于打印线程堆栈信息 , 帮助问题的定位 。一般配合top -Hp PID使用 。
通过top命令发现某个java服务占用1234%的CPU , 如图:

经验总结扩展阅读