java程序运行原理

java程序运行原理

1.Run-Time Data Areas(JVM运行时数据区)

我们在面试会被问到JVM运行时数据区,这是个什么东西呢?我们先看官网的解释吧,一切从官网出发,这里看的是jdk1.8的运行时数据区,其他版本可能会有不同(官网地址:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5)

官网中的解释是:The Java Virtual Machine defines various run-time data areas that are used during execution of a program. Some of these data areas are created on Java Virtual Machine start-up and are destroyed only when the Java Virtual Machine exits. Other data areas are per thread. Per-thread data areas are created when a thread is created and destroyed when the thread exits.
用有道翻译一下,大概意思是:Java虚拟机定义了在程序执行期间使用的各种运行时数据区域。其中一些数据区域是在Java虚拟机启动时创建的,只有在Java虚拟机退出时才会被销毁。其他数据区域是每个线程的。每线程数据区域在线程创建时创建,在线程退出时销毁。这和我们网上经常看到的说jvm中内存一些是线程独占,一些是线程共享的相符合(网友诚不欺我^_^),这里我贴一下官网的内容在这里插入图片描述

遗憾的是官网没有画图,有画图的话就一目了然了,我找了张比较清晰的图片给大家看看
在这里插入图片描述

1.1 Thre PC Register(程序计数器)

Java虚拟机可以支持多个线程同时执行。每个Java虚拟机线程都有自己的pc(程序计数器)寄存器。在任何时候,每个JVM线程都在执行单个方法的代码,即该线程的当前方法。如果该方法不是native方法,则pc寄存器包含当前正在执行的Java虚拟机指令的地址。如果线程当前正在执行的方法是native方法,则Java虚拟机的pc寄存器的值是未定义的。Java虚拟机的pc寄存器足够宽,可以在特定平台上保存一个returnAddress或native指针。总结一下就是程序计数器会找到当前方法的运行位置,程序计数器肯定是线程独占的

1.2 JVM Stack(jvm虚拟机栈)

每个Java虚拟机线程都有一个私有的Java虚拟机堆栈,与线程同时创建。Java虚拟机栈存储栈帧。Java虚拟机堆栈类似于C等传统语言的堆栈:它保存局部变量和部分结果,并在方法调用和返回中发挥作用。因为除了推入和弹出帧外,JVM堆栈从来不会被直接操作,所以帧可以被堆分配。Java虚拟机堆栈的内存不需要连续。我们关注:栈是线程独占,里面存放方法内部的一些局部变量方法的返回结果

1.3 Heap(堆)

Java虚拟机有一个堆,该堆在所有Java虚拟机线程之间共享堆是为所有类实例和数组分配内存的运行时数据区域。堆是在虚拟机启动时创建的。对象的堆存储由自动存储管理系统(称为垃圾收集器)回收;对象永远不会被显式地释放。Java虚拟机没有特定类型的自动存储管理系统,可以根据实现者的系统需求选择存储管理技术。堆的大小可以是固定的,也可以根据计算的需要进行扩展,如果不需要更大的堆,还可以进行收缩。堆的内存不需要是连续的。我们关注:堆是被线程共享的,里面存储对象和数组等

1.4 Method Area(方法区)

Java虚拟机有一个方法区域,该区域在所有Java虚拟机线程之间共享。方法区类似于常规语言中用于编译代码的存储区,或类似于操作系统进程中的“文本”段。它存储每个类的结构,如运行时常量池、字段和方法数据,以及方法和构造函数的代码,包括在类和实例初始化以及接口初始化中使用的特殊方法。方法区域是在虚拟机启动时创建的。虽然方法区域在逻辑上是堆的一部分,但简单实现可能选择不进行垃圾收集或压缩它。该规范没有规定方法区域的位置或用于管理已编译代码的策略。方法区域可以是固定的大小,也可以根据计算的需要进行扩展,如果不需要更大的方法区域,则可以进行收缩。方法区域的内存不需要是连续的。我们关注:方法区是线程共享的,里面存储编译好的字节码及类的结构,如:字段,方法数据,和运行时常量池

1.5 Run-Time Constant Pool(运行时常量池)

运行时常量池是constant_pool表在类文件中的每个类或每个接口的运行时表示。它包含几种类型的常量,从编译时已知的数字字面值到必须在运行时解析的方法和字段引用。运行时常量池的功能类似于传统编程语言的符号表,尽管它包含的数据范围比典型的符号表更广。每个运行时常量池都是从Java虚拟机的方法区域分配的。类或接口的运行时常量池是在Java虚拟机创建类或接口时构造的。每个运行时常量池都是从Java虚拟机的方法区域分配的。类或接口的运行时常量池是在Java虚拟机创建类或接口时构造的。

