Java 虚拟机基础知识(一)

在这里插入图片描述

该文章首发于微信公众号“字节流动”

Java 虚拟机特性

Java 虚拟机具有两大特性:

  1. 平台无关性。Java 虚拟机屏蔽了与具体操作系统平台相关的信息,使得 Java 语言编译程序只需生成在 Java 虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。Java 虚拟机在执行字节码时,实际上最终还是把字节码解释成具体平台上的机器指令执行。
  2. 语言无关性。Java 虚拟机不和包括 Java 在内的任何语言绑定,它只与 .class 文件这种特定的二进制文件格式相关联,而 Java 虚拟机不关心 .class 文件的来源。基于这一特性出现了很多运行在 JVM 之上的编程语言,如 Groovy , JRuby, Jyphon, Scala 等。

Java 虚拟机运行时内存区域划分

在这里插入图片描述
由图所示,Java 虚拟机将运行时数据区划分为 5 块,分别是: Java 堆, 方法区(静态存储区),虚拟机栈,本地方法栈以及程序计数器。

在这里插入图片描述

Java 堆

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

Java 堆唯一的目的就是存放对象实例,绝大部分对象实例和数组都要在堆上分配。Java 堆可以处于物理上不连续的内存空间,只需保证逻辑上连续即可。如果在堆中没有完成实例的内存分配,并且堆也无法再扩展,将会抛出 OutOfMemoryError 异常。

方法区(静态存储区)

方法区也是线程间共享内存区域,用于存储已被虚拟机加载的类信息,常量,静态变量以及即时编译器编译后的代码等数据。

方法区可以处于物理上不连续的内存空间。当方法区无法满足内存分配需求时,将会抛出 OutOfMemoryError 异常。

Java 虚拟机栈

Java 虚拟机栈为线程私有内存区域,生命周期与线程相同。

Java 虚拟机栈描述的是 Java 方法执行的内存模型,每个方法执行的同时都会创建一个栈帧,主要用于存储局部变量表,操作数栈,动态链接,方法出口信息。每一个方法从调用到直至执行完的过程,都对应着一个栈帧在虚拟机栈入栈和出栈的过程。

Java 虚拟机规范中对 Java 虚拟机栈规定了两种异常情况:

  1. 线程请求的栈深度大于虚拟机所允许的深度,将会抛出 StackOverflowError 异常,(不过现在大多数虚拟机都支持虚拟机栈的动态扩展);
  2. 虚拟机栈在进行扩展时无法申请到足够的内存,将会抛出 OOM 异常。

局部变量表

局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量,其中存放的数据的类型是编译期可知的**各种基本数据类型、对象引用(reference)**和 returnAddress 类型(它指向了一条字节码指令的地址)。

局部变量表所需的内存空间在编译期间完成分配,即在 Java 程序被编译成 Class 文件时,就确定了所需分配的最大局部变量表的容量。当进入一个方法时,这个方法需要在栈中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。

操作数栈

操作数栈又常被称为操作栈,操作数栈的最大深度也是在编译的时候就确定了。

动态链接

一般来说,方法正常退出时,调用者的程序计数器的值就可以作为返回地址,栈帧中很可能保存了这个计数器值,而方法异常退出时,返回地址是要通过异常处理器来确定的,栈帧中一般不会保存这部分信息。

本地方法栈 (Native Method Stack)

本地方法栈与 Java 虚拟机栈的作用非常相似,虚拟栈为虚拟机执行的 Java 方法服务,本地方法栈则为使用到的 Native 方法服务。

与虚拟机栈一样,本地方法栈也会抛出 StackOverflowError 和 OutOfMemoryError 异常。

程序计数器

程序计数器为一小块线程私有的内存空间,可以看成当前线程所执行字节码的行号指示器。

程序计数器存在的意义是确保线程切换后或者线程被 CPU 重新调度后,恢复到正确的执行位置。如果一个线程执行的 Java 方法,程序计数器记录的是正在执行的虚拟机字节码指令的位置;如果正在执行的是 Native 方法,则计数器的值为空。程序计数器存储区域是 JVM 规范中没有规定任何 OutOfMemoeyError 情况的唯一一块区域。

直接内存

直接内存不是虚拟机运行时数据区域的一部分,也不是 Java 虚拟机规范中定义的内存区域,但这部分内存也被频繁使用,也是可能导致 OutOfMemoryError 异常的内存区域。

在 JDK1.4 中新引入了 NIO 机制,它是一种基于通道与缓冲区的新 I/O 方式,可以直接从操作系统中分配直接内存,即在堆外分配内存,这样能在一些场景中提高性能,因为避免了在 Java 堆和 Native 堆中来回复制数据。

联系与交流

微信公众号
我的公众号
个人微信
我的微信

发布了53 篇原创文章 · 获赞 18 · 访问量 20万+

猜你喜欢

转载自blog.csdn.net/Kennethdroid/article/details/86758852
今日推荐