JVM之内存管理(一)

部分内容来源:JavaGuide+二哥Java


图解JVM内存结构


内存管理快速复习

栈帧:局部变量表,动态链接(符号引用转为真实引用),操作数栈(存储中间结算结果),方法返回地址

运行时常量池:常量池表,符号引用,字面量

对象创建过程:类加载检查(类没有加载就进行类加载)+分配内存+初始化零值+设置对象头+执行对象初始化方法

类加载过程:

  1. 加载:通过类的全限定名获取该类的二进制字节流,存到类常量池,内存中生成Class类对象
  2. 连接:
    1. 验证:验证Class二进制字节流合规(例如验证魔数和版本号)
    2. 准备:为类对象分配内存
    3. 解析:符号引用转为直接引用
  1. 初始化:执行初始化方法

内存分配:指针碰撞(CAS重试)+空闲列表

内存分配失败:CAS配上重试机制+TLAB为每个线程预先在Eden区分配一块内存

对象:对象头(运行时数据+类型指针,GC年龄,Hash码)+实例数据

对象访问定位:使用句柄(间接访问)+直接指针访问

JVM堆内存分区:新生代(Eden+S1+S2)+老年代

对象什么时候会进入老年代:长期存活的对象,大对象,动态年龄判断分配担保机制

逃逸分析:在栈内为对象分配内存

Stop The World :停止所有用户线程

Oop Map:记录了对象内部所有引用字段(指针)的位置

安全点:可以暂停所有线程执行特定操作的位置

常量池包括:

类常量池

运行时常量池

字符串常量池

JDK1.6:常量池在永久代

JDK1.7:运行时常量池+类常量池在永久代,字符串常量池在堆

JDK1.8:运行时常量池+类常量池在元空间,字符串常量池在堆


引用类型有哪些?有什么区别?

强引用指的就是代码中普遍存在的赋值方式,比如 A a = new A () 这种。强引用关联的对象,永远不会被 GC 回收

软引用可以用 SoftReference 来描述,指的是那些有用但是不是必须要的对象

系统在发生内存溢出前会对这类引用的对象进行回收

弱引用可以用 WeakReference 来描述,他的强度比软引用更低一点

弱引用的对象下一次 GC 的时候一定会被回收,而不管内存是否足够

虚引用也被称作幻影引用,是最弱的引用关系,可以用 PhantomReference 来描述,他必须和 ReferenceQueue 一起使用,同样的当发生 GC 的时候,虚引用也会被回收

可以用虚引用来管理堆外内存


说一下弱引用?举例子在哪里可以引用?

Java 中的弱引用是一种引用类型,它不会阻止一个对象被垃圾回收


在 Java 中,弱引用是通过 Java.lang.ref.WeakReference 类实现的

弱引用的一个主要用途是创建非强制性的对象引用,这些引用可以在内存压力大时被垃圾回收器清理,从而避免内存泄露


弱引用的使用场景:

  • 缓存系统:弱引用常用于实现缓存,特别是当希望缓存项能够在内存压力下自动释放时。如果缓存的大小不受控制,可能会导致内存溢出。使用弱引用来维护缓存,可以让 JVM 在需要更多内存时自动清理这些缓存对象。
  • 对象池:在对象池中,弱引用可以用来管理那些暂时不使用的对象。当对象不再被强引用时,它们可以被垃圾回收,释放内存。
  • 避免内存泄露:当一个对象不应该被长期引用时,使用弱引用可以防止该对象被意外地保留,从而避免潜在的内存泄露

说一下你对内存泄露和内存溢出的了解

什么是内存泄露:

内存泄漏是指程序在运行过程中不再使用的对象仍然被引用而无法被垃圾收集器回收,从而导致可用内存逐渐减少

虽然在 Java 中,垃圾回收机制会自动回收不再使用的对象,但如果有对象仍被不再使用的引用持有,垃圾收集器无法回收这些内存,最终可能导致程序的内存使用不断增加


内存泄露常见原因:

  • 静态集合:使用静态数据结构(如 HashMap 或 ArrayList)存储对象,且未清理。
  • 事件监听:未取消对事件源的监听,导致对象持续被引用。
  • 线程没被回收:未停止的线程可能持有对象引用,无法被回收。

内存溢出:

内存溢出是指 Java 虚拟机(JVM)在申请内存时,无法找到足够的内存,最终引发 OutOfMemoryError 。这通常发生在堆内存不足以存放新创建的对象时


