要对Java程序进行内存优化和性能调优金沙js娱乐场官方网站:,它可以从程序和API中装载class文件

假如了解Java虚拟机的内存模型,(Java代码不能显式释放内存,假如了解Java虚拟机的内存模型,(Java代码不能显式释放内存,因为所有的Java程序都在虚拟机上运行,Java虚拟机实例负责运行一个Java程序,因为所有的Java程序都在虚拟机上运行,Java虚拟机实例负责运行一个Java程序,Java程序的执行依赖于编译环境和运行环境,而Java平台由Java虚拟机和Java核心类所构成

Java虚拟机体系结构深入研究总结,java虚拟机深入研究

工作以来,代码越写越多,程序也越来越臃肿,效率越来越低,对于我这样一个追求完美的程序员来说,这是绝对不被允许的,于是除了不断优化程序结构外,内存优化和性能调优就成了我惯用的“伎俩”。

要对Java程序进行内存优化和性能调优,不了解虚拟机的内部原理(或者叫规范更严谨一点)是肯定不行的,这里推荐一本好书《深入Java虚拟机
(第二版)》(Bill Venners著,曹晓刚 蒋靖
译,实际上本文正是作者阅读本书之后,对Java虚拟机的个人理解阐述)。当然了,了解Java虚拟机的好处并不仅限于上述两点好处。从更深一点的技术层
面上看,了解Java虚拟机的规范和实现,将更加有助于我们编写高效、稳定的Java代码。比如,假如了解Java虚拟机的内存模型,了解虚拟机的内存回
收机制,那么我们就不会过分依赖它,而会在需要的时候显式的”释放内存”(Java代码不能显式释放内存,但是可以通过释放对象引用告知垃圾回收器回收该
对象需要被回收),以降低不必要的内存消耗;假如我们了解Java栈的工作原理,那么我们就可以通过减少递归层数,减少循环次数来降低堆栈溢出的风险。可
能对于应用开发人员来说,可能不会直接去涉及这些Java虚拟机底层实现的工作,但是了解这些背景知识,或多或少,都会对我们写的程序产生潜移默化的好的
影响。

本篇文章,将简明扼要的说明Java虚拟机的体系结构和内存模型,如有用词不妥或解释不准确之处,请不吝指正,深感荣幸!

Java 虚拟机体系结构

 

金沙js娱乐场官方网站 1

类装载子系统

Java虚拟机有两种类装载器,分别是启动类装载器和用户自定义装载器。

通类装载子系统通过类的全限定名(包名和类名,网络装载还包括 URL)将 Class
装载进运行时数据区。对于每一个被装载的类型,Java虚拟机都会创建一个java.lang.Class类的实例来代表该类型,该实例被放在内存中的堆
区,而装载的类型信息则位于方法区,这一点和所有其他对象都是一样的。

类装载子系统在装载一个类型前,除了要定位和导入对应的二进制class文件外,还要验证导入类的正确性,为类变量分配并初始化内存,以及解析符号引用为直接引用,这些动作严格按照以下顺序进行:

1)装载——查找并装载类型的二进制数据;

2)连接——执行验证,准备以及解析(可选)

3)验证 确保被导入类型的正确性

4)准备 为类变量分配内存,并将其初始化为默认值

5)解析 把类型中的符号引用转换为直接应用

方法区

对于每一个被类装载子系统装载的类型,虚拟机都会保存下列数据到方法区:

  • 类型的全限定名

  • 类型超类的全限定名(java.lang.Object没有超类)

  • 类型是类类型还是接口类型

  • 类型的访问修饰符

  • 任何直接超接口的全限定名有序列表

除了上述基本类型信息,还将保存如下信息:

  • 类型的常量池

  • 字段信息(包括字段名、字段类型、字段修饰符)

  • 方法信息(包括方法名、返回类型、参数的数量和类型、方法修饰符,如果方法不是抽象和本地的,还将保存方法的字节码、操作数栈和该方法栈帧中的局部变量区的大小和异常表)

  • 常量以外的所有类变量(其实就是类的静态变量,因为静态变量是所有实例共享的,且与类型直接相关,所以他们是类一级的变量,作为类的成员被保存在方法区)

一个到类ClassLoader的引用

//返回的就是刚才保存的ClassLoader引用   
String.class.getClassLoader();

一个到Class类的引用

//将返回刚才保存的Class类的引用   
String.class;

注意,方法区也是可以被垃圾回收器回收的。

Java程序在运行时创建的所有类实例或数组都放在同一个堆中,而每一个Java虚拟机也是有一个对空间,所有线程共享一个堆(这就是一个多线程的Java程序会产生对象访问的同步问题的原因了)。

由于每一种Java虚拟机都有对虚拟机规范的不同实现,所以我们可能不知道每一种Java虚拟机在堆中是以何种形式表示对象实例的,不过我们可以通过下面这可能的实现来一窥端倪:

金沙js娱乐场官方网站 2

程序计数器

对于运行中的Java程序而言,每一个线程都有自己的PC(程序计数器)寄存器,它是在该线程启动时创建的,大小为一个字长,用来保存需要被执行的下一行代码的位置。

Java栈

每一个线程都有一个Java栈,以栈帧为单位保存线程的运行状态。虚拟机对Java栈的操作有两种:压栈和出栈,二者都已帧为单位。栈帧保存了传入参数、局部变量、中间运算结果等数据,在方法完成时被弹出,然后释放。

看一下两个局部变量相加时栈帧的内存快照

金沙js娱乐场官方网站 3

