一 、java运行时内存分配

359 阅读4分钟

内存划分

很多人都将JVM的内存划分为 堆内存heap和栈内存 stack,但是这并不完全准确,更详细的划分如下图:

2023-08-31-16-54-23-image.png

  • helloWorld.java 被编译器调用javac命令 生成 helloWorld.class

  • helloWorld.class 被classLoader加载到 内存中

  • JVM运行时,主要分为5个区

    • 每个线程私有的:虚拟机栈,本地方法栈,程序计数器

    • 所有线程共享的:方法区,堆

1. 程序计数器

JAVA是多线程的,CPU可以在多个线程中切换时间片,当某一个线程被CPU挂起时,需要记录代码已经执行到的位置,以便这个线程重新获得时间片时,知道应该从哪儿继续执行。

程序计数器注意事项:

  1. 在java虚拟机规范中,程序计数器这一块并没有规定任何 OutOfMemoryError的情况

  2. 程序计数器是私有的,随着线程的创建而诞生,随着线程的死亡而消亡

  3. 当一个线程正在执行某个JAVA方法时,这个计数器记录的是正在执行的 虚拟机字节码指令的地址,它无法记录native方法的执行位置

2. 虚拟机栈

也是线程私有的,生命周期与线程完全同步。

在虚拟机规范中,规定了两种异常状况:

  1. StackOverflowError: 栈溢出,当线程 请求的深度超出虚拟机栈所允许的深度时抛出

  2. OutOfMemoryError:内存溢出,当虚拟机动态扩容到无法申请到足够内存时抛出

JVM是基于 虚拟机栈的解释器执行的。(DVM则是基于寄存器解释器执行的)

虚拟机栈的作用是,描述JAVA方法执行时的内存模型。每个方法执行时,JVM都会在虚拟机栈中创建一个"栈帧",

栈帧概念

所谓 “栈帧” StackFrame ,就是 用于虚拟机进行方法调用和方法执行时的数据结构,栈帧就是一种数据结构。每个栈帧包括如下成分:

  • 局部变量表

    • 变量值的存储空间,调用方法时传递的参数,以及方法内部创建的局部变量,都存在这里
  • 操作数栈

    • 它是一个后入先出栈,在方法刚执行的时候,栈是空的,当方法执行时,会有各种字节码指令被压入和弹出栈
  • 动态链接

    • 当一个方法要调用其他方法时,需要将要调用方法的符号引用转化为内存中的直接引用,而符号表则存在于 方法区中。
  • 返回地址

    • 当一个方法执行完毕之后,可以正常退出,也可以异常退出。正常退出可以是 执行到方法最后,也可以是遇到某个return指令。异常退出,则是运行中出现了未被处理的异常,导致方法退出。

举例

这是一个简单案例:

2023-08-31-17-13-33-image.png

最终会输出i+j+10=13的结果,但是在JVM中执行过程远比我们想的复杂。

我们将这个java文件编译成class之后,再使用javap命令来查看字节码指令,结果如下:

2023-08-31-17-14-54-image.png

3. 本地方法栈

与虚拟机栈基本相同,是针对本地native方法,做jni开发的接触得多一些。

但是有些虚拟机的实现已经将 本地方法栈和虚拟机栈合二为一了。

4. 堆

  • 唯一的作用就是存放对象实例。

  • 它也是JAVA GC的主要管理区域。

  • 堆是所有线程共享的区域,正因为是多线程共享堆空间,所以需要考虑线程安全的问题。

  • 堆中的内存分为新生代老年代

  • 新生代 又分为EdenSurvivor, 不同的区域存放不同生命周期的对象

  • 不同的区域采用不同的垃圾回收算法,具有针对性,从而提高垃圾回收的效率

5. 方法区

  • 也是被所有线程共享的

  • 用来存储,已经被JVM加载的类信息,常量,静态变量,即时编译后的代码,数据

关于异常

  • StackOverflowError

    • 递归调用时造成栈溢出的主要原因,使用递归却没有设定递归的结束的条件,或者执行时永远达不到结束的条件。

    • 每调用一次递归方法,都会在虚拟机栈中创建出一个栈帧,而无限创建栈帧,会导致栈溢出

  • OutOfMemoryError

    • 理论上,虚拟机栈,堆,方法区,都有发生OutOfMemoryError的可能,但是实际上,内存溢出大多发生在堆中。

    • 经常出现内存溢出的情况包括:循环或者频繁调用的方法中,如果大量申请内存,就有可能最后导致虚拟机动态扩容到无法申请到足够的内存的程度,于是抛出error。

总结

以上5个区域:程序计数器,虚拟机栈,本地方法栈, 堆,方法区,仅仅是虚拟机规范,而不是具体实现。具体实现有:Sun公司的HotSpot,还有 Android的DVM , ART。

image.png