【JVM学习笔记】JVM内存区域定义与内存结构

news2024/12/23 22:10:55

目录

    • 定义和说明
        • JVM内存区域的定义
        • 内存区域说明
        • 堆说明
        • 非堆-方法区说明
        • 堆栈的区别
        • HotSpot虚拟机
    • JVM线程独占内存
        • 程序计数器:Program Counter Register
        • Java虚拟机栈:Java Virtual Machine Stack
        • 本地方法栈:Native Method Stack
    • JVM共享内存
        • Java堆:Java Heap
        • 方法区:Method Area
          • 常量池表:Constant Pool Table
          • 运行时常量池:Run-Time Constant Pool
    • 参考




定义和说明

JVM内存区域的定义

JVM(Java Virtual Machine,JavaVM,Java虚拟机),定义了不同的运行时数据区域(run-time data area),一部分区域随JVM启动而创建,随JVM消亡而销毁。其它区域则属于每个线程,随线程启动而创建,随线程关闭而销毁。


内存区域说明

JavaVM的定义,决定其内存将会分为两部分:JVM共享内存、JVM线程独占内存。

  • JVM共享内存分为两部分:堆(Heap)、非堆——方法区(Method Area)
  • JVM线程独占内存分为:虚拟机栈(Java Virtual Machine Stacks)、本地方法栈(Native Method Stacks)、程序计数器(pc Register)

堆说明

堆就是Java代码可及的内存,是留给开发人员使用的,所有类实例和数组的内存均从此处分配。


非堆-方法区说明

  • 非堆就是JVM留给自己用的
  • 这部分的物理内存实际上和堆是连续的,它也是堆的一部分,只是在概念上将其与堆区分开来
  • 方法区实际上只是JVM定义的一个概念,这块区域如何实现取决于具体的虚拟机实例。
  • HotSpot虚拟机在JDK7之前就用永久代实现方法区的功能定义,在JDK8弃用了永久代并使用元空间(metaspace)实现方法区。

堆栈的区别

  • 堆解决的是数据存储的问题,即数据怎么放、放在哪儿。
  • 栈解决程序的运行问题,即程序如何执行,或者说如何处理数据

HotSpot虚拟机

  • 大家使用的主流JDK主要是OpenJDK和OracleJDK这两个版本,他们的VM实现都是HotSpotVM,所以一般我们提到JVM都是代指HotSpotVM
  • 因此下面的内存结构分析,乃至我后面的所有JVM文章,也都是基于HotSpotVM来的。

在这里插入图片描述





JVM线程独占内存

线程私有,生命周期与线程相同。

程序计数器:Program Counter Register

作用:又称pc Register, 是一块较小的内存空间,可以被看作当前线程所执行的字节码的行号指示器。字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,它是程序控制流的指示器。

对于不同的方法程序计数器分别会记录不同的值:

  • 执行普通方法:那么程序计数器就会记录当前虚拟机执行处的地址(address),当线程切换后方便线程继续不中断的执行该方法。
  • 执行native方法:程序计数器的值为undefined。

此外,Java虚拟机的多线程是通过线程轮流切换、分配处理器执行时间的方式来实现,为了线程切换后能恢复到正确的执行位置,每个线程都会拥有一个独立的程序计数器独立存储各自的计数器值。各线程之前计数器互不影响。



Java虚拟机栈:Java Virtual Machine Stack

作用:虚拟机栈描述的是Java方法执行的线程内存模型,在物理内存上不一定是连续的。在任意一个时间点,每个JVM线程都只会执行一个方法的代码,这个方法对于该线程来说就是一个栈帧(Frame)。每个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。

  • 用于存储局部变量表、操作数栈、动态连接、方法出口等信息。它可能是对分配的。

栈的注意项

  • 虚拟机栈可以为固定大小、也允许在运行时动态扩展。如果虚拟机栈为固定大小时,它的大小将在创建时自主决定。
  • 每个VM实现都会提供用户可以自行控制的栈初始化大小,在支持动态扩展时还会提供栈的最大值设置。

局部变量表:存放编译期可知的各种Java虚拟机基本数据类型、引用类型和returnAddress类型(指向了一条字节码指令的地址)。这些数据类型在局部变量表中的存储空间以局部变量槽(Slot)表示。除了long和double占用两个槽外,其他都只占一个。局部变量表的内存空间在编译期就完成分配了,且在运行期不会改变;

