JVM实战04(方法区内存溢出)

2021/02/22 Jvm

JVM实战04(方法区内存溢出)

目录

一些概念

1.堆

JDK8和之后的版本,持久代取消变为元空间,
元空间是本地内存。

2.方法区

1.线程共享,存储虚拟机加载的类型信息、常量、静态变量等。

2.jdk8开始方法区成为一个逻辑上的概念,如上图:
  静态变量和字符串常量池存放在堆中,
  而运行时常量池以及一些类信息存放在元空间。

  方法区由堆和元空间一起组成。

实战

public static void main(String[] args)
{
    Set<String> set = new HashSet<>();
    int i = 0;
    while (true)
    {
        //intern():
        // 1.是一个本地方法
        // 2.如果字符串常量池里面已经包含了等于字符串X的字符串,
        //   那么就返回常量池这个字符串的引用。
        //   如果常量池中不存在,则会把当前字符串添加到常量池,
        //   并返回引用。
        set.add(String.valueOf(i++).intern());
    }

}

设置永久代参数: -XX:PermSize=6m -XX:MaxPermSize=6m

jDK 6:
  报永久代溢出(java.lang.OutOfMemoryError:PermGen space)

jDK 7+:
  7以后的版本,不报错,原因是把字符串常量放到了堆,设置-Xmx堆大小参数才会
  报堆内存溢出。
public class OOMTest
{
    private int[] arr = new int[200];

    private String name = "zhangShan";

    public static void main(String[] args)
    {
        List<Object> list = new ArrayList<>();
        while (true)
        {
            list.add(new OOMTest());
        }
    }
}


JDK1.8:
  1.设置元空间的初始大小和最大大小:-XX:MetaspaceSize=1m -XX:MaxMetaspaceSize=1m
  2.运行时报:
           OutOfMemoryError: Metaspace    //元空间溢出

    原因是因为:
      a.我们结合上面方法区的图可以看到,方法区是一个逻辑上的概念,它横跨堆和元空间。
        我们看到类信息是放到方法区的并且实际属于元空间内存。所以,我们不停的new OOMTest类,
        是添加到元空间的。
      b.本来元空间是本地内存,只要本地内存够大是不会报元空间内存溢出。但是由于我们参数设置了元空间
      最大为1m,所以运行实例代码很快就会报元空间内存溢出。

方法区溢出的场景

1.常量池里对象太大。

2.加载的类的“种类”太多(因为类是存放方法区 ps:实际是元空间那部分):
    .动态代理的操作库生产了大量的动态类
    .JSP项目,JSP太多,因为JSP是第一次被访问的时候才会编译成java类。极端
     情况下,编译项目时没有问题,但请求JSP页面导致类加载过多造成方法区溢出。
    .脚本语言动态加载

避免方法区溢出

1.根据JDK版本,为常量池保留足够空间(主要是字符串常量池):
  JDK 6:     设置合理的永久代参数 -XX:PermSize -XX:MaxPermSize。
  >=JDK 7:   设置合理的堆内存大小Xms、Xmx。

2.防止类加载过多(JD8类信息存放到了元空间):
  <=JDK 7:     设置合理的永久代参数。
  >=JDK 8:     不配置元空间相关配置(JVM动态分配),或配置合理大小的元空间。

Search

    Table of Contents