内存溢出常见原因:

  • 大量对象创建:程序中不断创建大量对象,超出 JVM 堆的限制。
  • 持久引用:大型数据结构(如缓存、集合等)长时间持有对象引用,导致内存累积。
  • 递归调用:深度递归导致栈溢出

JVM的内存泄露有几种溢出情况?

堆内存溢出:当出现 Java.lang.OutOfMemoryError:Java heap space 异常时,就是堆内存溢出了。原因是代码中可能存在大对象分配,或者发生了内存泄露,导致在多次 GC 之后,还是无法找到一块足够大的内存容纳当前对象

栈溢出:如果我们写一段程序不断的进行递归调用,而且没有退出条件,就会导致不断地进行压栈。类似这种情况,JVM 实际会抛出 StackOverFlowError;当然,如果 JVM 试图去扩展栈空间的时候失败,则会抛出 OutOfMemoryError

元空间溢出:元空间的溢出,系统会抛出 Java.lang.OutOfMemoryError: Metaspace。出现这个异常的问题的原因是系统的代码非常多或引用的第三方包非常多或者通过动态代码生成类加载等方法,导致元空间的内存占用很大

直接内存内存溢出:在使用 ByteBuffer 中的 allocateDirect () 的时候会用到,很多 JavaNIO (像 netty) 的框架中被封装为其他的方法,出现该问题时会抛出 Java.lang.OutOfMemoryError: Direct buffer memory 异常


栈中存的是指针还是对象?

在 JVM 内存模型中,栈(Stack)主要用于管理线程的局部变量和方法调用的上下文

而堆(Heap)则是用于存储所有类的实例和数组


当我们在栈中讨论 “存储” 时,实际上指的是存储基本类型的数据(如 int, double 等)和对象的引用,而不是对象本身。


这里的关键点是,栈中存储的不是对象,而是对象的引用

也就是说,当你在方法中声明一个对象,比如 MyObject obj = new MyObject ();,

这里的 obj 实际上是一个存储在栈上的引用,指向堆中实际的对象实例

这个引用是一个固定大小的数据(例如在 64 位系统上是 8 字节),它指向堆中分配给对象的内存区域


说一下程序计数器的作用?为什么程序计数器是私有的?

Java 程序是支持多线程一起运行的,多个线程一起运行的时候 cpu 会有一个调动器组件给它们分配时间片,比如说会给线程 1 分给一个时间片,它在时间片内如果它的代码没有执行完,它就会把线程 1 的状态执行一个暂存,切换到线程 2 去,执行线程 2 的代码,等线程 2 的代码执行到了一定程度,线程 2 的时间片用完了,再切换回来,再继续执行线程 1 剩余部分的代码


我们考虑一下,如果在线程切换的过程中,下一条指令执行到哪里了,是不是还是会用到我们的程序计数器啊

没个线程都有自己的程序计数器,因为它们各自执行的代码的指令地址是不一样的呀,所以每个线程都应该有自己的程序计数器


说一下方法区中方法的执行过程

当程序中通过对象或类直接调用某个方法时,主要包括以下几个步骤:

解析方法调用:JVM 会根据方法的符号引用找到实际的方法地址(如果之前没有解析过的话)

栈帧创建:在调用一个方法前,JVM 会在当前线程的 Java 虚拟机栈中为该方法分配一个新的栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息

执行方法:执行方法内的字节码指令,涉及的操作可能包括局部变量的读写、操作数栈的操作、跳转控制、对象创建、方法调用等。

返回处理:方法执行完毕后,可能会返回一个结果给调用者,并清理当前栈帧,恢复调用者的执行环境


JVM内存中的栈和堆有什么区别?

用途

栈主要用于存储局部变量、方法调用的参数、方法返回地址以及一些临时数据。每当一个方法被调用,一个栈帧(stack frame)就会在栈中创建,用于存储该方法的信息,当方法执行完毕,栈帧也会被移除

堆用于存储对象的实例(包括类的实例和数组)。当你使用 new 关键字创建一个对象时,对象的实例就会在堆上分配空间


生命周期

栈中的数据具有确定的生命周期,当一个方法调用结束时,其对应的栈帧就会被销毁,栈中存储的局部变量也会随之消失

堆中的对象生命周期不确定,对象会在垃圾回收机制(Garbage Collection, GC)检测到对象不再被引用时才被回收


存取速度

栈的存取速度通常比堆快,因为栈遵循先进后出(LIFO, Last In First Out)的原则,操作简单快速

堆的存取速度相对较慢,因为对象在堆上的分配和回收需要更多的时间,而且垃圾回收机制的运行也会影响性能


