Java面试题——第三篇(JVM)

news2024/12/25 0:00:51

1. 什么情况下会发生栈内存溢出

栈是线程私有的,他的生命周期和线程相同,每个方法在执行的时候都会创建一个栈帧,用来存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机中从入栈到出栈的过程。
栈内存溢出是指线程请求的栈深度大于虚拟机所允许的最大深度,则会抛出StackOverflow异常。
另一个可能是栈中引用了大的变量,导致OOM异常。

2. JVM内存结构,Eden和Survivor的比例

在这里插入图片描述

内存结构

  • 程序计数器: 当前线程执行的字节码的行号指示器,是线程私有的,也是唯一一个不会发生OOM异常的区域。
  • Java虚拟机栈:也是线程私有的,保存方法调用的动态链接、操作数、局部变量等信息,方法调用就是虚拟机栈出栈入栈的过程。
  • 本地方法栈:线程私有的,和虚拟机栈类似,只是保存的是对于native方法的调用。
  • 堆:所有线程共享,Java虚拟机中管理的内存最大的一个区域,所有线程共享的区域,唯一目的是存放对象实例。也是垃圾回收的主要区域。
  • 方法区:线程共享,存放虚拟机加载的类信息、常量、静态变量等数据。
  • 运行时常量池:是方法区的一部分,存放编译器生成的各种字面量和符号引用。
  • 直接内存:并不是虚拟机运行时数据区域的一部分,使用Native函数直接分配堆外内存,通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。避免了在Java堆和Native堆中来回复制数据。

Eden和Survivor比例

JVM对堆进行分代,新生代分为三个部分,1个Eden区、2个Survivor区。默认比例为8:1:1。一般情况下,新创建的对象都会被分配到Eden区(一些大对象除外)。这些对象经过一次Minor GC后,如果仍然存在,将会被移到Survivor区。对象每次熬过一次Minor GC,年龄就会增加1岁。当年龄增加到一定程度时,将会被移动到老年代.

3. JVM为什么要分为新生代、老年代和持久代。新生代中为什么要分为Eden区域和Survivor区。

  • 堆内存是虚拟机管理的内存中最大的一块,堆内存区域划分,主要是为了提高对象内存分配和回收效率。
  • 新创建的对象在新生代中分配,经过多次回收仍然存活的对象放入老年代。静态变量、类信息存放在永久代中。大部分对象都是朝生夕死的,只需要在新生代中频繁执行Minor GC,老年代中对象生命周期长,内存回收的效率相对较低,不需要频繁进行回收,永久代中回收效果太差,一般不进行回收。
  • 另外,分代以后可以根据不同的区域执行不同的垃圾回收算法。例如,新生代通常采用 标记-复制算法 , 老年代通常采用 标记-清除-整理 算法。

新生代分为Eden区、From Survivor区、To Survivor区,默认比例为8:1:1。划分的目的是因为JVM采用复制算法来回收新生代,设置这个比例是为了充分利用内存空间,减少浪费。新生成的对象在Eden区分配,当Eden区域没有足够内存时触发Minor GC,对象进入Survivor区域。

4. JVM中一次完整的GC流程是什么样,对象如何晋升到老年代,介绍几种主要的JVM参数。

完整GC流程

  • GC开始时,对象只存在于Eden区和From Survivor区域,To Survivor是空白的保留区域。GC进行时,Eden中所有存活的对象会被复制到To Survivor区域,而在From Survivor区域的对象,根据年龄决定去向,如果年龄+1大于15,则晋升到老年代,否则,进入To Survivor区域。接着,清空Eden区域和From Survivor区域。接着,From Survivor和To Survivor区域交换角色,即,To Survivor变成From Survivor。

对象晋升到老年代的几种可能:

  • 当对象达到成年,经过15次GC,对象晋升到老年代。
  • 大的对象直接在老年代创建。
  • 新生代的Survivor空间内存不足时,对象可能直接晋升到老年代。

