发表时间:2022-03-25来源:网络
目录
前言
一、了解Java程序运行的过程
二、字节码文件的“装到”JVM的机制——类加载机制
1.步骤
2.步骤详解
3.class初始化时机
4.类加载的实现者——类加载器
Bootrap ClassLoader介绍
Extension ClassLoader介绍
Application ClassLoader介绍
5.双亲委派加载机制/模型(面试常考)
双亲委派加载机制/模型的作用
为什么要自定义类加载器?
三、初始运行时数据区
四、JVM执行引擎的组成
五、总结
阅读本文之前请先仔细思考以下问题:
为什么要学习JVM?学习JVM是为了什么?
为什么Java语言代码能一次编写,到处运行(Write Once,Run Anywhere)?
JVM是多语言的平台,JRuby、Groovy、Scala等为什么能运行在JVM之上?
Java到底是解释型的语言还是编译型的语言?
补充1:JDK:JRE:JVM三者之间的区别?
JDK=JRE+开发工集(例如:Javac编译工具等)
JRE=JVM+基础标准类库
补充2:理解“JVM是跨语言的平台”这句话

Java程序的运行过程可以概括为先通过编译工具javac将.java文件编译为.class字节码文件,接着通过java.exe把字节码文件加载到JVM,如下图所示:

从定性角度分析,.java文件转换到.class文件只不过是转换了一种形式而已,转换得到的.class文件称为字节码文件,它有固定的结构(参考字节码文件格式和虚拟机规范https://docs.oracle.com/javase/specs/jvms/se8/html/index.html)。当然,除了阅读这个文档之外,还可以通过javap命令来反编译字节码文件去分析其中的内容。
loading->linking->initializing
1.加载:通过类的全限定名来获得文件名,并查找导入磁盘的class文件
2.链接:
1)验证:保证字节码文件的格式正确(版本号、字节码、符号引用等)。
2)准备:为类的静态变量分配内存,并将其初始化为默认初始值。
3)解析:把类中的符号引用转化为直接引用。符号引用指的是由一组符号描述的目标,它可以是任何的字面量;直接引用是指直接指向目标的指针,或者说是真实的指针地址。

3.初始化:为类的静态变量赋值,然后执行类的初始化(static)语句。初始化的详细过程:
1)如果类还没有被加载和链接(即没有执行前两个过程),那就先进行加载和初始化;
2)如果类存在父类,并且父类还没有初始化,那就先初始化直接父类;
3)如果类中存在初始化语句,顺序执行初始化语句。
1.创建类的实例(四种方式);(哪四种方式:new一个对象;反射;序列化、反序列化;克隆)(说得再直白点,如果类的实例已经存在,那么相应的字节码文件一定也存在)
2.访问类中的某个静态变量,或者对静态变量进行赋值;
3.主动调用类的静态方法;
4.Class.forname("包类名");(完成了类的初始化)
5.完成子类的初始化,也完成对本类的初始化(接口例外);
6.该类是程序引导入口(main方法入口或者test入口)。
Demo实例分析:
//Demo.java //F.java public class F { public static int FCount = 200; static { System.out.println(FCount); } } //S.java public class S extends F { public static int SCount = 100; static { System.out.println(Scount); } } //Test.java public class Test { public static void main(String[] args) S.SCount = 1000; //问题:会先执行哪条打印语句 } }问题答案:先执行FCount的打印语句。原因:见初始化时机第5条。
JDK自带的Class Loader一共有三个,可以分成两大类:
1.Bootrap ClassLoader;
2.Extension ClassLoader;
3.Application ClassLoader
1.JVM自带的引导类加载器,由C/C++的语言实现,在Java中打印null;
2.加载Java的核心类库,$JAVA_HOME中jre/lib/rt.jar、resource.jar或Java程序运行指定的Xbootclasspath选项jar包;
3.指定加载java,javax,sun等开头的包类名。
如果自定义了一个类,这个类的包名为java.lang,那么new一个这个自定义类的对象就会报错,因为java开头的包类名不能自定义类!
1.Java语言编写的类加载器sun.misc.Launcher$ExtClassLoader(静态内部类)
2.指定Bootrap ClassLoader为Parent加载器-->getParent()可以获取Bootrap ClassLoader
3.负责架子啊java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/ext或-Djava.ext.dirs指定目录下的jar包(如果我们自定义的class需要交给Ext来加载可以放置到ext的目录下)
1.Java语言编写的类加载器sun.misc.Launcher$APPClassLoader(静态内部类)
2.该加载器是Java程序默认的类加载器,Java应用的类都是该类加载器加载的
3.指定Extension ClassLoader为parent加载器-->getParent()可以获取Extension ClassLoader
4.负责加载环境变量classpath指定的目录,或者java.class.path指定的目录类库

