Java虚拟机的简单介绍

简介:
Java 虚拟机的简单介绍
1 ,什么是Java虚拟机
Java 虚拟机(JVM)是Java Virtual Machine的缩写,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能模拟来实现的。Java虚拟机有自己完善的硬件架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。 
2 ,为什么使用Java虚拟机?
Java 语言的一个非常重要的特点就是与平台的无关性。而使用Java虚拟机是实现这一特点的关键。一般的高级语言如果要在不同的平台上运行,至少需要编译成不同的目标代码。而引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用模式Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。  
3 Java虚拟机的基本原理
Java 虚拟机屏蔽了与具体操作系统平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。Java虚拟机在执行字节码时,实际上最终还是把字节码解释成具体平台上的机器指令执行。

Java虚拟机类装载的原理及实现

一、引言  
   Java 虚拟机 (JVM) 的类装载就是指将包含在类文件中的字节码装载到 JVM 并使其成为 JVM 一部分的过程。 JVM 的类动态装载技术能够在运行时刻动态地加载或者替换系统的某些功能模块 而不影响系统其他功能模块的正常运行。
本文将分析 JVM 中的类装载系统,探讨 JVM 中类装载的原理、实现以及应用。
二、 Java虚拟机 的类装载实现与应用
2.1  装载过程简介  
所谓装载就是寻找一个类或是一个接口的二进制形式并用该二进制形式来构造代表这个类或是这个接口的 class 对象的过程,其中类或接口的名称是给定了的。当然名称也可以通过计算得到,但是更常见的是通过搜索源代码经过编译器编译后所得到的二进制形式来构造。

  在 Java 中,类装载器把一个类装入 Java 虚拟机中,要经过三个步骤来完成:装载、链接和初始化,其中链接又可以分成校验、准备和解析三步,除了解析外,其它步骤是严格按照顺序完成的,各个步骤的主要工作如下:
装载:查找和导入类或接口的二进制数据;  
  链接:执行下面的校验、准备和解析步骤,其中解析步骤是可以选择的;  
  校验:检查导入类或接口的二进制数据的正确性;  
  准备:给类的静态变量分配并初始化存储空间;  
  解析:将符号引用转成直接引用;  
  初始化:激活类的静态变量的初始化 Java 代码和静态 Java 代码块。  
  至于在类装载和虚拟机启动的过程中的具体细节和可能会抛出的错误,请参看《 Java 虚拟机规范》以及《深入 Java 虚拟机》。   由于本文的讨论重点不在此就不再多叙述。
2.2  装载的实现
JVM 中类的装载是由 ClassLoader 和它的子类来实现的 ,Java ClassLoader  是一个重要的 Java 运行时系统组件。它负责在运行时查找和装入类文件的类。
Java 中, ClassLoader 是一个抽象类,它在包 java.lang , 可以这样说,只要了解了在 ClassLoader 中的一些重要的方法,再结合上面所介绍的 JVM 中类装载的具体的过程,对动态装载类这项技术就有了一个比较大概的掌握,这些重要的方法包括以下几个 :
loadCass 方法  loadClass(String name ,boolean resolve) 其中 name 参数指定了 JVM 需要的类的名称 , 该名称以包表示法表示 , Java.lang.Object resolve 参数告诉方法是否需要解析类,在初始化类之前 , 应考虑类解析,并不是所有的类都需要解析,如果 JVM 只需要知道该类是否存在或找出该类的超类 , 那么就不需要解析。这个方法是 ClassLoader  的入口点。
defineClass 方法   这个方法接受类文件的字节数组并把它转换成 Class 对象。字节数组可以是从本地文件系统或网络装入的数据。它把字节码分析成运行时 数据结构 、校验有效性等等。

   findSystemClass 方法  findSystemClass 方法从本地文件系统装入文件。它在本地文件系统中寻找类文件 , 如果存在 , 就使用 defineClass 将字节数组转换成 Class 对象 , 以将该文件转换成类。当运行 Java 应用程序时 , 这是 JVM  正常装入类的缺省机制。
  resolveClass 方法  resolveClass(Class c) 方法解析装入的类 , 如果该类已经被解析过那么将不做处理。当调用 loadClass 方法时 , 通过它的 resolve  参数决定是否要进行解析。 