主要的JVM参数
默认情况下,JVM初始分配的堆内存大小是物理内存的1/64,最大分配的堆内存大小是物理内存的1/4.

  • -Xms:初始堆大小
  • -Xmx:堆最大内存
  • -Xss:栈内存
  • -XX:PermSize 初始化永久代内存
  • -XX:MaxPermSize 最大永久代内存
  • -XX:NewSize 设置年轻代初始值
  • -XX:MaxNewSize 设置年轻代最大值

5. 介绍几种垃圾收集器,各自的优缺点,重点介绍cms和G1收集器,包括原理,流程,优缺点。

  1. 串行收集器(Serial 收集器)
  • 工作原理:串行收集器是工作在新生代的单线程垃圾收集器。只会使用一个CPU或者一个收集线程来完成垃圾回收工作,在进行垃圾收集时,会暂停所有用户线程,直到垃圾收集结束。
  • 优点:简单高效,开销低
  • 缺点:进行垃圾收集时需要暂停所有应用线程,对于需要高响应性的应用来说是不可接受的。
  1. ParNew收集器
  • 工作原理:ParNew收集器是串行收集器的多线程版本;除了使用多线程外,其收集算法、STW、对象分配规则、回收策略和Serial收集器完全一样。
  • 优点:提高吞吐量,减少暂停时间。能和CMS收集器配合工作,在Server模式下,是许多新生代收集器的首选。
  • 缺点:单CPU环境中可能不如串行收集器,随着CPU数量增加,线程交互的开销可能成为性能瓶颈。
  1. Parallel Scavenge收集器
  • 工作原理:Parallel Scavenge收集器也是一个使用复制算法,多线程,工作于新生代的垃圾收集器。他关注的是吞吐量,而CMS关注的是停顿时间。
  • 优点:提高吞吐量,减少暂停时间。
  • 缺点:不适用于对停顿时间要求极高的应用。
  1. CMS收集器
  • 工作原理:CMS收集器是基于“标记-清除”算法实现的并发收集器。
  • 工作流程:
  1. 初始标记:标记出和GC Roots直接相连的对象,需要暂停所有线程。
  2. 并发标记:和用户线程并发执行,标记出所有可达对象。
  3. 重新标记:修正并发标记期间因用户线程操作导致标记变动的情况,需要暂停所有用户线程。
  4. 并发清除:与用户线程并发执行,清除未被标记的对象。
  • 优点:与用户线程并发执行,减少停顿时间。
  • 缺点:基于“标记-清除”算法,收集结束时可能会产生大量空间碎片;对CPU资源敏感;无法处理浮动垃圾。
  1. G1收集器
  • 工作原理:G1收集器是一种面向服务器的垃圾收集器,基于“标记-整理”和“复制”算法实现。他将堆空间划分为多个大小相等的独立区域,并优先收集垃圾最多的区域。
  • 优点:适用于高吞吐量的应用程序,在多个处理器之间并行的进行操作,提高处理效率。
  • 缺点:启动时间长,需要对整个堆空间进行分区,启动时间较长。需要额外的内存来存储标记信息和回收状态信息。

6. 垃圾回收算法的实现原理

  • 标记-清除算法:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。
  • 标记-整理算法:标记过程和上述标记-清除算法一样,但后续步骤不是直接对可回收对象进行清理的,而是让所有存活的对象都向一端移动,然后直接清理掉边界外的内存。
  • 复制算法:将可用内存按容量分为两块,每次只使用其中一块,当这一块内存用完后,将还活着的对象复制到另一块上面。然后再把已使用过的空间一次清理掉。

7. 当出现内存溢出,怎么排查错误

  • 首先控制台查看错误日志。
  • 使用jmap查看堆转储快照。
  • 定位出内存溢出的空间:堆、栈、还是永久代。
  • 如果是堆内存溢出,看是否创建了超大的对象。
  • 如果是栈内存溢出,看是否创建了超大的对象或者产生了方法调用的死循环。

8. JVM内存模型相关,重排序、内存屏障、happen-before、主内存、工作内存等。

