HotSpot VM内存溢出分析概述(详)-JAVA

前言

  如果您觉得有用的话,记得给博主点个赞,评论,收藏一键三连啊,写作不易啊^ _ ^。
  而且听说点赞的人每天的运气都不会太差,实在白嫖的话,那欢迎常来啊!!!


HotSpot VM内存溢出分析概述(详)-JAVA

扩展一下VM中对象的创建:
以 HotSpot VM 举例:
在这里插入图片描述


一、VM辅助工具

1、工具: eclipse,JDK1.8

eclipse 配置 VM 设置
1)、Run->Debug configurations->Java Application 双击

在这里插入图片描述
2)、Arguments->VM arguments 对虚拟机的内存参数进行设置
参数例子:
-Xmx2018m -Xms2018m -Xmn2g -Xss128k
设置堆的大小为2018M ,不可扩展,将堆的最小值和最大值设置成一样即可避免堆自动扩展

-Xmn2g:设置年轻代大小为2G。整个JVM内存大小=年轻代大小 + 年老代大小 + 永久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8

年轻代:存储新生的对象
年老代:用于存放新生代中经过多次垃圾回收仍然存活的对象
永久代(HotSpot VM): 永久代的参数-XX:PermSize和-XX:MaxPermSize(1.7之前),1.8以后HotSpot VM取消了永久代,该参数也随之消失

在Java8中,元空间(Metaspace)取代永久代,方法区存在于元空间(Metaspace)。同时,元空间不再与堆连续,而且是存在于本地内存(Native memory)

默认情况下元空间是可以无限使用本地内存的,但为了不让它如此膨胀,JVM同样提供了参数来限制它使用的使用。
-XX:MetaspaceSize,class metadata的初始空间配额,以bytes为单位,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当的降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize(如果设置了的话),适当的提高该值。
-XX:MaxMetaspaceSize,可以为class metadata分配的最大空间。默认是没有限制的。
-XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩余空间容量的百分比,减少为class metadata分配空间导致的垃圾收集。
-XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩余空间容量的百分比,减少为class metadata释放空间导致的垃圾收集。

-Xss128k:设置每个线程的堆栈大小 ,在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成


2、 分析VM堆转储快照文件 – jhat

jhat是jdk内置的工具之一,jhat内置了一个微型的HTTP/Web 服务器,生成堆转储快照的分析结果
启动例子:

jhat D:\work\text\java_pid18348.hprof

java_pid18348.hprof是堆转储快照文件。
注: 有时dump出来的堆很大,在启动时会报堆空间不足的错误,可加参数:jhat -J-Xmx512m 。
是堆转储快照文件的路径。

启动成功显示:
在这里插入图片描述
启动样子:
在这里插入图片描述

注:

在这里插入图片描述
模块功能:

序号 模块 注释
1 All Classes (including platform) 显示出堆中所包含的所有的类
2 All Members of the Rootset 从根集能引用到的对象
3 Instance Counts for All Classes (including platform) 显示平台包括的所有类的实例数量
4 Heap Histogram 堆实例的分布表
5 Object Query Language (OQL) query 执行对象查询语句

二、内存溢出分析

1.java堆溢出( OutOfMemoryError)

VM 参数设置 :

-verbose:gc -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:SurvivorRatio=8

-XX:+HeapDumpOnOutOfMemoryError 可以让VM 出现内存溢出异常的时候Dump出当前的内存堆转储快照,便于分析:
-XX:+PrintGCDetails 开启GC日志输出
-XX:SurvivorRatio=8 设置新生代中Eden区与Survivor区的大小比值, 设置为8则两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10,SurvivorRatio默认为8。

限制堆的大小,来运行下面代码示例。
代码示例:

import java.util.ArrayList;
import java.util.List;
/**
 * -verbose:gc -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:SurvivorRatio=8
 * */
public class OutErrorTest {
    
    
      static class Test{
    
    
            
      }
      public static void main(String[] args) {
    
    
            List<Test> list = new ArrayList<>();
            while(true) {
    
    
                  list.add(new Test());
            }
      }
}

运行结果:
在这里插入图片描述
堆转储快照分析:

在这里插入图片描述


2.虚拟机栈和本地方法栈溢出( StackOverflowError)

前提:
虚拟机HotSpot
问题叙述:
当栈容量无法容纳新的栈帧时 抛出StackOverflowError
有可能出现的情况:

2.1当存放对象实例所需要内存大于虚拟机启动时申请的栈内存时抛出异常,即由于 【VM 栈 容量太小】,当新的栈帧内存无法分配时候,抛出异常

VM 参数设置 :
verbose:gc -Xms1024m -Xmx1024m -Xss100k -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:SurvivorRatio=8

代码示例:

/**
 * -verbose:gc -Xms1024m -Xmx1024m -Xss100k
 * -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:SurvivorRatio=8
 * */
public class StackOutTestOne {
    
    
      private int index = 1;
      public void getStack() {
    
    
            index++;
            getStack();
      }
      public static void main(String[] args) {
    
    
            StackOutTestOne i = new StackOutTestOne();
            try {
    
    
                  i.getStack();
            } catch (Throwable e) {
    
    
                  System.out.println("===============================stack lenght :" +i.index);
                  throw e;
            }
      }
}

