`
yucang52555
  • 浏览: 67916 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

深入java虚拟机——生命周期

阅读更多

      java虚拟机的存在就是为了一个任务——执行java程序。程序开始是他运行,结束时他停止。对应的,需要指出,在一台机器上运行了三个程序的话,就会有三个运行中的虚拟机(曾经一度错误的理解过)。
      java虚拟机实例通过调用某个初始类的main()方法来运行一个Java程序。而这个main()方法必须是共有的(public)、静态的(static)、返回值为void,并且接受一个字符串数组作为参数。任何拥有这样一个main()方法的类都可以作为Java程序运行的起点,任何其他的线程都是由这个初始线程启动的。
      在Java虚拟机内部有两种线程:守护线程和非守护线程。守护线程通常是由虚拟机自己使用的,比如执行垃圾收集任务的线程。但是,Java程序也可以把它创建的任何线程标记为守护线程。而Java程序中的初始线程——就是开始于main()的那个,是非守护线程。   只要还有任何非守护线程在运行,那么这个Java程序也在继续运行。当该程序中所有的非守护线程都终止时,虚拟机实例将自动退出。假若安全管理器允许,程序本身也能够通过调用Runtime类或者System类的exit()方法来退出。
      从另一个角度来说,C/C++等纯编译语言从源码到最终执行一般要经历:编译、连接和运行三个阶段,连接是在编译期间完成,而java在编译期间仅仅是将源码编译为Java虚拟机可以识别的字节码Class类文件,Java虚拟机对中Class类文件的加载、连接都在运行时执行,虽然类加载和连接会占用程序的执行时间增加性能开销,但是却可以为java语言带来高度灵活性和扩展性,java的针对接口编程和类加载器机制实现的OSGi以及热部署等就是利用了运行时类加载和连接的特性。


      java的Class类在虚拟机中的生命周期依次包括如下几个步骤:加载(loading)—>验证(verifycation)—>准备(prepareation)—>解析(Resolution)—>初始化(initialization)—>使用(using)—>卸载(unloading)。上一段中我们包含的连接包括:验证、准备、解析三个步骤。另外需要指明的是:加载、验证、准备、初始化和卸载这个五个阶段的顺序是确定的,而解析阶段则不一定,在某些情况下为了支持java语言的运行时动态绑定,也可以在初始化阶段之后再开始。各个步骤操作如下:


      (1)、加载:java虚拟机把Class类文件加载到内存中,并对Class文件中的数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的java类型的过程。
      在加载阶段,java虚拟机需要完成以下3件事:
      a.通过一个类的全限定名来获取定义此类的二进制字节流。
      b.将定义类的二进制字节流所代表的静态存储结构转换为方法区的运行时数据结构。
      c.在java堆中生成一个代表该类的java.lang.Class对象,作为方法区数据的访问入口。
  加载阶段与连接阶段是交叉进行的,加载阶段尚未完成,连接阶段可能已经开始,这些夹在加载阶段之中进行的动作仍然属于连接阶段,加载和连接阶段仍然保持着固定的先后顺序。


      (2)、验证:验证是连接阶段的第一步,其目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机的安全,如果验证失败,会抛出java.lang.VerifyError异常。
      验证阶段的主要工作有:
      a.文件格式验证:验证Class文件魔数、主次版本、常量池、类文件本身等等。
      b.元数据验证:主要是对字节码描述的信息进行语义分析,包括是否有父类、是否是抽象类、是否是接口、是否继承了不允许被继承的类(final类)、是否实现了父类或者接口的方法等等。
      c.字节码验证:是整个验证过程中最复杂的,主要进行数据流和控制流分析,如保证跳转指令不会跳转到方法体之外的字节码指令、数据类型转换安全有效等。
      d.符号引用验证:发生在虚拟机将符号引用转化为直接引用的时候(连接第三阶段-解析阶段进行符号引用转换为直接引用),符号引用验证的目的是确保解析动作能正常执行,如果无法通过符号引用验证,则会抛出java.lang.IncompatibleClassChangeError异常的子类异常,如java.lang.IllegalAccessError、java.lang.NoSuchFieldError、java.lang.NoSuchMethodError等。
验证阶段对于虚拟机来说非常重要,但是不是一个必需的阶段,如果所运行的代码已经反复被使用和验证过了,可以通过-Xverify:none参数关闭大部分的验证措施,以提高虚拟机时间效率。


      (3).准备:准备阶段是正式为类变量(静态变量,注意不是实例变量)分配内存并设置类变量初始值的阶段,这些内存都将在方法区中进行分配。
      特别注意:
      对于普通非final的类变量,如public static int value = 123;在准备阶段过后的初始值是0(数据类型的零值),而不是123,而把123赋值给value是在初始化阶段才进行的动作。
      对于final的类变量,即常量,如public staticfinal int value =123;在准备阶段过程的初始值直接就是123了,不需要准备为零值。


      (4).解析:解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。
      符号引用(SymbolicReference):以一组符号来描述所引用的目标,与虚拟机内存布局无关,引用的目标不一定已经被加载到虚拟机内存中。
      直接引用(DirectReference):可以直接指向目标的指针、相对偏移量或者是一个能间接定位到目标的句柄。直接引用和虚拟机实现的内存布局相关,同一个符号引用在不同虚拟机上翻译处理的直接引用不一定相同,如果有了直接引用,则引用的目标对象必须已经被加载到虚拟机内存中。
      解析的动作主要针对类或接口、字段、类方法、接口方法四类符号引用进行解析。


      (5).初始化:初始化是类使用前的最后一个阶段,在初始化阶段java虚拟机真正开始执行类中定义的java程序代码。
      Java虚拟机规范严格规定了有且只有以下四种情况必须立即对类进行初始化:
      a.遇到new、获取静态变量(final常量除外)、为静态变量赋值以及调用静态方法时,如果类没有进行过初始化,则需要先触发其初始化。
      b.使用java.lang.reflect包的方法对类进行反射调用的时候(Class.forName(…)),如果类还没有初始化,需要先触发对其的初始化。
      c.当初始化一个类的时候,如果发现其父类还没有初始化,则需要先触发对其父类的初始化。
      d.当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的类),虚拟机会先初始化这个类。
     上述四种情况称为对一个类的主动引用,除此之外的引用方式都不会触发初始化,称为被动引用。初始化的过程其实就是一个执行类构造器方法的过程,类构造器执行的特点和注意事项:
      1).类构造器方法是由编译器自动收集类中所有类变量(静态非final变量)赋值动作和静态初始化块(static{……})中的语句合并产生的,编译器收集的顺序是由语句在源文件中出现的顺序决定。静态初始化块中只能访问到定义在它之前的类变量,定义在它之后的类变量,在前面的静态初始化中可以赋值,但是不能访问。
      2).类构造器方法与实例构造器方法不同,它不需要显式地调用父类构造器方法,虚拟机会保证在调用子类构造器方法之前,父类的构造器方法已经执行完毕。
      3).由于父类构造器方法先与子类构造器执行,因此父类中定义的静态初始化块要先于子类的类变量赋值操作。
      4). 类构造器方法对于类和接口并不是必须的,如果一个类中没有静态初始化块,也没有类变量赋值操作,则编译器可以不为该类生成类构造器方法。
      5).接口中不能使用静态初始化块,但可以有类变量赋值操作,因此接口与类一样都可以生成类构造器方法。
      这里我们也可以考虑一下接口与类不同点:
      首先,执行接口的类构造器方法时不需要先执行父接口的类构造器方法,只有当父接口中定义的静态变量被使用时,父接口才会被初始化。其次,接口的实现类在初始化时同样不会执行接口的类构造器方法。


      (6).使用:
      java虚拟机会保证一个类的方法在多线程环境中被正确地加锁和同步,如果多个线程同时去初始化一个类,只会有一个线程去执行这个类的方法,其他线程都需要阻塞等待,直到活动线程执行方法完毕。初始化阶段,当执行完类构造器方法之后,才会执行实例构造器的方法,实例构造方法同样是按照先父类,后子类,先成员变量,后实例构造方法的顺序执行。当初始化完成之后,java虚拟机就可以执行Class的业务逻辑指令,通过堆中java.lang.Class对象的入口地址,调用方法区的方法逻辑,最后将方法的运算结果通过方法返回地址存放到方法区或堆中。


      (7).卸载:当对象不再被使用时,java虚拟机的垃圾收集器将会回收堆中的对象,方法区中不再被使用的Class也要被卸载,否则方法区(Sun HotSpot永久代)会内存溢出。
      Java虚拟机规定只有当加载该类型的类加载器实例为unreachable状态时,当前被加载的类型才被卸载.启动类加载器实例永远为reachable状态,由启动类加载器加载的类型可能永远不会被卸载,类型卸载仅仅是作为一种减少内存使用的性能优化措施存在的,具体和虚拟机实现有关,对开发者来说是透明的.
      卸载自定义来加载器加载的类的可靠做法为:
      a.每次创建特定类加载器的新实例来加载指定类型的不同版本,这种使用场景下,一般就要牺牲缓存特定类型的类加载器实例以带来性能优化的策略了.
      b.对于指定类型已经被加载的版本, 会在适当时机达到unreachable状态,被unload并垃圾回收.每次使用完类加载器特定实例后(确定不需要再使用时), 将其显示赋为null, 这样可能会比较快的达到jvm 规范中所说的类加载器实例unreachable状态, 增大已经不再使用的类型版本被尽快卸载的机会.

程序猿行业技术生活交流群:181287753(指尖天下),欢迎大伙加入交流学习。

 

0
0
分享到:
评论

相关推荐

    深入java虚拟机(三)——类的生命周期(下)类的初始化1

    摘要视图订阅曹胜欢欢迎关注微信账号:java那些事:csh624366188.每天一篇java相关的文章登录 | 注册学习专区(140)Java程序员从笨鸟到菜

    深入java虚拟机(二)——类的生命周期(上)类的加载和连接1

    摘要视图订阅曹胜欢欢迎关注微信账号:java那些事:csh624366188.每天一篇java相关的文章登录 | 注册学习专区(140)Java程序员从笨鸟到菜

    java7rt.jar源码-Java_JVM:这是我的JavaJVM学习笔记

    JVM的生命周期: 1.启动 通过引导类加载器(Bootstrap class loader)创建一个初始类(Initial Class)来完成 2.执行 执行一个所谓的Java程序时,真正的执行的是一个叫做Java虚拟机的进程 3.退出 程序正常结束;程序...

    java初学者必看

    1.7.2 Java虚拟机的生命周期 1.7.3 Java虚拟机的体系结构 1.8 垃圾收集器 1.9 本章习题 第2章 Java开发环境 2.1 J2SE的下载和安装 2.1.1 J2SE的下载 2.1.2 J2SE的安装 2.2 环境变量的配置与测试 2.2.1 设置...

    Java开发技术大全 电子版

    3.2.4对象的生命周期106 3.2.5对象的创建106 3.2.6对象的使用108 3.2.7对象的释放和垃圾收集机制108 3.3成员变量的定义与使用109 3.3.1成员变量的定义109 3.3.2成员变量的访问权限110 3.3.3实例成员变量和...

    Android典型技术模块开发详解

    3.2 Activity的生命周期 3.3 基本用法 3.3.1 创建Activity 3.3.2 启动Activity 3.3.3 窗口Activity 3.3.4 Activity生命周期验证 3.4 Activity之间通信 3.4.1 Activity传递一般类型 3.4.2 Activity传递对象类型 3.4.2...

    新版Android开发教程.rar

    程序可以采用 JAVA 开发,但是因为它的虚拟机 (Virtual Machine) Dalvik ,是将 JAVA 的 bytecode 转成 自 己的格式,回避掉需要付给 SUN 有关 JAVA 的授权费用。 对手机制造者的影响 � Android 是款开源的移动计算...

    Android程序设计基础

    通过逐渐在游戏中添加特性,你将学习Android编程的许多方面,包括用户界面、多媒体和Android生命周期。..  第一部分中将首先介绍Android,内容涉及如何安装Android模拟器,如何使用IDE(Intergrated Development ...

    Android高级编程.pdf

    3.4 Android应用程序生命周期 3.5 理解应用程序的优先级和进程状态 3.6 分离资源 3.6.1 创建资源 3.6.2 使用资源 3.6.3 To-DoList资源示例 3.6.4 为不同的语言和硬件创建资源 3.6.5 运行时配置更改 3.7 深入探讨...

    Android高级编程--源代码

    3.4 Android应用程序生命周期 48 3.5 理解应用程序的优先级和进程状态 49 3.6 分离资源 50 3.6.1 创建资源 50 3.6.2 使用资源 57 3.6.3 To-Do List资源示例 60 3.6.4 为不同的语言和硬件创建资源 61 3.6.5 ...

    工程硕士学位论文 基于Android+HTML5的移动Web项目高效开发探究

    而将那些被认为“过时”或有功能缺失的浏览器下的测试工作安排在开发周期的最后阶段 渐进增强 一开始只构建站点的最少特性,然后不断针对各浏览器追加功能。先让网站能够正常工作于尽可能旧的浏览器上,然后不断为它...

Global site tag (gtag.js) - Google Analytics