本地方法栈

这是 Java 调用操作系统本地库的地方,用来实现 JNI(Java Native
Interface,Java 本地接口)

执行引擎

Java虚拟机的核心,控制装入 Java
字节码并解析;对于运行中的Java程序而言,每一个线程都是一个独立的虚拟机执行引擎的实例,从线程生命周期的开始到结束,他要么在执行字节码,要么在执行本地方法。

本地接口

连接了本地方法栈和操作系统库。

注:文中所有提到”Java虚拟机”的地方都是指”JavaEE和JavaSE平台的Java虚拟机规范”。

问啊-定制化IT教育平台,牛人一对一服务,有问必答,开发编程社交头条
官方网站:www.wenaaa.com

QQ群290551701
聚集很多互联网精英,技术总监,架构师,项目经理!开源技术研究,欢迎业内人士,大牛及新手有志于从事IT行业人员进入!

工作以来,代码越写越多,程序也越来越臃肿,效率越来越低,对于我这样一个追求…

Java虚拟机体系结构深入研究总结

 

工作以来,代码越写越多,程序也越来越臃肿,效率越来越低,对于我这样一个追求完美的程序员来说,这是绝对不被允许的,于是除了不断优化程序结构外,内存优化和性能调优就成了我惯用的“伎俩”。

要对Java程序进行内存优化和性能调优,不了解虚拟机的内部原理(或者叫规范更严谨一点)是肯定不行的,这里推荐一本好书《深入Java虚拟机
(第二版)》(Bill Venners著,曹晓刚 蒋靖
译,实际上本文正是作者阅读本书之后,对Java虚拟机的个人理解阐述)。当然了,了解Java虚拟机的好处并不仅限于上述两点好处。从更深一点的技术层
面上看,了解Java虚拟机的规范和实现,将更加有助于我们编写高效、稳定的Java代码。比如,假如了解Java虚拟机的内存模型,了解虚拟机的内存回
收机制,那么我们就不会过分依赖它,而会在需要的时候显式的”释放内存”(Java代码不能显式释放内存,但是可以通过释放对象引用告知垃圾回收器回收该
对象需要被回收),以降低不必要的内存消耗;假如我们了解Java栈的工作原理,那么我们就可以通过减少递归层数,减少循环次数来降低堆栈溢出的风险。可
能对于应用开发人员来说,可能不会直接去涉及这些Java虚拟机底层实现的工作,但是了解这些背景知识,或多或少,都会对我们写的程序产生潜移默化的好的
影响。

本篇文章,将简明扼要的说明Java虚拟机的体系结构和内存模型,如有用词不妥或解释不准确之处,请不吝指正,深感荣幸!

Java 虚拟机体系结构

 

金沙js娱乐场官方网站 4

类装载子系统

Java虚拟机有两种类装载器,分别是启动类装载器和用户自定义装载器。

通类装载子系统通过类的全限定名包名和类名,网络装载还包括 URL)将 Class
装载进运行时数据区。对于每一个被装载的类型,Java虚拟机都会创建一个java.lang.Class类的实例来代表该类型,该实例被放在内存中的堆
区,而装载的类型信息则位于方法区,这一点和所有其他对象都是一样的。

类装载子系统在装载一个类型前,除了要定位和导入对应的二进制class文件外,还要验证导入类的正确性,为类变量分配并初始化内存,以及解析符号引用为直接引用,这些动作严格按照以下顺序进行:

1)装载——查找并装载类型的二进制数据;

2)连接——执行验证,准备以及解析(可选)

3)验证 确保被导入类型的正确性

4)准备 为类变量分配内存,并将其初始化为默认值

5)解析 把类型中的符号引用转换为直接应用

方法区

对于每一个被类装载子系统装载的类型,虚拟机都会保存下列数据到方法区:

  • 类型的全限定名

  • 类型超类的全限定名(java.lang.Object没有超类)

  • 类型是类类型还是接口类型

  • 类型的访问修饰符

  • 任何直接超接口的全限定名有序列表

除了上述基本类型信息,还将保存如下信息:

  • 类型的常量池

  • 字段信息(包括字段名、字段类型、字段修饰符)

  • 方法信息(包括方法名、返回类型、参数的数量和类型、方法修饰符,如果方法不是抽象和本地的,还将保存方法的字节码、操作数栈和该方法栈帧中的局部变量区的大小和异常表)

  • 常量以外的所有类变量(其实就是类的静态变量,因为静态变量是所有实例共享的,且与类型直接相关,所以他们是类一级的变量,作为类的成员被保存在方法区)

一个到类ClassLoader的引用

//返回的就是刚才保存的ClassLoader引用  
String.class.getClassLoader();

一个到Class类的引用

//将返回刚才保存的Class类的引用  
String.class;

注意,方法区也是可以被垃圾回收器回收的。

Java程序在运行时创建的所有类实例或数组都放在同一个堆中,而每一个Java虚拟机也是有一个对空间,所有线程共享一个堆(这就是一个多线程的Java程序会产生对象访问的同步问题的原因了)。

由于每一种Java虚拟机都有对虚拟机规范的不同实现,所以我们可能不知道每一种Java虚拟机在堆中是以何种形式表示对象实例的,不过我们可以通过下面这可能的实现来一窥端倪:

金沙js娱乐场官方网站 5

程序计数器

对于运行中的Java程序而言,每一个线程都有自己的PC程序计数器)寄存器,它是在该线程启动时创建的,大小为一个字长,用来保存需要被执行的下一行代码的位置。