存储空间

栈的空间相对较小,且固定,由操作系统管理

当栈溢出时,通常是因为递归过深或局部变量过大

堆的空间较大,动态扩展,由 JVM 管理

堆溢出通常是由于创建了太多的大对象或未能及时回收不再使用的对象


可见性

栈中的数据对线程是私有的,每个线程有自己的栈空间。堆中的数据对线程是共享的,所有线程都可以访问堆上的对象


static修饰的类型

未被static修饰的基本类型:基本类型的变量(局部变量)存放在栈中,而不是堆中。例如int num = 10;,变量num是在方法执行时在栈中开辟空间存储的。基本类型的成员变量存放在堆中(当所属对象在堆中时) 。

static修饰的基本类型:被static修饰的基本类型(静态变量)存放在方法区(在 JDK 8 及之后,方法区的实现是元空间),而不是栈中。静态变量属于类,在类加载时就会分配空间并初始化,存储在方法区供类的所有对象共享。

包装类型:包装类型属于对象类型,其对象实例确实几乎都存在堆中,但包装类型的对象在某些场景下会有缓存机制。例如Integer-128127之间的值会被缓存,当创建这个范围内的Integer对象时,不会在堆中重新创建,而是直接引用缓存中的对象

此外,部分包装类(如IntegerShortByteCharacterLong)存在对象缓存机制。Integer为例,在创建-128127之间的Integer对象时,不会在堆中重新创建,而是直接引用方法区中缓存的对象;超出这个范围才会在堆中创建新对象


线程的内部有什么?

程序计数器

本机方法栈

Java虚拟机栈


说一下JVM栈的内部组成

JVM栈的内部组成

Java虚拟机栈是线程私有的
它的生命周期和线程相同
除了Native方法是调用本地方法栈实现,其他的所有方法的调用都是通过Java虚拟机栈来实现的
Java虚拟机栈的内部是由栈帧组成
方法调用的数据需要通过栈进行传递,每一次方法调用都会有一个对应的栈帧被压入栈中,每一个方法调用结束后,都会有一个栈帧被弹出


Java虚拟机栈的内部是由一个又一个的栈帧组成
栈帧内部:局部变量表、操作数栈、动态链接、方法返回地址
 


说一下栈帧的内部

局部变量表

存放了数据类型,对象引用


操作数栈

主要作为方法调用的中转站使用,用于存放方法执行过程中产生的中间计算结果
另外,计算过程中产生的临时变量也会放在操作数栈中

动态链接


场景:主要服务一个方法需要调用其他方法的场景
Class 文件的常量池里保存有大量的符号引用比如方法引用的符号引用。
当一个方法要调用其他方法,需要将常量池中指向方法的符号引用转化为其在内存地址中的直接引用。动态链接的作用就是为了将符号引用转换为调用方法的直接引用


方法返回地址

就是我们方法结束后的返回地址


说一下栈帧内部有啥?

局部变量表

操作数栈

动态链接

方法返回地址


说一下JVM栈会出现的问题

tackOverFlowError错误:栈帧过多爆了

函数循环调用过多

我们这个线程递归调用的时候,我们会往栈里面压入栈帧,如果压入的栈帧过多,就会爆出

Java方法的两种返回方式

一:Returen正常返回

二:抛出异常


OutOfMemoryError:内存空间不够爆了

虚拟机动态扩展栈时,无法申请到足够的内存空间


什么是本地方法栈

为本地方法服务

(也就是和我们的操作系统有关,我们的操作系统的方法)


说一下JVM的内存区域

JVM 内存区域最粗略的划分可以分为

当然,按照虚拟机规范,可以划分为以下⼏个区域:

JVM 内存分为线程私有区线程共享区

线程共享区:方法区和堆

线程隔离的数据区: 虚拟机栈 、本地方法栈 和 程序计数器

1)程序计数器

程序计数器(Program Counter Register)也被称为 PC 寄存器,是⼀块较⼩的内存空间。

它可以看作是当前线程所执⾏的字节码的⾏号指示器。

2Java 虚拟机栈

Java 虚拟机栈(Java Virtual Machine Stack)也是线程私有的,它的⽣命周期与线程相同。

Java 虚拟机栈描述的是 Java ⽅法执⾏的线程内存模型:⽅法执⾏时,JVM 会同步创建⼀个栈帧,⽤来存储局部变量表、操作数栈、动态连接等。

3)本地方法栈

