术语:类型通常代表类和接口(当类装载器装载了新的类型)。
一. java体系结构介绍
1.java体系结构,包括四个独立技术
(1)java程序设计语言
(2)java class 文件格式
(3)java应用编程接口(API)
(4)java虚拟机:主要任务是转载class文件并且执行其中的字节码。它由类装载器(可以从程序和api中装载class文件,只有需要的类才会装载)和执行引擎组成(字节码由引擎来执行)。
二.平台无关性
(1)为什么要平台无关?
减少开发和多个平台上部署应用程式需的成本和时间。
(2)java体系结构如何支持平台无关
对平台无关的支持是分布在整个java体系结构中的,所有的组成部分-语言、class文件、api以及虚拟机、都在对平台无关性的支持方面扮演着重要角色。
(3)影响平台无关性的因素
任何java程序的平台无关性程度都依赖于作者怎样编写它。
(4)平台无关的七个步骤
(5)平台无关性的策略
java平台开发商都能以非标准的、平台相关的方式来扩展java平台的标准组件,但是它们必须支持标准组件。
(6)平台无关性和网络移动对象
五.java虚拟机
1.虚拟机是什么
可能指下面三种东西:
2.虚拟机的生命周期
当启动一个java程序时,一个虚拟机实例也就诞生了。当程序关闭退出(包括非守护线程都中止),这个虚拟机实例也就随之消亡。如果在同一台计算机上同时运行3个程序,将得到三个虚拟机。
3.java虚拟机的体系结构
当虚拟机运行一个程序时,它需要内存来存储许多东西(如字节码,创建的对象,参数等),java虚拟机把这些东西都组织到几个“运行时数据区中”,便于管理。
虚拟机装载流程:虚拟机装载一个calss文件时,它会从这个class文件包含的二进制数据中解析类型信息。然后,把这个类型信息放到方法区中。当程序运行时,虚拟机会把所有该程序在运行时创建的对象都放到堆中(方法区和堆由虚拟机所有线程共享)。当新线程创建,会得到自己的pc寄存器和一个java栈(pc寄存器总是指示下一条将被执行的指令,而java栈则存储线程中java方法的调用状态,如局部变量,参数,返回值等等)。
数据类型可以分为两种:基本类型和引用类型。如下图:
java虚拟机中最基本的数据单元就是字,它的大小是由每个虚拟机实现的设计这来决定。字长必须足够大,至少选择32位作为字长。
java虚拟机中,负责查找并装载的那部分被称为类装载子系统。有两种装载器:启动类装载器(一般装载api calss)和用户自定义类装载器。前者是java实现的一部分,后者是java程序的一部分。由不同的类装载器装载的类将被放在虚拟机内部的不同命名空间中(每个装载器都有自己的命名空间)。
装载器执行顺序:
存储类型信息。方法区数据的访问是线程安全的。
类型信息,对每个装载的类型,虚拟机都会在方法区中存储以下类型信息:
除了基本类信息外,虚拟机还为被装载的类型存储以下信息:
java程序在运行时创建的所有类实例或数组都放在同一个堆中。而一个JAVA虚拟机实例中只存在一个堆空间,因此所有的线程都将共享这个堆。
第一种堆空间的设计:
把堆分为两部分:一个句柄池,一个对象池,而一个对象的引用就是一个指向句柄池的本地指针 。句柄池的条目有两个部分:一个是指向对象变量的指针,一个指向方法区类型数据的指针。(优点:利于堆碎片的整理,当移动对象池中的对象时,只需要修改句柄池的指针。缺点:每次访问对象实例都要经过两次指针传递)如下图:
第二种堆空间的设计:
使对象指针直接指向一组数据,而该数据包括对象实例数据以及指向方法区中类数据的指针。如下图:
很可能每个对象都有一个方法表,方法表加快了调用实例方法使的效率。
下图,一种把方法表和对象引用联系起来实现的方式,每个对象数据都包含一个指向特殊数据结构的指针,这个数据结构位于方法区,它包括:
1)一个指向方法区对应类数据的指针
2)此对象的方法表
对象锁用于协调多个线程访问同一个对象的同步
Java 中数组是真正的对象。和其它对象一样,数组总是存储在堆中。数组也拥有一个与它们类相关联的class实例,所有具有相同维度和类型的数组都是同一个类的实例,而不管数组的长度是多少。
多维数组被表示为数组的数组。如下图:
在堆中的每个数组对象还必须保存的数据是数组长度、数组数据,以及某些指向数组类数据的引用。
对于一个运行中的java程序而言,其中的每一个线程都有它自己的PC寄存器,它是在线程启动时创建的。当线程执行某个方法时,PC寄存器的内容总是下一条将被执行指令的“地址”,这里的地址可以是一个本地指针,也可以是方法字节码中相对于该方法起始指令的偏移量,如果执行一个本地方法,那么此时PC寄存器的值是“undefined”。
当启动一个新线程时,java虚拟机都会为它分配一个java栈。java栈以帧为单位保存线程的运行状态。虚拟机只会直接堆java栈执行两种操作:以帧为单位的压栈和出栈。
Java栈上所有的数据都是此线程私有的,任何线程都不能访问另一个线程的栈数据。当调用一个java方法时,虚拟机都会在该线程的java栈中压入一个新帧。而这个新帧自然成了当前帧。在执行这个方法时,它使用这个帧来存储参数、局部变量、中间运算结果等。java方法可以以两种方式返回,return和异常,返回后便此帧释放弹出。
栈帧由三部分组成:局部变量区、操作数栈、帧数据区
当虚拟机调用一个方法时,它从对应类的类型信息中得到此方法的局部变量区和操作数栈的大小,并据此分配栈帧内存,然后压入java栈中。
备注:注意runInstanceMethod(),局部变量中第一个参数是一个reference类型。虽然代码没有声明,但这个参数this对于任何一个实例方法都是隐含加入的,它用来表示调用该方法的对象本身。runClassMethod()没有,类方法与类相关,与具体对象无关。不能通过类方法访问类实例变量,因为在方法调用的时候没有关系到一个具体实例。
在JAVA中所有的对象都是按引用传递,并且都存储爱堆中,永远都不会在局部变量区或操作数栈中发现对象的拷贝,只会有 对象引用。
作用:它是用来存储一些中间变量值(个人理解)。
和局部变量区一样,操作数栈也是被组织成一个以字长为单位的数组。但是和前面不同的是,它不是通过索引来访问,而是通过标准的栈操作-压栈和出栈来访问。
虚拟机在操作数栈中存储数据的方式和在局部变量区中是一样的。
虚拟机把操作数栈作为她的工作区--大多数指令都要从这里弹出数据,执行运算,然后把结果压会操作数栈。
具体流程如下例:
除了局部变量区和操作数栈外,Java栈帧还需要一些数据来支持常量池解析、正常方法返回以及异常派发机制。这些信息都保存在java栈帧的帧数据区中。
每当虚拟机要执行某个需要用到常量池数据的指令时,它都会通过帧数据区中指向常量池的指针来访问它。以前讲过常量池中对类型、字段和方法的引用在开始时都是符号。当虚拟机在常量池中搜索的时候,如果遇到指向类、接口、字段、或者方法的入口,假若它们仍然是符号,虚拟机那时候才会进行解析。
除了用于常量池的解析外,帧数据区还要帮助虚拟机处理java方法的正常结束和异常终止。如果是正常结束,虚拟机必须恢复发起调用的方法的栈帧,包括设置PC寄存器指向发起调用的方法中的指令--即紧跟着调用了完成方法的指令的下一个指令。假如方法有返回值,虚拟机必须将她压入到发起调用的方法的操作数栈。
为了处理执行期间的异常退出情况,帧数据区还必须保存一个对此方法异常表的引用。