`

Java虚拟机体系结构

 
阅读更多

Java虚拟机体系结构

 

        如图,概念上讲,JVM由类加载器子系统,运行时数据区,执行引擎以及本地方法接口组成。
图4 JVM体系结构
1. 类加载器子系统主要用于定位类定义的二进制信息,然后将这些信息解析并加载至虚拟机,转化为虚拟机内部的类型信息的数据结构。类加载器子系统还承担着安全性的责任,并且是JVM的动态链接和动态加载的基础。将二进制信息=>类型信息的数据结构,中间需要经过很多步骤。首先类加载器是JVM安全沙箱的第一道防线,能够防止非信任类破坏虚拟机。每一个被加载的class文件需要经过四次校验才能被加载。校验通过后,类加载器的命名空间和运行时包的特性能够防止非信任类伪装成信任类来破坏虚拟机。类加载器在方法区构造具有这个类的信息的数据结构后,会在堆上创建一个Class对象作为访问这个数据结构的接口。同时,类加载还需要初始化类的静态数据,也就是调用类的<clinit>方法。以上就是一个类的加载、链接及初始化的过程。
2. 运行时数据区是JVM运行时的内存空间的组织,逻辑上又划分为多个区,这些区的生命周期和它是否线程共享有关,它们分别是:
        :用于存放对象或数组实例,也就是运行期间new出来的对象。堆的生命周期与JVM相同,并且在线程之间共享访问。由于多线程并发访问,所以需要考虑线程安全的问题,有两种方法。第一种是,加锁进行互斥访问。第二种是线程本地分配缓冲(Thread Local Allocate Buffer, TLAB),在线程创建时预先给每个线程分配一块区域,这块区域是线程私有的,对其他线程是不可见,也就不会被共享。JVM规范规定在申请不到足够的内存时,堆会抛出OutOfMemoryException。
        方法区:存放类型信息和运行时常量池(Runtime Constant Pool)。每个被类加载器加载的类都会在方法区中形成一个与子对应的类型信息的数据结构,包括:这个类的类名、直接超类、实现的接口列表、字段列表、方法列表等。运行时常量池是class文件中的常量池列表(Constant Pool List)在运行时的一种体现,其中存储各种基本数据类型及String类型的常量以及其他类、方法、字段的符号引用。方法区的生命周期与JVM相同,被多个线程共享,所以要考虑并发访问的安全性的问题。JVM规范规定在需要的内存得不到满足的情况下,方法区会抛出OutOfMemoryException。
        PC(Program Counter):线程私有的,生命周期与线程相同,是对CPU中PC的一种模拟。如果线程正在执行的是Java方法,则该线程的PC中存放的下一条字节码指令的地址。在进行Java方法的调用和返回时,需要更新PC以保存当前方法(Current Method)正在执行的字节码指令的地址。PC是JVM规范中唯一没有规定会抛出异常的存储区。
        JVM栈:线程私有,生命周期与线程相同,是对传统语言(比如C)中的方法调用栈的一种模拟。JVM栈中存放栈帧(Frame)用于进行方法调用和返回、存储局部变量以及计算的中间结果。JVM规范规定栈可以抛出两种异常:(1)StackOverflowException,在栈的深度大于某个规定值的情况下抛出。(2)OutOfMemoryException,在为新栈帧分配内存或者是为线程分配栈的内存时,申请不到足够的内存的情况下抛出。
        JVM栈中存放的是栈帧,每个栈帧对应着一次方法调用。每一时刻,JVM线程只能执行一个方法(Current Method),该方法的栈帧是JVM栈的栈顶的元素(叫做当前栈帧,Current Frame),当调用一个方法时,会初始化一个栈帧压入JVM栈;当方法调用返回或者抛出异常没有被处理的情况下,JVM栈会弹出该方法对应的栈帧。每一个栈帧中存放局部变量表(Local Variable Table)、操作数栈(Oprand Stack)以及其他栈帧信息。栈帧的大小在编译时就确定了,编译器会把局部变量表和操作数栈的大小记录在class文件中method_info的属性表中。局部变量表类似于数组存放局部变量和方法参数。由于JVM采用的是基于栈的指令集体系结构,而不是基于寄存器,所以JVM上的所有计算都是在操作数栈上进行的(比如,算术运算、方法调用、内存访问等)。
        本地方法栈:用于支持本地方法调用,抛出的异常与JVM栈相同。
3. 执行引擎用于执行JVM字节码指令,主要由两种实现方式:(1)将输入的字节码指令在加载时或执行时翻译成另外一种虚拟机指令;(2)将输入的字节码指令在加载时或执行时翻译成宿主主机本地CPU的指令集。这两种方式对应着字节码的解释执行和即时编译。比如在HotSpot VM中执行引擎的实现是一种解释-编译的层次结构:
(1)解释执行:解释执行字节码,并以方法为单位收集“热点(HotSpot)代码”的信息,将“热点代码”执行C0编译。
        (2)C0编译:将收集的“热点代码”编译成本地代码,并进行一些简单的优化。继续收集运行时信息,将一些频繁执行的本地代码进行C1编译。
        (3)C1编译:将C0阶段的本地代码,进行一些比较激进的优化。如果某些优化导致本地代码执行失败,此时JVM会退化到解释执行字节码阶段。
4. 自动内存管理用于管理运行时数据区的分配和释放。和C和C++相比,Java不需要程序员主动的管理内存(在new出对象后,不需要显示的delete),这样JVM就需要承担内存管理这个任务。内存管理的重点主要是在申请内存(new对象、类加载和初始化、启动线程时初始化栈等)得不到满足时,JVM可以自动回收那些不再存活的对象所占用的内存,也就是经常听到的垃圾收集。在回收过程中还要保证处理内存空间的碎片,以提高空间利用率。回收过程主要有两个关键点,标记存活对象和回收内存的算法。
       标记存活对象主要有引用计算和根搜索法两种。
       (1)引用计数,是一种很普遍的方法,在python、lua等一些脚本语言中都是使用这种算法。每个对象持有一个计数器,标记这个对象被引用的次数。进行垃圾收集时,那些引用计数为0的对象就是“死”对象,需要被收集。引用计数的一个缺点就是它没有办法处理循环引用的情况(A->B, B->A)。
       (2)根搜索,HotSpot虚拟机采用这种算法标记存活对象。把方法区、JVM栈中的所有的引用组成的集合作为搜索的根,从这个集合开始遍历直到结束。其中被遍历到的对象是存活对象;那些没有被遍历到的对象需要被垃圾收集。这样可以有效的避免循环引用的情况。
       回收内存的算法主要有:
       (1)复制算法,将内存分成两个部分,每一时刻只是用其中的一个。进行回收时,将所有存活的对象依次复制到另一个部分(依次复制避免了内存碎片的产生),接下来只用这一个部分。复制算法需要在两个内存区域来回复制,有一定的复制开销和空间开销(每一时刻只使用一个区域),但是可以很好的解决内存碎片的问题,适用于对象频繁创建并且生命周期短的情况。
       (2)标记清扫,先进行存活对象标记,回收时将“死”对象占用的内存直接释放掉,会产生大量的内存碎片。
       (3)标记整理,标记阶段与标记清扫算法一样,回收阶段释放“死”对象的内存后,还需要进行对象的移动使得所有对象依次在内存中排列,避免了内存碎片的产生。标记整理与复制算法相反,适用于对象创建不频繁,生命周期长得情况。
       (4)按代收集,将内存按照对象生命周期的不同划分为多个部分,每个部分采用不同的收集算法。目前,大部分商业虚拟机都是采用这种算法。比如,在HotSpot中,内存被划分为:新生代(New)、老年代(Old)和永久代(Perm)。新生代采用复制算法,老年代和永久代采用标记整理算法。内存分配、回收的策略是,对象首先在新生代分配,如果新生代内存不满足要求,则触发一次新生代内存的垃圾收集(Young GC,或者是Minor GC)。Young GC会导致部分新生代的对象被移动至老年代,一部分是因为新生代内存不足以放下所有的对象;另一部分是因为这些对象的年龄(每个对象都保存着这个对象被垃圾收集的次数,表示它的年龄。存储在对象头的age属性中)大到足以晋升到老年代。当新生代的对象进入老年代,而老年代的内存不满足要求时,则会触发一次整个新生代和老年代的垃圾收集(Full GC, 或者是Major GC)。
       在JVM中有多个后台线程用于完成自动内存管理,对于CPU来说这些后台线程和用户线程是一样的,都需要占用系统的资源。在GC线程进行垃圾收集时必须执行“Stop the World”这一操作,也就是暂停所有的用户线程。这就导致对于实时性要求比较高的系统,JVM的垃圾收集可能是一个短板。但是在JDK1.5,Sun提供了CMS(Concurrent Mark and Sweep)垃圾收集器,通过GC线程和用户线程并发执行减少GC时间,提高了JVM的实时性。在JVM的各种应用中,gc调优是一个关键的部分,主要目标是减少GC的次数并且降低每次GC的时间。关于这部分内容,后续的JVM内存管理会详细讨论。

五. JVM执行程序的流程

       在命令行执行"java Main"就会开启一个JVM实例,我们可以通过jps,jstat等JVM工具观察JVM的运行状态,下面以运行com.ntes.money.Main这个类为例来描述一下JVM执行一个程序的流程。
       当在命令行执行"java -Xmx=12m -Xms=12m -Dname=value com.ntes.money.Main"这个命令时,JVM的执行流程是,
       (1)加载JVM,主要是加载动态链接库,windows下是jvm.dll,Linux下是libjvm.so;
       (2)设置JVM启动参数,比如命令中的-Xmx=12m -Xms=12m用于设置堆大小。
       (3)初始化JVM。
       (4)调用类加载器子系统,加载com.ntes.money.Main。这里给出的是自定义类,根据类加载器双亲委派链,最后是由系统默认类加载器(Classpath类加载器)进行加载。首先,根据全路径类型转化为文件路径com/ntes/money/Main.class,然后读取Main.class中的二进制信息、解析、加载,在方法区中形成Main类对应的数据结构。这里可能抛出ClassNotFoundException,有两种原因。一是文件路径com/ntes/money/Main.class不存在;二是com/ntes/money/Main.class文件路径存在,但是Main.class文件中存储的不是Main类的信息,比如是Main1,Main2等其他类的信息。这种情况下,会抛出NoClassDefFoundError,然后导致ClassNotFoundException。
       (5)在方法区com.ntes.money.Main类对应的数据结构中,根据方法描述符及访问标志,查找main方法。这里的描述符,包括了方法的方法名、参数、返回值,也就是public static void main(String[])。如果找不到对应的main方法,会抛出NoSuchMethodError: main异常。
       (6)通过本地方法(JNI)执行main方法。
分享到:
评论

相关推荐

    Java虚拟机

    Java虚拟机 Java虚拟机体系结构 Java虚拟机的运行过程

    深入理解Java虚拟机体系结构

    主要介绍了深入理解Java虚拟机体系结构,具有一定借鉴价值,需要的朋友可以参考下

    深入Java虚拟机(原书第2版).pdf【附光盘内容】

     本书共分20章,第1-4章解释了java虚拟机的体系结构,包括java栈、堆、方法区、执行引擎等;第5-20章深入描述了java技术的内部细节,包括垃圾收集、java安全模型、java的连接模型和动态扩展机制、class文件、运算及...

    java虚拟机的详细原理

    一、什么是Java虚拟机 二、Java虚拟机的生命周期 三、Java虚拟机的体系结构

    Java虚拟机体系结构深入研究总结

     要对Java程序进行内存优化和性能调优,不了解虚拟机的内部原理(或者叫规范更严谨一点)是肯定不行的,这里推荐一本好书《深入Java虚拟机 (第二版)》(Bill Venners著,曹晓刚 蒋靖 译,实际上本文正是作者阅读本书...

    JAVA虚拟机精讲

    《Java虚拟机精讲》以极其精练的语句诠释了HotSpot VM 的方方面面,比如:字节码的编译原理、字节码的内部组成结构、通过源码的方式剖析HotSpot VM 的启动过程和初始化过程、Java 虚拟机的运行时内存、垃圾收集算法...

    Java虚拟机简介

    Java虚拟机简介 介绍了java虚拟机的体系结构组成,已经java虚拟机的执行过程

    JAVA虚拟机.ppt

    定义 简介 特点 使用主题 安装方法 支持的数据类型 规格描述 JVM指令系统 ... 体系结构 8.1Java指令集 8.2寄存器 8.3栈 8.4单位收集堆 8.5方法区 JVM的运行过程 Java虚拟机命令行参数说明 JVM存储区

    《深入Java虚拟机第二版》 高清完整PDF版

    本书共分20章,第1-4章解释了Java虚拟机的体系结构,包括Java栈、堆、方法区、执行引擎等;第5-20章深入描述了Java技术的内部细节,包括垃圾收集、Java安全模型、Java的连接模型和动态扩展机制、class文件、运算及...

    深入Java虚拟机(中文版第二版高清版)-带书签

    本书共分20章,第1-4章解释了Java虚拟机的体系结构,包括Java栈、堆、方法区、执行引擎等;第5-20章深入描述了Java技术的内部细节,包括垃圾收集、Java安全模型、Java的连接模型和动态扩展机制、class文件、运算及...

    深入Java虚拟机(原书第二版清晰版)

    本书共分20章,第1-4章解释了Java虚拟机的体系结构,包括Java栈、堆、方法区、执行引擎等;第5-20章深入描述了Java技术的内部细节,包括垃圾收集、Java安全模型、Java的连接模型和动态扩展机制、class文件、运算及...

    Java虚拟机和Java程序的生命周期?

    类加载器分为:Java虚拟机自带的加载器和用户自定义的类加载器; Java虚拟机自带的类加载器包括:启动类加载器,扩展类加载器,系统类加载器三种。 用户自定义的类加载器是ClassLoader类的实例,通过它来定制类的...

    深入JAVA虚拟机第二版.pdf 目录

    第1章 Java体系结构介绍 第2章 平台无关 第3章 安全 第4章 网络移动性 第5章 Java虚拟机 第6章 Java class文件 第7章 类型的生命周期 第8章 连接模型 第9章 垃圾收集 第10章 栈和局部变量操作 第11章 类型转换 第12...

    深入java虚拟机第二版

    第1-4章介绍了java虚拟机的体系结构,包栈、堆,方法区、执行引擎等; 第5-20章深入介绍了java的内部细节,垃圾回收、java安全模型、java的连接模型和动态扩张机制,class文件,运算及流程控制。 本书以利于理解的...

    深入Java虚拟机(原书第2版)及书中源代码

    第1章 Java体系结构介绍 第2章 平台无关 第3章 安全 第4章 网络移动性 第5章 Java虚拟机 第6章 Java class文件 第7章 类型的生命周期 第8章 连接模型 第9章 垃圾收集 第10章 栈和局部变量操作 第11章 类型转换 第12...

    jvm虚拟机原理.ppt

    经典JVM PPT,可以直接讲课的 java虚拟机原理介绍 Java虚拟机生命周期 Java虚拟机体系结构 Java 的class文件

    深入Java虚拟机

    5.3 Java虚拟机的体系结构 5.3.1 数据类型 5.3.2 字长的考量 5.3.3 类装载器子系统 5.3.4 方法区 5.3.5 堆 5.3.6 程序计数器 5.3.7 Java栈 5.3.8 栈帧 5.3.9 本地方法栈 5.3.10 执行...

Global site tag (gtag.js) - Google Analytics