JVM体系结构及各部分职能(一)

一.体系结构图

二、各部分职能

1.类装载器子系统

Java虚拟机自带的加载器又包括3种类加载器

根类加载器(Bootstrap):负责加载核心JavaClass(即所有java.*开头的类)

扩展类加载器(Extension):负责加载扩展的Javaclass(例如所有javax.*开头的类和存放在JRE的ext目录下的类)

系统类加载器(Ststem):负责加载应用程序自身的类

系统类加载器又称为应用类加载器

其中扩展类加载器和系统类加载器是使用Java实现的。而根加载器是使用C++实现的,JVMAPI也没有暴露根类加载器,程序员无法在Java代码中获取根加载器。

类通过JVMClassloader加载到内存经过以下几个步骤

加载 --> 连接 --> 初始化

加载:查找并加载类的二进制数据

连接:

(1)验证:确保被加载的类的正确性

(2)准备:为类的静态变量分配内存,并将其初始化为默认值

(3)解析:把类中的符号引用转换为直接引用

初始化:为类的静态变量赋予正确的初始值

我来分别解释一下这三个阶段都做了什么事情

加载:就是将二进制的字节码通过IO输入到JVM中,我们的字节码是存在于硬盘上面的,而所用的类都必须加载到内存中才能运行起来,加载就是通过IO把字节码从硬盘迁移到内存中。

连接:分为3个阶段,验证,准备和解析。

验证-->这里可能大家会疑问了,我们的类不是通过JVM编译成的字节码的吗,为什么这里还要验证加载类的正确性,难道通过Java虚拟机的javac编译器生成的字节码还会有错误不成?当然,javac编译出来的类都是正确的,但是如果是通过其他途径生成的字节码呢?是不是正确的呢?就比如你自己建一个文本文件,然后重命名该文件为Test.class,然后让JVM来运行这个类,显然是错误的。当然因为JDK的源码是开放的,所以JVM字节码的生成规则也是公开的,所以也有一些第三方的软件可以生成符合JVM规范的字节码文件,如CGlib

准备-->为类的静态变量分配内存,并将其初始化为默认值,这里我们一定要看清楚是为静态变量分配内存,而不是我们的实例变量,为什么我要强调静态变量,因为实例变量是什么时候产生的,是生成实例的时候产生的,而我们一般是在new一个对象的时候才对这个类进行实例化(前提是这个类已经被加载),而我们现在还没有加载完类,所以这个时候只能对静态变量分配内存空间(静态变量是属于这个类的而不属于某个对象),这个一定要分清楚。然后为该静态变量初始化为默认值(这个大家应该不陌生,int类型是0boolean就是false,引用类型是null等)。

解析-->把类中的符号引用转换为直接引用,这个我们等下在讨论(后面我们会讲什么是符号引用,什么是直接引用)

初始化:这个似乎与上面的初始化为默认值有点矛盾,我们再看一遍:为累的静态变量赋予正确的初始值,上面是赋予默认值,这里是赋予正确的初始值,什么是正确的初始值,就是用户给赋予的值。

注:类的这种加载机制我们称之为父委托加载机制,父委托机制的优点就是能够提高软件系统的安全性。因为在词机制下,用户自定义的类加载器不可能加载本应该由父加载器加载的可靠类,从而防止不可靠的恶意代码代替由父类加载器加载的可靠类,从而防止不可靠的甚至恶意的代码代替由父类加载器加载的可靠代码。如,java.lang.Object类总是由根类加载器加载的,其他任何用户自定义的类加载器都不可能加载含有恶意代码的java.lang.Object类。

由同一类加载器加载的属于相同包的类组成了运行时包。决定两个类是不是属于同一个运行时包,不仅要看他们的包名称是否相同,还要看定义类加载器是否相同。只有属于同一运行时包的类之间才能相互访问可见(默认访问级别)的类和成员。假设用户自定义了一个类java.lang.TestCase并由用于自定义的类加载器加载,由于java.lang.TestCase和核心类库java.lang.*由不同的类加载器加载,他们属于不同的运行时包,所以java.lang.TestCase不能访问核心库java.lang包中的包可见成员。

 

 

posted on 2013-03-08 14:06  ThinkFar  阅读(308)  评论(0编辑  收藏  举报