重排序:

  • 重排序指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段。

内存屏障

  • 内存屏障可以确保某些操作按照预定的顺序执行,防止编译器和处理器对指令进行重排序;内存屏障可以确保写入一个共享变量的操作在后续的读操作前对其他线程可见。

Happen-Before原则

  • 指一个操作的结果对另一个操作是可见的,即一个操作的结果可以被后续操作获取或感知到。这个原则保证了跨线程的内存可见性。

主内存

  • 所有线程共享的内存空间。

工作内存

  • 工作内存指每个线程特有的内存空间,工作内存中保存了被该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作(读取、赋值)都必须在工作内存中进行,而不能直接读写在主内存中的变量。

9. Java的反射机制

他允许程序在运行时查询和使用任何类的信息,包括类的成员变量、方法、构造函数等。这种机制主要通过java.lang.reflect包中的类和接口实现。

10. G1和CMS区别,吞吐量优先和响应优先的垃圾收集器选择

  • CMS是基于“标记-清除”实现的,主要步骤是初始标记、并行标记、重新标记、并发清除。
  • G1是基于“标记-整理”实现的,主要步骤是初始标记、并发标记、最终标记和筛选回收。

CMS的缺点是对CPU要求比较高。
G1的缺点是将内存划分成很多块,所以对内存段的大小有很大的要求。
CMS是清除,所以有很多内存碎片。
G1是整理,所以碎片空间很小。
CMS和G1都是响应优先,他们的目的都是尽量控制STW时间。
G1和CMS的Full Gc都是单线程的mark sweep compact算法,直到JDK10才优化成并行的。
CMS目前只用于老年代,G1将整个Java堆划分为多个大小不等的独立区域,虽然还保留有老年代和新生代的概念,但是不在物理隔开,他们都是Region的集合。

吞吐量优先选择Parallel Scavenge收集器。

11. 解释如下JVM参数含义

  • -server 服务器模式
  • -Xms512m:初始堆内存大小
  • -Xmx512m: 最大堆内存大小
  • -Xss1024k: 栈大小
  • -XX:PermSize=256m:初始永久代大小
  • -XX:MaxPermSize=512m:最大永久代大小
  • -XX:MaxTenuringThreshold=20:新生对象存活次数为20时,晋升到老年代
  • -XX:CMSInitiatingOccupancyFraction=80 :CMS在对老年代内存占用率达到80%时,开始GC
  • -XX:+UseCMSInitiatingOccupancyOnly : 只使用设定的阈值(如上80%),如果不指定,JVM仅在第一次GC时使用设定值,后续则自动调整。

12. 运行时数据中哪些区域是线程共享的,哪些是独享的

JVM内存区域中,程序计数器、虚拟机栈、本地方法栈是线程独享的。
堆、方法区是线程共享的,但是,值得注意的是,Java堆其实还为每一个线程单独分配一个TLAB(本地线程分配缓冲)。
创建对象时,内存分配过程是如何保证线程安全性?有两种解决方案:

  • 对分配内存空间的动作进行同步处理,例如CAS机制,配合失败重试的方式更新操作的线程安全性。
  • 每个线程在Java堆中预先分配一小块内存即线程本地分配缓冲(TLAB),然后再给对象分配内存时,在自己的分配缓冲上进行分配,当这部分空间用完后,在分配新的“私有”内存。

13. 什么是TLAB

TLAB(线程本地分配缓冲区)是Java虚拟机中的一种内存分配优化技术,特别是在使用垃圾收集器时非常常见,TLAB的目的是减少多线程环境下对象分配时的线程同步开销,提高内存分配效率。
工作原理:

  • 在多线程环境下,多个线程可能会同时尝试在堆上分配内存,如果不采取任何措施,这些线程可能会因为竞争同一个内存分配点而陷入等待。这不仅会降低程序性能,还可能引发线程饥饿。为了解决这个问题,JVM引入了TLAB,每个线程在分配内存时,都会尝试在TLAB中分配,TLAB是一块从堆中预先分配给线程的私有内存区域。如果TLAB中有足够的空间,线程就可以直接在其中分配内存,而无需进行任何同步操作,只有当TLAB耗尽时,线程才会尝试从堆中分配更多的内存来填充TLAB,这时可能需要进行同步操作。

