JVM虚拟机一:内存划分和溢出

JVM虚拟机一:内存划分和溢出

JVM内存划分

JVM把内存划分为以下几个主要的区域:
Method Area、VM Stack、Native Method Stack、Heap、Program Counter Register
 
方法区:是所有线程共享的内存区域,用于存储JVM加载的类信息、常量、静态变量、JIT的代码等数据;同时为了和堆区分,方法区也叫非堆(Non-Heap)。

方法区内部还包含一块叫“运行时常量区”,该区域主要存储类的各种字面量和符号引用等数据。

堆:是所有线程共享的内存区域,一般这是JVM中占用内存最大的区域,几乎所有的对象实例都存储在这个区域(像JIT的代码不在这个区域),该区域也是垃圾回收的主要区域,因此也叫GC堆。

虚拟机栈:是线程私有的,主要存储方法的内存模型,比如方法中的局部变量、操作数、方法出口等,当调用一个方法时,整个过程都对应到栈的IO操作,这个区域也就是平时所说的 栈。

本地方法栈:和虚拟机栈基本一样,区别就是调用本地方法,虚拟机栈是调用JAVA方法。

程序计数器:是线程私有的,主要是用来存储当前线程执行指令地址的,这样当多线程来回调度时,知道上次本线程执行到哪个指令。

JVM内存溢出

JVM内存溢出常见两种:栈溢出、内存溢出,JVM每个区域都有可能发生溢出。

1. 堆溢出:

当不断的创建对象,并且对象无法被回收时,容易发生溢出。

// 一个栗子
public class HeapTest {
    public static void main(String[] args) {

        List<OOMTemp> list=new ArrayList<>();
        AtomicInteger count=new AtomicInteger(0);

        Thread.setDefaultUncaughtExceptionHandler((t,e)->{
            System.out.println(count);
            System.out.println(e.getMessage());
        });

        // 810325
        while (true){
            list.add(new OOMTemp());
            count.incrementAndGet();
        }
    }

    static class OOMTemp{    }
}

在执行之前,首先设置堆内存大小:-Xms20m -Xmx20m,其中-xms是堆的最小内存,-xmx是最大内存,都设置为20m

这样会输出(第二行开始显示创建了dump文件,是为了分析为什么溢出,在执行前加上opts:-XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:/):

java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid4612.hprof …
Heap dump file created [42076663 bytes in 0.208 secs]
1215487
Java heap space

Process finished with exit code 1

通过dump文件可以看出来是OOMTemp实力问题:

2.栈溢出

在单线程环境下,不太容易模拟OOM,但是SOF还是可以的,直接不停的递归调用一个方法,方法会不断的执行压栈操作,即会发生StackOverflowError。

在执行之前,还是首先配置内存大小:-Xss1m,发生SOF的原因就是栈被打满,请求地址已经超出栈的最大地址。

如果想模拟OOM,可以采用多线程方式,并且每个线程的栈内存越大,越容易发生OOM。

每个进程的内存是有限的,比如32位window最大2GBit,那么 剩余内存 = 2GBit – 堆内存(xmx) – 方法区内存(maxpermsize),栈内存只能在剩余内存中分割,所以每个线程中栈内存越大,线程多了就会OOM。

 

hmoban主题是根据ripro二开的主题,极致后台体验,无插件,集成会员系统
自学咖网 » JVM虚拟机一:内存划分和溢出