通俗理解JVM系列(一)——虚拟机结构


系列导航

(一) 虚拟机结构
(二) 类加载机制
(三) GC算法理解
(四) 垃圾收集器及其选择
(五) GC调优



一.JDK整体体系架构

在这里插入图片描述

在不同的系统中,java代码能够做到一次编写、到处运行,其原因就是因为不同系统的java虚拟机(JVM)能够解析各个地方编写java代码。


二.JVM虚拟机

在这里插入图片描述

  • 堆、方法区是共享区域,调优也是调这一部分。

  • 栈、本地方法栈、程序计数器是线程私有的,由JVM自动化管理。

  • PS:对象的存储:对象的实例存储在堆空间,对象的元数据存储在方法区(元空间),对象的引用存储在栈空间


  • 全称为线程栈或者虚拟机栈,其特点是先进后出(FILO),主要存放程序在运行过程中产生的局部变量,不存放成员变量、对象。
  • 栈内存大小,在其编译时就已经确定。

栈帧

在这里插入图片描述

main()方法首先启动,方法进栈(压栈),而后执行a()方法,也进行压栈。在a()执行完成后,该方法出栈,而后main()出栈。


  • 一个栈内有多个栈帧,随方法调用而创建,随方法结束而死亡。一个方法,对应着一个栈帧。

  • 局部变量表:是一组变量值的存放空间,用于存放方法参数、局部变量。

  • 操作数栈:虚拟机将操作数栈作为运算工作区域,大多数指令需要在这里执行运算,然后把结果重新压回操作数栈。

  • 动态链接:如果将一个.class文件进行javap -v反汇编,那么可以看到一个常量池

    ......
        
    Constant pool:
       #1 = Methodref          #5.#25         // java/lang/Object."<init>":()V
       #2 = Class              #26            // Demo
       #3 = Methodref          #2.#25         // Demo."<init>":()V
       #4 = Methodref          #2.#27         // Demo.a:()I
       #5 = Class              #28            // java/lang/Object
       #6 = Utf8               <init>
       #7 = Utf8               ()V
       #8 = Utf8               Code
       #9 = Utf8               LineNumberTable
      #10 = Utf8               LocalVariableTable
           
    ......
    

    java源文件被编译的字节码(.class)文件中,所有的变量和方法引用都作为符号引用存储在常量池中,以减少数据量;在调用方法时,就是通过常量池中指向的方法的符号引用来表示。而动态链接,就是将符号引用,转化为直接引用。

  • 方法出口:在方法执行完毕后,返回之前调用它的地方。但出现异常,则不会返回地址。


程序计数器

每一个线程都会有一个独立的程序计数器,而其作用简单来说就是当前线程所执行的字节码的行号指示器

比如

1)有这么一个程序如下

public class Demo
{
    public static void main(String[] args)
    {
        Demo demo = new Demo();
        demo.a();
    }


    public int a()
    {
        int a = 1;
        int b = 2;
        int c = a + b;
        return c;
    }
}

2)javap命令可以将class文件分解、反编译,还可以查看java编译器生成的字节码等。

这时候使用javap -c demo.class进行编译,对代码进行反汇编;

每一句话都有对应的意义,可以通过JVM指令手册进行对照

Compiled from "Demo.java"
public class Demo {
  public Demo();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class Demo
       3: dup
       4: invokespecial #3                  // Method "<init>":()V
       7: astore_1
       8: aload_1
       9: invokevirtual #4                  // Method a:()I
      12: pop
      13: return

  public int a();
    Code:
       0: iconst_1
       1: istore_1
       2: iconst_2
       3: istore_2
       4: iload_1
       5: iload_2
       6: iadd
       7: istore_3
       8: iload_3
       9: ireturn
}

3)可以看到,每个方法中,语句都对应相应的行号,而程序计数器,可以存放当前所执行的地址(行号)


在这里插入图片描述

  • jdk1.8后,永生代已经被正式移除。
  • 堆的内存分配,新生代 + 幸存者区约占1/3,老年代约2/3。而在1/3中,新生代又占了其中约4/5,幸存者区共占1/5。

Minor GC
  1. 在Eden中内存满了以后,JVM执行引擎会进行垃圾收集,对其进行清理。这时,一些变量已经没有作用(比如栈帧中的一些方法中new出来的局部变量已经出栈、被释放,那么堆中的对象没有了指针指向它,成为了游离的状态,成为了无效对象),此时就会被回收;而没有被回收的对象,就会存入Survivor区。
  2. 对象有一个对象头,其中有一个分带年龄,当从Eden挪到Survivor时,分带年龄就会+1。
  3. 在Survivor区中,也会进行minor gc,当一个区域进行minor gc后对象仍然存在,那么就会将其分带年龄+1,并且将对象移动到另一个区域中;两个Survivor交替minor gc、对象交替在二者中轮转。
  4. 当对象的分带年龄到达了15时,此对象就会被移动到老年代中。

Full GC
  1. Full GC是清理整个堆空间,包括所有区域。

方法区

  • 又称元空间、Non-Heap(非堆),它直接使用内存,而不使用JVM中的内存空间。
  • 所有定义的方法的信息都保存在该区域,存储已被虚拟机加载的类信息、常量、静态变量、运行时常量池等数据。
  • 它是所有线程的共享区域,所以线程也被设计成安全的。

本地方法栈

本地方法栈具有线程隔离的特点,但是它服务的对象是JVM的native方法,比如调用C/C++库。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值