java运行过程

一、安装环境

大家在开发Java的时候,首先回装一个java的开发环境,一个JDK(也包含了JRE),然后设置环境变量,这个过程我就不细说了,大家装完后有没有发现,在装完这个环境的同时在安装JRE,在JDK的文件夹下还有一个jre文件夹,说这个有什么意思呢,首先我们要理解JDK和JRE的区别。

JDK:开发环境。

JRE:运行环境。

就是这么简单,那么是怎么运行起来的呢,我们一定经历过这样的一个阶段,在黑乎乎的窗口敲下下面的代码:

javac hello.java

这个过程就是江hello.java这个程序编译成二进制序列的.class文件,它用的就是JDK中的工具。

这个编译后的.class文件是给JRE看的,因为也只有它能看的懂了。它拿他只需要负责其运行就好。

说这个和运行有什么关系?JRE下包含了一个java运行的核心JVM(java虚拟机),JVM是java的核心,也是运行的核心。

二、java的运行

java的运行,首先,计算机会启动一个进程(注意:线程和进程的区别),运行JVM,所有java的线程都在这个JVM进程中执行,不断这个java程序有多少的线程。但是这个JVM也有其生命周期,总有一个程序的开始Main()来启动整个程序的线程,一般称为普通线程,JVM也有自己的线程,一般称为守护线程。当JVM中还有普通线程的时候,它的生命就一直持续,那么什么时候这个JVM会死亡呢?出现下面几种情况下,它就会死亡:

1、程序正常运行结束。

2、程序运行到System.exit()或者Runtime.getRuntime().exit()代码的时候结束

3、非法强制关闭JVM

4、遇到程序中未捕捉到的异常或者错误

关于JVM的介绍网上很多,我这里给出一个链接,大家可以看看:http://www.importnew.com/17770.html

2.1、类的加载

2.1.1 什么是类的加载

当程序再运行的过程中,会调用某个类,当这个类没有加载到内存的时候,那么系统做的第一步就是加载这个类到内存,说的直白点,就是读取我们编译好的.class文件到内存,但是这个过程中有一个重要的环节,就是在加载这个.class文件的时候也会创建一个一个对象java.lang.Class对象,和这个文件所对应的具体什么对象无关,也就是说,无论加载一个什么类,它都会为这个类创建一个java.lang.Class对象,可以这么理解,我们所谓的类,也是一个对象,不过我们的类是Class的实例,那么这个Class是什么对象,我从文档中,找到这样的一个介绍:

这里有一句话:Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。

这里面也透露了一个信息:类的加载是通过类加载器实现的。

2.1.2、类加载来源

类的加载器通过哪些渠道加载.class文件?有下面几个渠道:

1、本地的.class文件

2、JAR包中的.class文件

3、通过网络加载.class文件

在上面给的连接中,也对其做了一些介绍,JVM中,在启动的时候,会形成三个类加载器组成的出事类加载器:

1、Bootstrap ClassLoader:根类加载器(bootstrap 加载器负责载入基础的 Java API,比如包含 rt.jar。它只加载拥有较高信任级别的启动路径下找到的类,因此跳过了很多普通类需要做的校验工作。)

2、Extension ClassLoader:扩展类加载器(加载了标准 Java 扩展 API 中的类,比如 security 的扩展函数。)

3、System ClassLoader:系统类加载器(应用的默认类加载器,比如从 classpath 中加载应用类。)

4、用户自定义ClassLoader:用户自定义加载器

也许会很好奇,这些究竟会加载哪些包:

Bootstrap ClassLoader:%JAVA_HOME%jrelib文件夹下的一些包

Extension ClassLoader:%JAVA_HOME%jrelibext文件夹下的一些包

System ClassLoader:命令中-classpath选项或者java.class.path系统属性,或者CLASSPATH环境变量中定义的JAR包和类的路径。

2.1.3、类加载器执行顺序和类的加载流程

类的加载器的使用顺序如下:

一个类的加载的时候会先检查缓存中是否已经加载过,怎么识别呢,java关于类是否加载过的主要三个参数是,类名,包名 ,类加载器,三个元素组成了这个类加载的“id”,唯一的id,如果加载过了,就会直接加载,如果没有加载过会先让父元素先加载,如果父元素也无法加载的时候再阶级而下的加载器进行加载,如果未找到这个类,就会排除ClassNotFFoundException异常。

类的加载流程如下:

2.1.4、用户类加载器

在上面我们提到了用户类加载器,一般情况下用户不需要建立自己的加载器,但是在一些特殊情况下,我们也可以创建自己的类加载器,只要继承java.lang.Class对象,从之前的介绍可以看出这个类是一个抽象类。

我们主要介绍下这个类中的以下重要的方法:

1、loadClass(String name,boolean resolve):使用指定二进制名称加载类,name二进制名称,resolve是否分析这个类(关于分析后面介绍)

2、loadClass(String name):和上面的作用相同,相当于loadClass(name,false);

3、findClass(String name):使用指定二进制名查找类。

4、findLoadedClass(String name):如果 Java 虚拟机已将此加载器记录为具有给定二进制名称的某个类的启动加载器,则返回该二进制名称的类。

在我们创建用户类加载器的时候,最好是重写findClass方法,这个方法的执行的顺序如下:

1、先执行findLoadedClass(String)来检查是否已经加载了这个类,如果加载了直接返回这个类。

2、在父类加载器上调用loadClass方法,如果父类加载器为null,则使用根类加载器来加载。

3、调用findClass(String)查找类

所以重写findClass方法可以避免覆盖默认类加载器的父类委托,缓冲机制两种策略。如果重写loadClass方法,实现的逻辑比较复杂。

2.2、类的链接

类的链接包含了三块,具体详情,看这一块(http://blog.csdn.net/jintao_ma/article/details/51353453):

1、验证

验证是链接阶段的第一步,这一步主要的目的是确保class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身安全。
验证阶段主要包括四个检验过程:文件格式验证、元数据验证、字节码验证和符号引用验证。

2、准备:

类准备阶段扶着为类的静态属性分配内存,并设置默认初始值(注意:是默认初始值,不是初始值)。

3、解析:

将类的二进制数据中的符号引用直接替换成直接引用。

2.3、类的初始化

类的初始化,主要是为类的静态属性赋予初始值。通常会在两个地方进行初始化赋值,一个是静态变量直接赋予初始化值,一个是在静态初始化块中进行初始化赋值。

原文地址:https://www.cnblogs.com/chihirotan/p/6028172.html