JVM 学习笔记(四)

回顾:

  在之前的文章中,我们主要体现了当堆内存设置的比较小的情况下,比如:-Xmx20M -Xms20M,在项目运行的过程中,不断往内存中去添加对象,

这时候就会出现OOM,也就是内存溢出,本文章将展示方法区和虚拟机栈内存溢出的情况。

方法区内存溢出:

  为了使方法区内存溢出,我们将JVM的参数调整为:-XX:MetaspaceSize=50M -XX:MaxMetaspaceSize=50M。然后不断的往方法区中添加class信息,

前面我们介绍了方法区的作用是:用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

代码如下:

//pom.xml中添加如下依赖
<dependency> 
    <groupId>asm</groupId> 
    <artifactId>asm</artifactId> 
    <version>3.3.1</version> 
</dependency>


public class MyMetaspace extends ClassLoader {
    public static List<Class<?>> createClasses() {
        List<Class<?>> classes = new ArrayList<Class<?>>();
        for (int i = 0; i < 10000000; ++i) {
            ClassWriter cw = new ClassWriter(0);
            cw.visit(Opcodes.V1_1, Opcodes.ACC_PUBLIC, "Class" + i, null, "java/lang/Object", null);
            MethodVisitor mw = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
            mw.visitVarInsn(Opcodes.ALOAD, 0);
            mw.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
            mw.visitInsn(Opcodes.RETURN);
            mw.visitMaxs(1, 1);
            mw.visitEnd();
            Metaspace test = new Metaspace();
            byte[] code = cw.toByteArray();
            Class<?> exampleClass = test.defineClass("Class" + i, code, 0, code.length);
            classes.add(exampleClass);
        }
        return classes;
    }
}

  执行代码,果然会包方法区内存溢出:

 虚拟机栈内存溢出:

  前面我们介绍虚拟机栈是一个线程执行的区域,保存着一个线程中方法的调用状态。换句话说,一个Java线程的运行状态,由一个虚拟机栈来保存,所以虚拟机栈肯定是线程私有的,独有的,随着线程的创建而创建。每一个被线程执行的方法,为该栈中的栈帧,即每个方法对应一个栈帧。调用一个方法,就会向栈中压入一个栈帧;一个方法调用完成,就会把该栈帧从栈中弹出。

  代码演示:

public class StackDemo {
    public static long count = 0;

    public static void method(long i) {
        System.out.println(count++);
        method(i);
    }

    public static void main(String[] args) {
        method(1);
    }
}

  结果:

  理解和说明:

  Stack Space用来做方法的递归调用时压入Stack Frame(栈帧)。所以当递归调用太深的时候,就有可能耗尽Stack Space,爆出StackOverflow的错误。-Xss128k:设置每个线程的堆栈大小。JDK 5以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。根据应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。
  线程栈的大小是个双刃剑,如果设置过小,可能会出现栈溢出,特别是在该线程内有递归、大的循环时出现溢出的可能性更大,如果该值设置过大,就有影响到创建栈的数量,如果是多线程的应用,就会出现内存溢出的错误。
原文地址:https://www.cnblogs.com/talkingcat/p/13283464.html