JVM内存模型简述

JVM内存模型简述

版权申明:本文为原创文章,转载请注明原文出处

原文链接:https://blog.it-follower.com/posts/232958262.html

JVM内存模型,主要包含五个区域。其中线程共享:方法区和堆,线程私有:虚拟机栈、本地方法栈和程序计数器

方法区

方法区是用来存储被Java虚拟机加载的类信息,常量,静态变量,运行时常量池等。在jdk8以前,方法区中定义了永久代。因为使用永久代来实现了方法区,所以被描述为堆的一个逻辑部分。但是它确是“非堆”,只是设计堆中的收集器 扩展到了方法区而已。在jdk8的时候,永久代被替换成了元空间(Metaspace)。这样做的好处有以下原因:

  • Metaspace使用的是本地内存
  • 字符串常量池存在永久代中,容易出现性能问题和内存溢出。
  • 类和方法的信息难以确定,给永久代指定大小比较困难,太小容易操作永久代溢出,太大会导致老年代溢出。
  • 永久代会为GC带来不必要的复杂性。

java堆

Java堆在JVM中是一块最大的内存,用于存储对象实例及数组值,可以认为Java中所有通过new创建的对象的内存都在此分配,堆区由所有线程共享。如上图所示,在堆上划分了很多区域,新生代、老年代和永久代。其中新生代又划分为了一个eden区和两个survivor区。Java堆实际上是垃圾收集器(GC)管理的一块内存区域,经过之上的划分得以进行更有效率的垃圾回收。

虚拟机栈

Java虚拟机栈是线程私有的,每个线程都对应着一个虚拟机栈。每个方法执行的时候都会创建一个栈帧,它是方法运行时的基础数据结构。栈帧中主要存储的有局部变量表、操作数栈、动态链接、方法出口等,当方法被调用时,栈帧在JVM栈中入栈,当方法执行完成时,栈帧出栈。虚拟机栈中定义了两种异常,如果线程调用的栈深度大于虚拟机允许的最大深度,则抛出StatckOverFlowError(栈溢出);不过多数Java虚拟机都允许动态扩展虚拟机栈的大小(有少部分是固定长度的),所以线程可以一直申请栈,直到内存不足,此时,会抛出OutOfMemoryError(内存溢出)。

本地方法栈

本地方法栈和虚拟机栈类似,JVM并未要求本地方法栈一定实现某种语言的方法调用,使用者可以灵活去进行调用。用于支持native方法的执行,存储了每个native方法调用的状态。本地方法栈和虚拟机方法栈运行机制一致,它们唯一的区别就是,虚拟机栈是执行Java方法的,而本地方法栈是用来执行native方法的,在很多虚拟机中,会将本地方法栈与虚拟机栈放在一起使用。

程序计数器

程序计数器是一个比较小的内存区域,可能是CPU寄存器或者操作系统内存,其主要用于指示当前线程所执行的字节码执行到了第几行,可以理解为是当前线程的行号指示器。字节码解释器在工作时,会通过改变这个计数器的值来取下一条语句指令。 每个程序计数器只用来记录一个线程的行号,所以它是线程私有(一个线程就有一个程序计数器)的。
如果程序执行的是一个Java方法,则计数器记录的是正在执行的虚拟机字节码指令地址;如果正在执行的是一个本地(native,由C语言编写完成)方法,则计数器的值为Undefined,由于程序计数器只是记录当前指令地址,所以不存在内存溢出的情况,因此,程序计数器也是所有JVM内存区域中唯一一个没有定义OutOfMemoryError的区域。

评论