Java 虚拟机

4 篇文章 0 订阅

Java虚拟机

1  虚拟机简介

1.1 起源

为了减轻编译过程中,中间语言向不同机器代码转换这一烦琐的任务,提出了虚拟机的思想,最早是在五十年代末的UNCOL方案中首先被提出来的,建议设计一个通用的中间语言,该语言的名字为UNCOL(universalc

omputer oriented language),即面向通用计算机的中间语言。

 

   中间语言的概念与抽象机的概念并不完全相同,中间语言可以看作是抽象机的汇编语言。

 

   抽象机是具体计算机的理论模型。

 

   Turing机     图灵  机械可计算

 

 

1.2 通用抽象机

七十年代的通用抽象机JANUS,其基本思想是:把对输入源语言的分析阶段(词法、语法、以及部分语义)与代码生成阶段分割开来。前一部分应该是与具体机器无关的,后一部分则涉及源语言的各种成分在不同的机器上如何实现的问题。

 

   具体的计算机很多,差别很大等,实现起来较困难。

 

1.3 专用抽象机

为特定计算机设计的专用抽象机。

从通用抽象机到专用抽象机,表明人们的认识也有了提高,放弃了不切实际的目标,转而追求比较现实的目标。

 

1.4 面向语言的专用抽象机

这种AM只能用来实现和移植某一种特定的语言,许多著名的语言都有了自己的专用抽象机,如Pascal的P-CODE,ALGOL的Z-CODE等。


 

目的:

(1) 便于移植

把这个系统用抽象机的方法重新实现一遍,然后再把用抽象机写的系统提供给用户,使用户只需花费实现一个小抽象机的代价就可得到整个系统。

 

    (2)获得高效率

在特殊的硬件模型上获得高效率。抽象机可视为硬件模型的软件映射,它是介于语言的语义和硬件物理实现之间的一个层次,是从该语言中抽出其基本功能而设计的一台直接执行这些基本功能的理想机器。

 

2 Java虚拟机概述

 

    Java虚拟机(Java virtualmachine,JVM)是面向语言的专用抽象机,它的设计主要是受了P-Code的影响,它和P-Code一样都是面向堆栈的机器,JVM的设计兼顾了软件的移植与硬件的执行效率,目前已有以JVM指令为机器指令的Java CPU。

Java语言之所以实现了平台中立,其根本原因在于各操作系统平台都配有Java虚拟机,虚拟机屏蔽了不同软硬平台之间的差异,它负责解释或及时编译执行Java字节码文件。

 

    Java虚拟机技术是Java平台的核心技术,涉及的范围很广,其中包括解释技术、及时编译技术、异常处理技术、多线程技术、内存管理与无用单元的回收技术等。

 

3  Java虚拟机的体系结构与设计

 

图 Java虚拟机的体系结构

 

 

公用设备:

寄存器、Java栈、对象堆、

方法区

 

JVM为每一个线程分配一个Java栈,及一个本地方法栈。

在JVM中,所有的线程共享一个方法区,一个对象堆。JVM用本地方法栈支持本地方法调用,若JVM不支持本地方法,该栈可省略。

方法区:它存储方法数据及方法代码。

对象堆:是运行时的数据区,类的实例及数组从对象堆中分配。JVM有垃圾自动回收功能,释放堆中无用的空间及堆碎片合并。

 

JVM共有200余条指令,指令操作码长度为一个字节,由于JVM是面向堆栈的机器,因此指令的操作数来自栈顶,并将运算的结果保存于栈顶。

JVM的指令集大致可分为如下几类:

把常量压入堆栈

把局部变量压入堆栈

把堆栈值存入局部变量

数组管理

栈操作

算术指令

逻辑指令

转换操作

条件控制指令

函数返回指令

表跳转指令

对象域操作指令

方法调用指令

异常处理指令

对象操作指令

监控器指令

断点指令

 

    虚拟机规范中只是给出了虚拟机体系结构,及其指令集的定义,并未涉及到其具体的实现技术,在遵循虚拟机规范的前提下,不同的设计者可按不同的方案进行实现。

 

虚拟机的运行主要包括两个阶段:初始化阶段与运行阶段。

初始化阶段主要完成:

环境变量的设置;装入核心类库;加载基本Java类;内存管理及垃圾回收的初始化;线程管理的初始化;异常初始化等。

