首页 > 系统相关 >JVM 的内存结构

JVM 的内存结构

时间:2023-10-17 19:34:34浏览次数:37  
标签:Java 虚拟机 线程 内存 JVM 执行 结构

目录

JVM 内存结构

Java 程序的具体执行过程:

image

Java 源代码文件经过编译器编译后生成字节码文件,然后交给 JVM 的类加载器,加载完毕后,交给执行引擎执行。在整个执行的过程中,JVM 会用一块空间来存储程序执行期间需要用到的数据,这块空间一般被称为运行时数据区,也就是常说的 JVM 内存。

所以,当我们在谈 JVM 内存区域划分的时候,其实,值的就是这块空间——运行时数据区。其中,运行时数据区可以分为以下几个部分:

image

程序计数器

程序计数器(Program Counter Register)所占的内存空间不大,很小一块,可以看作是当前线程所执行的字节码指令的行号指示器。字节码解释器会在工作的时候改变这个计数器的值来选取下一条需要执行的字节码指令,像分支、循环、跳转、异常处理、线程恢复等功能都需要依赖这个计数器来完成。

在 JVM 中,多线程是通过线程轮流切换来获得 CPU 执行时间的,因此,在任一具体时刻,一个 CPU 的内核只会执行一条线程中的指令,因此,为了线程切换后能恢复到正确的执行位置,每个线程都需要有一个独立的程序计数器,并且不能互相干扰,否则就会影响到程序的正常执行次序。

也就是说,我们要求程序计数器是线程私有的。

《Java 虚拟机规范》中规定,如果线程执行的是非本地(native)方法,则程序计数器中保存的是当前需要执行的指令地址;如果线程执行的是本地方法,则程序计数器中的值是 undefined。

为什么本地方法在程序计数器中的值是 undefined 的?因为本地方法大多是通过 C/C++ 实现的,并未编译成需要执行的字节码指令。

由于程序计数器中存储的数据所占的空间不会随程序的执行而发生大小上的改变,因此,程序计数器是不会发生内存溢出现象(OutOfMemory)的。

Java 虚拟机栈

Java 虚拟机栈中是一个个栈帧,每个栈帧对应一个被调用的方法。当线程执行一个方法时,会创建一个对应的栈帧,并将栈帧压入栈中。当方法执行完毕后,将栈帧从栈中移除。栈遵循的是后进先出的原则,所以线程当前执行的方法对应的栈帧必定在 Java 虚拟机栈的顶部。

栈帧包含以下 5 个部分:

image

堆是所有线程共享的一块内存区域,在 Java 虚拟机启动的时候创建,用来存储对象(数组也是一种对象)。

以前,Java 中“几乎”所有的对象都会在堆中分配,但随着 JIT(Just-In-Time)编译器的发展和逃逸技术的逐渐成熟,所有的对象都分配到堆上渐渐变得不那么“绝对”了。从 JDK 7 开始,Java 虚拟机已经默认开启逃逸分析了,意味着如果某些方法中的对象引用没有被返回或者未被外面使用(也就是未逃逸出去),那么对象可以直接在栈上分配内存。

JIT

常见的编译型语言,如 C++,通常会把代码直接编译成 CPU 所能理解的机器码来运行。

而 Java 为了实现“一次编译,处处运行”的特性,把编译的过程分成两部分,首先它会先由 javac 编译成通用的中间形式——字节码,然后再由解释器逐条将字节码解释为机器码来执行。所以在性能上,Java 可能会干不过 C++ 这类编译型语言。

为了优化 Java 的性能 ,JVM 在解释器之外引入了 JIT 编译器:当程序运行时,解释器首先发挥作用,代码可以直接执行。

随着时间推移,即时编译器逐渐发挥作用,把越来越多的代码编译优化成本地代码,来获取更高的执行效率。解释器这时可以作为编译运行的降级手段,在一些不可靠的编译优化出现问题时,再切换回解释执行,保证程序可以正常运行。

逃逸分析

逃逸分析(Escape Analysis),简单来讲就是,Hotspot 虚拟机可以分析新创建对象的使用范围,并决定是否在 Java 堆上分配内存的一项技术。

堆是 Java 垃圾收集器管理的主要区域,因此也被称作 GC 堆(Garbage Collected Heap)。从垃圾回收的角度来看,由于垃圾收集器基本都采用了分代垃圾收集的算法,所以,堆还可以细分为:新生代和老年代。新生代还可以细分为:Eden 空间、From Survivor、To Survivor 空间等。进一步划分的目的是更好地回收内存,或者更快地分配内存。

堆这最容易出现的就是 OutOfMemoryError 错误,分为以下几种表现形式:

  • OutOfMemoryError: GC Overhead Limit Exceeded:当 JVM 花太多时间执行垃圾回收并且只能回收很少的堆空间时,就会发生该错误。

  • java.lang.OutOfMemoryError: Java heap space:假如在创建新的对象时, 堆内存中的空间不足以存放新创建的对象, 就会引发该错误。和本机的物理内存无关,和我们配置的虚拟机内存大小有关!

元空间

JDK 8 的时候,原有的方法区(更准确的说应该是永久代)被彻底移除,取而代之的是元空间。

方法区

方法区和堆一样,是线程共享的区域,它用来存储已经被 Java 虚拟机加载的类信息、常量、静态变量,以及便器编译后的代码等。