栈溢出(虚拟机栈和本地方法栈溢出情况相似):

  • 如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常。

  • 如果虚拟机的栈内存允许动态扩展,当扩展栈容量无法申请到足够的内存时,将抛出OutOfMemoryError异常。

-Xss | -XX:ThreadStackSize:设置该参数以设置栈容量



本地方法栈:Native Method Stack

作用:本地方法栈与虚拟机栈的作用相似,区别在于本地方法栈是为虚拟机使用到的native方法服务;而虚拟机栈是为执行Java方法(也就是字节码)服务。





JVM共享内存

被所有线程共享的一块区域

Java堆:Java Heap

概览:Java堆是虚拟机管理的内存的最大一块,它是被所有线程共享的一块区域。它是垃圾收集器管理的内存区域,也被称为GC堆(Garbage Collected Heap)。

作用:Java堆所管理的内存区域的作用就是存放对象实例。Java几乎所有的对象实例都在这里分配内存。

堆内存

  • 堆内存大小可以是固定值,也可以根据需要动态扩展,在需要更多内存时自动扩展,并在不需要时自动缩减。
  • 每个VM实现都可能会提供用户可以自行控制的堆初始化大小,并在支持动态扩展时还会提供堆的最大值设置。
  • Java堆可以处于物理上不连续的内存空间中,但在逻辑上他们应该被视为连续的。但对于大对象(典型的如数组对象),多数虚拟机实现出于实现简单、存储高效的考虑,很可能会要求连续的物理内存空间。

堆溢出:当我们不断地创建对象,且大量对象被使用(GC roots到对象可达),那么随着对象数量的增加,总容量触及最大堆的容量限制后,通过full gc 也不法清理内存空间时,就会产生内存溢出异常(OutOfMemoryError)。

OOM信息:java.lang.OutOfMemoryError:Java heap space

-XX:+/-HeapDumpOnOutOfMemoryError:可以让虚拟机在出现内存溢出异常的时候Dump出当前的内存堆转储快照以便进行事后分析

OOM解决:通过dump堆转存快照来分析异常

  • 首先应确认内存中导致OOM的对象是否是必要的,也就是要先分清楚到底是出现了内存泄漏(Memory Leak)还是内存溢出(Memory
    Overflow)。

  • 内存泄漏,可进一步通过工具查看泄漏对象到GC Roots的引用链,找到泄漏对象是通过怎样的引用路径、与哪些GC Roots相关联,才导致垃圾收集器无法回收它们,根据泄漏对象的类型信息以及它到GC Roots引用链的信息,一般可以比较准确地定位到这些对象创建的位置,进而找出产生内存泄漏的代码的具体位置。

  • 内存溢出,如果OOM对象都是必须存在的,那就应当检查Java虚拟机的堆参数(-Xmx与-Xms)设置,与机器的内存对比,看看是否还有向上调整的空间。再从代码上检查是否存在某些对象生命周期过长、持有状态时间过长、存储结构设计不合理等情况,尽量减少程序运行期的内存消耗。

-Xmx | -XX:MaxHeapSize:设置堆最大容量
-Xms | -XX:InitalHeapSize:设置堆最小容量



方法区:Method Area

概览:方法区实际上是一种概念,与Java堆一样,都是线程共享的内存区域。虽然在逻辑上方法区是Java堆的一个逻辑部分,但是它有一个别名“非堆”Non-Heap,用以于Java堆区分开来。

作用:用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。

方法区内存

  • 方法区内存大小可以是固定值,也可以根据需要动态扩展,在需要更多内存时自动扩展,并在不需要时自动缩减。
  • 每个VM实现都可能会提供用户可以自行控制的方法区初始化大小,并在支持动态扩展时还会提供方法区的最大值设置。
  • 方法区在内存上亦不要求连续。

注意:

  • JDK7之后,HotSpot把放在永久代的字符串常量池、静态变量移至了Java堆中。
  • JDK8之后,完全废弃了永久代的概念,改用元空间(Metaspace)来代替,把JDK 7中永久代还剩余的内容(主要是类型信息)全部移到元空间中。
  • 方法区的内存空间:与Java堆一样,此外它甚至还可以选择不实现垃圾收集(这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载)。