findLoadedClass 方法   当调用 loadClass 方法装入类时 , 调用 findLoadedClass  方法来查看 ClassLoader 是否已装入这个类 , 如果已装入 , 那么返回 Class 对象 , 否则返回 NULL 。如果强行装载已存在的类 , 将会抛出链接错误。
2.3  装载的应用
一般来说,我们使用 虚拟机 的类装载时需要继承抽象类 java.lang.ClassLoader, 其中必须实现的方法是 loadClass() ,对于这个方法需要实现如下操作 :(1)  确认类的名称 ;(2)  检查请求要装载的类是否已经被装载 ;(3)  检查请求加载的类是否是系统类 ;(4)  尝试从类装载器的存储区获取所请求的类 ;(5)  在虚拟机中定义所请求的类 ;(6)  解析所请求的类 ;(7)  返回所请求的类。
所有的 Java  虚拟机都包括一个内置的类装载器,这个内置的类库装载器被称为根装载器 (bootstrap ClassLoader) 。根装载器的特殊之处是它只能够装载在设计时刻已知的类 , 因此虚拟机假定由根装载器所装载的类都是 安全 的、可信任的 , 可以不经过安全认证而直接运行。当应用程序需要加载并不是设计时就知道的类时 , 必须使用用户自定义的装载器 (user-defined ClassLoader) 。下面我们举例说明它的应用。
public abstract class MultiClassLoader extends ClassLoader{
  ...
  public synchronized Class loadClass(String s, boolean flag)
   throws ClassNotFoundException
   {
    /*  检查类 s 是否已经在本地内存 */
    Class class1 = (Class)classes.get(s);

    /*  s 已经在本地内存 */
    if(class1 != null) return class1; 
    try/* 用默认的 ClassLoader  装入类 */ {
     class1 = super.findSystemClass(s);
     return class1;
    }
    catch(ClassNotFoundException _ex) {
     System.out.println(">> Not a system class.");
    }

    /*  取得类 s 的字节数组 */
    byte abyte0[] = loadClassBytes(s);
    if(abyte0 == null) throw new ClassNotFoundException();
    /*  将类字节数组转换为类 */
    class1 = defineClass(null, abyte0, 0, abyte0.length);
    if(class1 == null) throw new ClassFormatError();
    if(flag) resolveClass(class1); /* 解析类 */
    /*  将新加载的类放入本地内存 */
    classes.put(s, class1);
    System.out.println(">> Returning newly loaded class.");

    /*  返回已装载、解析的类 */
    return class1;
   }
   ...
}
三、 Java 虚拟机的类装载原理
     前面我们已经知道,一个 Java 应用程序使用两种类型的类装载器:根装载器 (bootstrap) 和用户定义的装载器 (user-defined) 。根装载器是 Java虚拟机 实现的一部分,举个例子来说,如果一个 Java 虚拟机是在现在已经存在并且正在被使用的 操作系统 的顶部用 C 程序来实现的,那么根装载器将是那些 C 程序的一部分。根装载器以某种默认的方式将类装入,包括那些 Java API 的类。在运行期间一个 Java 程序能安装用户自己定义的类装载器。根装载器是虚拟机固有的一部分,而用户定义的类装载器则不是,它是用 Java 语言写的,被编译成 class 文件之后然后再被装入到虚拟机,并像其它的任何对象一样可以被实例化。  Java 类装载器的体系结构如下所示:

