前言:
知道并清楚jvm运行时内存区域的划分是非常重要的,尤其是在进行jvm调优的时候,内存区域大小的配置关系影响着运行的性能以及是否会出现OOM的情况。
JVM的运行时内存区域
根据Java虚拟机规范的定义,JVM的运行时内存区域主要由Java堆、虚拟机栈、本地方法栈、方法区和程序计数器以及运行时常量池组成。其中堆、方法区以及运行时常量池是线程之间共享的区域,而栈(本地方法栈+虚拟机栈)、程序计数器都是线程独享的。
需要注意的是,上面的这6个区域,是虚拟机规范中定义的,但是在具体的实现上,不同的虚拟机,甚至是同一个虚拟机的不同版本,在实现细节上也是有区别的。
程序计数器:一个只读的存储器,用于记录Java虚拟机正在执行的字节码指令的地址。它是线程私有的,为每个线程维护一个独立的程序计数器,用于指示下一条将要被执行的字节码指令的位置。它保证线程执行一个字节码指令以后,才会去执行下一个字节码指令。
Java虚拟机栈:一种线程私有的存储器,用于存储Java中的同部变量。根据Java虚拟机规范,每次方法调用都会创建一个栈帧,该栈帧用于存储局部变量,操作数栈,动态链接,方法出口等信息。当方法执行完毕之后,这个栈帧就会被弹出,变量作用域就会结束,数据就会从栈中消失。
本地方法栈:本地方法栈是一种特殊的栈,它与Java虚拟机栈有着相同的功能,但是它支持本地代码(NativeCode)的执行。本地方法栈中存放本地方法(Native Method)的参数和局部变量,以及其他一些附加信息这些本地方法一般是用C等本地语言实现的,虚拟机在执行这些方法时就会通过本地方法栈来调用这些本地方法。
Java堆:是存储对象实例的运行时内存区域。它是虚拟机运行时的内存总体的最大的一块,也一直占据着虚拟机内存总量的一大部分。Java堆由Java虚拟机管理,用于存放对象实例,是几乎所有的对象实例都要在上面分配内存。此外,Java堆还用于垃圾回收,虚拟机发现没有被引用的对象时,就会对堆中对象进行垃圾回收,以放内存空间。
方法区:用于存储已被加载的类信息、常量、静态变量、即时编译后的代码等数据的内存区域。每加载一个类,方法区就会分配一定的内存空间,用于存储该类的相关信息,这部分空间随着需要而动态变化。方法区的具体实现形式可以有多种,比如堆、永久代、元空间等。
运行时常量池:是方法区的一部分。用于存储编译阶段生成的信息,主要有字面量和符号引用常量两类。其中符号引用常量包括了类的全限定名称、字段的名称和描述符、方法的名称和描述符。
堆和栈的区别
堆和栈是ava程序运行过程中主要存储区域,经常被拿来对比,他们主要有以下区别(这里的栈主要指的是虚拟机栈):
1、存储位置不同,堆是在IM堆内存中分配空间,而栈是在IM的栈内存中分配空间。
2、存储的内容不同,堆中主要存储对象,栈中主要存储本地变量。
3、堆是线程共享的,栈是线程独享的。
4、堆是垃圾回收的主要区域,不再引用这个对象,会被垃圾回收机制会自动回收。栈的内存使用是一种先进后出的机制,栈中的变量会在程序执行完毕后自动释放。
5、栈的大小比堆要小的多,一般是几百到几千字节。
6、栈的存储速度比堆快,代码执行效率高。
7、堆上会发生OutofMemoryError,栈上会发生StackOverflowError。