从源码分析可知,Extension ClassLoader和Application ClassLoader都是继承于ClassLoader这个抽象类。ClassLoader中的loadClass方法中实现了双亲委派:
loadClass源码:
protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } }这段代码的逻辑是:
1.先调用方法findLoadedClass()来判断是否已经存在这个类;
2.若存在,则跳过不执行;否则,执行try语句块中的代码;
3.递归判断parent是否为空并调用parent的loadClass方法。由于刚开始调用时类加载器为Application ClassLoader,所以parent为Extension ClassLoader不是null,继续调用parent的loadClass方法,Extension ClassLoader的parent为Bootrap ClassLoader,这时parent为null(表示不可访问),就会执行try语句块中else部分语句findBootstrapClassOrNull方法,这个方法调用C/C++编写的native方法private native Class findBootstrapClass(String name)来尝试加载这个类;接着也是一个递归过程。
4.如果Bootrap ClassLoader无法加载并且没有找到这个类就会交给子类Extension ClassLoader来尝试加载,如果它也无法加载并且没有找到对应的类,那么最终就会交给Application ClassLoader去加载,由Application ClassLoader实现类的加载过程。
1.避免类的重复加载;
2.保护程序安全,放置核心的Java语言环境遭受破坏。
如何理解?例如:java.lang包下的类都是非常宝贵的,例如String类,如果我自己定义了一个也叫做String的类,那么如何保证核心类库下的String类能够被正常加载呢?当new一个String对象时,实际上是由Bootrap ClassLoader调用native的方法findBootstrapClass得到的一个final类,而不是自定义的类所对应的对象。
进一步,如果想要破坏这样一个逻辑,或者说想要new的对象是自定义的String类的对象,那么可以通过重写loadClass方法来实现;换句话说,这也是为什么Application ClassLoader、Extension ClassLoader以及Bootrap ClassLoader没有重写这个方法的原因,所以只要调用loadClass方法就一定会按照这个逻辑执行。
1.适配数据源
2.环境隔离;
3.jar包共享;
4.防止源码泄露。

在JVM中划分了几大区域来分别存储不同时期或者不同class文件产生的不同的数据,有些区域随着JVM的启动就创建了并且随着JVM的关闭而销毁;其他的数据区域则是跟线程相关,即随着线程的创建而创建,随着线程的死亡而销毁。主要包括(参考JVM结构第2.5小节-Runi-Time Data Areas):
1. The pc Register(程序计数器)
通俗的讲,就是指下一个命令的地址。它与线程同创建同销毁。
由于JVM支持多线程,每个线程都会有自己的程序计数器,线程执行方法的过程中都会记录这个程序计数器。例如,线程A在调用某个方法,执行到某个过程时由于某种原因失去CPU执行权,而线程B获得了CPU执行权,当线程B完成某个任务之后,线程A又获得了CPU执行权,那么JVM需要知道从哪里开始继续执行。简单而言,就是用来保存当前当前线程当中执行的位置。
2. JVM stacks(Java虚拟机栈)
指当前执行线程的独占空间。与线程同创建同销毁。
初学时,我们简单粗暴的将JVM分为栈和堆,其中的栈就是这个虚拟机栈。
它以栈的形式存在,即先进后出。
保存的是栈帧(frame),每个栈帧中又保存了一些数据结构,例如局部变量、动态链接、返回地址等;或线程的运行状态,或理解为调用的方法。
3. Heap(堆)
程序运行中最需要关注的内容。
是线程共享的。与虚拟机同创建同消亡。
存储类的实例即对象和数组。
分为老年代、新生代,新生代又分为存活区、伊甸区,思考这样分的原因?
4. Method Area(方法区)
是所有线程共享的区域。随着JVM启动而创建。
保存的是类已经加载的信息,例如常量池、属性和方法数据以及方法体和构造器中编译后的代码。这些数据基本不变,而经常发生变化的保存在堆中
如果方法区的内存无法满足申请的需求时,会报出OutOfMemoryError的错误。
JDK8以前叫做永久代(Perm Space),1.8叫做metaspace。
5. Run-Time Constant Pool(运行时常量池)
每个常量池由JVM中的方法区进行分配。
6. Native Method Stacks(本地方法栈)
native方法(C/C++编写的方法)的保存位置,不能用JVM栈保存
JVM本质上也是一个程序、一个进独立的进程,最终还是要交给操作系统去运行,这就涉及到执行引擎。

思考问题:Java是编译型语言还是解释型语言? Ans:解释+编译
解释器:把class字节码文件中的指令翻译成机器语言、机器能够马上执行的指令;
即时编译器:
垃圾回收器:对内存共享区域(例如堆、方法区)的自动处理。

注意,图中把常量池合并到了方法区。
下一篇:Java的编译,加载过程
皓盘云建最新版下载v9.0 安卓版
53.38MB |商务办公
ris云客移动销售系统最新版下载v1.1.25 安卓手机版
42.71M |商务办公
粤语翻译帮app下载v1.1.1 安卓版
60.01MB |生活服务
人生笔记app官方版下载v1.19.4 安卓版
125.88MB |系统工具
萝卜笔记app下载v1.1.6 安卓版
46.29MB |生活服务
贯联商户端app下载v6.1.8 安卓版
12.54MB |商务办公
jotmo笔记app下载v2.30.0 安卓版
50.06MB |系统工具
鑫钜出行共享汽车app下载v1.5.2
44.7M |生活服务
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-02-15
2022-02-14