本地⽅法栈(Native Method Stacks)与虚拟机栈所发挥的作⽤是⾮常相似的,其区别只是虚拟机栈为虚拟机执⾏

Java ⽅法(也就是字节码)服务,⽽本地⽅法栈则是为虚拟机使⽤到的本地(Native)⽅法服务。

Java 虚拟机规范允许本地⽅法栈被实现成固定⼤⼩的或者是根据计算动态扩展和收缩的。

4Java

对于 Java 应⽤程序来说,Java 堆(Java Heap)是虚拟机所管理的内存中最⼤的⼀块。Java 堆是被所有线程共享

的⼀块内存区域,在虚拟机启动时创建。此内存区域的唯⼀⽬的就是存放对象实例,Java ⾥“几乎”所有的对象实例

都在这⾥分配内存。Java 堆是垃圾收集器管理的内存区域,因此⼀些资料中它也被称作“GC 堆”(Garbage Collected Heap,)。从回

收内存的⻆度看,由于现代垃圾收集器⼤部分都是基于分代收集理论设计的,所以 Java 堆中经常会出现 新⽣代 、⽼年代 、 Eden空间 、 From Survivor空间 、 To Survivor空间 等名词,需要注意的是这种划分只是根据垃圾回收机制来进⾏的划分,不是 Java 虚拟机规范本身制定的

5)方法区

⽅法区是⽐较特别的⼀块区域,和堆类似,它也是各个线程共享的内存区域,⽤于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据

它特别在 Java 虚拟机规范对它的约束⾮常宽松,所以⽅法区的具体实现历经了许多变迁,例如 jdk1.7 之前使⽤永久代作为⽅法区的实现


JVM的堆是用来干嘛的

Java 虚拟机所管理的内存中最大的一块

Java 堆是所有线程共享的一块内存区域,在虚拟机启动时创建

此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存


什么是逃逸分析

jdk1.7之后,已经默认开启逃逸分析

也就是某些方法中的对象引用没有被返回或者未被外面使用,那么就可以直接在栈上分配内存

也就是我们不用在堆给这个对象分配内存,我们在栈上给这个对象分内存就可以了

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

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

相关文章

无线射频模块如何通过CE RED认证?关键规范与准备策略详解

随着无线通信设备在欧洲市场的广泛应用,CE RED认证已成为模块类产品进入欧盟的强制通行证。作为专注于LoRa模块、对讲模块与FSK射频模块研发的技术企业,我们深知从设计、测试到量产,每一个环节都需紧扣合规底线。本文将围绕CE RED认证核心要求…

Golang中集合相关的库

一切编程语言的底层结构都是数组,其它复杂数据结构如Map, Stack,Heap和Queue都是基于数组建立起来的。 Go语言主流工具库推荐(含常用数据结构实现) 以下是目前Go生态中最主流且活跃的工具库,包含队列、栈、优先级队列…

ABAP 导入Excel形成内表

文章目录 创建导入模板程序实现代码代码解析运行结果 创建导入模板 程序实现 代码 *&---------------------------------------------------------------------* *& Report Z_EXCEL_UPLOAD_LHY *&--------------------------------------------------------------…

特殊配合力(SCA)作为全基因组关联分析(GWAS)的表型,其生物学意义和应用价值

生物学意义 解析非加性遗传效应 特殊配合力(SCA)主要反映特定亲本组合的杂交优势,由非加性遗传效应(如显性、超显性、上位性)驱动。显性效应涉及等位基因间的显性互作,上位性效应则涉及不同位点间的基因互作。通过SCA-GWAS,可以定位调控这些非加性效应的关键基因组区域…

应急响应基础模拟靶机-security1

PS:杰克创建在流量包(result.pcap)在根目录下,请根据已有信息进行分析 1、攻击者使用的端口扫描工具是? 2、通过流量及日志审计,攻击者上传shell的时访问web使用IP地址是多少? 3、审计流量日志,攻击者反弹shell的地址及端口? 4、攻击者…

uniapp-商城-47-后台 分类数据的生成(通过数据)

在第46章节中,我们为后台数据创建了分类的数据表结构schema,使得可以通过后台添加数据并保存,同时使用云函数进行数据库数据的读取。文章详细介绍了如何通过前端代码实现分类管理功能,包括获取数据、添加、更新和删除分类。主要代…

ClickHouse的基本操作说明

