一.概述

Java不同于C/C++这类传统的编译型语言,也不同于php这一类动态的脚本语言。可以说Java是一种半编译语言,我们所写的类会先被编译成.class文件,这个.class是一串二进制的字节流。然后当要使用这个类的时候,就会将这个类对应的.class文件加载进内存中。而将这个.class的内容加载进内存,正是通过Jvm类加载机制实现的。

虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。

二.类加载的各个步骤

加载

加载时“类加载”过程的第一步,在加载过程中,虚拟机需要完成以下三件事

  1. 通过一个类的全限定名来获取定义此类的二进制字节流。
  2. 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
  3. 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区的这个类的各种数据的访问入口。

值得一提的是,在加载阶段既可以使用系统提供的引导类加载器来完成,也可以由用户自定义的类加载器来完成,相对而是比较自由的,但对于数组则不是这样了,数组类本身不通过类加载创建,它是由Java虚拟机直接创建的。但数据所存放的元素类型是需要类加载器去创建的。

加载阶段与下一阶段的连接部分是交叉进行的,但加载阶段和连接阶段的开始时间仍然会保持固定的先后顺序。

验证

验证时连接阶段的第一步,这一阶段的目的是为了确保Class文件的字节流中包含的信息复合当前虚拟机的要求,并且不会危害虚拟机自身的安全。虽然说数组越界,将对象胡乱转型这些操作会被编译器拒绝编译,但.class文件并不一定要求从Java源码编译而来,可以从其他途径产生,故而需要对.class文件的二进制流进行验证。

验证阶段的重要性是不言而喻的,这一阶段是否严谨,直接决定了Java虚拟机是否能承受恶意代码的攻击,从执行性能的角度上讲,验证阶段的工作量在虚拟机的类加载系统中又占了相当大的一部分。

从整体上看,验证阶段大致可分为4部分的检验动作:文件格式验证,元数据验证,字节码验证,符号引用验证。

  • 符号验证:主要目的是保证输入的字节流能正确地解析并存储于方法区之内,格式上符合描述一个Java类型信息的要求。这一部分是基于二进制流验证的,之后会加载到内存中,后续验证是在内存中验证。
  • 元数据验证:这一验证主要是对类的元数据信息进行语义校验,保证不存在不符合Java语言规范的元数据信息。
  • 字节码验证:这一部分是验证阶段中最复杂的一阶段,主要目的是通过数据流和控制流分析,确定程序是合法的,符合逻辑的。
  • 符号引用验证:符号引用是发生在虚拟机将符号引用转化为直接引用的时候,目的是却好解析动作能正常执行。

准备

准备阶段是为正式类变量(静态变量)分配内存并设置类变量初始值的阶段,这些变量所使用的内存都讲在方法区中进行分配的。值得一提的是,这时候进行分配的仅为类变量(静态变量),而不包括实例变量。

通常情况下,设置类变量初始值,这个初始值指的是数据类型的默认值,比如int型则是0。但若类变量被final修饰,则情况又不一样,那样的话会直接对给定值进行赋值。

解析

解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。这里解释以下什么是符号引用,什么是直接引用。

符号引用:符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义得定位到目标即可。

直接引用:直接引用可以是指向目标的指针,相对偏移量或是一个能间接定位到目标的句柄。

解析动作主要针对类或接口,字段,类方法,接口方法,方法类型,方法句柄和调用点限定符7类符号引用进行。

初始化

类初始化阶段是类加载过程的最后一步,前面的类加载过程,除了在加载阶段用户应用程序可以通过自定义类加载器参与之外,其余动作完全由虚拟机主导和控制。到了初始化阶段,才会真正开始执行类中定义的Java代码。

在准备阶段,变量已经赋过一次系统要求的初始值,而在初始化阶段,则根据程序员通过程序制定的计划区初始化类变量和其他资源。

三.有意思的代码段

