简述Java虚拟机

来源:互联网 发布:步进电机闭环控制算法 编辑:程序博客网 时间:2024/05/16 05:18

JDK中包含JRE,在JDK的安装目录下有一个名为jre的目录,里面有两个文件夹bin和lib,在这里可以认为bin里的就是jvm,lib中则是jvm工作所需要的类库,而jvm和 lib和起来就称为jre。写代码需要用jdk,运行java需要jre。

JVM运行时数据区:

  • 指令区:程序计数器,虚拟机栈,本地方法栈。
  • 数据区:方法区,堆。

以下引用其他大佬的图解:
这里写图片描述


  1. 程序计数器:指向当前线程正在执行的字节码指令的地址。
    • 从代码来讲:程序中有些控制流,需要回来,比如if else switch return等,要实现这些控制流的跳转。
    • 从底层来讲:cpu的执行是一个时间片 ,当A线程在执行时会抢占cpu,如果A正在执行中,这时线程B抢占了cpu执行单元(有很多种,比如 抢占式、攻击式、非攻击式等),也就是抢占了时间片,这时线程A就会被挂起,
    • 当线程B使用完时间片后,需要继续执行线程A,可是线程A并不知道下一步要做什么了,这个时候就需要有个地方告诉线程A需要做什么(在线程体里面拥有自己单独的一个程序计数器,当线程恢复时,这个计数器要知道 刚刚的“当前”是谁,需要做什么),
    • 由此可知程序计数器是线程独享的一个存储结构,那就是线程安全的
  2. 程序计数器:指向当前线程运行方法时所需要的数据、指令、返回地址
    • 出栈入栈的最小单元:栈帧,是用于虚拟机执行时方法调用和方法执行时的数据结构,它是虚拟栈数据区的组成元素。
    • 典型的栈帧主要由 局部变量表(Local Stack Frame)、操作数栈(Operand Stack)、动态链接(Dynamic Linking)、返回地址(Return Address)组成:
    • 局部变量表:一个方法可以定义n个局部变量,一个局部变量占32位( boolean、byte、char、short、int、float、reference 和 returnAddress 8种类型),如果是对于64位的 long 和 double 变量而言,则分配两个32位的空间。
//以下展示了几个局部变量,i,j,h,start,sum都是放在局部变量表的,其中sum是先在操作数栈里处理后保存进来的public void methodOne(int i) {        int j = 0;        int sum = i + j;        Object h = obj;        Long start = System.currentTimeMillis();    }
  • 操作数栈: i+j 这种算术操作就是在操作数栈里完成的,sum先保存在操作数栈,然后再保存到局部变量表中。
  • 动态链接:每个栈帧都有一个运行时常量池的引用。这个引用指向栈帧当前运行方法所在类的常量池。通过这个引用支持动态链接。
    //其中的service也算是一种引用    @Autowired    private Serivice service;
  • 返回地址:一个方法开始后,可以有两种方法退出:1、遇到返回指令返回 2、遇到异常返回
    //此方法只做表达意思演示用,不可直接运行    public String method() {        Long start = System.currentTimeMillis();        //遇到返回指令返回        return ;        //遇到异常返回        try {            return ;        }        catch (Exception e)        {            return ;        }    }
  • xss栈的深度:这个栈能放多少个栈帧。
//如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError 异常;//如果虚拟机栈可以动态扩展(当前大部分的Java 虚拟机都可动态扩展,只不过Java 虚拟机规范中也允许固定长的虚拟机栈),//当扩展时无法申请到足够的内存时会抛出OutOfMemoryError 异常。//由于虚拟机栈遵从先进后出的原则,以下展示压栈的示例,垂直角度来看,methodTwo是在最上层,为顶栈,methodOne则在栈的最下面:public void methodOne(int i) {        int j = 0;        int sum = i + j;        Object acb = obj;        Long start = System.currentTimeMillis();        methodTwo();}public void methodTwo(){        int i = 0;        System.out.println("111111111");}

3 . 本地方法栈:本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其
区别不过是虚拟机栈为虚拟机执行Java 方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native 方法服务。

4 . 方法区:类信息(.class文件)、常量(带final)、静态变量(带static)、JIT(即时编译时的代码)(比如 ,spring中会大量动态代理,动态代理产生的类的信息和类就保存在方法区里),与Java 堆一样,是各个线程共享的内存区域,因为数据是线程共享的,所以存在线程安全问题。

5 . :对于大多数应用来说,Java 堆(Java Heap)是Java 虚拟机所管理的内存中最大的一块。Java 堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。
这一点在Java 虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配①,但是随着JIT 编译器的发展与逃逸分析技术的逐渐成熟,栈上分配、标量替换②优化技术将会导致一些微妙的变化发生,所有的对象都分配在堆上也渐渐变得不是那么“绝对”了。