java7内存结构

java7内存结构,第1张

java7内存结构

先给一张java7的内存结构图吧(我用Windows里面的画图工具画的,所以看起来不怎么美观)

 

首先对这个图有一个认识,从上面可以看到java7的内存结构大致分了五个部分:PC寄存器,java虚拟机栈、本地方法栈、java堆、方法区。其中PC寄存器、java虚拟机栈和本地方法栈是所有线程共享的一块内存区域。java堆和方法区是每一个线程隔离的一块区域,其中,方法区还有一个运行时常量池。

接下来看一看每一块区域里面存放的什么?

一、PC寄存器

在大学的时候学过计算机组成原理的时候都知道,内存里面有很多寄存器,大概几百个吧(目前的,之前大学学的时候老师说才几十个),每一种寄存器的用途都不一样,其中有一个寄存器就是程序计数器。这个寄存器的主要作用就是存放下一条需要执行的指令。

首先,为什么要有这个程序计数器呢?这是因为我们的处理器在一个时刻,只能执行一个线程中的指令。但是我们的程序往往都是多线程的,这时候处理器就需要来回切换我们的线程,为了在线程切换之后回到之前正确的位置上,此时就需要一个程序计数器,这也就很容易理解了我们的每个线程都有一个自己的程序计数器来保存自己之前的状态。

接下来如何理解这个程序计数器的功能呢?假如我们的程序代码假如是一行一行执行的,程序计数器永远指向下一行需要执行的字节码指令。在循环结构中,我们就可以改变程序计数器中的值,来指向下一条需要执行的指令。因此,在分支、循环、跳转、异常处理和线程恢复等等一些场景都需要这个程序计数器来完成。

最后看一下在什么情况下,应该存储什么内容。《java虚拟机规范》中说如果当前执行的是 Java 的方法,则该寄存器中保存当前执行指令的地址;倘若执行的是native 方法,则PC寄存器中为空(Undefined)。PC寄存区区域就是存放了N多个这样的寄存区。此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。因此可以把他的几个特点归纳如下。

  1. 程序计数器指定下一条需要执行的指令
  2. 每一个线程独有一个程序计数器
  3. 执行java代码时,寄存器保存当前指令地址
  4. 执行native方法时候,寄存器为空。
  5. 不会造成OutOfMemoryError情况

二、Java虚拟机栈

每一个线程都有自己的java虚拟机栈,这个栈与线程同时创建,一个线程中的每个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。每个线程有一个私有的栈,随着线程的创建而创建。栈里面存着的是一种叫“栈帧”的东西,每个方法会创建一个栈帧,栈帧中存放了局部变量表(基本数据类型和对象引用)、 *** 作数栈、动态连接和返回地址等信息。当前运行方法对应的栈帧叫做当前栈帧。下面主要对这个栈帧进行一个介绍。

先看一张图

 

首先,局部变量表里存放了编译期间可知的各种基本数据类型(8种)、对象引用、returnAddress类型(指向一条字节码指令的地址)。他有如下特点:

  • 64位长度的long和double类型占用2个局部变量空间(Slot),其余数据类型只占用一个。
  • 局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,
  • 在方法运行期间不会改变局部变量表的大小。

接下来 *** 作数栈,其实在栈帧刚刚创建的时候, *** 作数栈是空的,java虚拟机可以从局部变量表或者对象的实例字段中,复制一些常量或者变量值到 *** 作数栈中。也可以从 *** 作数栈中取走数据。他的深度在编译期就已经确定了。

动态连接是什么意思呢?在这里我们先有个基本的印象,下面举例子的时候,再来看这个解释比较容易理解一点,我们知道,在线程中一个方法去调用另外一个方法,是通过符号引用来实现的,动态连接的作用就是把这个符号引用表示的方法转化为实际方法的直接引用。

对于java虚拟机栈的描述,最后看一下可能发生的异常情况:

  • 如果线程请求分配的栈容量超过java虚拟机栈所允许的最大容量,java虚拟机就会抛出StackOverfolwError
  • 如果java虚拟机栈动态扩展,在扩展时没有申请到足够的内存或者是创建新线程时没有足够的内存再创建java虚拟机栈了,那么java虚拟机就会抛出outOfMemoryError

三、本地方法栈(Native Method Stack)

与虚拟机栈类似,区别是虚拟机栈执行java方法,本地方法站执行native方法。在虚拟机规范中对本地方法栈中方法使用的语言、使用方法与数据结构没有强制规定,因此虚拟机可以自由实现它。本地方法栈可以抛出StackOverflowError和OutOfMemoryError异常。不过这块区域我们不怎么去关心。

四、Java堆

Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建,用来存放对象实例。是内存中最大的一块区域。垃圾收集器(GC)在该区域回收不使用的对象的内存空间。但是并不是所有的对象都在这保存,深入理解java虚拟机中说道,随着JIT编译器的发展和逃逸分析技术逐渐成熟,栈上分配、标量调换优化技术将会导致一些微妙的变化,所有的对象都分配在堆上也逐渐变得不那么绝对了。

堆的大小可以固定也可以动态扩展,可通过-Xms(最小值)和-Xmx(最大值)参数设置,如果在堆中没有内存完成实例分配,且堆也无法在扩展时,会抛出OutOfMemoryError异常。

下面给一张java 堆的结构图

 

为了支持垃圾收集,堆被分为三个部分:

年轻代 : 常常又被划分为Eden区和Survivor(From Survivor To Survivor)区(Eden空间、From Survivor空间、To Survivor空间(空间分配比例是8:1:1)

老年代:

永久代 :(jdk 8已移除永久代,取而代之的是元空间。下面会讲解)

五、方法区

方法区也是所有线程共享。主要用于存储类的信息、常量池、静态变量、及时编译器编译后的代码等数据。方法区逻辑上属于堆的一部分。通常又叫“Non-Heap(非堆)”

                                                                         需要更多教程,微信扫码即可

                                                                              

                                                                                         

                                                        别忘了扫码领资料哦【高清Java学习路线图】

                                                                     和【全套学习视频及配套资料】
 

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5433749.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-11
下一篇 2022-12-11

发表评论

登录后才能评论

评论列表(0条)

保存