Java栈

每一个线程都有一个Java栈,以栈帧为单位保存线程的运行状态。虚拟机对Java栈的操作有两种:压栈和出栈,二者都已帧为单位。栈帧保存了传入参数、局部变量、中间运算结果等数据,在方法完成时被弹出,然后释放。

看一下两个局部变量相加时栈帧的内存快照

金沙js娱乐场官方网站 6

本地方法栈

这是 Java 调用操作系统本地库的地方,用来实现 JNIJava Native
Interface,Java 本地接口)

执行引擎

Java虚拟机的核心,控制装入 Java
字节码并解析;对于运行中的Java程序而言,每一个线程都是一个独立的虚拟机执行引擎的实例,从线程生命周期的开始到结束,他要么在执行字节码,要么在执行本地方法。

本地接口

连接了本地方法栈和操作系统库。

注:文中所有提到”Java虚拟机”的地方都是指”JavaEE和JavaSE平台的Java虚拟机规范”。

 

工作以来,代码越写越多,程序也越来越臃肿,效率越来越低,对于我这样一个追求完美的
程序员 来说,…

1 概述

  众所周知,Java支持平台无关性、安全性和网络移动性。而Java平台由Java虚拟机和Java核心类所构成,它为纯Java程序提供了统一的编程接口,而不管下层操作系统是什么。正是得益于Java虚拟机,它号称的“一次编译,到处运行”才能有所保障。

1 概述

  众所周知,Java支持平台无关性、安全性和网络移动性。而Java平台由Java虚拟机和Java核心类所构成,它为纯Java程序提供了统一的编程接口,而不管下层操作系统是什么。正是得益于Java虚拟机,它号称的“一次编译,到处运行”才能有所保障。

理解Java虚拟机体系结构

1.1 Java程序执行流程

  Java程序的执行依赖于编译环境和运行环境。源码代码转变成可执行的机器代码,由下面的流程完成:

金沙js娱乐场官方网站 7

  Java技术的核心就是Java虚拟机,因为所有的Java程序都在虚拟机上运行。Java程序的运行需要Java虚拟机、Java
API和Java
Class文件的配合。Java虚拟机实例负责运行一个Java程序。当启动一个Java程序时,一个虚拟机实例就诞生了。当程序结束,这个虚拟机实例也就消亡。

金沙js娱乐场官方网站 8

  Java的跨平台特性,因为它有针对不同平台的虚拟机。

1.1 Java程序执行流程

  Java程序的执行依赖于编译环境和运行环境。源码代码转变成可执行的机器代码,由下面的流程完成:

金沙js娱乐场官方网站 7

  Java技术的核心就是Java虚拟机,因为所有的Java程序都在虚拟机上运行。Java程序的运行需要Java虚拟机、Java
API和Java
Class文件的配合。Java虚拟机实例负责运行一个Java程序。当启动一个Java程序时,一个虚拟机实例就诞生了。当程序结束,这个虚拟机实例也就消亡。

金沙js娱乐场官方网站 8

  Java的跨平台特性,因为它有针对不同平台的虚拟机。

1 概述

1.2 Java虚拟机

  Java虚拟机的主要任务是装载class文件并且执行其中的字节码。由下图可以看出,Java虚拟机包含一个类装载器(class
loader),它可以从程序和API中装载class文件,Java
API中只有程序执行时需要的类才会被装载,字节码由执行引擎来执行。

金沙js娱乐场官方网站 11

  当Java虚拟机由主机操作系统上的软件实现时,Java程序通过调用本地方法和主机进行交互。Java方法由Java语言编写,编译成字节码,存储在class文件中。本地方法由C/C++/汇编语言编写,编译成和处理器相关的机器代码,存储在动态链接库中,格式是各个平台专有。所以本地方法是联系Java程序和底层主机操作系统的连接方式。

  由于Java虚拟机并不知道某个class文件是如何被创建的,是否被篡改一无所知,所以它实现了一个class文件检测器,确保class文件中定义的类型可以安全地使用。class文件检验器通过四趟独立的扫描来保证程序的健壮性:

  • class文件的结构检查
  • 类型数据的语义检查
  • 字节码验证
  • 符号引用验证

  Java虚拟机在执行字节码时还进行其它的一些内置的安全机制的操作,他们作为Java编程语言保证Java程序健壮性的特性,同时也是Java虚拟机的特性:

  • 类型安全的引用转换
  • 结构化的内存访问
  • 自动垃圾收集
  • 数组边界检查
  • 空引用检查

1.2 Java虚拟机

  Java虚拟机的主要任务是装载class文件并且执行其中的字节码。由下图可以看出,Java虚拟机包含一个类装载器(class
loader),它可以从程序和API中装载class文件,Java
API中只有程序执行时需要的类才会被装载,字节码由执行引擎来执行。

金沙js娱乐场官方网站 11

  当Java虚拟机由主机操作系统上的软件实现时,Java程序通过调用本地方法和主机进行交互。Java方法由Java语言编写,编译成字节码,存储在class文件中。本地方法由C/C++/汇编语言编写,编译成和处理器相关的机器代码,存储在动态链接库中,格式是各个平台专有。所以本地方法是联系Java程序和底层主机操作系统的连接方式。

  由于Java虚拟机并不知道某个class文件是如何被创建的,是否被篡改一无所知,所以它实现了一个class文件检测器,确保class文件中定义的类型可以安全地使用。class文件检验器通过四趟独立的扫描来保证程序的健壮性:

  • class文件的结构检查
  • 类型数据的语义检查
  • 字节码验证
  • 符号引用验证

  Java虚拟机在执行字节码时还进行其它的一些内置的安全机制的操作,他们作为Java编程语言保证Java程序健壮性的特性,同时也是Java虚拟机的特性:

  • 类型安全的引用转换
  • 结构化的内存访问
  • 自动垃圾收集
  • 数组边界检查
  • 空引用检查

