美文网首页
JVM虚拟机模型学习

JVM虚拟机模型学习

作者: 蓝调_4f2b | 来源:发表于2022-11-01 23:25 被阅读0次

    引言

    先上图


    JVM结构.png

    功能:存放局部变量(线程栈)

    1. 栈帧

    (1)概念
    一个方法对应一块栈帧内存区域,该栈帧中保存方法中声明的局部变量


    栈帧工作概念图.png

    (2)栈帧结构
    栈帧由局部变量表,操作数栈,动态链接,方法出口等部分组成。其中:
    操作数栈:方法运行时,用于变量操作的临时内存,如运算(1 + 1 = 2)时在操作数栈分配的内存中完成,后复制给栈帧中的局部变量;
    动态链接:运行时,将符号变为其代码对应的直接内存地址(直接引用);
    例如:func()运行时,将方法区中对应逻辑地址拷贝入动态链接之中;
    方法入口:返回main()栈帧;

    2.程序计数器

    (1) 概念:每一个线程独有的内存空间,存放每个线程即将执行的代码行号
    (2) 作用:多线程场景下,当前线程CPU被抢占,挂起后由程序计数器记录的行号恢复程序


    程序执行器概念图.png

    3. 栈,方法区与堆关系

    栈中对象通过指针指向其堆中分配地址
    方法区中常保存常量,静态变量及类信息


    栈,方法区与堆关系.png

    4. 对象的创建及结构

    对象创建流程.png

    4.1 分配内存的方法:

    (1)指针碰撞方法:
    条件:当Java堆内存绝对整齐的排列时(较常见)

    指针碰撞法.png
    (2)空闲列表方式:
    当java堆中已用内存不规整时,已使用的内存和空闲内存相互交错,无法使用指针碰撞方法,虚拟机中维持一个map,记录还有哪些内存块可用,有新对象要分配内存时,JVM分配map中的空闲内存块,并更新map中的信息。

    5. 堆结构与垃圾收集

    5.1 堆结构与垃圾收集结构图

    堆结构与垃圾收集.png

    注1: 一般老年代最后留存的对象为静态对象,在7*24小时web服务中,静态对象将一直存活
    注2: 一个OOM(内存溢出)小案例

    public class HeapTest {
      byte[] a = new byte[1024 * 100];
      public static void main(String[] args) {
        ArrayList<HeapTest> heapTests = new ArrayList<>();
        while(True) {
          heapTests.adds(new HeapTest());
         # 通过可达性分析方式不能回收该部分内存
        }
      }
    }
    

    5.2 对象的并发放入方式

    在高并发web服务中,对象会尝试通过高并发的方式放入堆中分配的空间,其中有两种方式:
    (1)Cas+失败重试方案
    (2)本地线程分配缓冲(TLAB:JDK1.8默认方式)
    JVM预先为每个线程分配对应的内存块

    5.3 对象结构

    (1)对象由对象头,实例数据及对齐填充组成


    对象结构.png

    (2)对象结构的优化:指针压缩(JDK默认:8字节对象压缩至4字节)

    • VM options: -XX: +UseCompressedOps
    • 对象指针压缩优点:大大缩小对象占用内存,例如:
      通过一定的压缩算法,使得:
      35位的对象可以由32位的机器进行保存。

    5.4 对象内存流程

    对象内存流程.png

    (1)对象逃逸现象及分析

    例1:
    User u = CreateUser();
    public User CreateUser() {
      User user = new User();
      user.setId(1);
      return user;
    }
    // 局部对象user逃逸出了它的create方法,JVM视情况将这种对象的内存分配至堆中保存
    
    例2:
    public void CreateUser() {
      User user = new User();
      user.setId(1);
      return ;
    }
    //  该局部变量user未逃逸,对于该种情况,JVM将判断对象所占内存大小,若内存较小,将对象保存入方法createUser对应的栈桢中;
    // 优点:该方法结束时,内存(栈桢)被销毁,该对象内存被回收,以这种方式降低GC频率
    

    (2)JVM对象逃逸处理
    JVM虚拟机通过参数(-XX:+DoEscapeAnalysis) JDK默认开启
    标量替换:若栈桢中无连续空间放置对象,仅将对象中成员变量放入栈桢中


    标量替换.png

    标量替换参数:XX: +EliminateAllocations

    5.5 对象的分配:

    (1)对象主要在Eden区分配,当Eden区空间不足时触发一次minor GC
    (2)大对象直接进入老年代(建议直接进入老年代,不然增加年轻代回收频率)
    大对象定义: -XX : PretenureSize Threshold = 10000000(大对象对应阈值)
    巧妙设计分代年龄:根据业务需要设置分代年龄,使对象不必经过15次GC才进入老年代区(-XX:MaxTenuringThreshold)

    6. 老年代空间分配担保机制

    老年代空间分配担保机制.png
    注:在做gc之前,会判断老年代空间是否可存下eden区所有Obj,若存不下进行一次minor gc,通过老年代担保机制判断老年代剩余空间是否小于历史每次minor gc后进入OLD区对象平均大小,若不满足进行一次full gc。
    优点:尽量减少做full gc的次数

    7. 对象内存的回收

    (1)引用计算方法:

    main() {
      obj A  = new Obj();    // A计数值+1
      obj B = new Obj();
      A.instance = B;         // A计数+2
      B.instance = A;
    }
    
    A = null;    // A计数值-1
    

    (2) 可达性分析
    将GC roots对象作为起点,从此向下搜索引用对象,可找到的标为非垃圾对象,其余为垃圾对象.

    根结点一般为:

    • 线程栈本地变量
    • 静态变量
    • 本地方法栈变量

    8. 常见引用类型

    • 强引用
    • 软引用
    • 弱引用
    • 虚引用

    9. 如何判断一个类已无用

    回收元空间(方法区)内存:3个条件需均满足

    • 类所有对象实例已被回收(该对象已无引用)
    • 加载该类classLoader被回收(一般为自定义类加载器被回收)
    • 该类对应java.lang.class 对象未被引用,无法在其他地方由反射访问该类
      一般被回收对象:Tomcat, JSP对象

    相关文章

      网友评论

          本文标题:JVM虚拟机模型学习

          本文链接:https://www.haomeiwen.com/subject/ylmdtdtx.html