1.6 Native Method Stacks(本地方法栈)

Java虚拟机的实现可以使用传统堆栈(俗称“C堆栈”)来支持native方法(用Java编程语言以外的语言编写的方法,实际上就是C语言)。本地方法栈的实现也可以使用一个翻译为Java虚拟机的指令集的语言如c . Java虚拟机实现,无法加载本地方法,自己不依赖传统的本地方法栈栈不需要供应。如果提供了,通常在创建每个线程时为每个线程分配本机方法堆栈。我们关注:本地方法栈用来调用C语言执行native方法,native方法是不加载进JVM的

2.java程序运行原理

上面已经讲了jvm的运行时数据区,现在来看看一个1+1=2 的例子在jvm中是如何运作的,我们先准备好东西,先上一段hello world代码

public class Demo {
    public static void main(String[] args) {
        int a = 2;
        int b = 3;
        int x = a + b;
        int y = a * b;
        System.out.println(x);
        System.out.println(y);
    }
}

我们用在Demo.java这目录下打开cmd用javac Demo.java编译一下,可以得到Demo.class,然后用javap Demo.class对class进行反编译,得到如下结果
在这里插入图片描述
要看懂上面反编译后的结果我们要先准备点知识,例如这些变量的加载,操作是怎么进行的?实在哪里进行的?我们还是从官网来找,还是上面的官网地址,我们来看看2.6Frames
在这里插入图片描述
什么是Frame?
Frame:栈帧
栈帧用于存储数据和部分结果,以及执行动态链接、为方法返回值和分派异常。每次调用方法时都会创建一个新栈帧。当一个栈帧的方法调用完成时,它将被销毁,无论这个完成是正常的还是异常的(它将抛出一个未捕获的异常)。栈帧是从创建栈帧的线程的Java虚拟机堆栈中分配的。每个栈帧都有自己的本地变量表操作数堆栈和当前方法类的运行时常量池的引用。栈帧可以被扩展为附加的特定于实现的信息,例如调试信息。本地变量表和操作数堆栈的大小在编译时确定,并随与帧相关的方法代码一起提供。因此,帧数据结构的大小只取决于Java虚拟机的实现,这些结构的内存可以在方法调用时同时分配。在给定的控制线程的任何点上,只有一个帧(用于执行方法的帧)是活动的。这个帧被称为当前帧,它的方法被称为当前方法。定义当前方法的类就是当前类。对本地变量和操作数堆栈的操作通常引用当前帧。如果一个帧的方法调用了另一个方法,或者它的方法完成了,那么它就不再是当前帧。当调用方法时,将创建一个新帧,并在控制转移到新方法时变为当前帧。在方法返回时,当前帧将其方法调用的结果(如果有的话)传回前一帧。当前帧随后被丢弃,因为前一帧变成了当前帧。注意,由线程创建的帧是该线程的本地帧,不能被任何其他线程引用。
我们现在对栈桢有一定了解了,变量的存储和运算实际上在栈桢的本地变量表操作数栈中进行的,对于上面的反编译结果中的iload等操作类型我们从官网中能看到一张表
在这里插入图片描述
这个表列出了这些一些操作指令,看到这些操作指令我们还是不知道什么意思,再来看看
在这里插入图片描述

  • 加载本地变量压入操作数栈:ILOAD,iload_ ,的Iload,lload_ , FLOAD,fload_ ,进入dload,dload_ ,aload,aload_ 。
  • 将操作数栈中的值存储到本地变量中:istore、istore_、 lstore、lstore_、fstore、fstore_、dstore、dstore_、 astore、astore_。
  • 将常量加载到操作数栈中:bipush、sipush、ldc、ldc_w、ldc2_w、 aconst_null、iconst_m1、iconst_、lconst_、fconst_、 dconst_。
    好有了这些知识我们就大概能看懂了
    在这里插入图片描述
    上面还有几个步骤各位看官自己想想吧,其实很简单
    getstatic:调用静态方法,就是程序中的System.out
    invokevirtual:调实例的方法,上面System的静态方法out,返回的是PrintStream实例,println是PrintStream的一个方法,好了,java程序的运行原理到此就结束了
    原创不易转载请标注在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值