结果:

在这里插入图片描述

2.2、当定义了大量的本地变量,超出定义【此方法帧】中本地变量表的长度时抛出异常,即由于【栈帧太大】,当新的栈帧内存无法分配时候,抛出异常

VM 参数设置 :
verbose:gc -Xms1024m -Xmx1024m -Xss100k -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:SurvivorRatio=8

代码示例:

/**
 * -verbose:gc -Xms1024m -Xmx1024m -Xss100k
 * -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:SurvivorRatio=8
 * */
public class StackOutTestTwo {
    
    
      private static int index = 0;
      public static void getStack() {
    
    
            @SuppressWarnings("unused")
            long a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a30,
            a31,a32,a33,a34,a35,a36,a37,a38,a39,a40,a41,a42,a43,a44,a45,a46,a47,a48,a49,a50,a51,a52,a53,a54,a55,a56,a57,a58,a59,a60,
            a61,a62,a63,a64,a65,a66,a67,a68,a69,a70,a71,a72,a73,a74,a75,a76,a77,a78,a79,a80,a81,a82,a83,a84,a85,a86,a87,a88,a89,a90=10000L;
            index++;
            getStack() ;
      
      }
      public static void main(String[] args) {
    
    
            try {
    
    
                  getStack();
            } catch (Throwable e) {
    
    
                  System.out.println("===============================stack lenght :" +index);
                  throw e;
            }
      }
}

结果:
在这里插入图片描述


3、方法区溢出(java.lang.OutOfMemoryError: Metaspace)

jdk 1.8 永久代退出历史舞台 元空间作为替代者上场

溢出原因:

方法区的主要职责是存放类型的相关信息,如:类名,访问修饰符、常量池、字段描述、方法描述等。
特殊: 字符串常量池已经移到java堆中。
溢出的原因:运行时产生大量的类填充方法区,并超过方法区的允许上线,导致方法区溢出。

VM 参数设置 :
-verbose:gc -Xms1024m -Xmx1024m -Xss100k -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:MetaspaceSize=5m -XX:MaxMetaspaceSize=5m

XX:MetaspaceSize 指定元空间初始空间大小,以字节为单位,达到该值触发GC 进行类型卸载。【同时收集器会对该值进行修改】
调整规则: 如果释放大量的空间,适当降低该值,如果释放了很少的空间,那么在不超过XX:MaxMetaspaceSize(设置时)的情况下,适当提高该值。

XX:MaxMetaspaceSize 设置元空间最大值,默认为-1,即不限制,或者只受限于本地内存大小

XX:MinMetaspaceFreeRatio 作用是在垃圾收集之后控制最小的元空间剩余容量的百分比,可减少因为元空间不足导致的垃圾收集频率。

XX:Max-MetaspaceFreeRatio 用于控制最大的元空间剩余容量百分比
代码示例:
利用CGLib 字节码技术 生成大量动态类 填充方法区

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
 * jdk 1.8 永久代退出历史舞台  元空间作为替代者上场
 * -verbose:gc -Xms1024m -Xmx1024m -Xss100k -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails
 * -XX:SurvivorRatio=8 -XX:MetaspaceSize=5m -XX:MaxMetaspaceSize=5m
 * */
public class MethodPernGenError {
    
    
      static class Test{
    
    }
      /**
       * 利用CGLib 字节码技术 生成大量动态类 填充方法区
       * */
      public static void main(String[] args) {
    
    
            while(true) {
    
    
                  Enhancer e =  new Enhancer();
                  e.setSuperclass(Test.class);
                  e.setUseCache(false);
                  e.setCallback(new MethodInterceptor() {
    
             
                        @Override
                        public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy proxy) throws Throwable {
    
    
                              // TODO Auto-generated method stub
                              return proxy.invokeSuper(arg0, arg2);
                        }
                  });
                  e.create();
            }
      }
}

结果:
在这里插入图片描述


4、本机直接内存溢出(OutOfMemoryError)

VM 参数设置 :

-verbose:gc -Xms1024m -Xmx1024m -Xss100k -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:MetaspaceSize=1024m -XX:MaxMetaspaceSize=1024m -XX:MaxDirectMemorySize=5m

-XX:MaxDirectMemorySize 直接内存的容量大小通过-XX:MaxDirectMemorySize 设定,如果不设定,
默认为堆最大值(-Xmx设置)指定一致

代码示例:

import java.lang.reflect.Field;
import sun.misc.Unsafe;
/**
 * -verbose:gc -Xms1024m -Xmx1024m -Xss100k -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:SurvivorRatio=8
 * -XX:MetaspaceSize=1024m -XX:MaxMetaspaceSize=1024m  -XX:MaxDirectMemorySize=5m
 * */
public class DirectMemoryError {
    
    
      private static final int X = 1027 * 1024;
      public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
    
    
            Field u = Unsafe.class.getDeclaredFields()[0];
            u.setAccessible(true);
            Unsafe unsafe = (Unsafe)u.get(null);
            while(true) {
    
    
                  unsafe.allocateMemory(X);
            }
      }
}

结果:

在这里插入图片描述

以上就是我本次的分享,谢谢观看!!!

猜你喜欢

转载自blog.csdn.net/weixin_38316697/article/details/109162807