JVM(五)

2021-09-02 10:08:58 浏览数 (1)

什么是内存溢出,那些区域会发生内存溢出

首先我们要知道java是如何运行一个JVM进程,如下代码

代码语言:javascript复制
public class Demo3 {
public static void main(String[] args) {
        String message = "hello world";
        System.out.println(message);
    }
}
  1. 首先是写好的代码编译成class文件
  1. 执行java命令
  1. jVM加载你写的类
  1. main线程执行方法
  1. 局部变量进入虚拟机栈
  1. 堆内存创建对象

如上几步,能发生OOM地方

  • metaspace区域用来存放类的信息,如果加载的类信息太多就可能导致OOM
  • 每个线程都有个虚拟机栈,就是所谓的栈内存,这里存放的是方法的里面的局部变量,栈内存大小一般是1m,不断的循环调用方法,就可能导致OOM
  • 堆内存,放一些对象,不断的创建对象,也是可能发生OOM

Metaspace如何触发内存溢出呢

我们用下面参数设置Metaspace内存大小

代码语言:javascript复制
-XX:MetaspaceSize=512m
-XX:MaxMetaspaceSize=512m

固定512M,当Metaspace满了之后,就会触发FULL GC,回收的条件也比较苛刻,如这个类加载器被回收,这个类的所有对象实例都被回收等等,所以一旦Metaspace满了,未必会回收里面的很多类,一旦回收之后,还是有很多存活的类,如果继续想Metaspace加入更多的类信息,就会导致OOM

导致Metaspace的一般原因如下

  • Metaspace的值太小,只有几十MB,(一般系统512MB)当对于一个稍微大型的系统,由于他有许多的类信心,就可能导致Metaspace不够用
  • 有很多系统用cgLIB之类的技术动态生成一些类,一旦代码没有控制住,就会创建过多的类,容易把Metaspace给塞满,进而引发内存溢出

虚拟机栈溢出

我们知道一个线程的虚拟机栈的内存大小是固定的,一般默认是1MB,正如我们之前讲的main()方法,就会产生一个main的栈帧,如下代码

代码语言:javascript复制
public class Demo3 {
public static void main(String[] args) {
        String message = "hello world";
        System.out.println(message);
        sayHello("我是肉丝");
    }    
public static  void sayHello(String name){
        System.out.println("你好,杰克" name);
    }
}

每一个方法的调用,都会在虚拟机栈中创建一个栈帧,保存对应的局部变量,但是此时我们要注意的是每一个帧栈也是要占内存的,虽然一些变量和其他的对象数据占不了太大的内存,但是实际上也是要占用的,

如果一个线程不断的调用各种方法,不停的把方法的栈帧压入虚拟机栈,就会不断的占用这个线程1MB的栈内存,最终会导致栈内存溢出

什么情况下会发生栈内存溢出呢

代码语言:javascript复制
public static  void sayHello(String name){
        sayHello(name);
    }

如上代码,出现这种不断递归调用,就有可能导致栈内存溢出,一般来说不会导致栈内存溢出,除非你的bug才会导致

堆内存溢出

  1. 首先平时我们系统不断创建对象,然后大量的对象进入Eden区,一旦Eden区满之后,就会发生一次YGC,然后存活对象进入S区
  1. 一旦高并发场景,ygc后很多请求还没有处理完毕,存活对象很多,S区,放不下,就会进入老年代
  1. 一旦老年代满了,就会发生FULL gc
  1. 不幸的是老年代GC之后,还是有很多对象存活,此时年轻代不断不断GC,把存活的对象转移到老年代,但是老年代也空间不足了

最终就会由于堆内存实在放不下对象,导致内存溢出,JVM崩溃

一般什么场景会导致堆内存溢出呢

  • 系统承载高并发,因为请求量过大,导致大量对象存活,所以要继续放入新的对象实在不行了,就会引起OOM
  • 系统内存泄露,就莫名其妙弄很多对象,结果对象都是存活的,没有及时取消他的引用,就会导致GC无法回收,引发内存泄漏最终OOM

0 人点赞