说明 文章内容包括数据库管理、表操作及查询等核心功能 创建数据库 -- 默认引擎(Atomic) CREATE DATABASE IF NOT EXISTS test_db; -- MySQL引擎(映射外部MySQL数据库) CREATE DATABASE mysql_db ENGINE MySQL(host:port, m…

Nacos源码—7.Nacos升级gRPC分析四

大纲 5.服务变动时如何通知订阅的客户端 6.微服务实例信息如何同步集群节点 6.微服务实例信息如何同步集群节点 (1)服务端处理服务注册时会发布一个ClientChangedEvent事件 (2)ClientChangedEvent事件的处理源码 (3)集群节点处理数据同步请求的源码 (1)服务端处理服务注册…

《Overlapping Experiment Infrastructure: More, Better, Faster》论文阅读笔记

文章目录 1 背景2 三个核心概念3 Launch层:特性发布的专用机制4 流量分发策略和条件筛选4.1 四种流量分发类型4.2 条件筛选机制 5 工具链与监控体系6 实验设计原则7 培训参考与推荐 1 背景 谷歌(Google)以数据驱动著称,几乎所有可…

国芯思辰| 医疗AED可使用2通道24位模拟前端SC2946(ADS1292)

生物电信号监测技术在医疗健康行业中发展迅速,成为评估人体生理健康状况的关键手段。心电(ECG)、脑电(EEG)和肌电(EMG)等信号,通过精密模拟前端芯片捕捉和处理,对医疗诊断…

数据结构【二叉搜索树(BST)】

二叉搜索树 1. 二叉搜索树的概念2. 二叉搜索树的性能分析3.二叉搜索树的插入4. 二叉搜索树的查找5. 二叉搜索树的删除6.二叉搜索树的实现代码7. 二叉搜索树key和key/value使用场景7.1 key搜索场景:7.2 key/value搜索场景: 1. 二叉搜索树的概念 二叉搜索…

RDMA高性能网络通信实践

RDMA高性能网络通信实践 一、背景介绍二、方法设计A.实现方案B.关键技术点 三、代码及注释四、注意事项 一、背景介绍 远程直接内存访问(RDMA)技术通过绕过操作系统内核和CPU直接访问远程内存,实现了超低延迟、高吞吐量的网络通信。该技术广…

ndarray数组掩码操作,True和False获取数据

#数组掩码的表示方法 def testht05():a np.arange(1,10)mask [True,False,True,True,False,True,False,True,True]print(a[mask]) 另外的用法: #掩码操作获取子集 def testht06():a np.arange(1,100)print(a[a%3 0 & (a%7 0)] )b np.array([A,"B&qu…

索引工具explain

EXPLAIN 是 MySQL 中一个非常有用的工具,用于分析查询的执行计划。通过 EXPLAIN,你可以了解 MySQL 是如何执行查询的,包括它如何使用索引、表的扫描方式等。这有助于优化查询性能。以下是 EXPLAIN 输出的各个字段的详细解释: 基本用法 EXPLAIN SELECT * FROM table_name …

Git回顾

参考视频:【GeekHour】一小时Git教程 一句话定义:Git是一个免费开源的分布式版本控制系统。 版本控制系统可以分为两种,1.集中式(SVN,CVS);2.分布式(git) git的工作区域和文件状态…

python打卡day20

特征降维------特征组合(以SVD为例) 知识点回顾: 奇异值的应用: 特征降维:对高维数据减小计算量、可视化数据重构:比如重构信号、重构图像(可以实现有损压缩,k 越小压缩率越高&#…

GuPPy-v1.2.0安装与使用-生信工具52

GuPPy:Python中用于光纤光度数据分析的免费开源工具 01 背景 Basecalling 是将原始测序信号转换为碱基序列的过程,通俗地说,就是“把碱基识别出来”。这一过程在不同代测序技术中各不相同: 一代测序是通过解析峰图实现&#xff1…

47. 全排列 II

题目 给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。 示例 1: 输入:nums [1,1,2] 输出: [[1,1,2],[1,2,1],[2,1,1]] 示例 2: 输入:nums [1,2,3] 输出:[[1,2,3…

ERP系统操作流程,如何快速搭建流程体系

ERP流程图,如何搭建和建立,ERP系统操作流程,ERP系统操作流程图,采购流程,销售流程,仓库流程,MRP流程,PMC流程,财务流程,应收流程,应付流程&#x…

class path resource [] cannot be resolved to absolute file path

问题情景 java应用程序在IDE运行正常,打成jar包后执行却发生异常: java.io.FileNotFoundException: class path resource [cert/sync_signer_pri_test.key] cannot be resolved to absolute file path because it does not reside in the file system:…