• 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左右。
      线程栈的大小是个双刃剑,如果设置过小,可能会出现栈溢出,特别是在该线程内有递归、大的循环时出现溢出的可能性更大,如果该值设置过大,就有影响到创建栈的数量,如果是多线程的应用,就会出现内存溢出的错误。
  • 相关阅读:
    4年Java程序员十面阿里终拿下offer,评级P6+年薪30-40w无股票
    真香警告!手绘172张图解HTTP协议+703页TCP/IP协议笔记
    Git官方和创始人都推荐的Git权威指南,广度深度和实战性史无前例
    阿里“教授”总结整理手写大型网站技术架构:核心原理与案例分析
    GitHub上120K Stars国内第一的Java多线程PDF到底有什么魅力?
    霸榜GitHub必读书籍:编写高质量代码改善Java程序员的151个建议
    GitHub上260K Stars的P8架构师纯手写的Java高并发编程详解
    LeetCode每日一题:802 找到最终安全状态
    LeetCode每日一题:662二叉树最大宽度
    Springboot之security框架 登录安全验证授权流程
  • 原文地址:https://www.cnblogs.com/talkingcat/p/13283464.html
Copyright © 2020-2023  润新知