`
yefeng_laixi
  • 浏览: 15764 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

深入java虚拟机读书笔记——第五章 java虚拟机【2】

阅读更多

五. 类装载区子系统

1.         Java虚拟机有两种类装载器,启动类转载器和用户自定义类装载器,前者是Java虚拟机实现的一部分,后者则是Java程序的一部分;

2.         装载、连接、初始化:(第七章,第八章会详细说明)

   l  装载:查找并装载类型的二进制数据

   l  连接:执行验证、准备以及解析,解析是可选的

验证:确保被导入类型的正确性;

准备:为类变量分配内存,并初始化为默认值;

解析:把类型中的符号引用转换为直接引用;

   l  初始化:把类变量初始化为正确的初始值

3.         命名空间:每个类加载器都有自己的命名空间,维护着自己加载的类型,该类型上有加载它的类加载器的标识;

六. 方法区

1. 当虚拟机装载某个类时,它使用类装载器定位相应的class文件,然后读入class文件中的线性二进制流,提取其中的类型信息,并将其存储到方法区;该类型中的类变量静态变量也存储到方法区中;

2. 所有线程共享方法区,因此它们对方法区数据的访问必须设计成线程安全的;

3. 方法区的大小不是固定的,方法区也可以被垃圾收集;

4. 方法区中存取的类型信息:

u  全限定名;

u  直接超类的全限定名(如果这个类是Object类,则没有超类)

u  是类类型还是接口类型;

u  访问修饰符(publicabstractfinal的某个子集)

u  任何直接接口的全限定名的有限列表;

u  该类型的常量池:

u  字段信息;

u  方法信息;

u  除常量以外的所有类变量、静态变量;

u  一个到类ClassLoader的引用;

u  一个到Class类的引用;

 

5. 常量池:该类型所用常量的一个有序集合,包括直接常量和对其它类型、字段和方法的符号引用

6. 字段信息:类型的字段,包括声明顺序都要在方法区中保存,如字段名、字段的类型以及字段的修饰符

7. 方法信息:类型的方法,包括声明顺序都要在方法区中保存,如方法名、返回类型、参数数量,类型,顺序以及方法的修饰符;如果不是本地方法和抽象方法,还要保存方法的字节码、操作数栈和方法的栈桢中局部变量的大小以及异常表;

8. (静态)变量:类变量由所有类实例共享,即使没有任何类实例,它也可以被访问,因此它们总是作为类型信息的一部分保存到方法区中;

9. 指向ClassLoader类的引用:每个类被装载的时候,虚拟机必须跟踪它是由启动类装载器还是由用户自定义类装载器装载的。如果是用户自定义类装载器,则虚拟机必须在类型信息中保存对该类装载器的引用

10. 虚拟机在动态连接期间使用这个信息,当某个类引用另一个类型时,虚拟机会请求装载发起引用类型的类装载器来装载被引用的类型; 这个动态连接的过程,对于虚拟机分离命名空间也很重要;

11. 指向Class类的引用:虚拟机会为每个被装载的类型创建一个Class类的实例,并把这个实例的引用存储在方法区中;

12. 方法表:为了提高访问效率,虚拟机实现中可能还包含其它数据结构来加快对原始数据的访问速度,如方法表;虚拟机会为每个装载的非抽象类生成一个方法表,并把它作为类型信息的一部分保存在方法区中 方法表是一个数组,其元素是所有它的实例可能调用的实例方法的直接引用,包括那些从超类继承过来的方法;

七. JVM调用类的main方法的过程

       转载于:http://dryr.blog.163.com/blog/static/5821101320107143513109/

JVM先装载类,再链接类,再初始化类(以代码的文本顺序执行类变量初始化器、类静态初始化方法或接口的属性(field)初始化器),完成类的初始化后,才执行类的main方法。在链接过程中,可以静态链接(解析),也可以动态链接(解析),下面的图例说明的是动态链接。

八.堆

1. Java程序在运行时创建的所有类实例或者数组都放在同一个堆中,而一个Java虚拟机实例中只存放一个堆空间,因此所有线程都将共享这个堆。又由于一个Java程序独占一个Java虚拟机实例,因而每个Java程序都有自己的堆空间它们不会彼此干扰。但是同一个Java程序的多个线程却共享着同一个堆空间,在这种情况下,就得考虑多线程访问对象(堆数据)的同步问题;

2. 垃圾收集器的主要工作就是自动回收不再被运行的程序引用的对象所占用的内存,此外,它也可能去移动那些还在使用的对象,以此减少堆碎片;

3. 一种可能的堆空间设计就是,把堆分为两部分:一个句柄池,一个对象池。这种设计的好处是有利于堆碎片的整理,缺点是每次访问对象的实例变量都需要经过两次指针传递;

4.另一种设计方式是使对象直接指向一组数据,而数据包括对象实例数据以及指向方法区类数据的指针。这种设计方式的优点是只需要一个指针就可以访问对象的实例数据,但是移动对象就变得更加复杂;

5. 方法表加快实例调用方法的效率,每一个对象数据都包含一个指向特殊结构的指针,该数据结构包含2个部分:一个指向方法区对应的类数据的指针,一个是此对象的方法表;

6. 方法表是个数字指针,其中每一项都是一个指向实例方法数据的指针,方法表指向的实例方法数据包括以下信息:

  l  此方法的操作数栈和局部变量区的大小

  l  此方法的字节码

  l  异常表

  

7. 锁是用来实现多个线程对共享数据的互斥访问的,而等待集合是用来让多个线程为完成一个共同目标而协调工作的;等待集合由等待方法和通知方法联合使用;

八. Java

1. 每当启动一个新线程时,Java虚拟机都会为它分配一个Java栈,以帧为单位保存线程的运行状态。虚拟机只会对直接对Java栈执行两种操作:以帧为单位的压栈或出栈;

2. Java方法可以以两种方式完成。一种通过return返回的,称为正常返回,一种是通过抛出异常而异常中止的。不管以哪种方法返回,虚拟机都会将当前帧弹出Java栈然后释放掉,这样上一个方法的帧就成为当前帧了;

3. Java栈上的所有数据都是此线程私有的,任何线程都不能访问另一个线程的栈数据,因此我们不需要考虑多线程情况下栈数据的访问同步问题;

4. 栈帧由三部分组成:局部变量区,操作数栈和帧数据区。局部变量区和操作数栈的大小要视对应的方法而定;

    l  局部变量区:

局部变量Java堆栈帧中被组织为一个从0计数的数组,指令通过提供他们的索引从本地变量区中取得相应的值。Int,float,reference, returnValue占一个字,byte,short,char被转换成int然后存储,longdoubel占两个字;

    l  操作数栈(Operand Stack

和局部变量区一样,操作数栈也是被组织成一个以字长为单位的数组,但是和前面不同的是,它不是通过索引来访问,而是通过标准的栈操作(压栈和出栈)来访问的;

    l  帧数据区

除了局部变量区和操作数栈外,Java栈帧还需要一些数据来支持常量池的解析,正常方法返回以及异常派发机制,这些信息都保存在Java栈帧的帧数据区中;

       虚拟机演示2Int类型局部变量相加,在保存到另一个局部变量的过程:

        

5一个场景:当前线程执行某一点上帧1是当前帧,它的方法是A方法,当A方法调用B方法时,新的帧会被创建假设为帧2,并压入java栈成为当前帧,B方法调用返回时,如果方法调用有返回值则当前帧(帧2)把结果传递给上一帧(帧1),帧2被弹出java栈撤销抛弃,帧1又成为当前帧:

   

九. 本地方法栈

         当线程调用一个本地方法时,就进入了一个全新并且不再受到虚拟机限制的世界。

      任何本地方法接口都会使用某种本地方法栈,当线程调用Java方法时,虚拟机会创建一个新的栈帧并压入Java栈。然而当它调用的是本地方法时,虚拟机会保持Java栈不变,不再线程的Java栈中压入新的栈,虚拟机只是简单地动态链接并直接调用指定的本地方法,可以把这看做是虚拟机利用本地方法来动态扩展自己

. 执行引擎

任何Java虚拟机实现的核心都是它的执行引擎,在Java虚拟机规范中,执行引擎的行为使用指令集来定义;运行中Java程序的每一个线程都是一个独立虚拟机执行引擎的实例,从线程生命周期的开始到结束,它要么在执行字节码,要么在执行本地方法。Java虚拟机的实现可能用一些对用户程序不可见的线程,比如垃圾收集器,这样的线程不需要实现的执行引擎实例,所有属于用户运行程序的线程,都是在实际工作的执行引擎。 

十一.  本地方法接口(Native Method Interface

Java虚拟机的实现并不是必须实现本地方法接口。一些实现可能根本不支持本地方法接口。Sun的本地方法接口是JNI(Java Native Interface)

部分图片来源于:

http://www.cnblogs.com/chenqiangjsj/archive/2011/03/27/1996873.html

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics