zoukankan      html  css  js  c++  java
  • JVM内存模型深度解析

    一、JVM整体结构和内存模型

    上图示例中的代码:

    public class Math {
        public static int initData = 666;
        public static User user = new User();
        
        public int compute() {
            int a = 1;
            int b = 2;
            int c = (a + b) * 10;
            return c;
        }
        
        public static void main(String[] args) {
            Math math = new Math();
            math.compute();
            System.out.println("test");
        }
    }

            通过类加载器将类(类元信息)加载到方法区中,compute() 方法也被加载到方法区中;java代码在执行 math.compute() 的时候,通过 math 对象的对象头中的头指针找到存储在方法区中的 compute() 方法的指令码的入口地址,然后将这个入口地址放到栈中的动态链接内存区域中; 

    (1)类装载子系统,将class文件经过加载、验证、准备、解析、初始化后生产类元信息放入到方法区中(类装载子系统主要将类装载到方法区);常量池是在方法区中的;  

    (2)栈里面的变量引用指向堆中的对象;基本类型的局部变量放到对应的栈中,对象放到堆中

    (3)对象(在堆中)的对象头中有一个指针,指向的是这个对象所属类的类元信息(方法区中);

    (4)静态变量是存在方法区中的,其引用指向堆中的对象;

    方法区:线程共享的,通常用来储存装载的类的元结构信息。垃圾回收很少发生;

      比如:运行时常量池 + 静态变量 + 常量 + 字段 + 方法字节码 + 在类/实例/接口初始化用到的特殊方法等。方法区是一个抽象的概念,它的实现是永久区(Java7) 或 元空间(Java8,使用的是物理内存,不是JVM的内存)

    本地方法栈:线程有的,调用本地方法的时候分配的内存;

    程序计数器:线程私有的,用来存储指向下一条指令的地址,由执行引擎读取下一条指令;

    :线程私有的,主管Java程序的运行,栈是在线程创建的时候创建;存放基本类型的变量、实例方法、引用类型变量都是在函数的栈内存中分配

    二、JVM内存参数设置

     JVM调优:尽可能的让对象不要放入到老年代去,那么就要减少 Minor GC 垃圾回收的次数,则新生代的内存大小要设置的大点。

    minor GC:回收未有引用的垃圾对象;

    (1)  当new 一个对象,该对象被放入伊甸区(Eden),创建的对象越来越多,伊甸区(Eden)快满的时候启动一种轻垃圾回收(Minor GC),未被回收的对象被放入幸存0区(Survivor 0),Eden被清空;

    (2)继续 new 对象,当伊甸园区再次快放满的时候,启动垃圾回收机制(Minor GC)去清理 伊甸园区 和 幸存0区,将这两个区域的垃圾对象回收完之后,将它们未被回收的对象一起放入到幸存1区(Survivor 1),Survivor 0 和 Eden 被清空;

    (3)继续会 new 对象,当伊甸园区再次快满的时候,启动垃圾回收机制(Minor GC)去清理 伊甸园区 和 幸存1区将这两个区域的垃圾对象回收完之后,将它们未被回收的对象一起放入到幸存0区(Survivor 0),Survivor 1 和 Eden 被清空;

    (4)如此循环往复。对象的对象头中有个叫分代年龄的存储信息(默认是15),当这个对象的分代年龄达到了15了,还没有被垃圾机制回收,则会将这些对象放入到老年代;幸存区放慢也会进行垃圾回收然后放入到老年代;

    (5)当老年代快满的时候触发一个重量级的GC(Full GC),清理未被引用的任何对象,清理之后还是无法再保存对象,就会产生OOM异常(OutOfMemoryError)。

     不管哪种垃圾回收机制都会去调用 STW,去停止所有线程的继续执行;

     栈内存溢出(StackOverflowError)示例

    public class StackOverflowTest {
        static int count = 0;
    
        static void redo() {
            count++;
            redo();
        }
        
        public static void main(String[] args) {
            try {
                redo();
            } catch (Throwable t) {
                t.printStackTrace();
                System.out.println(count);
            }
        }
    }

    运行结果:

    java.lang.StackOverflowError
       at com.tuling.jvm.StackOverflowTest.redo(StackOverflowTest.java:12)
       at com.tuling.jvm.StackOverflowTest.redo(StackOverflowTest.java:13)
       at com.tuling.jvm.StackOverflowTest.redo(StackOverflowTest.java:13)

    结论:
    -Xss设置越小count值越小,说明一个线程栈里能分配的栈帧就越少,但是对JVM整体来说能开启的线程数会更多;

     Spring Boot程序的JVM参数设置格式(Tomcat启动直接加在bin目录下catalina.sh文件里)

    java ‐Xms2048M ‐Xmx2048M ‐Xmn1024M ‐Xss512K ‐XX:MetaspaceSize=256M ‐XX:MaxMetaspaceSize=256M ‐jar microservice‐eureka‐server.jar

    三、逃逸分析

    JVM的运行模式有三种:

    • 解释模式 ( Interpreted Mode):只使用解释器(-Xint强制JVM使用解释模式),执行一行JVM字节码就编译一行为机器码;
    • 编译模式 (Compiled Mode):只使用编译器(-XcompJVM使用编译模式),先将所有JVM字节码一次编译为机器码,然后一次性执行所有机器码;
    • 混合模式(Mixed Mode):依然使用解释模式执行代码,但是对于一些 "热点" 代码采用编译模式执行,JVM一般采用混合模式执行代码;

    解释模式启动快,对于只需要执行部分代码,并且大多数代码只会执行一次的情况比较适合;编译模式启动慢,但是后期执行速度快,而且比较占用内存,因为机器码的数量至少是JVM字节码的十倍以上,这种模式适合代码可能会被反复执行的场景;混合模式是JVM默认采用的执行代码方式,一开始还是解释执行,但是对于少部分 “热点 ”代码会采用编译模式执行,这些热点代码对应的机器码会被缓存起来,下次再执行无需再编译,这就是我们常见的JIT(Just In Time Compiler)即时编译技术

    在即时编译过程中JVM可能会对我们的代码最一些优化,比如对象逃逸分析等。

     对象逃逸分析:就是分析对象动态作用域,当一个对象在方法中被定义后,它可能被外部方法所引用,例如作为调用参数传递到其他地方中; 

    public User test1() {
        User user = new User();
        user.setId(1);
        user.setName("zhuge");
        //TODO 保存到数据库
        return user;
    }
    
    public void test2() {
        User user = new User();
        user.setId(1);
        user.setName("zhuge");
        //TODO 保存到数据库
    }

            test1方法中的user对象被返回了,这个对象的作用域范围不确定,test2方法中的user对象我们可以确定当方法结束这个对象就可以认为是无效对象了,对于这样的对象我们其实可以将其分配的栈内存,让其在方法结束时跟随栈内存一起被回收掉。
    JVM对于这种情况可以通过开启逃逸分析参数(-XX:+DoEscapeAnalysis)来优化对象内存分配位置,JDK7之后默认开启逃逸分析,如果要关闭使用参数(-XX:-DoEscapeAnalysis)。



  • 相关阅读:
    select2插件用法
    MVC htmlAttributes and additionalViewData
    Log4Net 配置详解
    Windows10搭建ASP.NET MVC5 IIS局域网(外网可以访问的)服务器
    网络连接状态指示器(NCSI ,Network Connectivity Status Indicator)
    微软官方开发文档结构WPF
    旋转编码器,无限旋转的音量按钮
    Steam Dota2 安装方法以及一些设置·
    Windows资源管理器打开文件夹卡顿原因及解决办法
    字幕变集合制作工具
  • 原文地址:https://www.cnblogs.com/yufeng218/p/12865831.html
Copyright © 2011-2022 走看看