五分钟了解JVM–JAVA内存区域

这是我参与新手入门的第1篇文章

众所周知,Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域,这些区域有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而一直存在,有的区域则是依赖用户线程的启动和技术而建立和销毁。

主要分为:方法区、堆、栈(虚拟机栈、本地方法栈)、程序计数器
image.png

2、程序计数器

程序计数器是一块比较小的内存,它可以看作是当前线程所执行的字节码行号指示器。如果执行的是本地(Native)方法,则计数器值为空

由于java虚拟机的多线程是通过线程轮流切换、分配处理器执行时间的方式来实现的,在任何一个确定时刻,一个处理器都只会执行一条线程中的指令。为了线程切换后还能恢复到原来的执行位置,所以每条线程都有独立的程序计数器。

此内存区域是唯一一个没有规定任何内存溢出(OutOfMemoryError)情况的区域。

3、Java虚拟机栈

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

每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧用于存储局部变量表、操作数栈、动态连接、方法出口等信息(每个方法从调用到执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程

iShot2021-07-07 14.08.11.png
局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在栈帧中分配多大的局部变量空间是完全确定的

内存溢出:

  1. 如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowEroor异常
  2. 如果Java虚拟机栈容量可以拓展,当栈拓展时无法申请到足够的内存,会抛出OutOfMemoryError异常。

4、本地方法栈

与虚拟机栈主要区别为:虚拟机栈为虚拟机执行java(字节码服务)。而本地方法栈则为虚拟机使用到本地方法(Native)服务,HotSpot虚拟机直接把本地方法栈和虚拟机栈合二为一。

5、Java堆

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

对象实例:new 出来的对象,对象引用 ,存放实例的地址

Java堆是垃圾收集器管理的内存区域,Java堆中经常出现的“新生代”、“老年代”、“永久带”、“Eden空间”、“From Survivor空间”、“To Survivor”这些区域划分仅仅是一部分垃圾回收器的共同特性或者说设计风格而已,而非某个Java虚拟机具体实现的固有内存布局。

目前主流的虚拟机都是按照可拓展来实现的(通过参数 -Xmx和 -Xms设定),如果Java对象未完成实例分配,并且堆也无法再拓展时,Java虚拟机将抛出OutOfMemoryError异常

6、方法区

方法区可以看作是一块独立于堆的内存空间 。用于存储已被虚拟机加载的类型信息(类class、接口、枚举、注解)、常量(包含各种字面量,和对类型,域和方法的符号引用)、静态变量、即时编译器编译后的代码缓存等数据。

特点:

  • (1)在jvm启动时被创建,并且它的实际的物理内存空间中和java堆区一样都是可以不连续的。
  • (2)方法区的大小,跟堆空间一样,可以选择固定大小或者扩展。
  • (3)方法区的大小决定了系统可以保存多少个类,如果系统定义了太多的类,导致方法区溢出,虚拟机同样会抛出内存溢出错误
  • java8以前(java.lang.OutOfMemoryError:PermGen space)或者java8以后(java.lang.OutOfMemoryError:Metaspace)
  • (4)关闭jvm就会释放这个区域的内存

HotSpot方法区演变过程:
Jdk7之前,方法区实现方式是永久代,Jdk8开始方法区实现使用元空间取代永久代。元空间不在虚拟机的内存中,而是使用本地内存

6.1、运行时常量池

运行时常量池是方法区的一部分,常量池表是class文件的一部分,用于存放编译期生成的各种字面量与符号引用,这部分内容将在类加载存放到方法区的运行时常量池中。

运行时常量池创建时机:在加载类和接口到虚拟机后,就会创建对应的运行时常量池。

运行时常量池具备动态性,比如使用String类的intern方法加入运行时常量池中。

当创建类或者接口的运行时常量池,如果构造运行时常量池所需的内存空间超过了方法区所能提供的最大值,则jvm会抛出OutOfMemoryError异常
栈、堆、方法区交互

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享