众所周知,Java支持平台无关性、安全性和网络移动性。而Java平台由Java虚拟机和Java核心类所构成,它为纯Java程序提供了统一的编程接口,而不管下层操作系统是什么。正是得益于Java虚拟机,它号称的“一次编译,到处运行”才能有所保障。

1.3 Java虚拟机数据类型

  Java虚拟机通过某些数据类型来执行计算。数据类型可以分为两种:基本类型和引用类型,如下图:

金沙js娱乐场官方网站 13

  但boolean有点特别,当编译器把Java源码编译为字节码时,它会用int或byte表示boolean。在Java虚拟机中,false是由0表示,而true则由所有非零整数表示。和Java语言一样,Java虚拟机的基本类型的值域在任何地方都是一致的,不管主机平台是什么,一个long在任何虚拟机中总是一个64位二进制补码的有符号整数。

  对于returnAddress,这个基本类型被用来实现Java程序中的finally子句,Java程序员不能使用这个类型,它的值指向一条虚拟机指令的操作码。

1.3 Java虚拟机数据类型

  Java虚拟机通过某些数据类型来执行计算。数据类型可以分为两种:基本类型和引用类型,如下图:

金沙js娱乐场官方网站 13

  但boolean有点特别,当编译器把Java源码编译为字节码时,它会用int或byte表示boolean。在Java虚拟机中,false是由0表示,而true则由所有非零整数表示。和Java语言一样,Java虚拟机的基本类型的值域在任何地方都是一致的,不管主机平台是什么,一个long在任何虚拟机中总是一个64位二进制补码的有符号整数。

  对于returnAddress,这个基本类型被用来实现Java程序中的finally子句,Java程序员不能使用这个类型,它的值指向一条虚拟机指令的操作码。

1.1 Java程序执行流程

2 体系结构

  在
Java虚拟机规范中,一个虚拟机实例的行为是分别按照子系统、内存区、数据类型和指令来描述的,这些组成部分一起展示了抽象的虚拟机的内部体系结构。

金沙js娱乐场官方网站 15

2 体系结构

  在
Java虚拟机规范中,一个虚拟机实例的行为是分别按照子系统、内存区、数据类型和指令来描述的,这些组成部分一起展示了抽象的虚拟机的内部体系结构。

金沙js娱乐场官方网站 15

Java程序的执行依赖于编译环境和运行环境。源码代码转变成可执行的机器代码,由下面的流程完成:

2.1 class文件

  Java
class文件包含了关于类或接口的所有信息。class文件的“基本类型”如下:

u1 1个字节,无符号类型
u2 2个字节,无符号类型
u4 4个字节,无符号类型
u8 8个字节,无符号类型

  如果想了解更多,Oracle的JVM SE7给出了官方规范:The Java® Virtual
Machine
Specification

  class文件包含的内容:

金沙js娱乐场官方网站 17

ClassFile {

    u4 magic;                                     //魔数:0xCAFEBABE,用来判断是否是Java class文件
    u2 minor_version;                             //次版本号
    u2 major_version;                             //主版本号
    u2 constant_pool_count;                       //常量池大小
    cp_info constant_pool[constant_pool_count-1]; //常量池
    u2 access_flags;                              //类和接口层次的访问标志(通过|运算得到)
    u2 this_class;                                //类索引(指向常量池中的类常量)
    u2 super_class;                               //父类索引(指向常量池中的类常量)
    u2 interfaces_count;                          //接口索引计数器
    u2 interfaces[interfaces_count];              //接口索引集合
    u2 fields_count;                              //字段数量计数器
    field_info fields[fields_count];              //字段表集合
    u2 methods_count;                             //方法数量计数器
    method_info methods[methods_count];           //方法表集合
    u2 attributes_count;                          //属性个数
    attribute_info attributes[attributes_count];  //属性表

}

金沙js娱乐场官方网站 18

2.1 class文件

  Java
class文件包含了关于类或接口的所有信息。class文件的“基本类型”如下:

u1 1个字节,无符号类型
u2 2个字节,无符号类型
u4 4个字节,无符号类型
u8 8个字节,无符号类型

  如果想了解更多,Oracle的JVM SE7给出了官方规范:The Java® Virtual
Machine
Specification

  class文件包含的内容:

金沙js娱乐场官方网站 19😉

ClassFile {

    u4 magic;                                     //魔数:0xCAFEBABE,用来判断是否是Java class文件
    u2 minor_version;                             //次版本号
    u2 major_version;                             //主版本号
    u2 constant_pool_count;                       //常量池大小
    cp_info constant_pool[constant_pool_count-1]; //常量池
    u2 access_flags;                              //类和接口层次的访问标志(通过|运算得到)
    u2 this_class;                                //类索引(指向常量池中的类常量)
    u2 super_class;                               //父类索引(指向常量池中的类常量)
    u2 interfaces_count;                          //接口索引计数器
    u2 interfaces[interfaces_count];              //接口索引集合
    u2 fields_count;                              //字段数量计数器
    field_info fields[fields_count];              //字段表集合
    u2 methods_count;                             //方法数量计数器
    method_info methods[methods_count];           //方法表集合
    u2 attributes_count;                          //属性个数
    attribute_info attributes[attributes_count];  //属性表

}

