深入理解Java虚拟机(一) 运行时数据区划分

 

       前言:从我学Java的第一天开始,我的大学老师就告诉我 Java语言相比C、C++的语言有一个非常强大的功能,那就是自动内存管理;我们用Java编码时不需要申请或释放内存等,这些工作全部交由我们的Java虚拟机(以下简称JVM)来帮助我们管理。从那之后 ,我发现我们在写Java代码的就不需要去管理内存。 但我经常会发现一些问题,诸如内存溢出或内存泄露,从那以后我就开始关注JVM,它是怎样帮我们管理内存;对比C++程序员,用一句通俗的话讲,那就是墙外的人向进去看看,墙内的人想出来走走~~因此,本篇文章主要阐述是我对于JVM运行时数据区的内存划分的个人理解。

什么是Java虚拟机

​ 作为一个Java程序员,我们每天都在写代码,我们写的代码都要在一个叫做Java虚拟机上执行。

网上是这么阐述的:虚拟机是一种抽象化的计算机,通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机有自己完善的硬体架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。JVM屏蔽了与具体操作系统平台相关的信息,使得Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。

 

什么是运行时数据区

要理解JVM运行时数据区,就要先了解JVM的体系结构,如下

由此可见, 运行时数据区的划分, 是和JVM的体系结构相关的。 本文主要介绍运行时数据区的划分。

 

程序计数器(Program Counter Register)

  • 程序计数器就是一块较小的内存空间,可以把他看做是当前线程所执行的字节码的行号指示器;

  • 程序计数器处于线程独占区;

  • 如果线程执行的是Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址。如果执行的是native方法,这个计数器的值为undifined

  • 此区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域;

 

虚拟机栈(VM Stack)

  • 虚拟机栈描述的是Java方法执行的动态内存模型

  • 该区域也是线程私有的,它的生命周期也与线程相同

  • 栈帧

    • 每个方法执行,都会创建一个栈帧,伴随着方法从创建到执行完成。用于存放局部变量表,操作数栈,动态链接,方法出口。

  • 局部变量表

    • 存放编译器可知的各种基本数据结构,引用类型,return Address类型。

    • 局部变量表的内存空间在编译器完成分配,当进入一个方法时,这个方法需要在栈中分配多少内存是固定的,在方法运行期间是不会改变局部变量表的大小。

  • 操作数栈

    • 操作数栈又常被称为操作栈,操作数栈的最大深度也是在编译的时候就确定了。32位数据类型所占的栈容量为1,64为数据类型所占的栈容量为2。当一个方法开始执行时,它的操作栈是空的,在方法的执行过程中,会有各种字节码指令(比如:加操作、赋值元算等)向操作栈中写入和提取内容,也就是入栈和出栈操作。

    • Java虚拟机的解释执行引擎称为“基于栈的执行引擎”,其中所指的“栈”就是操作数栈。

     

  • 动态链接

    • 每个栈帧都包含一个指向运行时常量池(在方法区中,后面介绍)中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接

  • 方法出口

    • 当一个方法被执行后,有两种方式退出该方法:执行引擎遇到了任意一个方法返回的字节码指令或遇到了异常,并且该异常没有在方法体内得到处理。无论采用何种退出方式,在方法退出之后,都需要返回到方法被调用的位置,程序才能继续执行。

    • 方法退出的过程实际上等同于把当前栈帧出站,因此退出时可能执行的操作有:恢复上层方法的局部变量表和操作数栈,如果有返回值,则把它压入调用者栈帧的操作数栈中,调整PC计数器的值以指向方法调用指令后面的一条指令。

本地方法栈(Native Stack)

  • 虚拟机栈为虚拟机执行Java方法服务

  • 本地方法栈为虚拟机执行native方法服务

 

Java堆(heap)

  • 存放对象和数组实例

  • 它是所有线程共享的一块内存区域

  • 垃圾收集器管理的主要区域,因此很多时候也被称为“GC堆”

  • 新生代,老年代

  • 根据Java虚拟机规范的规定,Java堆可以处在物理上不连续的内存空间中,只要逻辑上是连续的即可。如果在堆中没有内存可分配时,并且堆也无法扩展时,将会抛出OutOfMemoryError异常。

 

方法区(Method Area)

  • 存储虚拟机加载的类信息,常亮,静态变量,即时编译器编译后的代码等信息

  • 方法区域又被称为“永久代”,但这仅仅对于Sun HotSpot虚拟机来讲

  • 用于存放编译器生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。

  • 异常定义

 

内存溢出

如果我们平时在编码的时候使用不当,就将会出现内存溢出的情况,下面列举以下常见的内存溢出的测试方法:

 

 

  写在后面的话: 写的不好,请多多包含。谢谢!!!

PS:下一期预告:深入理解Java虚拟机(二) : 垃圾回收

 

 

 

 

原文地址:https://www.cnblogs.com/ft-greate/p/10291983.html