1 Java 的类装载的体系结构
   Java 的类装载模型是一种代理 (delegation) 模型。当 JVM  要求类装载器 CL(ClassLoader) 装载一个类时 ,CL 首先将这个类装载请求转发给他的父装载器。只有当父装载器没有装载并无法装载这个类时 ,CL 才获得装载这个类的机会。这样 所有类装载器的代理关系构成了一种树状的关系。树的根是类的根装载器 (bootstrap ClassLoader) ,  JVM  中它以 "null" 表示。除根装载器以外的类装载器有且仅有一个父装载器。在创建一个装载器时 如果没有显式地给出父装载器 那么 JVM 将默认系统装载器为其父装载器。 Java 的基本类装载器代理结构如图 2 所示:

2 Java 类装载的代理结构
下面针对各种类装载器分别进行详细的说明。  
  根 (Bootstrap)  装载器 : 该装载器没有父装载器,它是 JVM 实现的一部分,从 sun.boot.class.path 装载运行时库的核心代码。  
  扩展 (Extension)  装载器 : 继承的父装载器为根装载器,不像根装载器可能与运行时的操作系统有关,这个类装载器是用纯 Java 代码实现的,它从 java.ext.dirs ( 扩展目录 ) 中装载代码。  
  系统 (System or Application)  装载器 : 装载器为扩展装载器,我们都知道在安装 JDK 的时候要设置环境变量 (CLASSPATH ) ,这个类装载器就是从 java.class.path(CLASSPATH  环境变量 ) 中装载代码的,它也是用纯 Java 代码实现的,同时还是用户自定义类装载器的缺省父装载器。  
  小应用程序 (Applet)  装载器 装载器为系统装载器,它从用户指定的网络上的特定目录装载小应用程序代码。  
  在设计一个类装载器的时候,应该满足以下两个条件:
对于相同的类名,类装载器所返回的对象应该是同一个类对象  
  如果类装载器 CL1 将装载类 C 的请求转给类装载器 CL2 ,那么对于以下的类或接口 ,CL1 CL2 应该返回同一个类对象 :a)S C 的直接超类 ;b)S C 的直接超接口 ;c)S C 的成员变量的类型 ;d)S C 的成员方法或构建器的参数类型 ;e)S C 的成员方法的返回类型。  
  每个已经装载到 JVM 中的类都隐式含有装载它的类装载器的信息。类方法 getClassLoader  可以得到装载这个类的类装载器。一个类装载器认识的类包括它的父装载器认识的类和它自己装载的类,可见类装载器认识的类是它自己装载的类的超集。注意我们可以得到类装载器的有关的信息,但是已经装载到 JVM 中的类是不能更改它的类装载器的。  
   Java 中的类的装载过程也就是代理装载的过程。比如 :Web 浏览器中的 JVM 需要装载一个小应用程序 TestApplet JVM 调用小应用程序装载器 ACL(Applet ClassLoader) 来完成装载。 ACL 首先请求它的父装载器 即系统装载器装载 TestApplet 是否装载了这个类 由于 TestApplet 不在系统装载器的装载路径中 所以系统装载器没有找到这个类 也就没有装载成功。接着 ACL 自己装载 TestApplet ACL 通过网络成功地找到了 TestApplet.class  文件并将它导入到了 JVM 中。在装载过程中 , JVM 发现 TestAppet 是从超类 java.applet.Applet 继承的。所以 JVM 再次调用 ACL 来装载 java.applet.Applet 类。 ACL 又再次按上面的顺序装载 Applet 结果 ACL 发现他的父装载器已经装载了这个类 所以 ACL 就直接将这个已经装载的类返回给了 JVM ,  完成了 Applet 类的装载。接下来 ,Applet 类的超类也一样处理。最后 , TestApplet 及所有有关的类都装载到了 JVM 中。
四、结论
类的动态装载机制是 JVM 的一项核心技术 也是容易被忽视而引起很多误解的地方。本文介绍了 JVM 中类装载的原理、实现以及应用,尤其分析了 ClassLoader 的结构、用途以及如何利用自定义的 ClassLoader 装载并执行 Java 类,希望能使读者对 JVM 中的类装载有一个比较深入的理解。