金沙js娱乐场官方网站 20😉

Java技术的核心就是Java虚拟机,因为所有的Java程序都在虚拟机上运行。Java程序的运行需要Java虚拟机、Java
API和Java
Class文件的配合。Java虚拟机实例负责运行一个Java程序。当启动一个Java程序时,一个虚拟机实例就诞生了。当程序结束,这个虚拟机实例也就消亡。

2.2 类装载器子系统

  类装载器子系统负责查找并装载类型信息。其实Java虚拟机有两种类装载器:系统装载器和用户自定义装载器。前者是Java虚拟机实现的一部分,后者则是Java程序的一部分。

金沙js娱乐场官方网站 21

  • 启动类装载器(bootstrap class loader):它用来加载 Java
    的核心库,是用原生代码来实现的,并不继承自java.lang.ClassLoader。
  • 扩展类装载器(extensions class loader):它用来加载 Java
    的扩展库。Java
    虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载
    Java 类。
  • 应用程序类装载器(application class loader):它根据 Java
    应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java
    应用的类都是由它来完成加载的。可以通过
    ClassLoader.getSystemClassLoader()来获取它。

  除了系统提供的类装载器以外,开发人员可以通过继承
java.lang.ClassLoader类的方式实现自己的类装载器,以满足一些特殊的需求。

  类装载器子系统涉及Java虚拟机的其它几个组成部分以及来自java.lang库的类。ClassLoader定义的方法为程序提供了访问类装载器机制的接口。此外,对于每一个被装载的类型,Java虚拟机都会为它创建一个java.lang.Class类的实例来代表该类型。和其它对象一样,用户自定义的类装载器以及Class类的实例放在内存中的堆区,而装载的类型信息则位于方法区。

  类装载器子系统除了要定位和导入二进制class文件外,还必须负责验证被导入类的正确性,为类变量分配并初始化内存,以及解析符号引用。这些动作还需要按照以下顺序进行:

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

2.2 类装载器子系统

  类装载器子系统负责查找并装载类型信息。其实Java虚拟机有两种类装载器:系统装载器和用户自定义装载器。前者是Java虚拟机实现的一部分,后者则是Java程序的一部分。

金沙js娱乐场官方网站 21

  • 启动类装载器(bootstrap class loader):它用来加载 Java
    的核心库,是用原生代码来实现的,并不继承自java.lang.ClassLoader。
  • 扩展类装载器(extensions class loader):它用来加载 Java
    的扩展库。Java
    虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载
    Java 类。
  • 应用程序类装载器(application class loader):它根据 Java
    应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java
    应用的类都是由它来完成加载的。可以通过
    ClassLoader.getSystemClassLoader()来获取它。

  除了系统提供的类装载器以外,开发人员可以通过继承
java.lang.ClassLoader类的方式实现自己的类装载器,以满足一些特殊的需求。

  类装载器子系统涉及Java虚拟机的其它几个组成部分以及来自java.lang库的类。ClassLoader定义的方法为程序提供了访问类装载器机制的接口。此外,对于每一个被装载的类型,Java虚拟机都会为它创建一个java.lang.Class类的实例来代表该类型。和其它对象一样,用户自定义的类装载器以及Class类的实例放在内存中的堆区,而装载的类型信息则位于方法区。

  类装载器子系统除了要定位和导入二进制class文件外,还必须负责验证被导入类的正确性,为类变量分配并初始化内存,以及解析符号引用。这些动作还需要按照以下顺序进行:

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

Java的跨平台特性,因为它有针对不同平台的虚拟机。

2.3 方法区

  在Java虚拟机中,关于被装载的类型信息存储在一个方法区的内存中。当虚拟机装载某个类型时,它使用类装载器定位相应的class文件,然后读入这个class文件并将它传输到虚拟机中,接着虚拟机提取其中的类型信息,并将这些信息存储到方法区。方法区也可以被垃圾回收器收集,因为虚拟机允许通过用户定义的类装载器来动态扩展Java程序。

  方法区中存放了以下信息:

  • 这个类型的全限定名(如全限定名java.lang.Object)
  • 这个类型的直接超类的全限定名
  • 这个类型是类类型还是接口类型
  • 这个类型的访问修饰符(public, abstract, final的某个子集)
  • 任何直接超接口的全限定名的有序列表
  • 该类型的常量池(一个有序集合,包括直接常量[string,
    integer和floating point常量]和对其它类型、字段和方法的符号引用)
  • 字段信息(字段名、类型、修饰符)
  • 方法信息(方法名、返回类型、参数数量和类型、修饰符)
  • 除了常量以外的所有类(静态)变量
  • 指向ClassLoader类的引用(每个类型被装载时,虚拟机必须跟踪它是由启动类装载器还是由用户自定义类装载器装载的)
  • 指向Class类的引用(对于每一个被装载的类型,虚拟机相应地为它创建一个java.lang.Class类的实例。比如你有一个到java.lang.Integer类的对象的引用,那么只需要调用Integer对象引用的getClass()方法,就可以得到表示java.lang.Integer类的Class对象)

