深入java虚拟机读书笔记



 

 术语:类型通常代表类和接口(当类装载器装载了新的类型)。

一. java体系结构介绍
1.java体系结构,包括四个独立技术
(1)java程序设计语言
(2)java class 文件格式
(3)java应用编程接口(API)
(4)java虚拟机:主要任务是转载class文件并且执行其中的字节码。它由类装载器(可以从程序和api中装载class文件,只有需要的类才会装载)和执行引擎组成(字节码由引擎来执行)。

二.平台无关性
(1)为什么要平台无关?
减少开发和多个平台上部署应用程式需的成本和时间。
(2)java体系结构如何支持平台无关
   对平台无关的支持是分布在整个java体系结构中的,所有的组成部分-语言、class文件、api以及虚拟机、都在对平台无关性的支持方面扮演着重要角色。

  • java平台:java体系机构通过几种途径支持java程序的平台无关,其中主要是通过java平台自己。java平台扮演一个运行时java程序与其下的硬件和操作系统之间的缓冲角色。java程序被编译为可运行于java虚拟机中的二进制程序,并且假定java api的class文件在运行时都是可用的。接着虚拟机与你下你个程序,那些api则给予程序访问地称计算机资源的能力。无论java程序被部署到何处,它只需要与java平台交互,而不需要担心底层的硬件和操作系统。因此,它能够运行于任何拥有java平台的计算机。
  • java语言:java语言通过以下方式支持平台无关--即它的基本数据类型和值域和行为都是由语言自己定义的。像c和c++,基本类型int的值域是由它的占位宽度决定的,而她的占位宽由目标平台决定。
  • java class文件:javaclass文件可以在任何平台上创建,也可以被任何平台的java虚拟机装入并运行。它的格式,包括多字节值的高位有限存放约定,有严格的定义,并且是与java虚拟机 所在平台无关。
  • 可伸缩性:java平台可以在各种各样不同类型的计算机上实现(从嵌入式到大型主机)



(3)影响平台无关性的因素
    任何java程序的平台无关性程度都依赖于作者怎样编写它。

  • java平台的部署:只有拥有java平台的计算机或设备,才能运行java程序。
  • java平台的版本
  • 本地方法:是否调用了本地方法(不要自己调用本地方法,而是通过java的api来调用)
  • 非标准运行时的库:非标准库是否调用本地方法?
  • 对虚拟机的依赖:不要依赖及时终结来达到程序的正确性;不要依赖线程的优先级来达到程序的正确性。
  • 对用户界面的依赖
  • java平台实现中的bug
  • 测试:在不同主机和不同java平台测试java程序,是平台无关性的一个关键因素


(4)平台无关的七个步骤

  • 选择程序要运行的主机和设备的集合
  • 在目标宿主机种选择自认为足够好的java平台版本,在该版本java平台上编写,运行程序
  • 对于目标主机,选择一些程序将要运行的java平台实现
  • 编写程序,是她通过javaapi标准运行库来访问计算机
  • 编写程序,使它只通过java api的标准运行库来访问计算机
  • 编写程序,使它不依赖垃圾收集器及时终结的正确性,也不依赖线程的优先级
  • 努力设计一个用户界面,使它在你所有的目标宿主机上都能正常工作



(5)平台无关性的策略
    java平台开发商都能以非标准的、平台相关的方式来扩展java平台的标准组件,但是它们必须支持标准组件。

(6)平台无关性和网络移动对象

五.java虚拟机
1.虚拟机是什么
   可能指下面三种东西: 

  • 抽象规范
  •   
  • 一个具体的抽象
  •   
  • 一个运行中的虚拟机实例
  •  


2.虚拟机的生命周期
   当启动一个java程序时,一个虚拟机实例也就诞生了。当程序关闭退出(包括非守护线程都中止),这个虚拟机实例也就随之消亡。如果在同一台计算机上同时运行3个程序,将得到三个虚拟机。