14. Java中的数组是存储在堆上还是栈上

在Java中,数组同样是一个对象,所以对象在内存中如何存放同样适用于数组,所以,数组的实例是保存在堆中,而数组的引用是保存在栈上的。

15. Java对象创建的过程是怎样的

  1. 当虚拟机遇到new指令,到常量池定位到这个类的符号引用。
  2. 检查符号引用代表的类是否被加载、解析、初始化过,如果没有的话,执行类加载过程。
  3. 虚拟机为对象分配内存,根据Java内存是否规整,分别通过“指针碰撞”和“空闲列表”来分配。
  4. 虚拟机将分配到的内存空间都初始化为零值。
  5. 虚拟机对对象进行必要的设置。
  6. 执行方法、成员变量进行初始化。

16. 类加载过程

Java 虚拟机类加载过程分为加载、验证、准备、解析和初始化5个阶段。类加载过程

17. 在Java中,可以作为GC Roots的对象有什么

  • 虚拟机栈中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中引用的对象。

18. JDK1.8 虚拟机内存模型变化

在JDK1.8中最大的变化是取消了永久区Perm,而是用元数据空间MetaSpace来进行替换,元空间占用的内存不是虚拟机内部的,而是本地内存空间,使用本地内存空间,类的元数据等不再受到永久代大小限制,而是和系统可用内存空间一致。

19. 频繁GC的原因

  • 频繁调用System.gc()
  • 设置的堆大小比较小,可以提高堆的空间。
  • 构建对象非常频繁,并且有很多大对象。

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

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

相关文章

室内浮毛空气净化器除臭吗?好用的室内浮毛空气净化器推荐

家里养了5只猫,满天飞的猫毛发,随风飘到各个角落,可以说苦不堪言。养了毛孩子之后,家里异味,鼻炎过敏,宠物掉毛真的是太闹心了!水杯里,床上都是小猫咪跑酷睡觉留下的毛毛&#xff0c…

公网域名流量禁用详解

公网域名流量禁用是一个涉及网络安全和流量管理的复杂操作,它通常需要根据具体的网络环境和业务需求来实施。以下是一些可能的步骤和考虑因素: 一、明确禁用目标 首先,需要明确禁用公网域名流量的具体目标。这可能包括: 阻止未…

Vue Flow: 高效构建可视化工作流的利器

💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「storm…

DeEcoStudio快捷键设置

例如 打 bgc 快捷键 直接出 .backgroundColor( ) 第一步:点击文件——>设置 第二步:找到编辑器——>实时模板 第三步:点击加号——>点击实时模板 第四步:设置快捷键 第五步:点击变更——>点击全选…

jangow靶机教程

项⽬地址 https://www.vulnhub.com/entry/jangow-101754/ 用vmware需要修改部分配置,才能通过C段扫描成功 1.在系统启动时(⻓按shift键)直到显示以下界⾯ 选择第⼆个,按回⻋ 继续选择第⼆个 2.按e进⼊编辑,进⼊以下界⾯ 删除"recove…

Linux学习笔记:Linux基础知识汇总(个人复习版)

常用命令: 1、ls -a:显示所有文件(包括隐藏文件),简洁版 -l:显示所有文件,详细版 -R:显示所有文件以及子目录下文件,简洁版 可以搭配使用。 2、netstat -i&#x…

gitlab-runner /var/run/docker.sock connect permission denied

usermod -aG docker gitlab-runner sudo service docker restart参考:https://gitlab.com/gitlab-org/gitlab-runner/-/issues/3492

LeetCode接雨水