2.3 方法区

  在Java虚拟机中,关于被装载的类型信息存储在一个方法区的内存中。当虚拟机装载某个类型时,它使用类装载器定位相应的class文件,然后读入这个class文件并将它传输到虚拟机中,接着虚拟机提取其中的类型信息,并将这些信息存储到方法区。方法区也可以被垃圾回收器收集,因为虚拟机允许通过用户定义的类装载器来动态扩展Java程序。

  方法区中存放了以下信息:

  • 这个类型的全限定名(如全限定名java.lang.Object)
  • 这个类型的直接超类的全限定名
  • 这个类型是类类型还是接口类型
  • 这个类型的访问修饰符(public, abstract, final的某个子集)
  • 任何直接超接口的全限定名的有序列表
  • 该类型的常量池(一个有序集合,包括直接常量[string,
    integer和floating point常量]和对其它类型、字段和方法的符号引用)
  • 字段信息(字段名、类型、修饰符)
  • 方法信息(方法名、返回类型、参数数量和类型、修饰符)
  • 除了常量以外的所有类(静态)变量
  • 指向ClassLoader类的引用(每个类型被装载时,虚拟机必须跟踪它是由启动类装载器还是由用户自定义类装载器装载的)
  • 指向Class类的引用(对于每一个被装载的类型,虚拟机相应地为它创建一个java.lang.Class类的实例。比如你有一个到java.lang.Integer类的对象的引用,那么只需要调用Integer对象引用的getClass()方法,就可以得到表示java.lang.Integer类的Class对象)

1.2 Java虚拟机

2.4 堆

  Java程序在运行时创建的所有类实例或数组(数组在Java虚拟机中是一个真正的对象)都放在同一个堆中。由于Java虚拟机实例只有一个堆空间,所以所有线程都将共享这个堆。需要注意的是,Java虚拟机有一条在堆中分配对象的指令,却没有释放内存的指令,因为虚拟机把这个任务交给垃圾收集器处理。Java虚拟机规范并没有强制规定垃圾收集器,它只要求虚拟机实现必须“以某种方式”管理自己的堆空间。比如某个实现可能只有固定大小的堆空间,当空间填满,它就简单抛出OutOfMemory异常,根本不考虑回收垃圾对象的问题,但却是符合规范的。

  Java虚拟机规范并没有规定Java对象在堆中如何表示,这给虚拟机的实现者决定怎么设计。一个可能的堆设计如下:

金沙js娱乐场官方网站 23

  一个句柄池,一个对象池。一个对象的引用就是一个指向句柄池的本地指针。这种设计的好处有利于堆碎片的整理,当移动对象池中的对象时,句柄部分只需更改一下指针指向对象的新地址即可。缺点是每次访问对象的实例变量都要经过两次指针传递。

2.4 堆

  Java程序在运行时创建的所有类实例或数组(数组在Java虚拟机中是一个真正的对象)都放在同一个堆中。由于Java虚拟机实例只有一个堆空间,所以所有线程都将共享这个堆。需要注意的是,Java虚拟机有一条在堆中分配对象的指令,却没有释放内存的指令,因为虚拟机把这个任务交给垃圾收集器处理。Java虚拟机规范并没有强制规定垃圾收集器,它只要求虚拟机实现必须“以某种方式”管理自己的堆空间。比如某个实现可能只有固定大小的堆空间,当空间填满,它就简单抛出OutOfMemory异常,根本不考虑回收垃圾对象的问题,但却是符合规范的。

  Java虚拟机规范并没有规定Java对象在堆中如何表示,这给虚拟机的实现者决定怎么设计。一个可能的堆设计如下:

金沙js娱乐场官方网站 23

  一个句柄池,一个对象池。一个对象的引用就是一个指向句柄池的本地指针。这种设计的好处有利于堆碎片的整理,当移动对象池中的对象时,句柄部分只需更改一下指针指向对象的新地址即可。缺点是每次访问对象的实例变量都要经过两次指针传递。

Java虚拟机的主要任务是装载class文件并且执行其中的字节码。由下图可以看出,Java虚拟机包含一个类装载器(class
loader),它可以从程序和API中装载class文件,Java
API中只有程序执行时需要的类才会被装载,字节码由执行引擎来执行。

2.5 Java栈

  每当启动给一个线程时,Java虚拟机会为它分配一个Java栈。Java栈由许多栈帧组成,一个栈帧包含一个Java方法调用的状态。当线程调用一个Java方法时,虚拟机压入一个新的栈帧到该线程的Java栈中,当该方法返回时,这个栈帧就从Java栈中弹出。Java栈存储线程中Java方法调用的状态–包括局部变量、参数、返回值以及运算的中间结果等。Java虚拟机没有寄存器,其指令集使用Java栈来存储中间数据。这样设计的原因是为了保持Java虚拟机的指令集尽量紧凑,同时也便于Java虚拟机在只有很少通用寄存器的平台上实现。另外,基于栈的体系结构,也有助于运行时某些虚拟机实现的动态编译器和即时编译器的代码优化。

2.5 Java栈

  每当启动给一个线程时,Java虚拟机会为它分配一个Java栈。Java栈由许多栈帧组成,一个栈帧包含一个Java方法调用的状态。当线程调用一个Java方法时,虚拟机压入一个新的栈帧到该线程的Java栈中,当该方法返回时,这个栈帧就从Java栈中弹出。Java栈存储线程中Java方法调用的状态–包括局部变量、参数、返回值以及运算的中间结果等。Java虚拟机没有寄存器,其指令集使用Java栈来存储中间数据。这样设计的原因是为了保持Java虚拟机的指令集尽量紧凑,同时也便于Java虚拟机在只有很少通用寄存器的平台上实现。另外,基于栈的体系结构,也有助于运行时某些虚拟机实现的动态编译器和即时编译器的代码优化。