3.java虚拟机的体系结构

 
深入java虚拟机读书笔记_第1张图片

    当虚拟机运行一个程序时,它需要内存来存储许多东西(如字节码,创建的对象,参数等),java虚拟机把这些东西都组织到几个“运行时数据区中”,便于管理。

    虚拟机装载流程:虚拟机装载一个calss文件时,它会从这个class文件包含的二进制数据中解析类型信息。然后,把这个类型信息放到方法区中。当程序运行时,虚拟机会把所有该程序在运行时创建的对象都放到堆中(方法区和堆由虚拟机所有线程共享)。当新线程创建,会得到自己的pc寄存器和一个java栈(pc寄存器总是指示下一条将被执行的指令,而java栈则存储线程中java方法的调用状态,如局部变量,参数,返回值等等)。


 5.3.1 数据类型

    数据类型可以分为两种:基本类型和引用类型。如下图:

 
深入java虚拟机读书笔记_第2张图片
 

5.3.2 字长的考量

    java虚拟机中最基本的数据单元就是字,它的大小是由每个虚拟机实现的设计这来决定。字长必须足够大,至少选择32位作为字长。

5.3.3 类装载器子系统

    java虚拟机中,负责查找并装载的那部分被称为类装载子系统。有两种装载器:启动类装载器(一般装载api calss)和用户自定义类装载器。前者是java实现的一部分,后者是java程序的一部分。由不同的类装载器装载的类将被放在虚拟机内部的不同命名空间中(每个装载器都有自己的命名空间)。

    装载器执行顺序:

  •     装载-查找并装载类的二进制数据
  • 连接-执行验证(确保导入类型的正确性)、准备(为变量分配内存,并将其初始化为默认值)、解析(可选,把类型中的符号引用转换为直接引用)
  • 初始化-把类变量初始化为正确值

5.3.4  方法区

    存储类型信息。方法区数据的访问是线程安全的。

    类型信息,对每个装载的类型,虚拟机都会在方法区中存储以下类型信息:

  • 这个类型的全限定名。
  • 这个类型的直接超类的全限定名
  • 这个类型是类类型还是接口类型
  •  这个类的访问修饰符
  • 任何直接超接口的全限定名的有序列表

    除了基本类信息外,虚拟机还为被装载的类型存储以下信息:

  •     该类型的常量池
  • 字段信息
  • 方法信息
  • 除了常量以外所有类变量
  • 一个到类classloader的引用
  • 一个到class类的引用

5.3.5  堆

java程序在运行时创建的所有类实例或数组都放在同一个堆中。而一个JAVA虚拟机实例中只存在一个堆空间,因此所有的线程都将共享这个堆。

  • 堆的两种设计

第一种堆空间的设计:

把堆分为两部分:一个句柄池,一个对象池,而一个对象的引用就是一个指向句柄池的本地指针 。句柄池的条目有两个部分:一个是指向对象变量的指针,一个指向方法区类型数据的指针。(优点:利于堆碎片的整理,当移动对象池中的对象时,只需要修改句柄池的指针。缺点:每次访问对象实例都要经过两次指针传递)如下图:


深入java虚拟机读书笔记_第3张图片
 

 

 

 第二种堆空间的设计:

使对象指针直接指向一组数据,而该数据包括对象实例数据以及指向方法区中类数据的指针。如下图:

 

 

 

深入java虚拟机读书笔记_第4张图片

 

  • 对象的方法表

很可能每个对象都有一个方法表,方法表加快了调用实例方法使的效率。

下图,一种把方法表和对象引用联系起来实现的方式,每个对象数据都包含一个指向特殊数据结构的指针,这个数据结构位于方法区,它包括:

1)一个指向方法区对应类数据的指针

2)此对象的方法表


深入java虚拟机读书笔记_第5张图片
 
 

  • 堆上对象数据的对象锁

对象锁用于协调多个线程访问同一个对象的同步

 

  • JAVA对象逻辑上还与等待集合数据相关联

 

  • 堆中数组的内部表示

      Java 中数组是真正的对象。和其它对象一样,数组总是存储在堆中。数组也拥有一个与它们类相关联的class实例,所有具有相同维度和类型的数组都是同一个类的实例,而不管数组的长度是多少。

      多维数组被表示为数组的数组。如下图:

     

  
