🔔🔔🔔好消息!好消息!🔔🔔🔔-my_content

如果您需要注册ChatGPT,想要升级ChatGPT4。凯哥可以代注册ChatGPT账号代升级ChatGPT4

有需要的朋友👉:微信号 kaigejava2022

JVM学习第一篇思考:一个Java代码是怎么运行起来的-上篇

2021-06-24 14:44   4791 浏览

JVM学习第一篇思考:一个Java代码是怎么运行起来的-上篇

作为一个使用Java语言开发的程序员,我们都知道,要想运行Java程序至少需要安装JRE(安装JDK也没问题)。我们也知道我们Java程序员编写的程序代码文件是*.java的,而JRE运行的是*.class的文件。所以,我们需要将java文件编译成class文件然后才可以。那么,你有没有想过,一个java文件是怎么运行起来的呢?中间都经历了哪些环节呢?我们都知道JVM是Java虚拟机,那么,有没有思考过JVM的内存模型是什么呢?我们new出来的对象,声明不同类型的变量又是存放在JVM哪个位置呢?

本文是凯哥(凯哥Java:kaigejava)学习JVM系列教程第一篇。欢迎大家一起学习

本文目标:

通过本文学习后,希望大家对JVM类加载过程有个了解。

0dc56608de62186fc4a21157255e5978.png

上面程序很简单。那么,有没有想过上面代码怎么运行的呢?

选中main方法,然后ruan as...,编译后,运行输出。这个流程我想大家都很熟悉的。那么对应的流程应该是什么样的呢?如下图:

3d99cf3b96db759238fbcff2d612cc0a.png

在Run的时候,先将.java文件编译成.class文件。然后,在通过类加载器,将class文件加载到JVM中,然后在运行。输出结果。

那么为什么编译好的AppTest.class可以加载到JVM中呢?可以被JVM识别呢?

一个java类的一生都会经历哪些步骤呢?

如下图:

fb77dc8b840434f5f4f561550f0bacf6.png

在我们run的时候,AppTest.java类先经过编译后,编译成了AppTest.class文件。JVM把class文件加载到内存后需要经历:加载-验证-准备-解析-初始化-使用-卸载这七个阶段。

第一个问题:JVM在什么时候会加载一个类呢?起始也就是在什么时候会加载.class字节码文件到JVM的内存中去呢?上面我们写的,当我们run的时候,才执行的。所以答案就很明确了,就是在你代码中需要使用到这个类的时候,就去加载的。

具体每一步:

加载

加载阶段是将class文件从磁盘或者jar等读到JVM内存中,并为其创建一个Class对象。任何一个类被使用时候系统都会为其创建一个Class对象的。

加载的同时将加载的这些数据转换成方法区中运行时数据(运行时候数据区:静态变量、静态代码块、常量池等),作为方法区数据的访问入口

这个很好理解的。我要想使用你,需要先得到你,是不是。结合上面我们自己写的AppTest类。在此阶段应该是:

519da5e3306c7e0b8cfc8bfc8f6d23f6.png

扩展:

在类加载阶段JVM都做了什么?获取class文件方式都有哪些?

1.1:在类加载的时候JVM完成了以下:

  • 根据类的全路径(全限定名)来获取到该类的二进制字节流

(我们知道,在电脑的世界中,什么都是二进制形式存在的)

  • 将加载的字节流中所代表的静态存储结构转换成方法区运行时数据结构

(这个话具体怎么理解,有哪位能留言教教凯哥)

  • 将加载的对象在内存中生成一个代表了该类的jvaa.lang.Class对象。这个Class对象作为加载进来对象在方法区各种数据的访问入口。

(要想在内存中访问AppTest这个字节码类中的属性或者方法的时候,可以在内存中方法区找到对应的Class对象。这个Class就是入口)

关于方法区在后面文章中,凯哥会详细讲讲。

1.2:获取class文件的方式

  • 可以直接从本地的磁盘文件获取

  • 可以从忘了下载class文件

  • 可以从ZIP或者jar等文件中

  • Java源文件动态编译的class文件

在一个类运行生命周期内,类加载(加载获取类的二进制字节流)阶段,是可控性最强的阶段。因为在这个阶段,我们程序员可以使用系统提供的类加载去来加载完成,也可以使用自己自定义的类加载来完成.(类加载器在后面文章详细讲讲)

1.3:类加载的具体时机,在文章最后,凯哥会列出来。

验证

将上一步加载到内存中的Class对象进行校验。确保加载的类的信息符合JVM的规范。确保没有安全方面的问题。

这个很好理解了,我要使用你,得到你好,我要检查你是不是符合标准的。如果不合法,就没法使用。

在此阶段如下图:

8393ee41b2c8f0fb54066c5d09149e80.png

扩展:验证都验证哪些方面?

  • 文件给是验证:验证加载的字节流是否 符合Class文件格式的规范。

例如:是否已咖啡babe开头(0xCAFEBABE),主次版八号是否在当前JVM的处理范围内等等