方法区溢出:当方法区要去分配的内存超过最大内存限制时,抛出内存溢出异常(OutOfMemoryError)。

设置方法区大小:
JDK7及前:

  • -XX:PermSize:设置永久代初值
  • -XX:MaxPermSize:设置永久代最大值

JDK8及以后:

  • -XX:MetaspaceSize:元空间初值
  • -XX:MaxMetaspaceSize:元空间最大值

常量池表:Constant Pool Table

概览:它实际上是某个类、接口中的常量集合,任意一个类中,都有会无数常量(例如类名、接口名、方法名都是一个常量),在编译完成后常量池表就固定了,运行时也不会发生改变。它是属于方法区中类型信息的一部分。

作用:类型信息中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池表(Constant Pool Table)。它用于存放编译期生成的各种字面量与符号引用,这些内容将在类加载后放到方法区的运行常量池中。


运行时常量池:Run-Time Constant Pool

概览:运行时常量池实际上是类文件中一个类或接口中的常量池表(constant pool table)的集合展现,是方法区的一部分。JDK7之前常量池都是分配在永久代中,JDK7之后,HotSpot把放在永久代的字符串常量池、静态变量移至了Java堆中。

作用:与常量池表相比,运行时常量池具备动态性,运行期间也可以将新的常量放入池中,比如String的intern()方法。

运行时常量池溢出:它不像常量池表不可变动,运行时常量池在扩展时,需要分配的内存超过方法区限制时,抛出内存溢出异常(OutOfMemoryError)。




参考

深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)周志明
Run-Time Data Areas




本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/14485.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Arduino与Proteus仿真实例-密码门禁控制仿真

密码门禁控制仿真 1、应用介绍 本文将演示如何实现密码门禁控制逻辑仿真。 此次仿真主要涉及如下内容: 密码输入、更新、验证门禁控制逻辑此次仿真将使用继电器和直流电机作为电子门禁元件仿真器件。 在前面的文章中,对密码输入、更新、验证、储存,做了详细的仿真,请参…

API:低代码平台的秘诀

应用编程接口 (API) 是应用程序以可编程格式访问其关键能力和功能的一种方式,从而其他应用程序可以利用它们。API 本质上支持应用程序之间的无缝数据流,使开发人员能够在应用程序中添加更多功能,而无需依赖大量编码。 举一个简单的例子。 您…

实战!接口优化的18种方案

前言 大家好,我是捡田螺的小男孩。 之前工作中,遇到一个504超时问题。原因是因为接口耗时过长,超过nginx配置的10秒。然后 真枪实弹搞了一次接口性能优化,最后接口从11.3s降为170ms。本文将跟小伙伴们分享接口优化的一些通用方案…

maven如何手动添加jar包到本地仓库

1 下载需要添加的jar包 可以在maven库中查找下载,也可以在对应官网下载 maven库网址 2 第二步:将下载的jar包放到指定位置(位置自己指定) 3 第三步:配置本地maven库 (1)首先检查本地maven库…

结构优化软件SolidThinking Inspire的自学攻略

作者:孙一凡,仿真秀专栏作者 2004年上大学那会,ANSYS软件推广应用还没现在这么广泛,有个老师接项目就是用ANSYS计算,觉得很是高大上!ABAQUS还是一个小众软件,甚至一本参考资料书籍都买不到。短…

前端一面经典react面试题(边面边更)

react 的虚拟dom是怎么实现的 首先说说为什么要使用Virturl DOM,因为操作真实DOM的耗费的性能代价太高,所以react内部使用js实现了一套dom结构,在每次操作在和真实dom之前,使用实现好的diff算法,对虚拟dom进行比较&…

SVM与基于马氏距离的径向基函数(MDRBF)核结合组合(Matlab代码实现)

👨‍🎓个人主页:研学社的博客 💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜…

Scala 数组

Scala 语言中提供的数组是用来存储固定大小的同类型元素,数组对于每一门编辑应语言来说都是重要的数据结构之一。 声明数组变量并不是声明 number0、number1、...、number99 一个个单独的变量,而是声明一个就像 numbers 这样的变量,然后使用…

【设计模式】建造者模式

1. 概述 建造者模式将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,从而更精确控制复杂对象的生产过程; 通过隔离复杂对象的构建与使用,也就是将产品的创建与产品本身分离开来,使得同样的构建过程可以…

