一个java文件是怎么一步一步执行的

2020-05-09 16:04:26来源:博客园 阅读 ()

新老客户大回馈,云服务器低至5折

一个java文件是怎么一步一步执行的

 

说点什么呢,java比你想的要难

写了多年java,发现好多人并不知道一个class文件怎么被解析执行的,所以我也发表下看法

1.  编写java源文件

?

2.  把java源文件编译成.class字节码文件,JVM不认识源文件

?

 3.  JVM处理class文件

搞java开发,不得不提的就是JVM了,JVM全称是Java Virtual Machine(简称JVM,中文叫Java虚拟机,请务必记住JVM,看到不少人整天JVM的,都不知道它的全称是什么),java的宿主环境,可以认为JVM就是虚拟仿真出来的一台计算机。简单绘了一张图,如下(一图胜千言):

?

java之所以一次编写,到处运行,就是因为虚拟机(虚拟虚拟,虚拟出来的计算机,一台被托管的电脑)的缘故。

 

3.1 jvm处理class文件

 加载是指将java源文件编译之后的class文件读入到内存中,然后在堆区创建一个java.lang.Class对象,用于封装类在方法区内的数据结构。类加载的最终目的是封装类在方法区的数据结构,并向java程序员提供访问方法区数据的接口。

类的生命周期一共分为5个阶段,加载、连接、初始化、使用、卸载。

?

加载:类的加载过程主要完成3件事件,1.通过类的全限定名来获取定义此类的二进制字节流,2.将这个类字节流代表的静态存储结构转为方法区的运行时数据结构,3.在堆中生成一个代表此类的java.lang.Class对象,作为访问方法区这些数据结构的入口。这个过程主要是类加载器完成的,如图

?

 

从上图我们就可以看出类加载器之间的父子关系和管辖范围。

BootStrap是最顶层的类加载器,它是由C++编写而成,并且已经内嵌到JVM中了,主要用来读取Java的核心类库JRE/lib/rt.jar

ExtensionClassLoader是是用来读取Java的扩展类库,读取JRE/lib/ext/*.jar

AppClassLoader是用来读取CLASSPATH指定的所有jar包或目录的类文件

甚至可以自定义加载器

加载过程用到了很牛掰的双亲委派模型,它是这样的一套机制:

"类加载器"加载类时,先判断该类是否已经加载过了;

如果还未被加载,则首先委托其"类加载器"的"父类加载器"去加载该类,这是一个向上不断搜索的过程,当类所有的"祖宗类加载器"(包括了bootstrap  classloader)都没有加载到类,则回到发起者"类加载器"去加载,如果还加载不了,则抛出ClassNotFoundException.

请参考http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/java/lang/ClassLoader.java

连接:这个过程分3个阶段(校验,准备,解析)完成。首先是校验,此阶段主要校验class文件包含的信息是否符合jvm的规范。具体的校验通过对文件格式,元数据,字节码,符号引用验证来完成。然后是准备,此阶段为类变量分配内存,并将其初始化为默认值。最后是解析,即把类型中的符号引用转换成为直接引用。具体的解析有4种,1.类或接口的解析,2.字段解析,3.类方法解析,4.接口方法解析。完成这3个阶段就完成了类的连接。

初始化(很重要):即执行类的构造器方法的过程。有5种方法可以完成初始化:1.调用new方法,2.使用Class类的newInstance方法(反射机制),3.使用Constructor类的newInstance方法(反射机制),4.使用Clone方法创建对象,5.使用(反)序列化机制创建对象

码农开发用new关键字创建对象,而框架(spring,mybatis等)特别喜欢用第2和3种方式创建对象,还记得我说的框架四要素吗,其中有一要素就是反射机制

使用:完成类的初始化后,就可以对类进行实例化,在程序中进行使用了

卸载:当类被加载,连接和初始化后,它的生命周期就始了,当代表类的class对象不在被引用时,class对象就会结束生命周期,类在方法区内的数据就会被卸载。因此一个类何时结束生命,取决于代表它的class对象何时结束生命。

 

3.2  JVM的内存结构

Java程序在运行时,需要在内存中的分配空间。为了提高运算效率,就对数据进行了不同空间的划分,因为每一片区域都有特定的处理数据方式和内存管理方式,如上图,Java中的内存分配了5个区

 Method Area方法区

方法区是被所有线程共享,所有字段和方法字节码,以及一些特殊方法如构造函数,接口代码也在此定义。简单说,所有定义的方法的信息都保存在该区域,此区域属于共享区间。

静态变量+常量+类信息+运行时常量池存在方法区中,实例变量存在堆内存中。

 Heap 堆:堆这块区域是JVM中最大的,应用的对象和数据都是存在这个区域,这块区域也是线程共享的,也是 gc 主要的回收区,一个 JVM 实例只存在一个堆类存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,以方便执行器执行。

?

注jdk8永久代改为了metaspace元空间

Stack 栈:栈也叫栈内存,主管Java程序的运行,是在线程创建时创建,它的生命期是跟随线程的生命期,线程结束栈内存也就释放,对于栈来说不存在垃圾回收问题,只要线程一结束该栈就Over,生命周期和线程一致,是线程私有的, 基本类型的变量和对象的引用变量都是在函数的栈内存中分配。遵循“先进后出”/“后进先出”原则。

PC Register程序计数器

每个线程都有一个程序计算器,就是一个指针,指向方法区中的方法字节码(下一个将要执行的指令代码),由执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不记。

Native Method Stack本地方法栈

它的具体做法是Native Method Stack中登记native方法,在Execution Engine执行时加载native libraies。

 

暂且这么多,以后接着补充。。。。。。

 

参考:

0. The Java® Language Specification(8)  https://docs.oracle.com/javase/specs/jls/se8/html/index.html

1. The Java® Virtual Machine Specification (8) https://docs.oracle.com/javase/specs/jvms/se8/html/index.html

我想于java码农而言,没有比这这两个官方上千页的规范更重要的了,看清楚了:一个是Java语言规范,一个是Java虚拟机规范

?

?

当然还有其他规范为了方便码农开发

 ?

 

2. Advanced Java Bytecode Tutorial  https://www.jrebel.com/blog/java-bytecode-tutorial

3.  深入JVM:ClassLoader相关知识简介   https://developer.51cto.com/art/201009/227269.htm

4. Java Class Loader  https://javapapers.com/core-java/java-class-loader/

5. Understanding the Java ClassLoader  https://www.ibm.com/developerworks/java/tutorials/j-classloader/j-classloader.html

6. Advanced Java Class Tutorial: A Guide to Class Reloading  https://www.toptal.com/java/java-wizardry-101-a-guide-to-java-class-reloading

7. 看完这篇文章你还敢说你懂JVM吗? https://virtual.51cto.com/art/201901/591418.htm?mobile

8. JVM  internals  https://blog.jamesdbloom.com/JVMInternals.html#jvm_system_threads

9.  Difference between initializing a class and instantiating an object? 

https://stackoverflow.com/questions/15074083/difference-between-initializing-a-class-and-instantiating-an-object

10. class-loader-subsystem-jvm-internals  https://codepumpkin.com/class-loader-subsystem-jvm-internals/

 

参考书籍:

0.  Java编程思想  (第1,,2,5,8,14章节)

?

 

 1.  Virtual Machines

?

模仿一台计算机


原文链接:https://www.cnblogs.com/dongguangming/p/12858832.html
如有疑问请与原作者联系

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:为什么 Java 线程没有 Running 状态?

下一篇:【JAVA SE基础篇】10.运算符优先级与类型转换