比如你在JDK1.8下编译的class文件,放到JDK1.6版本的JVM中,有可能就运行不了的

  • 元数据验证:对字节码描述的信息进行语义分析。保证描述信息符合Java语言规范。

例如:这个类如果有父类,是否实现了父类的抽象方法等.

  • 字节码验证

  • 符号引用验证:确保解析动作是正确的。

例如:通过符号引用能找到对应点的类和方法。比如com.kaigejava.Person.getAge()

在比如:符号引用中类、属性、方法的访问性是否能被当前类访问等等。

准备

准备阶段,就是给加载进来且验证通过的Class类分配空间的。这里是给类里面的变量(也就是static修饰的变量)分配空间的,同时给变量一个默认的初始值。

如下图:

f9a123180414f77b8d5df047eb05ba93.png

在准备阶段时候static int m 被分配了4个字节的空间,且分配了默认初始值为0(注意默认初始值是0).

PS:int类型占用4个字节。int的默认值是0.如果是对象的话。默认为null

在此阶段AppTest.class如下图:

05aea98149ca60402169f4faea615a86.png

该阶段需要注意:

  • 在此阶段值只对static修饰的静态变量进行内存分配,赋默认值的(比如0、0L、0D、null、false等);

  • 对于final修饰的静态字面值常量直接赋初始值(注意:这里的初始值并不是默认值。如果不是字面值静态常量,那么会和静态变量一样赋默认值)

比如:final int x = 1;这个在此阶段就给赋值的就是1而不是0

解析

解析是将常量池中的符号引用替换为直接引用(内存地址)的过程。

在此阶段AppTest类如下图:

da19f75f7e9f8a2836687a3758600c80.png

扩展:

符号引用:

就是一组符号来描述目标的。可以是任何字面量。这个属于编译原理方面的东西。

比如:可以是一个类的完整类名字(com.kaigejava.Person)、字段的名称和描述符、方法的名称和描述等。

直接引用:

就是直接指向目标的指针、相对偏移量或者一个间接定位到目标的句柄。比如指向方法区中某一个类的一个指针。

例如:在AppTest这个类中,有个static的静态变量p。这个静态变量p又是一个自定义的类型(com.kaigejava.Person),那么在经过解析阶段后,这个静态的p变量将是一个指针(比如0xddff1),这个指针指向该类在方法区的内存地址值。具体见凯哥后续文章,将会详细讲解。

e4e6a52a97b48f7fe4b431b62e06a601.png

初始化

到了此阶段(初始化阶段),JVM才开始真正的执行类中定义的Java代码。

当进行到初始化阶段的时候,就是执行类的构造器<clinit>()方法的过程。

  • <clinit>()方法是由编译器自动收集类中的所有类变量赋值动作和静态语句。

  • <clinit>()方法与类的构造器不同。此方法不需要显示的调用类的父构造器(如果类有父类的话),虚拟机会保证在子类的<clinit>()方法执行前,父类的<clinit>()方法已经执行完毕。因此JVM中第一个被执行的<clinit>()方法的类肯定是java.lang.Object(因为Java中所有类的父类是Object类)

  • 因为父类的<clinit>()方法先执行,所以也就意味着父类中定义的static语句块要优先于子类的变量赋值操作

  • 如果一个类中没有静态变量或者是静态的语句块的时候,编译器可以不为这个类创建<clinit>()方法的

  • 虚拟机会保证一个类的的<clinit>()方法在多线程环境中被正确的加锁和同步。多线程访问,一个访问,其他在访问的话会被阻塞。

使用

类实例化也初始化成功之后,这个类就是一个正常的类了。我们可以正常使用了。

卸载

当遇到以下几种情况的时候,类会被卸载

  • 执行了System.exi()方法的时候

  • 程序正常执行结束

  • 程序在执行过程中遇到了异常或者是错误而异常终止

  • 由于操作系统出现错误导致Java虚拟机进程终止

今天问题:

现在我们知道了一个Java类是怎么运行起来的了。那么请看下面代码,运行后输出的顺序是什么?

public class JvmDemo {

public static void main(String[] args) {

Son son = new Son();

FatherInterface fatherInterface = new SonInterFace();

fatherInterface.say("凯哥Java");

}

}

class Father{

static String st1 = "父类Father中的静态变量";

String str2 ="父类Father中的非静态变量";

static {

System.out.println("当前执行了父类Father的静态代码块中的方法");

}

{

System.out.println("执行了父类Father类中的非静态代码块");

}

public Father(){

System.out.println("执行了父类Father中的构造方法了");

}

}

class Son{

static String str1 = "子类Son中的静态变量";

String str2 = "子类Son中的非静态变量";

static{

System.out.println("执行了子类son中的静态代码块");

}

{

System.out.println("执行了子类Son中的非静态代码块");

}

public Son(){

System.out.println("执行了子类son中的构造器方法");

}

}

interface FatherInterface{

static String str1 = "接口父类FatherInterface中的静态变量";

void say(String say);

}