题目描述 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。 示例 1: 输入:height [0,1,0,2,1,0,1,3,2,1,2,1] 输出:6 解释:上面是由数组 [0,1,0,2,1,0,1,…

深入SpringBoot:SpringCache的集成与使用

目录 一、SpringCache集成声明式与编程式1. 引入依赖2. SpringCache配置3. key的设置4. 使用 二、SpringCache使用1. 基于声明式注释的缓存1.1 Cacheable注解1.2 Cacheable注解属性1.2.1 CacheManager和CacheResolver1.2.2 cacheName1.2.3 key和KeyGenerator1.2.4 同步缓存 2. …

Linux 内核源码分析---处理 VFS 对象及标准函数

处理VFS对象 注册文件系统:在文件系统注册到内核时,文件系统是编译为模块,或者持久编译到内核中。 fs/super.c 中的register_filesystem用来向内核注册文件系统。我们可以通过/proc/filesystems查看系统所有的文件系统类型。 一个文件系统不…

Idea绿色下载安装教程-最新,2024版本通用-附下载链接

插件链接: 脚本 Idea下载安装完成后 进入激活码输入页面,然后关闭IDEA 按照下面流程进行激活 1. 按照以下步骤,亲测可用,记得一定要先关闭idea 2. 选择对应软件 3.选择bin、目录对应选项 5.激活 6.成功

MySQL--读写分离与分布式存储

前言:本博客仅作记录学习使用,部分图片出自网络,如有侵犯您的权益,请联系删除 一、读写分离 1、什么是读写分离 在数据库集群架构中,让主库负责处理写入操作,而从库只负责处理select查询,让两…

部署MySQL数据库时常见错误

登录数据库 时,可能会出现如图错误 1.确认是否部署MySQL服务 2.过滤MySQL端口号,查看服务是否开启(如图上半部分,则是服务未开启) 3.如图部分,则是密码错误 4.如果忘记了 mysql 的密码,或者登陆…

Apple Vision Pro 游戏开发:挑战与反思

随着Apple Vision Pro的推出,许多游戏开发者开始尝试在这个全新的平台上构建沉浸式的虚拟现实体验。然而,开发者们很快发现,在这个新兴领域中面临着不少挑战,包括支付延迟、技术支持不足、设备性能限制等问题。本文将探讨这些挑战,并提出一些开发者需要注意的关键点。 支…

Qt:自定义钟表组件

使用QWidget绘制两种钟表组件,效果如下: 源码下载链接:GitHub - DengYong1988/Clock-Widget: Qt 自定义钟表组件 https://download.csdn.net/download/ouyangxiaozi/89616407 主要代码如下: ClockWgt.h #ifndef CLOCKWGT_H #d…

spring源码阅读-推断构造方法

如何构造一个对象? 1、默认情况下,或者只有一个构造方法的情况下,就会使用默认构造方法或者唯一的一个构造方法 2、由程序员指定构造方法入参值,通过getBean()的方式获取,可以指定参数类型以及个数,但是该…

【康复学习--LeetCode每日一题】572. 另一棵树的子树

题目: 给你两棵二叉树 root 和 subRoot 。检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在,返回 true ;否则,返回 false 。 二叉树 tree 的一棵子树包括 tree 的某个节点和这个节点的所有后代节点。tree …

SpringBoot3里的文件上传

需求分析: 在用户更换头像或者添加文章时,都需要携带一个图片的URL访问地址。当用户访问文件上传接口将图片的数据上传成功后,服务器会返回一个地址。我们后台需要提供一个文件上传的接口,用来接收前端提交的文件的数据并且返回文…

C++入门基础(完整版)含下卷

C入门基础 hello 各位未来的程序员大佬们,这一篇是详细介绍入门基础,和上一篇不同的是这一篇补完了引用的知识和inline,nullptr的知识,希望大家有所收获 namespace的价值 在C/C中,变量、函数和后⾯要学到的类都是⼤…

【w门大学】云计算与大数据零基础特训班视频教程合辑

提取地址 云计算与大数据零基础特训班 课程目录