JVM内存模型详解

JVM内存模型也叫JVM运行时区域,是认识和了解JVM工作原理的基础,从java诞生以来,JVM内存模型基本保持着大同小异的整体形态,由此可见JVM内存模型是相当稳定的,直到jdk1.8之后JVM内存模型中才将permGen(永生代),也就是过去的方法区完全去除,使用metaspace取而代之,但是从整个JVM内存形态来说其实并没有产生太大的变化,有点“换汤不换药”的味道。除此之外JVM内存模型的设计原理也充分考虑了java程序的运行过程以及GC的策略,所以JVM内存模型是一个既基础又复杂的内存结构。

jdk1.8以前的内存模型:

jdk1.8内存模型:

两种内存结构其实大同小异,这种差异在最后进行说明,图中深绿色的部分区域代表是线程私有的,而浅黄色部分代表的是所有线程共享的区域。首先来分别对不同的内存部分进行说明。

一、程序计数器

程序计数器是JVM执行字节码指令时的一个计数器指针,在对java代码进行执行的时候(如分支、循环、方法跳出、线程切换)都会使用到这个计数器,这个计数器的内存空间是非常小的,并且为线程私有,java多线程程序是通过切换线程占用CPU时间片的方式来执行的,为了使得每次线程切换之后对应的线程都能够指向正确的代码位置(字节码指令),这个时候就会使用到程序计数器,每一个线程都有自己对应的程序计数器,所以程序计数器是一个线程私有的内存区域。对于java方法程序计数器会指向对应的字节码指令,而对应native方法则计数器为空。最后,这块区域是在JVM内存模型中唯一一块没有OOM异常的区域。

二、虚拟机栈

虚拟机栈也被称为java虚拟机栈,这块区域同样是线程私有的,也就是生命周期与线程相同。虚拟机栈描述的是java方法执行时的内存模型,每个java方法在执行的时候都会创建一个方法栈帧用于存储局部变量表,操作数栈,动态的链接,方法返回值等。可以这么理解,一个java方法从开始执行到结束,实质上就是对应的一个方法栈帧从压栈到出栈的过程。

虚拟机栈中的局部变量表的空间大小是在编译的时期就已经确定了,也就是说在java方法的执行过程当中局部变量表的大小是确定不变的。在这个区域中如果线程所请求的栈的深度大于了虚拟机所允许的深度,就会抛出StackOverflowError(自己可以通过实现一个“跳不出来的”递归方法来模拟这种情况)。另外虚拟机栈的空间大小是可以动态扩展,如果是一个固定空间大小的虚拟栈(设置-Xss1m),java程序在创建线程时申请不到足够的空间会发生OOM。

三、本地方法栈

本地方法栈也是线程私有的并且与虚拟机栈的作用是完全一样的,不同点是本地方法栈是对native方法执行过程内存模型进行管理,会记录native方法在执行过程的相关数据(局部变量表等),完成对native方法栈的压栈和出栈过程。同样会发生StackOverflowError和OutOfMemoryError。

四、堆区

java 堆(heap)是JVM内存模型当中最大的一块区域, 并且是所有线程共享的一块区域,此区域主要用于存放java对象的实例,基本上所有的对象实例都会在这里分配内存(java.lang.Class对象不会,会在类加载中直接分配到方法区中)。堆是垃圾回收器主要“关照”的区域,由于有垃圾回收算法的策略影响,所以一整块大的堆实际也可以往下进行细分,新生代(Eden区,from suvivor区,to survivor区)、老年代。java的堆在物理空间上是可以允许不连续的,只要在逻辑上连续即可。可以通过-Xmx和-Xms来调整堆的大小,如果分配的对象实例已经超过了堆分配大小的剩余空间,这时候会产生OutOfMemoryError。

五、方法区

很多人把方法区叫做永生代,因为这一个区域主要是存放类加载过程中字节码文件的二进制字节流转变后的内存结构以及一些类的变量或静态变量,常量,这些数据模型都比较静态,看起来不会被垃圾回收,但是实际上并不是这样,JVM在后期的版本中实际上会对方法去中存储的数据进行垃圾回收,只是回收的效果不太明显(主要是针对于类的卸载以及常量池的回收)。在1.8以前可以设置-XX:MaxPermSize和-XX:PermSize来调整方法区的大小,如果分配的内存大小已经大于了方法区的剩余大小了,则会抛出OutOfMemoryError。可以通过一个死循环用cglib生成字节流进行加载的过程来模拟异常。

六、常量池

常量池主要存储编译时期的字面量和符号引用(类、接口、方法、变量的符号),它是方法区的一部分,因为是方法区的一部分,所以也会出现OutOfMemoryError。很多人可能认为常量池的数据是在程序编译的时期就已经确定好了,但是实际上在java运行过程当中也可以动态添加常量到池中(String的intern方法)

七、直接内存区

直接内存区域(Direct Memory)并不是JVM内存模型的一部分,而是通过native本地函数在操作系统中分配的一块内存区域,而在JVM中通过DirectByteBuffer对象对其进行引用操作(在JDK1.4引入NIO后有buffer和Channel出现),同样这一块内存区域因为会受到操作系统内存大小的限制,所以也会出现OutOfMemroyError。

七、MetaSpace

jdk1.8之后引入了一个新的区域叫做metaSpace,可以通过-XX:MaxMetaSpaceSize=256进行设置。在1.8之后已经废弃了之前的PermGen区域,所以无法通过设置PermSize和MaxPermSize来对其方法区的内存大小进行设置。MetaSpace实际上代替了PermGen区域用于存储类加载过程中的二进制字节流内存结构,而以前的常量池则被转移到了java堆中。这一块区域同样会出现OutOfMemoryError。

