• 图灵学院笔记-java虚拟机底层原理


    Table of Contents generated with DocToc

    笔记来源:图灵学院

    一、java虚拟机概述

    1. java虚拟机用于将字节码文件.class转化为不同操作系统上的机器码

    1. java虚拟机的内存模型


    二、栈内存解析

    2.1 概述

    java虚拟机为每一个java线程分配一个栈内存空间,存放变量

    public class Math{
        public static final int initData = 666;//常量存储在方法区
        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();
        }
    }
    

    a,b,c 保存在main线程的compute()栈帧中

    2.2 栈帧内部结构

    学习栈帧内部结构前,我们首先要阅读字节码文件。

    javac Math.java
    javap -c Math.class > Math.txt
    

    打开生成的Math.txt,可以看到反汇编后的代码如下:

    Compiled from "Math.java"
    public class jvm.Math {
      public static final int initData;
    
      public jvm.Math();
        Code:
           0: aload_0
           1: invokespecial #12            // Method java/lang/Object."<init>":()V
           4: return
    
      public int compute();
        Code:
           0: iconst_1
           1: istore_1
           2: iconst_2
           3: istore_2
           4: iload_1
           5: iload_2
           6: iadd
           7: bipush        10
           9: imul
          10: istore_3
          11: iload_3
          12: ireturn
    
      public static void main(java.lang.String[]);
        Code:
           0: new           #1                  // class jvm/Math
           3: dup
           4: invokespecial #25                 // Method "<init>":()V
           7: astore_1
           8: aload_1
           9: invokevirtual #26                 // Method compute:()I
          12: pop
          13: return
    }
    
    2.2.1 我们来解析一下compute()里面的代码:
    public int compute() { //一个方法对应一块栈帧内存区域
        int a=1;
        int b=2;
        int c = (a+b)*10;
        return c;
    }
    
    //=======================================
    
    public int compute();
        Code:
           0: iconst_1	//将常量1压入操作数栈
           1: istore_1  //将int类型的值存入局部变量1 -- a
           2: iconst_2  //将常量2压入操作数栈
           3: istore_2  //将int类型的值存入局部变量2 -- b
           4: iload_1	//从局部变量1装载int类型的值
           5: iload_2   //从局部变量2装载int类型的值
           6: iadd		//1+2
           7: bipush        10
           9: imul		//*10
          10: istore_3	//将int类型的值存入局部变量3 -- c
          11: iload_3	//装载c的值
          12: ireturn
    

    赋值过程:

    装载,执行过程(操作数栈:临时存储需要操作的值):

    2.2.2 main函数内存区域

    ​ main中存储了一个math对象的指针,指向堆(堆中有为这个对象开辟的内存空间):

    三、本地方法栈

    早期 JAVA 需要与C/C++进行交互,所以用本地方法栈存储C/C++方法

    四、堆

    首先我们看看堆的结构:

    ​ 堆假设只有600M的话,那么年轻代占了1/3,也就是200M,老年代占了2/3。最开始的时候,对象都存放在Eden伊甸区)中。

    ​ 当Eden区装满时,则字节码执行引擎开启一个线程,运行minor gc,扫描伊甸区中的对象,如果对象被引用了,则在s0区中复制一份,否则留在Eden中,被垃圾回收机制清理。

    ​ 其他区域也是类似的处理(在Eden,s0,s1中移动时,“年龄“会加大,当达到15时,会转入老年区,controller、service、bean、数据库持久层、缓存都是常见的老年区对象):

    ​ 当老年代区放满后,字节码执行引擎开启一个线程,运行full gc,对所有区域进行垃圾回收,减少堆的空间,但是老年区被引用的对象还是不会被回收,所以当老年区再次被放满溢出时,会触发OutOfMemoryError的异常,即java堆不够用了。

    扩展:STW【stop the world】,当 JVM 调用gc清理堆时,会暂停掉所有用户程序,比如电商购物时,点击按钮后通常会卡顿一下

    垃圾回收,减少堆的空间,但是老年区被引用的对象还是不会被回收,所以当老年区再次被放满溢出时,会触发OutOfMemoryError的异常,即java堆不够用了。

    扩展:STW【stop the world】,当 JVM 调用gc清理堆时,会暂停掉所有用户程序,比如电商购物时,点击按钮后通常会卡顿一下

    所以 JVM 调优就是:尽量想方设法减少 STW 时间

  • 相关阅读:
    检测ip是否ping通和ssh端口是否通
    python从excel取值
    后k8s时代-微服务
    nginx 笔记-01
    Linux中后台执行的方法nohup和screen
    为什么要自定义Java类加载器
    Synchronized实现原理
    一个线程两次或者多次调用start()方法会怎么样
    Java里锁的种类的总结
    Spring事务控制(PROPAGATION_NESTED)
  • 原文地址:https://www.cnblogs.com/theory/p/13338728.html
Copyright © 2020-2023  润新知