Java虚拟机--之--内存模型篇

1、前言

最近闲暇之余拿些时间来看看《深入理解Java虚拟机》一书,这本书早有耳闻,一直没有细看。阅读之际将一些收获或感想记于此分享给大家,一来是弥补Java实现原理这方面的盲区,二来也可以加深自己的理解。本篇文章大致内容如下:

2、Java虚拟机概述

在讲Java虚拟机之前,我们来看看Java的技术体系,Sun官方定义的Java技术体系包括如下几部分:

  • Java程序设计语言
  • 各种平台的Java虚拟机
  • Class文件格式
  • Java API类库
  • 第三方Java类库

初学Java时,相信大家都知道JDK(Java Development Kit)一词,Java开发工具,它主要包括Java程序设计语言、Java虚拟机和Java API类库JDK是支持Java程序开发的最小环境。同时我们可以把Java API类库中的Java SE部分和Java虚拟机这两部分统称为JRE(Java Runtime Environment)JRE是支持Java程序运行的标准环境

那么,何为Java虚拟机?它具体有什么作用?

所谓Java虚拟机,它是对当前操作系统的一种屏蔽,它有自己完善的硬体架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。它主要起到屏蔽了与具体操作系统平台相关的信息,使得Java程序只需生成在Java虚拟机上运行的目标代码(字节码)即.class文件,就可以在多种平台上不加修改地运行。从某种角度上像是对操作系统的封装,但仅仅只是针对Java程序,让Java程序在Java虚拟机中运行,而不用去考虑各个操作系统平台的不同。

显然它的作用就是屏蔽具体操作系统相关信息,运行Java程序编译过后生成的.class文件。

3、Java虚拟机的发展历程

1996年1月23日,Sun公司发布JDK 1.0,Java语言首次拥有了商用的正式运行环境,这个JDK中所带的虚拟机就是Clas-sic VM。

1998年12月4日,JDK迎来了一个里程碑式的版本JDK1.2,且这个版本中Java虚拟机第一次内置了JIT(JustInTime)编译器(JDK 1.2中曾并存过3个虚拟机,Classic VM、HotSpot VM和Exact VM)

在JDK 1.2时,它与HotSpot VM并存,但默认使用的是Classic VM(用户可用java-hotspot参数切换至HotSpot VM)。

在JDK 1.3时,HotSpotVM成为默认虚拟机,但Classic VM仍作为虚拟机的“备用选择”发布(使用java-classic参数切换)。

到JDK 1.4的时候,Classic VM才完全退出商用虚拟机的历史舞台,与Exact VM一起进入了Sun Labs Research VM之中。

1999年4月27日,HotSpot虚拟机发布,HotSpot最初由一家名为“Longview Technologies”的小公司开发,因为HotSpot的优异表现,这家公司在1997年被Sun公司收购了。

HotSpot虚拟机发布时是作为JDK 1.2的附加程序提供的,后来它成为了JDK 1.3及之后所有版本的Sun JDK的默认虚拟机。

2004年9月30日,JDK 1.5发布,在虚拟机层面上,这个版本改进了Java的内存模型(Java MemoryModel,JMM)。

2006年12月11日,JDK 1.6发布,这个版本对Java虚拟机内部做了大量改进,包括锁与同步、垃圾收集、类加载等方面的算法都有相当多的改动。

2008年和2009年,Oracle收购了Sun公司,Oracle对HotSpot虚拟机进行了一些整合。

4、Java虚拟机的内存模型

在分析Java虚拟机内存模型之前,我们来思考一下,Java程序的运行过程是怎样的呢?

首先我们编写好Java程序,文件名以.java为后缀,通过Java编译器对.java文件进行编译,生成.class后缀字节码文件,然后由Java虚拟机读取.class文件进行解释和运行。

通常我们初学Java的时候,很多书籍会让我们编写一个简单的输出helloword程序,

cmd执行命令:

javac hello.java           // 该过程就是编译器对Java代码进行编译,生成.class文件
java hello                 // 该过程就是Java虚拟机对字节码进行解释和执行

整个流程我们也可以视为如下图所示:

Java虚拟机在执行Java程序的时候,会为其分配内存空间,虚拟机将分配的内存空间划分为很多不同的数据区域,这些区域有各自的用途,以及创建和消耗等等。根据《Java虚拟机规范(Java SE 7版)》的规定,Java虚拟机所管理的内存主要包括如下几个部分:

  • 程序计数器

程序计数器(Program Counter Register)是一块较小的内存空间。它可以视为当前线程所执行的行号指示器,指令在执行时,需要通过改变这个计数器来选取下一条需要执行的指令。Java虚拟机的多线程是通过轮流切换并分配处理器执行时间的方式来实现的,在一个确定的时刻只有一个处理器执行一条线程中的指令,为了在线程切换后能恢复到正确的执行位置,每个线程都会有一个独立的程序计数器,因此,程序计数器是线程私有的。

  • Java虚拟机栈

Java虚拟机栈(Java Virtual Machine Stacks),和程序计数器一样也是线程私有的,它的生命周期与线程相同,与线程是同时创建的。

Java虚拟机栈存储线程中Java方法调用的状态,包括局部变量、参数、返回值以及运算的中间结果等。一个Java虚拟机栈包含了多个栈帧,一个栈帧用来存储局部变量表、操作数栈、动态链接、方法出口等信息。当线程调用一个Java方法时,虚拟机压入一个新的栈帧到该线程的Java栈中,当该方法执行完成,这个栈帧就从Java栈中弹出。我们平常所说的栈内存(Stack)指的就是Java虚拟机栈。 

  • 本地方法栈

本地方法栈(Native Method Stack)与Java虚拟机栈的作用非常类似,他们的区别就是Java虚拟机栈为虚拟机执行Java方法而服务,本地方法栈则是为虚拟机用到本地Native方法而服务。

在Java虚拟机规范中对本地方法栈的语言和数据结构等没有强制规定,因此具体的Java虚拟机可以自由实现它,比如HotSpot VM将本地方法栈和Java虚拟机栈合二为一。

  • Java堆

Java堆(Java Heap)是Java虚拟机所管理内存中最大的一块,被所有线程共享的一块内存区域,在虚拟机启动时创建。

Java堆存储的对象被垃圾收集器管理,这些受管理的对象无需也无法显示的销毁。从内存回收的角度,Java堆可以粗略的分为新生代和老年代。从内存分配的角度Java堆中可能划分出多个线程私有的分配缓冲区。根据Java虚拟机规范规定,Java堆的所使用的内存在物理上不需要连续,逻辑上连续即可。 如果在堆中没有足够的内存来完成实例分配,并且堆也无法进行扩展时,则会抛出OutOfMemoryError异常。

  • 方法区

方法区(Method Area)也是被所有线程共享的运行时内存区域。用来存储已经被Java虚拟机加载的类的结构信息、常量、静态变量和即时编译器编译后的代码等数据。如果方法区的内存空间不满足内存分配需求时,Java虚拟机会抛出OutOfMemoryError异常。

  • 运行时常量池

运行时常量池(Runtime Constant Pool)是方法区的一部分。Class文件不仅包含了类的版本、接口、字段和方法等信息,还包含了常量池,它用来存放编译时期生成的字面量和符号引用,这些内容会在类加载后存放在方法区的运行时常量池中。运行时常量池可以理解为是类或接口的常量池的运行时表现形式。 

到这里,大致把虚拟机的内存模块分析完毕了,接下来的文章将会继续分析虚拟机的垃圾回收等内容。

参考文献

《深入理解Java虚拟机》

《Java虚拟机规范(Java SE7版)》 

Java虚拟机(一)结构原理与运行时数据区域

猜你喜欢

转载自blog.csdn.net/u010349644/article/details/81950444