JVM知识点及面试题补充

news2025/1/19 20:42:42

JVM从软件层面屏蔽了不同操作系统的底层硬件与指令上的区别(所谓的Java跨平台能力

java中JRE(java运行时环境)包括java各种Libraries类库以及Java Virtual Machine(Java虚拟机)。

类加载子系统:

可以根据指定的权限定名来载入类或者接口

字节码执行引擎:

执行那些包含在被载入类的方法中的指令。

运行时数据区:

存储字节码,对象,参数,返回值,局部变量,运行的中间结果,常量这些

java执行总体:先通过javac命令,将java程序转化为字节码文件,再通过java命令进行运行字节码文件,然后JVM开始运行,先经过第一部分类装载子系统,将java的字节码文件装在到java运行时数据区(内存区)中(左边的一块),然后第二部分,通过字节码执行引擎,来执行内存模型中的代码。

堆:

new出的对象优先放在Eden区,Eden区放满之后进行minor gc(垃圾收集),java底层垃圾收集的原理是由字节码执行引擎运行,在后台开启一个垃圾收集线程,从GC Roots的根节点(即线程栈本地变量,静态变量,本地方法栈变量等),垃圾收集是针对整个年轻代进行,不只eden,如果收集了15次,还仍然存在的话就会进入老年代

如果老年代放满了,再放就会进行full gc,对整个堆内存区域进行gc操作,如果年轻代还有空间就接着用,如果老年代和年轻代都没有空间就会发生OOM,内存溢出

是否从年轻代放入到老年代中(有很多种可能)

1.长期存活的对象将进入老年代如果对象在eden存在并经过第一次minorgc后仍然能够存活,并且能够被servivor容纳的话,将被移动到survivor空间中,并将对象年龄设置为1,对象在survivor中每熬过一次minorgc年龄就增加一岁,当它的年龄增加到一定程度(默认为15,cms收集器默认为6岁,不同的垃圾收集器会略微有所不同),就会被晋升到老年代中,对象晋升到老年代的年龄阈值也可以通过参数 -XX:MaxTenuringThreshold来设置

2.大对象会直接进入老年代大对象就是需要大量连续内存空间的对象(比如字符串,数组这些),JVM参数 -XX:PretenureSizeThreshold可以设置大对象的大小,如果对象超过设置大熊啊就会直接进入老年代,不会进入年轻代,这个参数旨在Serial和ParNew两个收集器下有效(这样也是为了避免为大对象分配内存时的复制操作而降低了效率)

3.老年代空间分配担保机制如果上一步的结果是小于或者之前说的参数没有设置,那么就会触发一次Fullgc,对老年代和年轻代一起回收一次垃圾,如果回收完还是没有足够的空间存放新的对象就会发生OOM。当然,如果minorgc之后剩余存活的需要挪用到老年化年代的对象大小还是大于老年代可用空间,那么也会触发fullgc,fullgc之后如果还是没有空间存放minorgc之后存活的对象,那么也将发生OOM。年轻代每次minorgc之前JVM都会计算下老年代剩余可用空间,如果中国可用空间小于年轻代里现有的所有对象大熊啊之和(包括垃圾对象),就会看一个 -XX:-HandlePromotionFailure的参数来设置了。如果有这个参数,就会看看老年代的可用内存大小,是否大于之前每次minorgc后进入老年代的对象的平均大小。

4.对象动态年龄判断当前放对象的Survivor区域中(其中一块区域,放对象的那块s区),一批对象的总大小大于这块survivor区域内存大小的50%(-XX:TargetSurvivorRatio可以指定),那么此时大于等于这批对象年龄最大值的对象,就可以直接进入老年代了,例如Servivor区域中有一批对象,年龄1+年龄2+年龄n的多个年龄对象的总和超过了servivor的50%,此时就会把年龄n以上的对象都放入老年代,这个规则其实是希望哪些可能是长期存活的对象,尽早进入老年代,对象和动态年龄判断机制一般是minorgc之后触发的

栈(每个线程运行时所需要的内存)(又叫线程栈(因为每个线程都分配空间存储局部变量,和方法调用)):放置局部变量等,(只要开始运行,就会从栈中挖一小块运行,放置该线程中的局部变量,每个线程中,运行到main方法时就也会挖下一块栈,给main方法下的局部变量,用到其中的方法,也会挖一小块空间,放置方法内的局部变量,而方法中挖的这一块空间就叫做一块栈帧内存区域(一个方法对应一块栈帧内存区域),方法结束之后,直接把占用的空间直接划去(直接把这块内存删了))

执行main方法,就给main分配一些空间,执行到方法的时候就又会给方法分配空间(这两块空间不相同,相互独立,没有关系),然后方法先结束,内存先销毁,栈帧内存这种先进后出的方式,因为这跟方法的嵌套调用的先后顺序是相吻合的。

  • 栈帧内存区域中包含了(其实都是内存空间)
    • 局部变量表
      • 存储局部变量的数据存储结构
    • 操作数栈
      • 一块临时的空间,用于进行各种数的运算
    • 动态链接
      • 将符号引用转为直接引用(这里的符号包括着各种名词甚至是字符),如math.compute()运行时会去方法区找到该方法的位置
    • 方法出口
      • 规定好运行完某个方法后回到main方法的某个位置
  • 栈与堆的区别是什么?
    • 1.栈内存一般会用来存储局部变量和方法调用,但是堆内存是用来存储Java对象和数组的,堆会GC垃圾回收,而栈不会
    • 2.栈内存是线程私有的,堆内存是线程共有的
    • 3.两者异常错误不同,如果栈内存或者堆内存不足都会抛出异常.栈空间不足:java.lang.StackOverFlowError。 堆空间不足:java.lang.OutOfMemoryError

  • 程序计数器:线程私有的,记录下一行即将运行的代码的内存地址,程序计数器是每一块线程中都有的用来存储即将运行的代码的内存地址的空间(设置这块的原因是多线程,如果有一个CPU实现更高的线程,那么就会转变为运行另一个线程,而因此,刚才运行的代码就会在运行完那一行之后挂起,等到新线程运行完之后运行,但却不能重新运行,因此设计程序计数器,只要根据程序计数器中的东西继续运行就可以了)
    • javap -v xx.class打印堆栈大小,局部变量的数量和方法的参数
    • !!!字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,它是程序控制流的指示器,分支,循环,跳转,异常处理,线程恢复等基础功能都需要依赖这个计数器来完成。
  • 本地方法栈(用的不多其实)
    • 是一些native的方法,用到本地方法时本地方法栈会挖取一部分的内存运行本地方法

  • 方法区元空间
    • 其中放置的是常量,静态变量,类信息这些
      • public static User user = new User();
        • 静态变量user放在方法区元空间中,而new User()是放在堆中的
    • 在元空间中包含class{类信息}(,classloader{加载类},)运行时常量池
      • 常量池可以看作是一张表,虚拟机指令根据这张常量表找到要执行的类名,方法名,参数类型,字面量等信息

  • 你听说过直接内存吗?
  • 直接内存并不属于内存结构,并不由jvm进行管理,是虚拟机的系统内存,常见于NIO操作时,用于数据缓冲区,它分配回收成本较高,但是读写性能高

  • JVM调优工具
    • JVM调优的目的:减少gc操作,本质上是减少STW的发生
      • STW:停止用户发起的所有进程。每次进行gc都可能会导致业务卡顿,如果gc比较慢的话,业务会直接卡在那里,gc对业务的运行性能有影响
      • 面试问题:为什么会设计STW,如果没有STW,那么在进行gc的过程中,程序依旧在进行,这样原本的非垃圾对象可能也会变成垃圾对象,正因为有STW停止了进程,这样哪些是垃圾对象也就定形了,如果是变化的gc就很难进行垃圾收集了
    • Jmap
      • 此命令可以用来查看内存信息,实例个数以及占用内存大小
        • 命令jmap -histo 14660#查看历史生成的实例
        • jmap -histo:live 14660#查看当前存活的实例,执行过程中可能触发的一此fullgc
    • Jstack
      • 可以加进行id查找死锁
    • Jinfo
      • 查看正在运行的Java应用程序的扩展参数

  • CPU多核并发缓存架构
    • 数据从硬盘中拿到主内存中,再通过主内存到CPU
    • 早期一般CPU会直接从主内存中拿数据(不经过CPU缓存),因为主内存和CPU的运行速度差距过大,这样会导致CPU发挥不出其速度优势,因此引入了CPU高速缓存(速度跟CPU也差不多),这样也可以提高整个计算机的运行速度
  • JMM(java内存模型)内存模型
    • JAVA多线程内存模型跟CPU缓存模型类似,是基于cpu缓存模型建立的,Java线程内存模型是标准化的,屏蔽掉了底层计算机的不同

  • 直接内存
    • 直接内存是一种特殊的内存缓冲区,并不在 Java 堆或方法区中分配的,而是通过 JNI 的方式在本地内存上分配的。
    • 直接内存并不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用。而且也可能导致 OutOfMemoryError 错误出现。
    • JDK1.4 中新加入的 NIO(Non-Blocking I/O,也被称为 New I/O),引入了一种基于通道(Channel)与缓存区(Buffer)的 I/O 方式,它可以直接使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆中的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样就能在一些场景中显著提高性能,因为避免了在 Java 堆和 Native 堆之间来回复制数据。
    • 直接内存的分配不会受到 Java 堆的限制,但是,既然是内存就会受到本机总内存大小以及处理器寻址空间的限制。
    • 类似的概念还有 堆外内存 。在一些文章中将直接内存等价于堆外内存,个人觉得不是特别准确。
    • 堆外内存就是把内存对象分配在堆外的内存,这些内存直接受操作系统管理(而不是虚拟机),这样做的结果就是能够在一定程度上减少垃圾回收对应用程序造成的影响。

  • 常见面试题:
    • 如何判断对象是否死亡(两种方法)。
    • 简单的介绍一下强引用、软引用、弱引用、虚引用(虚引用与软引用和弱引用的区别、使用软引用能带来的好处)。
    • 如何判断一个常量是废弃常量
    • 如何判断一个类是无用的类
    • 垃圾收集有哪些算法,各自的特点?
    • HotSpot 为什么要分为新生代和老年代?
    • 常见的垃圾回收器有哪些?
    • 介绍一下 CMS,G1 收集器。
    • Minor Gc 和 Full GC 有什么不同呢?

死亡对象判断方法:

  • 引用计数法
    • 给对象中添加一个引用计数器:
    • 每当有一个地方引用它,计数器就加 1;
    • 当引用失效,计数器就减 1;
    • 任何时候计数器为 0 的对象就是不可能再被使用的。
    • 这个方法实现简单,效率高,但是目前主流的虚拟机中并没有选择这个算法来管理内存,其最主要的原因是它很难解决对象之间循环引用的问题
    • 所谓对象之间的相互引用问题,如下面代码所示:除了对象 objA 和 objB 相互引用着对方之外,这两个对象之间再无任何引用。但是他们因为互相引用对方,导致它们的引用计数器都不为 0,于是引用计数算法无法通知 GC 回收器回收他们。
  • 可达性分析算法:
  •  
    • 这个算法的基本思想就是通过一系列的称为 “GC Roots” 的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的,需要被回收。
    • 哪些对象可以作为 GC Roots 呢?
      • 虚拟机栈(栈帧中的局部变量表)中引用的对象
      • 本地方法栈(Native 方法)中引用的对象
      • 方法区中类静态属性引用的对象
      • 方法区中常量引用的对象
      • 所有被同步锁持有的对象
      • JNI(Java Native Interface)引用的对象
    • 对象可以被回收,就代表一定会被回收吗?
      • 即使在可达性分析法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于“缓刑阶段”,要真正宣告一个对象死亡,至少要经历两次标记过程;可达性分析法中不可达的对象被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行 finalize 方法。当对象没有覆盖 finalize 方法,或 finalize 方法已经被虚拟机调用过时,虚拟机将这两种情况视为没有必要执行。
      • 被判定为需要执行的对象将会被放在一个队列中进行第二次标记,除非这个对象与引用链上的任何一个对象建立关联,否则就会被真的回收

  • 如何判断一个常量是废弃常量:
    • 假如在字符串常量池中存在字符串 "abc",如果当前没有任何 String 对象引用该字符串常量的话,就说明常量 "abc" 就是废弃常量,如果这时发生内存回收的话而且有必要的话,"abc" 就会被系统清理出常量池了。

  • 如何判断一个类是无用的类?
    • 方法区主要回收的是无用的类,那么如何判断一个类是无用的类的呢?
    • 判定一个常量是否是“废弃常量”比较简单,而要判定一个类是否是“无用的类”的条件则相对苛刻许多。类需要同时满足下面 3 个条件才能算是 “无用的类”:
    • 该类所有的实例都已经被回收,也就是 Java 堆中不存在该类的任何实例。
    • 加载该类的 ClassLoader 已经被回收。
    • 该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
    • 虚拟机可以对满足上述 3 个条件的无用类进行回收,这里说的仅仅是“可以”,而并不是和对象一样不使用了就会必然被回收。

  • 垃圾收集算法
    • 标记清除算法
      • 标记-清除(Mark-and-Sweep)算法分为“标记(Mark)”和“清除(Sweep)”阶段:首先标记出所有不需要回收的对象,在标记完成后统一回收掉所有没有被标记的对象。
      • 它是最基础的收集算法,后续的算法都是对其不足进行改进得到。这种垃圾收集算法会带来两个明显的问题:
        • 效率问题:标记和清除两个过程效率都不高。
        • 空间问题:标记清除后会产生大量不连续的内存碎片
      • 关于具体是标记可回收对象还是不可回收对象,众说纷纭,两种说法其实都没问题,我个人更倾向于是前者。
      • 如果按照前者的理解,整个标记-清除过程大致是这样的:
      • 当一个对象被创建时,给一个标记位,假设为 0 (false);
      • 在标记阶段,我们将所有可达对象(或用户可以引用的对象)的标记位设置为 1 (true);
      • 扫描阶段清除的就是标记位为 0 (false)的对象。
    • 复制算法
      • 为了解决标记-清除算法的效率和内存碎片问题,复制(Copying)收集算法出现了。它可以将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。
      • 虽然改进了标记-清除算法,但依然存在下面这些问题:
      • 可用内存变小:可用内存缩小为原来的一半。
      • 不适合老年代:如果存活对象数量比较大,复制性能会变得很差。
    • 标记整理算法
      • 标记-整理(Mark-and-Compact)算法是根据老年代的特点提出的一种标记算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象回收,而是让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存。
      • 由于多了整理这一步,因此效率也不高,适合老年代这种垃圾回收频率不是很高的场景。
    • 分代收集算法
      • 当前虚拟机的垃圾收集都采用分代收集算法,这种算法没有什么新的思想,只是根据对象存活周期的不同将内存分为几块。一般将 Java 堆分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。
      • 比如在新生代中,每次收集都会有大量对象死去,所以可以选择”标记-复制“算法,只需要付出少量对象的复制成本就可以完成每次垃圾收集。而老年代的对象存活几率是比较高的,而且没有额外的空间对它进行分配担保,所以我们必须选择“标记-清除”或“标记-整理”算法进行垃圾收集。
      • 延伸面试问题: HotSpot 为什么要分为新生代和老年代?
      • 根据上面的对分代收集算法的介绍回答。

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

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

相关文章

Pycharm 编辑器编码格式设置

随笔 目录 1.背景 2. 修改编辑器编码设置 3. 最终修改 yml 写入 1.背景 由于写入yml文件中中文编码问题 ython 中讲数据写入yml 文件后,中文显示: "\u9A8C\u8BC1UDMA0_Tx_C0\u53D1\u9001\u6570\u636EUDMA0_Rx_C1\u65B9\u5411\u63A5\u6536\u65…

总结目前开源的视频生成/视频编辑大模型

Diffusion Models视频生成-博客汇总 前言:随着Sora的爆火,视频生成和视频编辑受到了越来越多的关注。这篇博客总结一下目前开源的视频生成和视频编辑的大模型,并分析他们各自的优缺点,以及在自己进行科研任务或者工作中应该如何选…

Linux网络配置全攻略:解读/etc/network/interfaces文件的精髓

欢迎来到我的博客,代码的世界里,每一行都是一个故事 Linux网络配置全攻略:解读/etc/network/interfaces文件的精髓 前言文件结构与基本概念配置网络接口的常用参数高级网络配置技巧实用工具与调试技巧实战案例与最佳实践 前言 在我们的日常生…

PM入门必备| 怎么写产品分析报告?

​小陪老师,产品经理是做些什么的呢?我去面试应该准备些什么呢? A: 首先要分清产品经理的类型,产品的面试需要准备的一般有Axure原型,需求文档,产品分析报告等,有些甚至需要展示项目经验。 tea…

“遥遥领先” time.sleep(6)?

日前,在一场万众瞩目的发布会上,华为自信满满地揭开了其大模型文生图技术的神秘面纱。然而,演示期间一个不经意间闪现的time.sleep(6)代码片段,如同投入平静湖面的一颗石子,激起了业界对于演示真实性与技术底蕴的热烈探…

Process Monitor下载安装使用教程(图文教程)超详细

「作者简介」:2022年北京冬奥会网络安全中国代表队,CSDN Top100,就职奇安信多年,以实战工作为基础对安全知识体系进行总结与归纳,著作适用于快速入门的 《网络安全自学教程》,内容涵盖系统安全、信息收集等…

Ansys Mechanical|中远程点的Behavior该如何设置?

Remote point是ANSYS mechanical中的一种常见节点自由度耦合建模形式,在转动装配体中的连接转动副、或者在施加远端约束及远端载荷的时候,我们经常用到远端单元来耦合一个面或者一条线。例如销轴似的滚动摩擦连接,如果我们希望将两个物体通过…

每日学习 - APK解包

文章目录 APK的定义解析APKAPK 是什么每个文件的意义classes.dexAndroidManifest.xmlassetslibres & resources.arsc 反编译工具apktool apk解包 秒了~ APK的定义 APK(Android Package Kit)是用于部署和分发Android操作系统上应用程序的软件包格式。…

ASTM通信协议校验和计算方法

Lis通信接口开发 <STX> FN <Frame> <ETB>or<ETX> <CS><CR> <LF> 其中&#xff1a; <STX>&#xff1a;起始帧头&#xff08;0x02&#xff09; FN&#xff1a;帧号&#xff08;范围0&#xff5e;7&#xff0c;1&#xff5e;7完…

Winform自定义控件 —— 开关

在开始阅读本文之前&#xff0c;如果您有学习创建自定义控件库并在其他项目中引用的需求&#xff0c;请参考&#xff1a;在Visual Studio中创建自定义Winform控件库并在其他解决方案中引用https://blog.csdn.net/YMGogre/article/details/126508042 0、引言 由于 Winform 框架并…

Python代码:九、十六进制数字的大小

1、题目 计算的世界&#xff0c;除了二进制与十进制&#xff0c;使用最多的就是十六进制了&#xff0c;现在使用input读入一个十六进制的数字&#xff0c;输出它的十进制数字是多少&#xff1f; 2、代码 import sysnum16 input() num10 int(num16,16) print(num10) 3、结…

GAN实例基于神经网络

目录 1.前言 2.实验 1.前言 需要了解GAN的原理查看对抗生成网络&#xff08;GAN&#xff09;&#xff0c;DCGAN原理。 采用手写数字识别数据集 2.实验 import argparse import os import numpy as np import mathimport torchvision.transforms as transforms from torchvi…

创维汽车总经理培训正式开展,打造新能源汽车销售的精英战队

在新能源汽车市场竞争日益激烈的背景下&#xff0c;创维汽车为加强核心竞争力&#xff0c;于2024年5月15日至17日在河南省安阳市举办了为期三天的总经理岗位认证培训。此次培训旨在强化经销商店端负责人们在新能源汽车销售与运营方面的能力&#xff0c;指明未来发展思路&#x…

(5.4–5.10)投融资周报|共38笔公开投融资事件,基础设施领跑,游戏融资活跃

5月4日至5月10日期间&#xff0c;加密市场共发生38笔投融资事件&#xff0c;其中基础设施18笔、游戏5 笔、其他4 笔、DeFi 3笔、Depin 3 笔、CeFi 2笔、NFT2笔、 RWA1笔。 本周千万美金以上融资有5笔&#xff1a; 加密货币交易公司Arbelos完成了一轮2800 万美元的种子轮融资&…

【极简】docker常用操作

镜像images是静态的 容器container是动态的&#xff0c;是基于镜像的&#xff0c;类似于一个进程。 查看docker images&#xff1a; docker images 或者docker image ls 查看docker container情况&#xff1a;docker ps -a&#xff0c;-a意思是--all 运行一个container: doc…

Python程序设计 文件处理(二)

实验十二 文件处理 第1关&#xff1a;读取宋词文件&#xff0c;根据词人建立多个文件 读取wjcl/src/step1/宋词.txt文件&#xff0c; 注意&#xff1a;宋词文件的标题行的词牌和作者之间是全角空格&#xff08;" ")可复制该空格 在wjcl/src/step3/cr文件夹下根据每…

【WEEK12】 【DAY3】整合MyBatis框架【中文版】

2024.5.15 Wednesday 目录 13.整合MyBatis框架13.1.整合测试13.1.1.新建springboot-05-mybatis项目13.1.2.导入MyBatis需要的依赖13.1.3.配置数据库连接信息13.1.3.1.修改application.properties13.1.3.2.修改Springboot05MybatisApplicationTests.java并测试 13.1.4.新建pojo文…

Spring使用小技巧--排除bean无法被调用问题

我们在项目中可能由于项目的复杂性&#xff0c;创建了个spring的bean&#xff0c;但是调用却出现报错&#xff0c;显示无法找到该bean的异常。 这个时候我们就需要找到出错的原因&#xff0c;很多人往往会忽略的一点就是&#xff0c;你所创建的bean有可能并没有被加载到ioc容器…

【Linux系统编程】第十九弹---进程状态(下)

​​​​​​​ ✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】 目录 1、僵尸进程 2、孤儿进程 3、运行状态 4、阻塞状态 5、挂起状态 6、进程切换 总结 1、僵尸进程 上一弹…

算法练习第22天|39. 组合总和、40.组合总和II

39. 组合总和 39. 组合总和 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/combination-sum/description/ 题目描述&#xff1a; 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target &#xff0c;找出 candidates 中可以使数字和为目标数…