public class StaticTest
{ public static void main(String[] args)
{
staticFunction();
} static StaticTest st = new StaticTest(); static
{
System.out.println("1");
} {
System.out.println("2");
} StaticTest()
{
System.out.println("3");
System.out.println("a="+a+",b="+b);
} public static void staticFunction(){
System.out.println("4");
} int a=110;
static int b =112;
}

这段代码的运行结果是什么呢?

答案是:

2

3

a=110,b=0

1

4

这是为什么呢,大家不妨思考以下。

理解这段代码不光是要明白Java的类加载机制,还需要明白初始化阶段,静态代码块与静态成员变量的初始化顺是与代码顺序有关的。

类加载的过程是:装载–>连接(验证,准备,解析)–>初始化。

1.在准备阶段,会为类变量设置默认值,所以在案例一中:st=null,b=0,

2.在初始化阶段,会先执行类构造器,

换句话说,就是执行static修饰的代码块和为static修饰的变量赋值而已。而static修饰的代码块和类变量的执行顺序是按照它在文件中的先后顺序执行的。而static StaticTest st = new StaticTest()排在第一,所以会执行 new StaticTest(),也就是进行对象的初始化

2.1.在对象的初始化过程中,会先执行成员变量(代码块),然后再执行构造方法.成员变量的执行顺序也是谁先声明,谁先执行,所以排在第一的代码块

2.2成员变量执行完后,执行构造方法.此时,a=110,b=0;

3.由static StaticTest st = new StaticTest();触发的非静态代码的初始化过程到此结束,接下来继续执行静态代码的初始化,于是输出 1 。

4.整个类加载到此结束,执行代码,输出 4 。

再看下一道

public class StaticTest
{ public static void main(String[] args)
{
staticFunction();
} static
{
System.out.println("1");
} {
System.out.println("2");
} StaticTest()
{
System.out.println("3");
System.out.println("a="+a+",b="+b);
} public static void staticFunction(){
System.out.println("4");
} int a=110;
static int b =112;
static StaticTest st = new StaticTest(); //将这条语句放到最下面
}

仅仅是改变一条语句,而这段代码的运行结果是

1

2

3

a=110,b=112

4

大家不妨运用上面的知识,想想是为什么。


推荐阅读:

大数据存储的进化史 --从 RAID 到 Hdfs

贝叶斯分类算法实例 --根据姓名推测男女

从分治算法到 MapReduce

JVM 之类加载的更多相关文章

  1. JVM学习一:JVM之类加载器概况

    18年转眼就3月份都快结束了,也就是说一个季度就结束了:而我也因为年前笔记本坏了,今天刚修好了,那么也应该继续学习和博客之旅了.今年的博客之旅,从JVM开始学起,下面我们就言归正传,进入正题. 一.J ...

  2. JVM的类加载

    一.基本类加载机制介绍 大体引用一下<深入理解Java虚拟机>一书中对类加载的定义:虚拟机将描述类的二进制字节流(即Class文件)加载到内存中,并对其进行验证.准备.解析.初始化,最终 ...

  3. (转) JVM——Java类加载机制总结

    背景:对java类的加载机制,一直都是模糊的理解,这篇文章看下来清晰易懂. 转载:http://blog.csdn.net/seu_calvin/article/details/52301541 1. ...

  4. JVM自定义类加载器加载指定classPath下的所有class及jar

    一.JVM中的类加载器类型 从Java虚拟机的角度讲,只有两种不同的类加载器:启动类加载器和其他类加载器. 1.启动类加载器(Boostrap ClassLoader):这个是由c++实现的,主要负责 ...

  5. JVM内存结构 JVM的类加载机制

    JVM内存结构: 1.java虚拟机栈:存放的是对象的引用(指针)和局部变量 2.程序计数器:每个线程都有一个程序计数器,跟踪代码运行到哪个位置了 3.堆:对象.数组 4.方法区:字节流(字节码文件) ...

  6. JVM之类加载机制

    JVM之类加载机制 JVM类加载机制分为五个部分:加载,验证,准备,解析,初始化,下面我们就分别来看一下这五个过程. 类加载五部分 加载 加载是类加载过程中的一个阶段,这个阶段会在内存中生成一个代表这 ...

  7. JVM——自定义类加载器

    )以上两种情况在实际中的综合运用:比如你的应用需要通过网络来传输 Java 类的字节码,为了安全性,这些字节码经过了加密处理.这个时候你就需要自定义类加载器来从某个网络地址上读取加密后的字节代码,接着 ...

  8. 深入理解JVM的类加载

    前言: 前面又说到Java程序实际上是将.class文件放入JVM中运行.虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验,转换,解析和初始化,最终形成可以被虚拟机直接使用的Java类 ...

  9. JVM的类加载机制全面解析

    什么是类加载机制 JVM把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被JVM直接使用的Java类型,这就是JVM的类加载机制. 如果你对Class文件的结 ...

  10. JVM的类加载过程以及双亲委派模型详解

    JVM的类加载过程以及双亲委派模型详解 这篇文章主要介绍了JVM的类加载过程以及双亲委派模型详解,类加载器就是根据指定全限定名称将 class 文件加载到 JVM 内存,然后再转化为 class 对象 ...