深入java虚拟机读书笔记_第6张图片
 

在堆中的每个数组对象还必须保存的数据是数组长度、数组数据,以及某些指向数组类数据的引用。

 

5.3.6  程序计数器(PC)

     对于一个运行中的java程序而言,其中的每一个线程都有它自己的PC寄存器,它是在线程启动时创建的。当线程执行某个方法时,PC寄存器的内容总是下一条将被执行指令的“地址”,这里的地址可以是一个本地指针,也可以是方法字节码中相对于该方法起始指令的偏移量,如果执行一个本地方法,那么此时PC寄存器的值是“undefined”。

 

5.3.7  Java栈

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

   Java栈上所有的数据都是此线程私有的,任何线程都不能访问另一个线程的栈数据。当调用一个java方法时,虚拟机都会在该线程的java栈中压入一个新帧。而这个新帧自然成了当前帧。在执行这个方法时,它使用这个帧来存储参数、局部变量、中间运算结果等。java方法可以以两种方式返回,return和异常,返回后便此帧释放弹出。

 

5.3.8  栈帧

栈帧由三部分组成:局部变量区、操作数栈、帧数据区

  • 压栈帧流程:

      当虚拟机调用一个方法时,它从对应类的类型信息中得到此方法的局部变量区和操作数栈的大小,并据此分配栈帧内存,然后压入java栈中。

  • 局部变量区:JAVA栈帧的局部变量区被组织为一个以字长为单位、以0开始计数的数组。字节码指令通过从0开始的索引来使用其中的数据。类型为int、float、reference和returnaddress的值在数组中只占据一项,而类型为byte、short和char的值在存入数组钱都将被转移为int值,同样占据一项。但是类型为long和double的值在数组中却占据连续的两项。(按方法的声明顺序来组成局部变量数组)。如下例:


深入java虚拟机读书笔记_第7张图片
 

   
深入java虚拟机读书笔记_第8张图片
 

    备注:注意runInstanceMethod(),局部变量中第一个参数是一个reference类型。虽然代码没有声明,但这个参数this对于任何一个实例方法都是隐含加入的,它用来表示调用该方法的对象本身。runClassMethod()没有,类方法与类相关,与具体对象无关。不能通过类方法访问类实例变量,因为在方法调用的时候没有关系到一个具体实例。

    在JAVA中所有的对象都是按引用传递,并且都存储爱堆中,永远都不会在局部变量区或操作数栈中发现对象的拷贝,只会有 对象引用。

  • 操作数栈

       作用:它是用来存储一些中间变量值(个人理解)。

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

      虚拟机在操作数栈中存储数据的方式和在局部变量区中是一样的。

      虚拟机把操作数栈作为她的工作区--大多数指令都要从这里弹出数据,执行运算,然后把结果压会操作数栈。

     具体流程如下例:

    
深入java虚拟机读书笔记_第9张图片
 

  • 帧数据区

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

    每当虚拟机要执行某个需要用到常量池数据的指令时,它都会通过帧数据区中指向常量池的指针来访问它。以前讲过常量池中对类型、字段和方法的引用在开始时都是符号。当虚拟机在常量池中搜索的时候,如果遇到指向类、接口、字段、或者方法的入口,假若它们仍然是符号,虚拟机那时候才会进行解析。

    除了用于常量池的解析外,帧数据区还要帮助虚拟机处理java方法的正常结束和异常终止。如果是正常结束,虚拟机必须恢复发起调用的方法的栈帧,包括设置PC寄存器指向发起调用的方法中的指令--即紧跟着调用了完成方法的指令的下一个指令。假如方法有返回值,虚拟机必须将她压入到发起调用的方法的操作数栈。

   为了处理执行期间的异常退出情况,帧数据区还必须保存一个对此方法异常表的引用。

 

 

5.3.9 本地方法栈

你可能感兴趣的:(java,多线程,数据结构,虚拟机,读书)