本文转自 weijie@java 51CTO博客,原文链接:http://blog.51cto.com/weijie/74932,如需转载请自行联系原作者
目录
相关文章
|
1月前
|
存储 Java 数据安全/隐私保护
【JVM】Java虚拟机栈(Java Virtual Machine Stacks)
【JVM】Java虚拟机栈(Java Virtual Machine Stacks)
34 0
|
4月前
|
监控 安全 Java
Java基础知识:解释一下Java虚拟机(JVM)是什么。
Java基础知识:解释一下Java虚拟机(JVM)是什么。
39 3
|
18天前
|
缓存 Java C#
【JVM故障问题排查心得】「Java技术体系方向」Java虚拟机内存优化之虚拟机参数调优原理介绍(一)
【JVM故障问题排查心得】「Java技术体系方向」Java虚拟机内存优化之虚拟机参数调优原理介绍
57 0
|
3月前
|
Linux Windows
FinalShell连接Linux虚拟机报错java.net.ConnectException: Connection timed out: connect(亲测有效)
FinalShell连接Linux虚拟机报错java.net.ConnectException: Connection timed out: connect(亲测有效)
142 0
|
1月前
|
存储 Java 开发者
深入理解Java虚拟机(JVM)内存管理
【2月更文挑战第11天】 在本篇文章中,我们将深入探讨Java虚拟机(JVM)的内存管理机制,一项对于优化Java应用性能至关重要的技术领域。不同于常规的技术文章摘要,我们不仅概述了JVM内存管理的基本概念,还将引导读者通过实际案例理解其在现实世界应用中的重要性。从堆(Heap)和栈(Stack)的区别开始,到垃圾收集(Garbage Collection)机制的工作原理,本文旨在为Java开发者提供一个清晰、系统的JVM内存管理知识框架,帮助他们在开发过程中做出更加明智的决策。
|
2月前
|
存储 算法 Java
Java虚拟机内存管理机制
【2月更文挑战第7天】本文主要介绍了Java虚拟机内存管理机制的基本原理和实现方式。Java虚拟机的内存管理机制是Java程序运行的重要组成部分,对程序性能和稳定性有着直接的影响。文章首先从Java虚拟机内存模型入手,介绍了Java虚拟机中堆内存、方法区、栈、PC寄存器等内存区域的功能特点和使用方式;然后详细阐述了Java虚拟机内存管理机制的垃圾回收算法和回收器的分类、优化和实现过程;最后介绍了一些常见的内存问题和优化技巧,以及如何通过代码调优和合理使用内存配置参数来提高程序的性能和稳定性。
|
3月前
|
安全 前端开发 Java
【JVM】<Java虚拟机>JVM架构&各种**虚拟机
【1月更文挑战第26天】【JVM】<Java虚拟机>JVM架构&各种**虚拟机
|
3月前
|
自然语言处理 Oracle Java
【JVM】<Java虚拟机>JVM和JAVA体系结构
【1月更文挑战第26天】【JVM】<Java虚拟机>JVM和JAVA体系结构
|
3月前
|
监控 算法 Java
垃圾回收机制与性能调优:描述Java虚拟机(JVM)的垃圾回收算法,并解释为什么需要这些算法。如何使用Java内存分析工具(如VisualVM、JConsole或MAT)来识别和解决内存泄漏问题?
垃圾回收机制与性能调优:描述Java虚拟机(JVM)的垃圾回收算法,并解释为什么需要这些算法。如何使用Java内存分析工具(如VisualVM、JConsole或MAT)来识别和解决内存泄漏问题?
26 1
|
4月前
|
关系型数据库 MySQL Java
linux入门之虚拟机构建以及java环境搭建(1)
linux入门之虚拟机构建以及java环境搭建(1)