class SonInterFace implements FatherInterface{

static String str1 = "子类SonInterFace中的静态变量";

String str2 = "子类SonInterFace中的非静态变量";

static{

System.out.println("执行了子类SonInterFace中的静态代码块");

}

{

System.out.println("执行了子类SonInterFace中的非静态代码块");

}

public SonInterFace(){

System.out.println("执行了子类SonInterFace中的构造器方法");

}

@Override

public void say(String say) {

System.out.println(FatherInterface.str1+"--say:"+say);

}

}

069716ab9a240d85bb1706ef91dfd993.png

d1728d9bbd02a0b15ace76c61063dd6e.png

运行后答案将在下一篇文章中揭晓。

下一篇预告:

因为这是第一篇,所以只是大致讲解了下一个类怎么加载过程。在下一篇文章中,咱们来讲解在加载阶段使用到类加载器、父类委派机制等、类在什么时候会被初始化等?。欢迎继续学习。


喜欢 0

上一篇: 常见JVM面试题及答案整理

下一篇: 深入理解JVM类加载机制

工作小总结&小工具类 Redis MQTT Maven mybatis ChatGPT uniapp zookeeper Thymeleaf语法 POI-TL sa-token PowerDesigner16.5 taos数据库 frp echarts Actor模型及Akka thingsboard 大疆无人机对接 CI/CD 教师资格证 小任务 面试其他 职场 淘宝客 支付宝支付 HBuilder X Flink Java集合类 多线程 ES Ribbon eureka Docker java游戏 网络通信 Nacos 芋道管理系统 Solr 分布式相关 Dubbo 数据结构 EasyPOI Drools RocketMQ JS 七天深入MySQL实战营 书籍 kafka spring Java基础 java web 若依(ruoyi) 分布式事务 面试宝典 mysql java8新特性 spring cloud ElasticSearch学习系列 HM_leadnews 即时通讯 并发 思维&学习 VUE 宝塔面板 算法刷题 设计模式 RabbitMQ学习系列教程 P3C规范 JVM学习系列 反射 自定义注解 网络美文 PHP源码 经验分享 资源 git项目 websocket 网赚 数据库读写分离 测试相关 其他随笔 shiro学习系列 fremarker学习系列 学习笔录-spring boot 网络文章 工作小总结

评论


深圳SEO优化公司陇南外贸网站建设邢台网站关键词优化价格保山网站关键词优化价格苏州百度网站优化公司汕头品牌网站设计哪家好杭州营销型网站建设报价楚雄网站优化软件价格日照seo多少钱鞍山SEO按天收费多少钱安康百度竞价包年推广威海关键词按天计费报价本溪百度seo报价深圳seo网站推广哪家好濮阳网站关键词优化价格新余营销网站推荐永州至尊标王营口营销型网站建设多少钱衡水网站改版菏泽外贸网站建设价格巢湖网站建设设计价格洛阳设计网站长治百搜标王哪家好蚌埠网站建设公司湖州关键词按天计费公司阿里seo优化价格横岗关键词排名哪家好沧州英文网站建设哪家好兴安盟关键词按天收费报价丽江网站优化软件推荐绍兴网站建设推荐歼20紧急升空逼退外机英媒称团队夜以继日筹划王妃复出草木蔓发 春山在望成都发生巨响 当地回应60岁老人炒菠菜未焯水致肾病恶化男子涉嫌走私被判11年却一天牢没坐劳斯莱斯右转逼停直行车网传落水者说“没让你救”系谣言广东通报13岁男孩性侵女童不予立案贵州小伙回应在美国卖三蹦子火了淀粉肠小王子日销售额涨超10倍有个姐真把千机伞做出来了近3万元金手镯仅含足金十克呼北高速交通事故已致14人死亡杨洋拄拐现身医院国产伟哥去年销售近13亿男子给前妻转账 现任妻子起诉要回新基金只募集到26元还是员工自购男孩疑遭霸凌 家长讨说法被踢出群充个话费竟沦为间接洗钱工具新的一天从800个哈欠开始单亲妈妈陷入热恋 14岁儿子报警#春分立蛋大挑战#中国投资客涌入日本东京买房两大学生合买彩票中奖一人不认账新加坡主帅:唯一目标击败中国队月嫂回应掌掴婴儿是在赶虫子19岁小伙救下5人后溺亡 多方发声清明节放假3天调休1天张家界的山上“长”满了韩国人?开封王婆为何火了主播靠辱骂母亲走红被批捕封号代拍被何赛飞拿着魔杖追着打阿根廷将发行1万与2万面值的纸币库克现身上海为江西彩礼“减负”的“试婚人”因自嘲式简历走红的教授更新简介殡仪馆花卉高于市场价3倍还重复用网友称在豆瓣酱里吃出老鼠头315晚会后胖东来又人满为患了网友建议重庆地铁不准乘客携带菜筐特朗普谈“凯特王妃P图照”罗斯否认插足凯特王妃婚姻青海通报栏杆断裂小学生跌落住进ICU恒大被罚41.75亿到底怎么缴湖南一县政协主席疑涉刑案被控制茶百道就改标签日期致歉王树国3次鞠躬告别西交大师生张立群任西安交通大学校长杨倩无缘巴黎奥运

深圳SEO优化公司 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化