时间: 2024-08-15 20:33:34

JVM内存模型详解的相关文章

java内存模型详解

内存模型 (memory model) 内存模型描述的是程序中各变量(实例域.静态域和数组元素)之间的关系,以及在实际计算机系统中将变量存储到内存和从内存取出变量这样的低层细节. 不同平台间的处理器架构将直接影响内存模型的结构. 在C或C++中, 可以利用不同操作平台下的内存模型来编写并发程序. 但是, 这带给开发人员的是, 更高的学习成本.相比之下, java利用了自身虚拟机的优势, 使内存模型不束缚于具体的处理器架构, 真正实现了跨平台.(针对hotspot jvm, jrockit等不同的

JVM内存配置详解(转)

前段时间在一个项目的性能测试中又发生了一次OOM(Out of swap sapce),情形和以前网店版的那次差不多,比上次更奇怪的是,此次搞了几天之后啥都没调整系统就自动好了,死活没法再重现之前的OOM了!问题虽然蹊跷,但也趁此机会再次对JVM堆模型.GC垃圾算法等进行了一次系统梳理: 基本概念 堆/Heap JVM管理的内存叫堆:在32Bit操作系统上有4G的限制,一般来说Windows下为2G,而Linux 下为3G:64Bit的就没有这个限制. JVM初始分配的内存由-Xms指定,默认是

Spark内存模型详解

1 堆内和堆外内存规划 Spark执行器(Executor)的内存管理建立在 JVM 的内存管理之上,Spark 对 JVM 的空间(OnHeap+Off-heap)进行了更为详细的分配,以充分利用内存.同时,Spark 引入了Off-heap 内存模式,使之可以直接在工作节点的系统内存中开辟空间,进一步优化了内存的使用(可以理解为是独立于JVM托管的Heap之外利用c-style的malloc从os分配到的memory.由于不再由JVM托管,通过高效的内存管理,可以避免JVM object o

Java虚拟机:内存模型详解

版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! 我们都知道,当虚拟机执行Java代码的时候,首先要把字节码文件加载到内存,那么这些类的信息都存放在内存中的哪个区域呢?当我们创建一个对象实例的时候,虚拟机要为对象分配内存,Java虚拟机又是如何配分内存的呢?这些都涉及到Java虚拟机的内存划分机制,今天我们就来探究一下Java虚拟机的内存模型. Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域,这些区域都有各自的用途以及创建和销毁的时间,有的区域随

JVM内存区域详解

1. 程序计数器 现在多线程越来越普遍了,但是对于单核处理器而言,同一个时刻只能够执行一行指令.多个线程的同时执行,实际上是通过线程切换来实现的.一种简单的方式就是,每个线程执行一段时间后,就切换到另外一个线程去执行.当线程A执行到某行字节码指令时被挂起,这个时候切换到线程B执行一段时间后,又需要切换回来执行线程A,那么需要从上一次中断的地方继续执行.所以需要每一个线程都有一个程序计数器,用来存储当前需要执行的字节码指令的地址. 2. 栈空间 每个线程都有一个执行方法,而方法的局部变量表.操作数

7.Java内存模型详解

https://blog.csdn.net/qq_37141773/article/details/103138476 一.虚拟机 同样的java代码在不同平台生成的机器码肯定是不一样的,因为不同的操作系统底层的硬件指令集是不同的. 同一个java代码在windows上生成的机器码可能是0101.......,在linux上生成的可能是1100......,那么这是怎么实现的呢? 不知道同学们还记不记得,在下载jdk的时候,我们在oracle官网,基于不同的操作系统或者位数版本要下载不同的jdk

Java学习之:JVM内存模型

一.文章来由 开始实习啦,实习转战Java开发工程师... 二.JVM内存模型总图 Java中通过多线程机制使得多个任务同时执行处理,所有的线程共享JVM内存区域main memory,而每个线程又单独的有自己的工作内存,当线程与内存区域进行交互时,数据从主存拷贝到工作内存,进而交由线程处理(操作码+操作数). 在之前,我们也已经提到,JVM的逻辑内存模型如下: 三.JVM内存模型详解 1.程序计数器 程序计数器(Program Counter Register)是一块较小的内存空间,它的作用可

Jvm(31),理解升级----通过JVM内存模型深入理解值传递和引用传递两种方式

值传递和引用传递分析 Java中数据类型分为两大类:基本类型和引用类型(也就是对象类型). 基本类型:boolean.char.byte.short.int.long.float.double 引用类型:类.接口.数组 因此,变量类型也可分为两大类:基本类型和引用类型. 在分析值传递和引用传递之前,建议了解下以上变量类型在Java内存管理模型中的位置,如果对此有所了解,将更加有助于理解两种传递的方式^_^ 在Java内存中,基本类型变量存储在Java栈(VM Stack)中,引用变量存储在堆(H

直通BAT必考题系列:深入详解JVM内存模型与JVM参数详细配置

VM基本是BAT面试必考的内容,今天我们先从JVM内存模型开启详解整个JVM系列,希望看完整个系列后,可以轻松通过BAT关于JVM的考核. BAT必考JVM系列专题 1.JVM内存模型 2.JVM垃圾回收算法 3.JVM垃圾回收器 4.JVM参数详解 5.JVM性能调优 JVM内存结构 由上图可以清楚的看到JVM的内存空间分为3大部分: 堆内存 方法区 栈内存 其中栈内存可以再细分为java虚拟机栈和本地方法栈,堆内存可以划分为新生代和老年代,新生代中还可以再次划分为Eden区.From Sur