在有些地方,方法区也被称为永久代。但其实不能这么理解。

《Java 虚拟机规范》中只规定了有方法区这么一个概念和它的作用,并没有规定如何去实现它。那么不同的 Java 虚拟机可能就会有不同的实现。永久代是 HotSpot 对方法区的一种实现形式。也就是说,永久代只是 HotSpot 中的一个概念,而方法区则是 Java 虚拟机规范中的一个定义,一种规范。

换句话说,方法区和永久代的关系就像是 Java 中接口和类的关系,类实现了接口。

在方法区中,还有一块非常重要的部分,也就是运行时常量池。在讲 class 文件的时候,提到了每个 class 文件都会有个常量池,用来存放字符串常量、类和接口的名字、字段名、常量等等。运行时常量池和 class 文件的常量池是一一对应的,它就是通过 class 文件中的常量池来构建的。

JDK 7 之前,运行时常量池中包含着字符串常量池,都在方法区。

JDK 7 的时候,字符串常量池从方法区中拿出来放到了堆中,运行时常量池中的其他东西还在方法区中。

元空间

JDK 8 的时候,HotSpot 移除了永久代,也就是说方法区不存在了,取而代之的是元空间。也就意味着字符串常量池在堆中,运行时常量池跑到了元空间。

再来说说为什么要将永久代 (PermGen) 或者说方法区替换为元空间 (MetaSpace) 。

第一,永久代放在 Java 虚拟机中,就会受到 Java 虚拟机内存大小的限制,而元空间使用的是本地内存,也就脱离了 Java 虚拟机内存的限制。

第二,JDK 8 的时候,在 HotSpot 中融合了 JRockit 虚拟机,而 JRockit 中并没有永久代的概念,因此新的 HotSpot 就没有必要再开辟一块空间来作为永久代了。


参考:

标签:Java,虚拟机,线程,内存,JVM,执行,结构
From: https://www.cnblogs.com/larry1024/p/17770470.html

相关文章

  • 【JVM】Java 的类加载机制
    目录字节码类加载过程LoadingVerificationPreparationResolutionInitialization类加载器双亲委派模型字节码Java源文件通过编译后,就会生成字节码:类加载过程Java的类加载过程可以分为5个阶段:载入、验证、准备、解析和初始化。这5个阶段一般是顺序发生的,但在动态绑定的......
  • Qt/C++开源作品45-CPU内存显示控件/和任务管理器一致
    一、前言在很多软件上,会在某个部位显示一个部件,专门显示当前的CPU使用率以及内存占用,方便用户判断当前程序或者当前环境中是否还有剩余的CPU和内存留给程序使用,在不用打开任务管理器或者资源查看器的时候直接得知当前系统的运行情况。尤其是视频监控系统,如果64路全开,肯定很占用CP......
  • Linux中查看服务器内存/CPU/网络占用情况的命令
    目录1、查看物理CPU个数2、查看服务器CPU内核个数3、服务器内存使用情况4、查看服务器的平均负载5、查看系统整体性能情况6、查看系统已载入的相关模块7、查找PCI设置8、网络流量监控9、查看系统内核1、查看物理CPU个数cat cat/proc/cpuinfo|grep"physic......
  • 【0-1】从0.1开始学Android逆向-APK基本结构概要分析
    概要最近在进行Android的逆向,在这里整理知识点和分享Android逆向知识。如果文章中有任何勘误,诚挚的邀请师傅们批评改正!0.什么是逆向?逆向工程(ReverseEngineering)是一种分析和解剖已有产品、系统或软件的过程,以了解其内部工作原理、设计、功能或源代码。逆向工程可以应用于多个......
  • U8 数据库表结构
    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★1、.ufysftem库      此库是系统参数数据库,其中主要存放各个帐套的共用信息,意义很大,日常应用的也很多。      A.ua_account:是帐套信息表,其中系统帐套存放路径、帐套启......
  • 动态内存分配
    0概述通常声明一个数组时需要使用一个常量来指定数组的长度,数组所占用的内存是在编译时就被分配。这种方式的声明的优点是简单,但是存在以下几个缺点:使用的元素数量超过数组声明的长度,当前数组就不能存储相应的数据;如果数组的长度被声明很大,实际使用的元素又比较少会导致内存......
  • idea内存溢出
    maven编译打包出现堆内存溢出.需要设置对应的maven的配置-Xms1024m-Xmx1024m-Xmn1024m 应用运行时出现内存溢出-Xms1024m-Xmx1024m ......
  • 2-快速上手——从0到1掌握算法面试需要的数据结构(一)
    数据结构层面,大家需要掌握以下几种:数组栈队列链表树(这里我们着重讲二叉树)对于这些数据结构,各位如果没有大量的可支配时间可以投入,那么其实不建议找厚厚的大学教材来刷。此时此刻,时间为王,我们追求的是效率的最大化。不同的数据结构教材,对数据结构有着不同的划分、不同的解......
  • Shell(八):循环与结构化命令
    反复执行某一个命令或某一组命令,需要用到循环结构化命令。循环命令用于特定条件下决定某些语句重复执行的控制方式,具有封闭型的单入单出性质。即进入循环结构后,只要循环条件未达到结束状态,就时钟执行循环体内的操作。Shell中提供三种常用的循环语句,分别是for循环......
  • 不管表里的内容 仅查表的结构: desc 表名;
    对比包含表中数据 ......