运行阶段主要指:

Java类的加载与连接、以及Java类的解释执行,或及时编译执行。

 

4  Java虚拟机技术

4.1多线程技术

Java语言提供了线程类用以描述并发,JVM则负责线程的建立、执行、切换、终止以及同步。

 

程序设计语言的并发作为对现实进行模拟的途径,及多处理机系统的需求已成为现代程序设计语言的特征。

并发从并行执行的粒度上可分为:指令级并发、语句级并发、子程序级并发、及程序级并发。

 

程序设计语言的并发设计问题:

    (1)采用何种结构来表示并发执行的单位

    如何并发执行和终止,即描述并发。

(2)并发活动之间的通信

    采用共享变量(shared variables)和消息传递(message passing)

    (3)并发活动的同步

 

Java语言引入的并发机制:

对对象加一个自己的执行线程,这个执行线程常被称为对象的体,当该对象创建时,它的体就立即作为单独的线程开始执行,在对象体的执行过程中,对象可以接收和发送消息。

 

Java语言用线程类来描述并发,并提供了同步关键字synchronized标识所需同步的语句和方法。

在设计上,Java语言线程采用共享变量的方式进行通信与同步。

JVM在运行时为每一个对象建立一个对象锁,对象锁保证了并发线程对象操作的互斥调用。

 

    虚拟机中线程状态分析及线程设计

图  线程的活动状态

    线程的调度

 

    (1) FIFO调度

采用先来先服务的策略,线程被无条件执行,直到其运行完毕或被堵塞。该方法一般适用于运行时间短,且对时间敏感线程的调度。

优点:算法简单且无线程的频繁切换,因此执行效率较高。

缺点:它没有考虑到线程的优先级使其应用受到一定的限制。

 

(2) 静态级别轮巡调度

优先级最高的线程被抢先执行,只有该线程执行完毕或被堵塞,其它线程才能被执行,优先级相同的线程按分时原则轮流执行。

AIX等操作系统采用该策略进行线程调度。

优点:考虑到线程的优先级

缺点:由于其严格按优先级的次序调度,可能会使低优先极的线程长时间地处于等待状态而不能被执行。

 

(3) 动态级别轮巡调度

线程的优先级按其执行的次数及时间进行动态调整。

Windows NT及OS/2的线程调度采用该策略。

优点:使各线程都有被执行的机会。

缺点:线程切换较为频繁,因此执行效率较低。

 

 

Java语言规定了线程的优先级,线程的最高优先级为10,最小为1。

因此,在虚拟机中一般采用后两种调度策略。

目前Java虚拟机主要以Sun公司和Microsoft公司的产品为主,它们的虚拟机都是利用操作系统的线程实现Java线程,因此,JVM线程调度策略依赖于具体的操作系统。

 

JVM中线程的实现可利用操作系统的线程,也可使用用户级的线程库,或者采用两者相结合的方式。

SUN最初在Solaris操作系统上实现的虚拟机采用的是用户级的线程库,以后改用内核线程和用户线程相结合的方式

 

    采用用户级的线程一般是基于以下几个因素:

    (1) 操作系统本身不支持多线程。

    (2) 不同操作系统线程的实现机制不同。不仅操作系统线程API不同,而且不同平台线程运行的效率相差很大。采用用户级的线程可使程序具有良好的移植性。

(3) 可根据JVM自身的特点建立最适合自身的线程库。

 

    JVM中Java线程应实现如下功能:线程的建立与撤消、线程的切换、线程的同步、线程输入/输出非阻塞处理。

    线程实现方式不同,线程的调度策略也不尽相同,一般来说利用操作系统线程实现的JVM线程,其线程调度策略依赖于具体的操作系统。

如Sun公司在Sparc上的虚拟机线程调度策略采用简单的优先权调度,Microsoft公司为Windows操作系统开发的虚拟机则采用动态级别轮巡调度。

 

线程同步

Java语言支持多线程设计.Java采用的多线程同步机制的基本原理是C.A.R.Hoare的临界区保护和管程机制.

管程(monitor)是由若干公共变量及其说明和所有访问这些变量的过程组成的一段程序区.这些过程可由多个进程互斥地调用,在任一时刻,最多只允许一个调用者能真正地进入管程,而其它调用者必须等待.