随机推荐

  1. Analyzing .net core application with SonarQube Scanner for MSBuild

    SonarQube是管理代码质量一个开放平台,省略安装过程,下面介绍下如何使用sonarqube去扫描c# 代码. 前提:下载SonarQube Scanner for MSBuild.https:/ ...

  2. selenium之 玩转鼠标键盘操作(ActionChains)

    用selenium做自动化,有时候会遇到需要模拟鼠标操作才能进行的情况,比如单击.双击.点击鼠标右键.拖拽等等.而selenium给我们提供了一个类来处理这类事件——ActionChains sele ...

  3. 【python小工具】我在bilibili个人资料里控制家里的电脑

    今天在52学习到的,大佬A是在网易云音乐   歌单设置  里,过程没看到,封装一个exe了,可以控制本地cmd命令\ 思路很奇特,想了一下感觉实现应该简单,就打算自己实现一下\ 两步走:网页正则和本地 ...

  4. Android--UI之TextView

    前言 开门见山,这一篇博客主要讲一下在Android开发中,UI控件TextView的一些使用方式,并且通过四个例子实现一般项目中需要的效果来讲解TextView的使用.并且在之后的一段时间之内,都会 ...

  5. Activity的生命周期函数

    前言: 上一篇文章写了关于Activity生命周期和生命周期状态的介绍,这一篇文章给大家聊聊Activity生命周期函数. 主Activity: 应用程序的入口一般都是桌面应用程序图标,用户点击应用图 ...

  6. Elasticsearch实践(一):基础入门

    本文以 Elasticsearch 6.2.4为例. 注:最新(截止到2018-09-23)的 Elasticsearch 是 6.4.1.5.x系列和6.x系列虽然有些区别,但基本用法是一样的. 官 ...

  7. JavaScript和Ajax部分(1)

    1. JavaScript变量有哪些数据类型? JavaScript是一种弱类型语言,在声明变量时不需要指变量的类型,变量的类型由赋给变量的值来决定. 常用的基本数据类型: 1 undefined(未 ...

  8. UnicodeDecodeError: 'ascii' codec can't decode byte

    这个问题遇到过很多次了,但是每次都没记住,用完就忘了,这次记录下. 通过关键词谷歌一下: 解决方案: # encoding=utf8 import sys reload(sys) sys.setdef ...

  9. CC2530微处理器接口开发技术——信号灯的设计与实现

    本问主要介绍了CC2530处理器的通用输入/输出接口(GPIO),以及GPIO的位操作,理解GPIO的基本原理和功能,最后使用C语言驱动CC2530的GPIO实现对信号灯的控制. CC2530的GPI ...

  10. MySQL中间件之ProxySQL(14):ProxySQL+PXC

    返回ProxySQL系列文章:http://www.cnblogs.com/f-ck-need-u/p/7586194.html 1.ProxySQL+PXC 本文演示ProxySQL代理PXC(Pe ...