赞
踩
本章概要介绍HotSpot VM,其有有三个主要组件:
VM运行时(Runtime)、JIT编译器(JIT Compiler)以及内存管理器(Memory Manager)
图3-1 HotSpot Vm基本架构
如图所示,JIT编译器(Client或Server)和垃圾收集器(Serial、Throughput、CMS或G1)都是可插拔的。
HotSpot为VM提供启动、线程管理、JNI(Java本地接口)等基本功能。
64位HotSpot VM对寻址对一些应用有帮助,但64位VM也带了性能损失:HotSpot VM内部Java对象表示(成为普通对象指针,Ordinary Object Pointers,或oops)的长度从32位变成了64为,导致CPU高速缓存行(CPU Cache Line)中可用的oops变少,从而降低了CPU缓存的效率。
缓存效率的降低常常导致性能比32位JVM下降8%~15%,最新的HopSpot VM添加了压缩指针(Compressed oops, -XX:+UseCompressedOops开启)的新特性。同时在一些ing台上也可以使用更多的CPU寄存器。
某些情况下只需要调整VM运行时环境的选项参数就可以显著改善Java应用的性能
HotSpot VM运行时换担当许多职责,包括命令行选项解析、VM生命周期管理、类加载、字节码解释、异常处理、同步、线程管理、Java本地接口、VM致命错误处理和C++(非Java)堆管理
HotSpot VM运行时可以解析命令行选项。
命令行选项可以指定选择哪个JIT编译器、选择何种垃圾收集器等,还有一些经启动器处理后传给完成启动的HotSpot VM,例如指定Java堆的大小。
命令行选项主要有3类:标准选项、非标准选项和非稳定选项
命令行选项用于控制HotSpot VM的内部变了,每个变量都有类型和默认值。
HotSpot VM运行时系统负责启动和停止HotSpot VM
启动HotSot VM的组件时启动器
HotSpot VM有若干个启动器。
启动器启动HotSpot MV时会执行一系列操作,步骤如下
启动器会直接处理一些命令行选项,例如-client或-server,它们决定加载哪个JIT编译器,其他参数则传给HotSpot VM
如果命令行没有指定堆大小和JIT编译器(client或server),则自行优化设置
与后创建的线程相比,初始线程:是启动新进程时操作系统内核分配的第一个线程,而新疆HotSpot VM进程中运行的出时间线程也是同样的道理。
不在初始线程中创建HotSpot VM,是为了可以对它进行定制,例如windows上更改栈的大小。
至此,HotSpot VM开始正式执行命令行指定的Java程序
一旦Java程序集或者Java main方法执行结束,HotSpot VM就必须检查和清理所有程序或者方法执行过程生成的未处理异常,并将返回值返回给调用方。然后会调用Java本地接口方法DetachCurrentThread将Java main方法与HotSpot VM脱离(Detached)。
每次HotSpot VM调用DetachcurrentThread时,线程数就会减1,因此Java本地接口知道何时可以安全关闭HotSpot VM,并确保当时HotSpot VM没有正在执行的操作,Java栈中也没有激活的Java帧。
因为HotSpot VM创建的静态数据结构无法再次初始化,所以一旦初始化到达某个确定点后,进程空间里就只能有一个HotSpot VM。在HotSpot VM的开发工程师看来,HotSpot VM启动至此已经是无法逆转了。
保护页是不可访问的内存页,用作内存访问区域的边界。例如,操作系统通常在线程栈顶压入一个保护页以保证引用不会超出栈的边界
如果HotSpot VM启动过程中发生错误,启动器则调用DestroyJavaVM方法关闭HotSpot VM。如果HotSpot VM启动后的执行过程中发生很严重的错误,也会调用DestroyJavaVM方法
DestroyJavaVM按以下步骤停止HotSpot VM
HotSpot VM支持符合JVM规定中所定义的类加载。
HotSpot VM和Java SE类加载库共同负责类加载。
HotSpot VM负责解析常量池符号,这个过程需要加载、链接,然后初始化Java类和Java接口
术语类加载用以描述类名或接口名映射到类(Class)对象的整个过程,Java Virtual Machine Specification则更明确地定义了类加载的3个阶段:加载、链接和初始化。
类加载的最佳时机是在解析Java字节码类文件中常量池符号的时候。
类加载时机
JavaApi如:Class.forName(), Classloader.loadClass()、反射API和JNI_FindClass都可以引发类加载。
HotSpot VM本身也可以引发类加载。
类加载内容
加载类时需要加载它的所有Java超类和所有Java超接口。此外,作为链接阶段的一部分,类文件验证也需要加载一些其他类**。实际上,加载夹断时HotSpot VM和特定类加载起比如java.langClassLoader之间相互协作的过程**。
第一步:
对于给定的Java类或接口,类加载时会依据它的名字找到Java类的二进制类文件,定义Java类,然后创建代表整个类或结偶的java.lang.Class对象。
如果没有找到Java类或接口的二进制表示 ,就会抛出NoClassDefFound。
此外,类加载阶段会对类的格式进行语法检查,如果有错,则会抛出ClassFormatError或UnsupportedClassVersionError。
Java类加载前,HotSpot VM必须先加载它的所有超类和超接口。
如果类的继承层次有错,例如Java类是它自己的超类或超接口(类层次递归),HotSpot VM则会抛出ClassCircularityError。
如果所引用的直接超接口本身并不是接口,或者直接超类实际上是接口,HotSpot VM则会抛出IncompatibleClassChangeError
第二步:
链接的第一步是验证,检查类文件的语义、常量池符号以及类型
如果检查有错,就会抛出VerifyError
第三步:
链接的下一步是准备,他会创建静态字段,初始化为标准默认值,以及分配方法表
请注意,此时还没有执行任何Java代码。
后续:
接下来是解析符号引用,这一步是可选的。然后初始化类,运行类构造器。
这是迄今为止,类中运行的第一段代码。值得注意的是,初始化类需要首先初始化超类(不会初始化超接口)
JVM Specification规定首次使用类时进行类初始化,而Java Language Specification则允许在链接阶段符号解析时灵活处理,只要保持语言的语义不变,JVM依次执行加载、链接和初始化,保证及时抛出错误即可。
出于性能优化的考虑,通常直到类初始化时候HotSpot VM才会加载和链接类。
当请求类加载器查找和加载某个类时,该类加载器可以转而请求别的类加载器来加载,这被称为类加载器委派。
就字节码解析而言,某个类的初始类加载器是指对该类进行常量池符号解析的类加载器
类加载器的委派关系定义了二进制类的查找顺序。
Java SE类加载器的层级(由高到低)查找顺序为:启动类加载器、扩展类加载器及系统类加载器(应用程序类加载器。
启动类加载器由HotSpot VM实现,负责加载BOOTCLASSPATH路径中的类,如包含JavaSE类库的rt.jar。
Client模式的HotSpotVM可以通过类型数据共享的特性使用与加载的类,这个特性默认开启,Server模式不支持;且即使是Client模式,也只有使用Serial收集器时才支持该机制
Java的类型由全限定名和类加载器唯一确定
换言之,类加载器定义了命名空间,这意味着两个不同的类加载器加载的类,即使全限定名相同,仍然是两个不同的类型。
如果有用户类加载器,HotSpotVM则需要确保类型安全不被恶意的类加载器破坏。
当类A调用B.someMethod()时,HotSpot VM会追踪和检查类加载器约束,从而确保A和B的类加载器所看到的sometMehtod()方法前面(包括参数列表和返回类型)是一致的
instanceKlass引用了与之对应的java.lang.Class实例,后者是前者的Java镜像。HotSpot VM内部使用称为klassOop的数据结构访问instanceKlass。后缀“Oop”表示普通对象指针,所以klassOop是引用java.lang.Class的HotSpot内部抽象,他是指向Klass(与Java类对应的内部表示)的普通对象指针
类加载过程中,HotSpot VM维护了3张散列表:SystemDictionary、PlaceHolderTable和LoaderConstraintTable。
目前只有在安全点才能移除SystemDictionary中的元素
这些散列表都需要加锁以保证访问安全,在HotSpotVM中,这个锁称为SystemDictionary_lock。
通常,HotSpotVM借助类加载器对象锁对加载类的过程进行序列化
Java是一门类型安全的语言,官方标准的Java编译器(javac)可以生成合法的类文件和类型安全的字节码,但Java虚拟机无法确保字节码一定是由可信的javac编译器产生的。
所以在连接时必须进行字节码验证以保障类型安全。
许多字节码约束都可以进行静态检查,例如字节码“ldc”的操作数必须是有效的常量池索引,其类型是CONSTANT_Integer,CONSTANT_String或CONSTANT_Float。另外一些的指令的类型检查和验证则需要在执行过程中动态分析代码。
目前有两种判断质量操作数类型和个数的字节码分析方法:
这是比较常用的方法,他对每个字节码进行抽象解释并在目标分枝或者异常处理器上合并类状态。
这个验证步骤的代码写在HotSpotVM的外部库libverify.so中,它使用JNI获取类和类型所需要的所有信息
Java编译器将每个目标分支或异常分支中的类型信息设置在code属性的StackMapTable中。
StackMapTable中包含若干个栈映射帧,每个栈映射帧都会用字节码偏移量指示表达式栈和局部变量表中元素的类型状态。
Java虚拟机验证字节码时只需要扫描一次就可以验证类型的正确性。独具字节码验证,这个方法比常用的类型推导来的快也更为轻巧。
目前先使用类型检查,如果类型检查失败(验证出错),HotSpot VM就会切换成类型推导进行验证,如果类型推导失败则抛出VerifyError。
未完待续…
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。