Java内存结构

Java内存结构

Java虚拟机的内存空间分为5个部分

  • 程序计数器
  • Java虚拟机栈
  • 本地方法栈
  • Java堆
  • 方法区

程序计数器

定义

程序计数器是一块较小的内存空间,可以看作是当前线程所执行的那条字节码的地址;如果当前线程执行的是Java方法,计数器记录的是正在执行的虚拟机字节码指令的地址,如果执行的是本地方法,则这个计数器值为空(Undefined)。

作用

  • 在多线程情况下,记录当前线程正在执行的那条字节码指令的地址,当线程切换回来后,就知道该线程执行到那里了。

特点

  • 唯一一个在Java虚拟机规范中没有规定任何OutofMemoryError情况的区域
  • 线程私有,各线程之间计数器互不影响,独立存储
  • 随线程创建而创建,随线程结束而销毁

Java虚拟机栈

Java虚拟机栈描述的是Java方法执行的内存模型。每个方法在执行过程中会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。

在这里插入图片描述

特点

  • 线程私有,生命周期与线程相同
  • 局部变量表随栈帧创建而创建,存放编译期可知的各种基本数据结构、对象引用(指向对象起始地址的引用指针)和returnAddress类型(指向一条字节码指令的地址)
  • 局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小
  • 两种异常情况
    • 如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常
    • 如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常

本地方法栈

本地方法栈与虚拟机栈所发挥的作用是非常相似的,只不过本地方法栈是为虚拟机使用的Native方法服务。

两种异常情况

  • 如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常
  • 如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常

Java堆

Java堆是Java虚拟机所管理的内存中最大的一块,被所有线程共享。Java堆是垃圾收集器管理的主要区域。

作用

  • 存放对象实例,几乎所有的对象实例都在这里分配内存(对象实例、数组)

特点

  • 堆中可能划分出很多区域,不同区域存放不同生命周期的对象,使用不同的垃圾回收算法,更具有针对性

  • 如果堆中没有完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常

  • Java堆可以处于物理上不连续的内存空间中,只要逻辑上连续即可

方法区

Java虚拟机规范把方法区描述为堆的一个逻辑部分。

方法区和Java堆一样,可以被所有线程共享。用于存储已被虚拟机加载的类信息、常量、静态常量、即时编译器编译后的代码(.class)。

特点

当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。

运行时常量池

运行时常量池是方法区的一部分,存放编译期生成的各种字面量和符号引用。在类被加载后,.class文件中的常量就存放在方法区中的运行时常量池中。在运行期间,可以向其中添加新的常量,比如String类的intern()方法。

直接内存

直接内存不是Java虚拟机规范中定义的内存区域,但可以被Java使用。

NIO中引入了一种基于通道(Channel)和缓冲区(Buffer)的I/O方式,它可以使用本地函数直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作,显著提高了性能。

内存不足时会导致OutOfMemoryError异常。

服务器管理员在配置虚拟机参数时,会根据实际内存设置-Xmx等参数信息,但经常忽略直接内存,使得各个内存区域总和大于物理内存限制,从而导致动态扩展时出现OutOfMemoryError异常。

参考:《深入理解Java虚拟机》· 周志明

-------------本文结束感谢您的阅读-------------
0%