推广 热搜: csgo  vue  angelababy  2023  gps  新车  htc  落地  app  p2p 

Java 程序是如何运行的呢?

   2023-08-07 网络整理佚名2450
核心提示:程序是如何运行的呢?栈中又会包含多个栈帧,每运行一个方法就创建一个栈帧,用于存储局部变量表、操作栈、方法返回值等。直接内存并不是虚拟机运行时数据区的一部分。程序员提供了访问方法区内的数据结构的接口。文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。参数来关闭大部分的类验证措施,以缩短虚拟机类加载的时间。对类的静态变量,静态代码块执行初始化操作

Java程序是如何运行的?

我们编写的是.java文件,需要通过javac编译生成.class文件,这样class文件才能被JVM识别。 我们经常看到的.jar文件实际上是.class文件的压缩包(减少文件数量,方便操作),加载到JVM后才能运行。

来自网络

从上面可以看出,JVM由以下几个部分组成

  1. 类加载、执行引擎、运行数据区以及垃圾回收器

运行数据区

图片.png

运行时数据区主要可以分为5个区域

1 方法区(Area)

方法区由两部分组成

  1. 永久代(Permanent Generation

存放类结构信息的地方,包括常量池、静态变量、构造函数、运行时常量池(Pool)等,以及编译后的代码(JIT)的代码缓存。 虽然JVM规范将方法区描述为堆的逻辑部分,但它有一个别名non-heap(非堆),所以不要混淆它。

(永久代) - -XX:设置上限,-XX:设置最小值示例:VM Args:-XX:=10M -XX:=10M。 如果空间不足,就会引发 java.lang.: space 异常。

从JAVA8开始,持久代被完全删除,并被另一个内存区域(也称为元空间)取代。 它是本地堆内存的一部分。 可以通过-XX:和-XX:来调整。 当达到XX:指定的阈值时,就会开始清理该区域。 如果本地空间中的内存耗尽,则会收到 java .lang.: for space 的错误消息。

代码缓存 - 该缓存区域用于存储编译后的代码。 编译后的代码是本机代码(与硬件相关),由JIT(Just In Time)编译器生成,这是JVM特有的。

2 java堆(Heap)

java实例或对象存储的地方。 这是GC的主要区域。 堆的大小可以通过 JVM 选项 -Xms 和 -Xmx 进行调整。 当堆耗尽时,JVM会抛出java.lang。 例外。 从存储的内容我们很容易知道方法区和堆是所有java线程共享的。

堆分为:

3 java栈(Stack)

java 堆栈始终与线程相关联。 每当创建一个线程时,JVM就会为该线程创建一个对应的java栈。 在这个java堆栈中,将包含多个堆栈帧。 每次运行方法时,都会创建一个堆栈帧来存储局部变量表、操作堆栈和方法返回值。 每个方法从调用到执行完成的过程对应着一个栈帧被压入java栈到出栈的过程。 所以java栈是线程私有的。

4 程序计数器(PC)

用于保存当前线程执行的内存地址。 由于JVM程序是由多个线程执行的(线程轮流切换),为了保证线程切换回来后能够恢复到原来的状态,需要有一个独立的计数器来记录之前被中断的地方。 可见程序计数器也是线程私有的。

5 原生方法栈(Stack)

它与java栈的功能类似,但它服务于JVM使用的方法。 原生方法栈的参数顺序和返回值与典型的C程序相同。

6 原生方法接口

主要调用C或C++实现的本地方法并返回结果。

7 直接存储器

直接内存不是虚拟机运行时数据区域的一部分。

在NIO中,引入了一种基于通道和缓冲区的I/O方法,可以使用函数直接分配堆外内存,然后通过java堆中存储的一个对象作为对这块内存的引用进行操作。 -XX:设置最大值,默认与java堆的最大值相同。

类加载

JVM将类加载分为3步:

链接(link)分为3步,如下图:

图片.png

负载(负载)

查找并加载类二进制数据(查找并导入Class文件)

加载是类加载过程的第一个阶段。 在加载阶段,虚拟机需要完成以下三件事:

1) 通过类的全限定名获取类定义的二进制字节流。

2)将此字节流表示的静态存储结构转换为方法区的运行时数据结构。

3)在Java堆中生成一个代表该类的java.lang.Class对象,作为方法区数据的访问入口。

与类加载的其他阶段相比,加载阶段(准确地说是加载阶段获取类的二进制字节流的动作)是最可控的阶段,因为开发者可以使用系统提供的类加载器来完成加载,也可以自定义自己的类加载器来完成加载。

加载阶段完成后,虚拟机外部的二进制字节流按照虚拟机要求的格式存储在方法区中,同时在Java堆中也创建了一个类java.lang.Class的对象,所以Java 程序员可以提供访问方法区中数据结构的接口。

那么class文件加载的原理是什么呢? 需要满足双亲委派原则,通过类加载器来加载。 类加载器分为以下几种类型:

[图片上传失败...(image--22)]

在加载过程中,会首先检查类是否已经加载。 检查的顺序是从下到上、从上到下逐层检查。 只要加载了某个类,就认为已加载,并且保证所有此类类都被加载一次。 加载的顺序是自上而下的,即上层尝试逐层加载该类。

链接(分3步) 1)验证:确保加载的类的正确性

验证是连接阶段的第一步。 该阶段的目的是保证Class文件字节流中包含的信息满足当前虚拟机的要求,不会危及虚拟机本身的安全。 在验证阶段,大致会完成四个阶段的检查动作:

文件格式验证:验证字节流是否符合Class文件格式的规范; 例如:是否以 开头,主次版本号是否在当前虚拟机的处理范围内,常量池中的常量是否有不支持的类型。

元数据验证:对字节码描述的信息进行语义分析(注:对比javac编译阶段的语义分析),确保其描述的信息符合Java语言规范的要求; 例如:这个类是否在.lang之外有除java之外的父类。

字节码验证:通过数据流和控制流分析,确定程序语义合法、逻辑正确。

符号引用验证:确保解析动作能够正确执行。

验证阶段非常重要,但不是必需的。 它对程序运行时间没有影响。 如果引用的类已经被反复验证,可以考虑使用--参数关闭大部分类验证措施,以缩短虚拟机类加载时间。 时间。

2)准备工作:为类的静态变量分配内存并初始化为默认值

准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存将在方法区中分配。 此阶段需要记住以下几点:

假设一个类变量定义为:int value = 3; 那么准备阶段之后变量值的初始值为0,而不是3,因为还没有执行过任何Java方法,而给3赋值的指令是在程序编译后存储在类构造函数中的()方法,所以给3赋值的动作会在初始化阶段执行。

3)解析:将类中的符号引用转换为直接引用

解析阶段是虚拟机用直接引用替换常量池中的符号引用的过程。 解析动作主要针对7类符号引用:类或接口、字段、类方法、接口方法、方法类型、方法句柄、调用限定符。 符号引用是描述目标的一组符号,可以是任何文字。

直接引用是直接指向目标的指针、相对偏移量或间接定位目标的句柄。

初始化

对类的静态变量和静态代码块进行初始化操作

初始化为类的静态变量赋予正确的初始值,JVM负责类的初始化,主要针对类变量。 Java中初始化类变量有两种方法:

 
标签: jvm 虚拟机 线程
反对 0举报 0 收藏 0 打赏 0评论 0
 
更多>同类资讯
推荐图文
推荐资讯
点击排行
网站首页  |  关于我们  |  联系方式  |  使用协议  |  版权隐私  |  网站地图  |  排名推广  |  广告服务  |  积分换礼  |  网站留言  |  RSS订阅  |  违规举报
Powered By DESTOON