深入了解Java之虚拟机内存

在讨论JVM内存区域分析之前,先来看一下Java程序详细运行的过程:


Java程序运行过程

Java 程序的运行过程:Java 源码文件(.Java文件)-> Java Compiler(Java编译器)->Java 字节码文件(.class文件)->类载入器(Class Loader)->Runtime Data Area(运行时数据)-> Execution Engine(运行引擎)。

我们今天就来分析一下Java程序运行过程的 Runtime Data Area(执行时数据) 这一块

那么执行时数据区包含哪几部分呢?

  • 程序计数器(Program Counter Register)
  • Java虚拟机栈(VM Stack)
  • 本地方法栈(Native Method Stack)
  • 方法区(method area)
  • 堆(heap)

一、程序计数器

用来指示程序运行哪一条指令,这跟汇编语言的程序计数器的功能在逻辑上是一样的。JVM规范中规定,假设线程运行的是非native方法,则程序计数器中保存的是当前须要运行的指令地址。假设线程运行的是native方法,则程序计数器中的值undefined。

每一个线程都有自己独立的程序计数器。为什么呢?由于多线程下,一个CPU内核仅仅会运行一条线程中的指令,因此为了使每一个线程在线程切换之后可以恢复到切换之前的程序运行的位置。所以每一个线程都有自己独立的程序计数器。

二、Java虚拟机栈

Java虚拟机栈中存放的是一个个栈帧,当程序运行一个方法时。就会创建一个栈帧并压入栈中,当方法运行完成之后,便会将栈帧移除栈。我们所说的“栈”是指Java虚拟机栈,一个栈帧中包含:局部变量表、操作数栈、动态连接、方法返回地址、附加信息

1、局部变量表

主要是存储方法中的局部变量。包含方法中局部变量的信息和方法的參数。

如:各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型,它不等同于对象本身。可能是一个指向对象起始地址的引用指针。也可能是指向一个代表对象的句柄或其它与此对象相关的位置)和returnAddress类型(指向了一条字节码指令的地址)。当中64位长度的long和double类型的数据会占用2个局部变量空间(Slot),其余的数据类型仅仅占用1个。局部变量表的大小在编译器就能够确定其大小了,因此在程序运行期间局部变量表的大小是不会改变的。在Java虚拟机规范中,对这个区域规定了两种异常状况:假设线程请求的栈深度大于虚拟机所同意的深度,将抛出StackOverflowError异常;假设虚拟机栈能够动态扩展(当前大部分的Java虚拟机都可动态扩展,仅仅只是Java虚拟机规范中也同意固定长度的虚拟机栈),假设扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。

操作数栈

虚拟机把操作数栈作为它的工作区。程序中的全部计算过程都是在借助于操作数栈来完毕的,大多数指令都要从这里弹出数据。运行运算,然后把结果压回操作数栈。

动态连接

每一个栈帧都包括一个指向执行时常量池中该栈帧所属方法的引用(指向执行时常量池:在方法执行的过程中有可能须要用到类中的常量),持有这个引用是为了支持方法调用过程中的动态连接

方法返回地址

当一个方法运行完成之后。要返回之前调用它的地方,因此在栈帧中必须保存一个方法返回地址。

附加信息

虚拟机规范同意详细的虚拟机实现添加一些规范里没有描写叙述的信息到栈帧中,比如与高度相关的信息,这部分信息全然取决于详细的虚拟机实现。

在实际开发中。通常会把动态连接,方法返回地址与其他附加信息所有归为一类,称为栈帧信息。

三、本地方法栈、

本地方法栈(Native Method Stack)与虚拟机栈所发挥的作用是很相似的。它们之间的差别只是是虚拟机栈为虚拟机运行Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务。

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

四、堆(heap)

在C语言中。程序猿能够通过malloc函数和free函数在堆上申请和释放空间。

那么在Java中是怎么样的呢?Java中的堆是用来存储对象本身的以及数组(当然。数组引用是存放在Java栈中的),差点儿全部的对象实例都在这里分配内存。在Java中,程序猿基本不用去关心空间释放的问题,Java的垃圾回收机制会自己主动进行处理。

另外,堆是被全部线程共享的。在JVM中仅仅有一个堆。

五、方法区

方法区(Method Area)与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机载入的类信息、常量、静态变量、以及编译器编译后的代码等。执行时常量池(Runtime Constant Pool)是方法区的一部分。

Class文件里除了有类的版本号、字段、方法、接口等描写叙述信息外,另一项信息是常量池(Constant Pool Table)。用于存放编译期生成的各种字面量和符号引用,这部分内容将在类载入后进入方法区的执行时常量池中存放。

在JVM规范中,没有强制要求方法区必须实现垃圾回收。非常多人习惯将方法区称为“永久代”。是由于HotSpot虚拟机以永久代来实现方法区。从而JVM的垃圾收集器能够像管理堆区一样管理这部分区域,从而不须要专门为这部分设计垃圾回收机制。只是自从JDK7之后,Hotspot虚拟机便将执行时常量池从永久代移除了。

注意:native方法是指:指使用 Java 以外的其它语言编写的方法

原文地址:https://www.cnblogs.com/lxjshuju/p/7299388.html