Python编程从入门到实践 第七章:用户输入和while循环 练习答案记录

Python编程从入门到实践 第七章:用户输入和while循环 练习答案记录 练习题导航Python编程从入门到实践 第七章:用户输入和while循环 练习答案记录7.1 函数input()的工作原理7.1.1 编写清晰的程序7.1.2 使用int()来获取数值输入7.1.3 求模运算符练习7-1 汽…

SpringMVC学习篇(十)

springmvc拦截器之重复提交 1 出现原因 在新增和修改界面点击提交后(转发的方式跳转) 再次刷新页面,如果不做处理的话,会造成重复提交, 从而使得新增商品多次或者更改商品多次2 解决方案 2.1 准备工作 导入servlet-api依赖和spring-webmvc依赖 <dependency><group…

限流的几种方式及简单实现

文章目录计数器信号量滑动窗口漏桶令牌桶测试示例代码计数器 计数器限流方式比较粗暴&#xff0c;一次访问就增加一次计数&#xff0c;在系统内设置每 N 秒的访问量&#xff0c;超过访问量的访问直接丢弃&#xff0c;从而实现限流访问。 具体大概是以下步骤&#xff1a; 将时…

Spring Security认证之登录用户数据获取

本文内容来自王松老师的《深入浅出Spring Security》&#xff0c;自己在学习的时候为了加深理解顺手抄录的&#xff0c;有时候还会写一些自己的想法。 登录用户数据获取 登录成功之后&#xff0c;在后续的业务逻辑中开发者可能还需要获取到登录成功的用户对象&#xff0c;如果不…

轻松学会JavaScript事件

文章目录事件与事件流事件监听&#xff08;绑定事件方法&#xff09;JavaScript事件鼠标事件表单事件键盘事件UI事件快速投票事件可以说是JavaScript最引人注目的特性&#xff0c;因为它提供了一个平台&#xff0c;让用户不仅能浏览页面中的内容&#xff0c;而且能跟页面进行交…

贪心算法+动态规划 | 眼光不同决定深度不同

前言 上大学那会有门课程叫做【算法与实践】, 算法配上 C 那感觉不要提多爽了。现在回想起来算法不局限于语言&#xff0c;只不过每个语言的语法不一罢了&#xff0c; 但是算法的内在逻辑都是相通的&#xff0c;今天我们通过三个案列来了解分析下算法之一 【贪心算法】。 简介…

显示控件——滑动选择

该控件也是一种调节控件&#xff0c;通过在既定范围内的滑动来选择具体选项值以图达到对变量的调控效果&#xff0c;其UI操作效果类似于拨动密码锁的滚轮。 位置信息&#xff1a;控件在工程页面区域的位置 “X”“Y”为控件区域左上角坐标。 “W”“H”为控件区域宽度和高度&a…

cpu设计和实现(取指)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 cpu设计的本质是数字电路的设计。要是没有verilog、vhdl这些语言&#xff0c;那么剩下来使用的方法基本只有卡诺图这一种了。在数字电路中&#xf…

看懂这篇文章,你就懂了Mybatis的二级缓存

缓存的概述和分类 概述 缓存就是一块内存空间.保存临时数据 为什么使用缓存 将数据源&#xff08;数据库或者文件&#xff09;中的数据读取出来存放到缓存中&#xff0c;再次获取的时候 ,直接从缓存中获取&#xff0c;可以减少和数据库交互的次数,这样可以提升程序的性能&a…

【MySQL】MySQL复制原理与主备一致性同步工作原理解析(原理篇)(MySQL专栏启动)

&#x1f4eb;作者简介&#xff1a;小明java问道之路&#xff0c;专注于研究 Java/ Liunx内核/ C及汇编/计算机底层原理/源码&#xff0c;就职于大型金融公司后端高级工程师&#xff0c;擅长交易领域的高安全/可用/并发/性能的架构设计与演进、系统优化与稳定性建设。 &#x1…

Qt之天气预报实现(一)预备篇

文章目录序章一、思路整理1.1 我的Qt版本信息1.2 我使用的API二、高德开放平台API的申请和使用2.1 API的申请步骤2.1.1 注册高德开放平台账号&#xff08;若已有账号请无视&#xff09;2.1.2 创建API_KEY2.2 API的使用2.2.1 天气查询文档和城市编码下载位置&#xff08;必读&am…