当Java虚拟机由主机操作系统上的软件实现时,Java程序通过调用本地方法和主机进行交互。Java方法由Java语言编写,编译成字节码,存储在class文件中。本地方法由C/C++/汇编语言编写,编译成和处理器相关的机器代码,存储在动态链接库中,格式是各个平台专有。所以本地方法是联系Java程序和底层主机操作系统的连接方式。

2.5.1 栈帧

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

2.5.1 栈帧

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

由于Java虚拟机并不知道某个class文件是如何被创建的,是否被篡改一无所知,所以它实现了一个class文件检测器,确保class文件中定义的类型可以安全地使用。class文件检验器通过四趟独立的扫描来保证程序的健壮性:

2.5.1.1 局部变量区

  局部变量区被组织为以字长为单位、从0开始计数的数组。字节码指令通过从0开始的索引使用其中的数据。类型为int,
float, reference和returnAddress的值在数组中占据一项,而类型为byte,
short和char的值在存入数组前都被转换为int值,也占据一项。但类型为long和double的值在数组中却占据连续的两项。

金沙js娱乐场官方网站 25

2.5.1.1 局部变量区

  局部变量区被组织为以字长为单位、从0开始计数的数组。字节码指令通过从0开始的索引使用其中的数据。类型为int,
float, reference和returnAddress的值在数组中占据一项,而类型为byte,
short和char的值在存入数组前都被转换为int值,也占据一项。但类型为long和double的值在数组中却占据连续的两项。

金沙js娱乐场官方网站 25

class文件的结构检查

2.5.1.2 操作数栈

  和局部变量区一样,操作数栈也是被组织成一个以字长为单位的数组。它通过标准的栈操作访问–压栈和出栈。由于程序计数器无法被程序指令直接访问,Java虚拟机的指令是从操作数栈中取得操作数,所以它的运行方式是基于栈而不是基于寄存器。虚拟机把操作数栈作为它的工作区,因为大多数指令都要从这里弹出数据,执行运算,然后把结果压回操作数栈。

2.5.1.2 操作数栈

  和局部变量区一样,操作数栈也是被组织成一个以字长为单位的数组。它通过标准的栈操作访问–压栈和出栈。由于程序计数器无法被程序指令直接访问,Java虚拟机的指令是从操作数栈中取得操作数,所以它的运行方式是基于栈而不是基于寄存器。虚拟机把操作数栈作为它的工作区,因为大多数指令都要从这里弹出数据,执行运算,然后把结果压回操作数栈。

类型数据的语义检查

2.5.1.3 帧数据区

  除了局部变量区和操作数栈,Java栈帧还需要帧数据区来支持常量池解析、正常方法返回以及异常派发机制。每当虚拟机要执行某个需要用到常量池数据的指令时,它会通过帧数据区中指向常量池的指针来访问它。除了常量池的解析外,帧数据区还要帮助虚拟机处理Java方法的正常结束或异常中止。如果通过return正常结束,虚拟机必须恢复发起调用的方法的栈帧,包括设置程序计数器指向发起调用方法的下一个指令;如果方法有返回值,虚拟机需要将它压入到发起调用的方法的操作数栈。为了处理Java方法执行期间的异常退出情况,帧数据区还保存一个对此方法异常表的引用。

2.5.1.3 帧数据区

  除了局部变量区和操作数栈,Java栈帧还需要帧数据区来支持常量池解析、正常方法返回以及异常派发机制。每当虚拟机要执行某个需要用到常量池数据的指令时,它会通过帧数据区中指向常量池的指针来访问它。除了常量池的解析外,帧数据区还要帮助虚拟机处理Java方法的正常结束或异常中止。如果通过return正常结束,虚拟机必须恢复发起调用的方法的栈帧,包括设置程序计数器指向发起调用方法的下一个指令;如果方法有返回值,虚拟机需要将它压入到发起调用的方法的操作数栈。为了处理Java方法执行期间的异常退出情况,帧数据区还保存一个对此方法异常表的引用。

字节码验证

2.6 程序计数器

  对于一个运行中的Java程序而言,每一个线程都有它的程序计数器。程序计数器也叫PC寄存器。程序计数器既能持有一个本地指针,也能持有一个returnAddress。当线程执行某个Java方法时,程序计数器的值总是下一条被执行指令的地址。这里的地址可以是一个本地指针,也可以是方法字节码中相对该方法起始指令的偏移量。如果该线程正在执行一个本地方法,那么此时程序计数器的值是“undefined”。

2.6 程序计数器

  对于一个运行中的Java程序而言,每一个线程都有它的程序计数器。程序计数器也叫PC寄存器。程序计数器既能持有一个本地指针,也能持有一个returnAddress。当线程执行某个Java方法时,程序计数器的值总是下一条被执行指令的地址。这里的地址可以是一个本地指针,也可以是方法字节码中相对该方法起始指令的偏移量。如果该线程正在执行一个本地方法,那么此时程序计数器的值是“undefined”。

符号引用验证

2.7 本地方法栈

  任何本地方法接口都会使用某种本地方法栈。当线程调用Java方法时,虚拟机会创建一个新的栈帧并压入Java栈。当它调用的是本地方法时,虚拟机会保持Java栈不变,不再在线程的Java栈中压入新的栈,虚拟机只是简单地动态连接并直接调用指定的本地方法。

其中方法区和堆由该虚拟机实例中所有线程共享。当虚拟机装载一个class文件时,它会从这个class文件包含的二进制数据中解析类型信息,然后把这些类型信息放到方法区。当程序运行时,虚拟机会把所有该程序在运行时创建的对象放到堆中。

