JAVA 内存结构

文章参考《深入理解Java虚拟机》第3版

1、Java内存结构

1.1、JVM运行时数据区域

Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域

Java虚拟机运行时数据区.jpg

1.2、按照线程是否共享区分

线程共享 线程私有
堆内存 程序计数器、Java虚拟机栈、本地方法栈
方法区

1.3、按照内存区分

JVM内存与本地内存的区别

Java虚拟机在执行的时候会把管理的内存分配成不同的区域,这些区域被称为虚拟机内存,同时,对于虚拟机没有直接管理的物理内存,也有一定的利用,这些被利用却不在虚拟机内存数据区的内存,我们称它为本地内存

JVM内存:受虚拟机内存大小的参数控制,当超过参数设置的大小时会报OOM 本地内存:不受虚拟机内存限制,只受物理内存容量限制;内存的占用超出物理内存的大小,同样会报OOM
堆内存 方法区
程序计数器、Java虚拟机栈、本地方法栈

2、程序计数器(Program Counter Register)

程序计数器就是当前线程所执行的字节码的行号指示器,通过改变计数器的值来选取下一行指令,通过它来实现分支、跳转、循环、异常处理、恢复线程等基础功能。

  • 由于Java虚拟机的多线程是通过线程轮流切换、分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器都只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要一个独立的程序计数器,也就是程序计数器是线程私有的。
  • 如果线程在执行一个Java方法,计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是本地(Native)方法,这个计数器值则应为空(Undefined)

3、Java虚拟机栈(Java Virtual Machine Stack)

Java虚拟机栈也是线程私有的,它的生命周期与线程相同;虚拟机栈描述的是Java方法执行的线程内存模型:

  • 每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧(Stack Frame)。每个栈帧包含内容:局部变量表、操作数栈、动态连接、方法出口等信息。

  • 局部变量表中存储着方法里的java基本数据类型(byte/boolean/char/int/long/double/float/short)、对象引用(reference类型)和returnAddress类型(指向了一条字节码指令的地址)注:这里的基本数据类型指的是方法内的局部变量

  • 每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。

    虚拟机栈可能会抛出两种异常

    • 如果线程请求的栈深度大于虚拟机所允许的栈深度,则会抛出StackOverFlowError即栈溢出
    • 如果虚拟机的栈容量可以动态扩展,当栈扩展时无法申请到足够内存时会抛出OutOfMemoryError即OOM内存溢出

4、本地方法栈(Native Method Stacks)

本地方法栈与虚拟机栈所发挥的作用是非常相似的,其区别只是虚拟机栈为虚拟机执行Java方法服务,而本地方法栈则是为虚拟机使用到的本地(Native)方法服务。

本地方法栈也会在栈深度溢出或者栈扩展失败时分别抛出StackOverflowError和OutOfMemoryError异常。

5、堆内存(Java Heap)--GC堆

Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建;此内存区域的唯一目的就是存放对象实例。

Java堆可通过参数-Xms和-Xmx实现可扩展;在Java堆中没有内存完成实例分配,并且堆也无法再扩展时,会抛出OutOfMemoryError异常。

6、方法区(Method Area)--非堆内存

方法区与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。

运行时常量池(Runtime Constant Pool)是方法区的一部分。并非预置入Class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可以将新的常量放入池中,这种特性通过String类的intern()方法实现。

当方法区无法满足新的内存分配需求或者当常量池无法再申请到内存时会抛出OutOfMemoryError异常。

7、直接内存(Direct Memory)

直接内存并不是虚拟机运行时数据区的一部分,也不是《Java虚拟机规范》中定义的内存区域。

一种基于通道(Channel)与缓冲区(Buffer)的I/O方式(NIO类-New Input/Output),它使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆里面的DirectByteBuffer对象作为遮脸内存的引用进行操作。这样能在一些场景中显著提高性能,因此避免了在Java堆和Native堆中来回复制数据。

当配置虚拟机参数时,容易忽略直接内存,使得各个内存区域综合大于物理内存限制,从而导致动态扩展时出现OutOfMemoryError异常。

posted on 2021-11-13 11:04  cxbks  阅读(133)  评论(0)    收藏  举报