对象的封装与管程十分相似,因此,管程可以很容易地用对象来实现.Java是面向对象的程序语言,在Java中,线程已不再是一个过程,而是一个对数据和方法进行了封装的对象.当定义了一个线程(Thread)类的对象时,也就定义了一个线程.

同时,对象封装的数据和方法在被多个线程调用时, 也相当于管程.Java使用锁机制来解释monitor的行为:

即对每个对象都使用一把锁,锁的状态有两种:上锁(lock)和解锁(unlock).Java语言使用修饰符synchronize来表示需共享临界资源(变量或对象)的方法.

在Java虚拟机这一级,synchronized方法的上解锁过程对用户也是隐蔽的.在monitor机制下,若一个线程已得到了monitor,则其它线程要反复进行上锁测试,这可能造成其它线程无休止的“盲等”.对此需要扩充monitor的设计,使用显示的等待(wait)和唤醒(signal)原语来解决此问题.

Java语言提供了三个标准Object类方法:wait(),notify()来实现线程之间的控制转移.避免反复检测锁的状态. 一个Java对象除了连着一把锁外,还有一个wait集.一个wait()方法调用,将挂起(suspend)当前运行的线程,并将其放入相应synchronized对象的wait集中去,使其由运行态转入不可运行态,直至有其它线程使用了notify()方法来唤醒它.

一个Java源程序经Java编译器编译后,变成独立于平台的类格式文件,再由Java虚拟机运行.Java虚拟机仅规定了Java类文件格式字节码形式的虚拟机指令集及部分操作规范,而具体的实现,如运行数据的内存布局、垃圾收集.

 

Java语言提供了同步语句与同步方法协调线程对共享资源的使用,虚拟机中提供了访问共享资源的监视器指令-monitorentor、monitorexit。

当线程访问共享资源时,该线程执行monitorentor指令,获取共享资源所在对象(或类)的信号,若该信号已被其它线程占有则该线程必须进入锁的等待队列,等待该对象(或类) 信号被释放,当已获取对象信号的线程释放共享资源时,执行monitorexit指令释放该信号,若该信号的等待队列非空,则使第一个等待线程获取该信号。

 

4.2 无用单元回收

 

   算法研究已有30多年的历史,主要包括两个方面:单元回收的精确性与算法执行的效率。

   无用单元回收算法

    (1)Mark and Sweep 算法

在程序的内存空间中,变量及其堆中的记录构成一有向图,变量组成有向图的根。

该算法分为两个阶段,标记(Mark)阶段与清除(Sweep)阶段。

在标记阶段,被标记的节点为有用节点,没有被标记的节点为无用节点。

在清除阶段中,清除被标记节点的标志,同时,将没标记的节点放入无用空间链表中。

 

    (2)引用计数算法

    为每一个对象分配一个计数器,当被其它对象引用时,计数器加1,否则减1,若计数器为零,则为无用对象。

 

    (3)Copying Collection 算法

    该算法在遍历图的同时,将遍历到的对象从原有堆中拷贝到新分配的堆空间中。

 

    (4)Generational Collection算法

基于这个前提:新产生的对象被淘汰的概率最高,因此该算法按对象生存时间的长短,将对象堆分为若干代。

新产生的对象为第零代,回收时只回收第零代,第零代回收若干次后,将第零代中的对象上升为第一代,一般情况下只回收第零代,但有可能经过一段时间后,第一代中聚集了许多无用对象,此时可回收第一代

 

    (5)增量式无用单元回收算法

    每次只扫描内存单元的一小部分,回收部分内存,之后在继续运行程序。

 

    Java内建的垃圾收集自动对程序进行内存管理,几乎消除了因内存泄漏而引起的问题, Java的多线程则提供了很多的灵活性,但它们在带来好处的同时,也使程序付出了很大的代价,垃圾收集和线程同步消耗了大量的系统资源,耗费了大量的时间,

    无用单元的自动回收虽然减轻了程序员的负担,却降低了程序的运行效率。据统计Java程序运行时约有20%的时间用来进行无用单元回收。有时无用单元回收会很明显地暂时终止程序的运行,这对实时响应程序来说是十分不利的。

 

4.3 Java运行技术

 

(1) 解释技术

 

(2) 及时编译技术

 

(3) 动态编译技术

 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值