像其它运行时内存区一样,本地方法栈占用的内存区可以根据需要动态扩展或收缩。

2.7 本地方法栈

  任何本地方法接口都会使用某种本地方法栈。当线程调用Java方法时,虚拟机会创建一个新的栈帧并压入Java栈。当它调用的是本地方法时,虚拟机会保持Java栈不变,不再在线程的Java栈中压入新的栈,虚拟机只是简单地动态连接并直接调用指定的本地方法。

其中方法区和堆由该虚拟机实例中所有线程共享。当虚拟机装载一个class文件时,它会从这个class文件包含的二进制数据中解析类型信息,然后把这些类型信息放到方法区。当程序运行时,虚拟机会把所有该程序在运行时创建的对象放到堆中。

像其它运行时内存区一样,本地方法栈占用的内存区可以根据需要动态扩展或收缩。

Java虚拟机在执行字节码时还进行其它的一些内置的安全机制的操作,他们作为Java编程语言保证Java程序健壮性的特性,同时也是Java虚拟机的特性:

3 执行引擎

  在Java虚拟机规范中,执行引擎的行为使用指令集定义。实现执行引擎的设计者将决定如何执行字节码,实现可以采取解释、即时编译或直接使用芯片上的指令执行,还可以是它们的混合。

  执行引擎可以理解成一个抽象的规范、一个具体的实现或一个正在运行的实例。抽象规范使用指令集规定了执行引擎的行为。具体实现可能使用多种不同的技术–包括软件方面、硬件方面或树种技术的结合。作为运行时实例的执行引擎就是一个线程。

  运行中Java程序的每一个线程都是一个独立的虚拟机执行引擎的实例。从线程生命周期的开始到结束,它要么在执行字节码,要么执行本地方法。

3 执行引擎

  在Java虚拟机规范中,执行引擎的行为使用指令集定义。实现执行引擎的设计者将决定如何执行字节码,实现可以采取解释、即时编译或直接使用芯片上的指令执行,还可以是它们的混合。

  执行引擎可以理解成一个抽象的规范、一个具体的实现或一个正在运行的实例。抽象规范使用指令集规定了执行引擎的行为。具体实现可能使用多种不同的技术–包括软件方面、硬件方面或树种技术的结合。作为运行时实例的执行引擎就是一个线程。

  运行中Java程序的每一个线程都是一个独立的虚拟机执行引擎的实例。从线程生命周期的开始到结束,它要么在执行字节码,要么执行本地方法。

类型安全的引用转换

3.1 指令集

  方法的字节码流由Java虚拟机的指令序列构成。每一条指令包含一个单字节的操作码,后面跟随0个或多个操作数。操作码表示需要执行的操作;操作数向Java虚拟机提供执行操作码需要的额外信息。当虚拟机执行一条指令时,可能使用当前常量池中的项、当前帧的局部变量中的值或者位于当前帧操作数栈顶端的值。

  抽象的执行引擎每次执行一条字节码指令。Java虚拟机中运行的程序的每个线程(执行引擎实例)都执行这个操作。执行引擎取得操作码,如果操作码有操作数,就取得它的操作数。它执行操作码和跟随的操作数规定的动作,然后再取得下一个操作码。这个执行字节码的过程在线程完成前将一直持续,通过从它的初始方法返回,或者没有捕获抛出的异常都可以标志着线程的完成。

3.1 指令集

  方法的字节码流由Java虚拟机的指令序列构成。每一条指令包含一个单字节的操作码,后面跟随0个或多个操作数。操作码表示需要执行的操作;操作数向Java虚拟机提供执行操作码需要的额外信息。当虚拟机执行一条指令时,可能使用当前常量池中的项、当前帧的局部变量中的值或者位于当前帧操作数栈顶端的值。

  抽象的执行引擎每次执行一条字节码指令。Java虚拟机中运行的程序的每个线程(执行引擎实例)都执行这个操作。执行引擎取得操作码,如果操作码有操作数,就取得它的操作数。它执行操作码和跟随的操作数规定的动作,然后再取得下一个操作码。这个执行字节码的过程在线程完成前将一直持续,通过从它的初始方法返回,或者没有捕获抛出的异常都可以标志着线程的完成。

结构化的内存访问

4 本地方法接口

  Java本地接口,也叫JNI(Java Native
Interface),是为可移植性准备的。本地方法接口允许本地方法完成以下工作:

  • 传递或返回数据
  • 操作实例变量
  • 操作类变量或调用类方法
  • 操作数组
  • 对堆的对象加锁
  • 装载新的类
  • 抛出异常
  • 捕获本地方法调用Java方法抛出的异常
  • 捕获虚拟机抛出的异步异常
  • 指示垃圾收集器某个对象不再需要

 

参考:

《深入Java虚拟机》

 

引用:

4 本地方法接口

  Java本地接口,也叫JNI(Java Native
Interface),是为可移植性准备的。本地方法接口允许本地方法完成以下工作:

  • 传递或返回数据
  • 操作实例变量
  • 操作类变量或调用类方法
  • 操作数组
  • 对堆的对象加锁
  • 装载新的类
  • 抛出异常
  • 捕获本地方法调用Java方法抛出的异常
  • 捕获虚拟机抛出的异步异常
  • 指示垃圾收集器某个对象不再需要

 

 

参考:

《深入Java虚拟机》

自动垃圾收集

数组边界检查

空引用检查