面试题-JVM 初级面试题(40道含答案)_jvm初级面试题,Golang开发全套学习

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Golang全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注go)
img

正文

6、运行时栈帧包含哪些结构?

局部变量表

操作数栈

动态连接

返回地址

附加信息

7、JVM 的内存模型是什么?

JVM 试图定义一种统一的内存模型,能将各种底层硬件以及操作系统的内存访问差异进行封装,使 Java 程序在不同硬件以及操作系统上都能达到相同的并发效果。它分为工作内存和主内存,线程无法对主存储器直接进行操作,如果一个线程要和另外一个线程通信,那么只能通过主存进行交换。

8、JVM 如何确定垃圾对象?

JVM 采用的是可达性分析算法,通过 GC Roots 来判定对象是否存活,从 GC Roots 向下追溯、搜索,会产生 Reference Chain。当一个对象不能和任何一个 GC Root 产生关系时,就判定为垃圾。

软引用和弱引用,也会影响对象的回收。内存不足时会回收软引用对象;GC 时会回收弱引用对象。

9、哪些是 GC Roots?

在虚拟机栈(栈帧中的本地变量表)中引用的对象,譬如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等。

在方法区中类静态属性引用的对象,譬如Java类的引用类型静态变量。

在方法区中常量引用的对象,譬如字符串常量池(String Table)里的引用。

在本地方法栈中JNI(即通常所说的Native方法)引用的对象。

Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象(比如 NullPointExcepiton、OutOfMemoryError)等,还有系统类加载器。

所有被同步锁(synchronized关键字)持有的对象。

反映 Java 虚拟机内部情况的 JMXBean、JVMTI中注册的回调、本地代码缓存等。

10、被引用的对象就一定能存活吗?

不一定,看 Reference 类型,弱引用在 GC 时会被回收,软引用在内存不足的时候,即 OOM 前会被回收,但如果没有在 Reference Chain 中的对象就一定会被回收。

11、强引用、软引用、弱引用、虚引用是什么,有什么区别?

强引用,就是普通的对象引用关系,如 String s = new String(“ConstXiong”)

软引用,用于维护一些可有可无的对象。只有在内存不足时,系统则会回收软引用对象,如果回收了软引用对象之后仍然没有足够的内存,才会抛出内存溢出异常。SoftReference 实现

弱引用,相比软引用来说,要更加无用一些,它拥有更短的生命周期,当 JVM 进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。WeakReference 实现

虚引用是一种形同虚设的引用,在现实场景中用的不是很多,它主要用来跟踪对象被垃圾回收的活动。PhantomReference 实现

12、你做过 JVM 调优,说说如何查看 JVM 参数默认值?

jps -v 可以查看 jvm 进程显示指定的参数

使用 -XX:+PrintFlagsFinal 可以看到 JVM 所有参数的值

jinfo 可以实时查看和调整虚拟机各项参数

13、工作中常用的 JVM 配置参数有哪些?

Java 8 为例

日志
-XX:+PrintFlagsFinal,打印JVM所有参数的值
-XX:+PrintGC,打印GC信息
-XX:+PrintGCDetails,打印GC详细信息
-XX:+PrintGCTimeStamps,打印GC的时间戳
-Xloggc:filename,设置GC log文件的位置
-XX:+PrintTenuringDistribution,查看熬过收集后剩余对象的年龄分布信息

内存设置
-Xms,设置堆的初始化内存大小
-Xmx,设置堆的最大内存
-Xmn,设置新生代内存大小
-Xss,设置线程栈大小
-XX:NewRatio,新生代与老年代比值
-XX:SurvivorRatio,新生代中Eden区与两个Survivor区的比值,默认为8,即Eden:Survivor:Survivor=8:1:1
-XX:MaxTenuringThreshold,从年轻代到老年代,最大晋升年龄。CMS 下默认为 6,G1 下默认为 15
-XX:MetaspaceSize,设置元空间的大小,第一次超过将触发 GC
-XX:MaxMetaspaceSize,元空间最大值
-XX:MaxDirectMemorySize,用于设置直接内存的最大值,限制通过 DirectByteBuffer 申请的内存
-XX:ReservedCodeCacheSize,用于设置 JIT 编译后的代码存放区大小,如果观察到这个值有限制,可以适当调大,一般够用即可

设置垃圾收集相关
-XX:+UseSerialGC,设置串行收集器
-XX:+UseParallelGC,设置并行收集器
-XX:+UseConcMarkSweepGC,使用CMS收集器
-XX:ParallelGCThreads,设置Parallel GC的线程数
-XX:MaxGCPauseMillis,GC最大暂停时间 ms
-XX:+UseG1GC,使用G1垃圾收集器

CMS 垃圾回收器相关
-XX:+UseCMSInitiatingOccupancyOnly
-XX:CMSInitiatingOccupancyFraction,与前者配合使用,指定MajorGC的发生时机
-XX:+ExplicitGCInvokesConcurrent,代码调用 System.gc() 开始并行 FullGC,建议加上这个参数
-XX:+CMSScavengeBeforeRemark,表示开启或关闭在 CMS 重新标记阶段之前的清除(YGC)尝试,它可以降低 remark 时间,建议加上
-XX:+ParallelRefProcEnabled,可以用来并行处理 Reference,以加快处理速度,缩短耗时

G1 垃圾回收器相关
-XX:MaxGCPauseMillis,用于设置目标停顿时间,G1 会尽力达成
-XX:G1HeapRegionSize,用于设置小堆区大小,建议保持默认
-XX:InitiatingHeapOccupancyPercent,表示当整个堆内存使用达到一定比例(默认是 45%),并发标记阶段就会被启动
-XX:ConcGCThreads,表示并发垃圾收集器使用的线程数量,默认值随 JVM 运行的平台不同而变动,不建议修改

建议面试时最好能记住 CMS 和 G1的参数,特点突出使用较多,被问的概率大

14、谈谈对 OOM 的认识

除了程序计数器,其他内存区域都有 OOM 的风险。

栈一般经常会发生 StackOverflowError,比如 32 位的 windows 系统单进程限制 2G 内存,无限创建线程就会发生栈的 OOM

Java 8 常量池移到堆中,溢出会出 java.lang.OutOfMemoryError: Java heap space,设置最大元空间大小参数无效

堆内存溢出,报错同上,这种比较好理解,GC 之后无法在堆中申请内存创建对象就会报错

方法区 OOM,经常会遇到的是动态生成大量的类、jsp 等

直接内存 OOM,涉及到 -XX:MaxDirectMemorySize 参数和 Unsafe 对象对内存的申请

15、什么情况发生栈溢出?

-Xss可以设置线程栈的大小,当线程方法递归调用层次太深或者栈帧中的局部变量过多时,会出现栈溢出错误 java.lang.StackOverflowError

16、你有哪些手段来排查 OOM 的问题?

增加两个参数 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof,当 OOM 发生时自动 dump 堆内存信息到指定目录

同时 jstat 查看监控 JVM 的内存和 GC 情况,先观察问题大概出在什么区域

使用 MAT 工具载入到 dump 文件,分析大对象的占用情况,比如 HashMap 做缓存未清理,时间长了就会内存溢出,可以把改为弱引用

17、遇到过元空间溢出吗?

元空间在本地内存上,默认是没有上限的,不加限制出了问题会影响整个服务器的,所以也是比较危险的。-XX:MaxMetaspaceSize 可以指定最大值。

一般使用动态代理的框架会生成很多 Java 类,如果占用空间超出了我们的设定最大值,会发生元空间溢出。

18、遇到过堆外内存溢出吗?

Unsafe 类申请内存、JNI 对内存进行操作、Netty 调用操作系统的 malloc 函数的直接内存,这些内存是不受 JVM 控制的,不加限制的使用,很容易发生溢出。这种情况有个显著特点,dump 的堆文件信息正常甚至很小。
-XX:MaxDirectMemorySize 可以指定最大直接内存,但限制不住所有堆外内存的使用。

19、谈谈你知道的垃圾回收算法

判断对象是否可回收的算法有两种:

Reference Counting GC,引用计数算法

Tracing GC,可达性分析算法

JVM 各厂商基本都是用的 Tracing GC 实现

大部分垃圾收集器遵从了分代收集(Generational Collection)理论。
针对新生代与老年代回收垃圾内存的特点,提出了 3 种不同的算法:

1、标记-清除算法(Mark-Sweep)
标记需回收对象,统一回收;或标记存活对象,回收未标记对象。
缺点:

大量对象需要标记与清除时,效率不高

标记、清除产生的大量不连续内存碎片,导致无法分配大对象

2、标记-复制算法(Mark-Copy)
可用内存等分两块,使用其中一块 A,用完将存活的对象复制到另外一块 B,一次性清空 A,然后改分配新对象到 B,如此循环。
缺点:

不适合大量对象不可回收的情况,换句话说就是仅适合大量对象可回收,少量对象需复制的区域

只能使用内存容量的一半,浪费较多内存空间

3、标记-整理算法(Mark-Compact)
标记存活的对象,统一移到内存区域的一边,清空占用内存边界以外的内存。
缺点:

移动大量存活对象并更新引用,需暂停程序运行

20、谈谈你知道的垃圾收集器

Serial
特点:

JDK 1.3 开始提供

新生代收集器

无线程交互开销,单线程收集效率最高

进行垃圾收集时需要暂停用户线程

适用于客户端,小内存堆的回收

ParNew
特点:

是 Serial 收集器的多线程并行版

JDK 7 之前首选的新生代收集器

第一款支持并发的收集器,首次实现垃圾收集线程与用户线程基本上同时工作

除 Serial 外,只有它能与 CMS 配合

Parallel Scavenge
特点:

新生代收集器

标记-复制算法

多线程并行收集器

追求高吞吐量,即最小的垃圾收集时间

可以配置最大停顿时间、垃圾收集时间占比

支持开启垃圾收集自适应调节策略,追求适合的停顿时间或最大的吞吐量

Serial Old
特点:

与 Serial 类似,是 Serial 收集器的老年代版本

使用标记-整理算法

Parallel Old
特点:

JDK 6 开始提供

Parallel Scavenge 的老年代版

支持多线程并发收集

标记-整理算法

Parallel Scavenge + Parallel Old 是一个追求高吞吐量的组合

CMS
特点:

标记-清除算法

追求最短回收停顿时间

多应用于关注响应时间的 B/S 架构的服务端

并发收集、低停顿

占用一部分线程资源,应用程序变慢,吞吐量下降

无法处理浮动垃圾,可能导致 Full GC

内存碎片化问题

G1
特点:

JDK 6 开始实验,JDK 7 商用

面向服务端,JDK 9 取代 Parallel Scavenge + Parallel Old

结合标记-整理、标记-复制算法

首创局部内存回收设计思路

基于 Region 内存布局,采用不同策略实现分代

不再使用固定大小、固定数量的堆内存分代区域划分

优先回收价收益最大的 Region

单个或多个 Humongous 区域存放大对象

使用记忆集解决跨 Region 引用问题

复杂的卡表实现,导致更高的内存占用,堆的 10%~20%

全功能垃圾收集器

追求有限的时间内最高收集效率、延迟可控的情况下最高吞吐量

追求应付内存分配速率,而非一次性清掉所有垃圾内存

适用于大内存堆

Shenandoah
特点:

追求低延迟,停顿 10 毫秒以内

OpenJDK 12 新特性,RedHat 提供

连接矩阵代替记忆集,降低内存使用与伪共享问题出现概率

ZGC
特点:

JDK 11 新加的实验性质的收集器

追求低延迟,停顿 10 毫秒以内

基于 Region 内存布局

未设分代

读屏障、染色指针、内存多重映射实现可并发的标记-整理算法

染色指针和内存多重映射设计精巧,解决部分性能问题,但降低了可用最大内存、操作系统受限、只支持 32 位、不支持压缩指针等

成绩亮眼、性能彪悍

21、生产环境用的什么JDK?如何配置的垃圾收集器?

Oracle JDK 1.8

JDK 1.8 中有 Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old、CMS、G1,默认使用 Parallel Scavenge + Parallel Old。

Serial 系列是单线程垃圾收集器,处理效率很高,适合小内存、客户端场景使用,使用参数 -XX:+UseSerialGC 显式启用。

Parallel 系列相当于并发版的 Serial,追求高吞吐量,适用于较大内存并且有多核CPU的环境,默认或显式使用参数 -XX:+UseParallelGC 启用。可以使用 -XX:MaxGCPauseMillis 参数指定最大垃圾收集暂停毫秒数,收集器会尽量达到目标;使用 -XX:GCTimeRatio 指定期望吞吐量大小,默认 99,用户代码运行时间:垃圾收集时间=99:1。

CMS,追求垃圾收集暂停时间尽可能短,适用于服务端较大内存且多 CPU 的应用,使用参数 -XX:+UseConcMarkSweepGC 显式开启,会同时作用年轻代与老年代,但有浮动垃圾和内存碎片化的问题。

G1,主要面向服务端应用的垃圾收集器,适用于具有大内存的多核 CPU 的服务器,追求较小的垃圾收集暂停时间和较高的吞吐量。首创局部内存回收设计思路,采用不同策略实现分代,不再使用固定大小、固定数量的堆内存分代区域划分,而是基于 Region 内存布局,优先回收价收益最大的 Region。使用参数 -XX:+UseG1GC 开启。

我们生产环境使用了 G1 收集器,相关配置如下
-Xmx12g

-Xms12g

-XX:+UseG1GC

-XX:InitiatingHeapOccupancyPercent=45

-XX:MaxGCPauseMillis=200

-XX:MetaspaceSize=256m

-XX:MaxMetaspaceSize=256m

-XX:MaxDirectMemorySize=512m

-XX:G1HeapRegionSize 未指定

核心思路:

每个内存区域设置上限,避免溢出

堆设置为操作系统的 70%左右,超过 8 G,首选 G1

根据老年代对象提升速度,调整新生代与老年代之间的内存比例

等过 GC 信息,针对项目敏感指标优化,比如访问延迟、吞吐量等

22、如何查看 JVM 当前使用的是什么垃圾收集器?

-XX:+PrintCommandLineFlags 参数可以打印出所选垃圾收集器和堆空间大小等设置

如果开启了 GC 日志详细信息,里面也会包含各代使用的垃圾收集器的简称

23、如何开启和查看 GC 日志?

常见的 GC 日志开启参数包括:
-Xloggc:filename,指定日志文件路径
-XX:+PrintGC,打印 GC 基本信息
-XX:+PrintGCDetails,打印 GC 详细信息
-XX:+PrintGCTimeStamps,打印 GC 时间戳
-XX:+PrintGCDateStamps,打印 GC 日期与时间
-XX:+PrintHeapAtGC,打印 GC 前后的堆、方法区、元空间可用容量变化
-XX:+PrintTenuringDistribution,打印熬过收集后剩余对象的年龄分布信息,有助于 MaxTenuringThreshold 参数调优设置
-XX:+PrintAdaptiveSizePolicy,打印收集器自动设置堆空间各分代区域大小、收集目标等自动调节的相关信息
-XX:+PrintGCApplicationConcurrentTime,打印 GC 过程中用户线程并发时间
-XX:+PrintGCApplicationStoppedTime,打印 GC 过程中用户线程停顿时间
-XX:+HeapDumpOnOutOfMemoryError,堆 oom 时自动 dump
-XX:HeapDumpPath,堆 oom 时 dump 文件路径

Java 9 JVM 日志模块进行了重构,参数格式发生变化,这个需要知道。

GC 日志输出的格式,会随着上面的参数不同而发生变化。关注各个分代的内存使用情况、垃圾回收次数、垃圾回收的原因、垃圾回收占用的时间、吞吐量、用户线程停顿时间。

借助工具可视化工具可以更方便的分析,在线工具 GCeasy;离线版可以使用 GCViewer。

如果现场环境不允许,可以使用 JDK 自带的 jstat 工具监控观察 GC 情况。

24、JVM 监控与分析工具你用过哪些?介绍一下。

jps,显示系统所有虚拟机进程信息的命令行工具
jstat,监视分析虚拟机运行状态的命令行工具
jinfo,查看和调整虚拟机参数的命令行工具
jmap,生成虚拟机堆内存转储快照的命令行工具
jhat,显示和分析虚拟机的转储快照文件的命令行工具
jstack,生成虚拟机的线程快照的命令行工具
jcmd,虚拟机诊断工具,JDK 7 提供
jhsdb,基于服务性代理实现的进程外可视化调试工具,JDK 9 提供
JConsole,基于JMX的可视化监视和管理工具
jvisualvm,图形化虚拟机使用情况的分析工具
Java Mission Control,监控和管理 Java 应用程序的工具

MAT,Memory Analyzer Tool,虚拟机内存分析工具
vjtools,唯品会的包含核心类库与问题分析工具
arthas,阿里开源的 Java 诊断工具
greys,JVM进程执行过程中的异常诊断工具
GCHisto,GC 分析工具
GCViewer,GC 日志文件分析工具
GCeasy,在线版 GC 日志文件分析工具
JProfiler,检查、监控、追踪 Java 性能的工具
BTrace,基于动态字节码修改技术(Hotswap)实现的Java程序追踪与分析工具

下面可以重点体验下:
JDK 自带的命令行工具方便快捷,不是特别复杂的问题可以快速定位;

阿里的 arthas 命令行也不错;

可视化工具 MAT、JProfiler 比较强大。

25、JIT 是什么?

Just In Time Compiler 的简称,即时编译器。为了提高热点代码的执行效率,在运行时,虚拟机将会把这些代码编译成与本地平台相关的机器码,并进行各种层次的优化,完成这个任务的编译器就是 JIT。

26、谈谈双亲委派模型

Parents Delegation Model,这里的 Parents 翻译成双亲有点不妥,类加载向上传递的过程中只有单亲;parents 更多的是多级向上的意思。

除了顶层的启动类加载器,其他的类加载器在加载之前,都会委派给它的父加载器进行加载,一层层向上传递,直到所有父类加载器都无法加载,自己才会加载该类。

双亲委派模型,更好地解决了各个类加载器协作时基础类的一致性问题,避免类的重复加载;防止核心API库被随意篡改。

JDK 9 之前

启动类加载器(Bootstrp ClassLoader),加载 /lib/rt.jar、-Xbootclasspath

扩展类加载器(Extension ClassLoader)sun.misc.Launcher$ExtClassLoader,加载 /lib/ext、java.ext.dirs

应用程序类加载器(Application ClassLoader,sun.misc.Launcher$AppClassLoader),加载 CLASSPTH、-classpath、-cp、Manifest

自定义类加载器

JDK 9 开始 Extension ClassLoader 被 Platform ClassLoader 取代,启动类加载器、平台类加载器、应用程序类加载器全都继承于 jdk.internal.loader.BuiltinClassLoader

类加载代码逻辑
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { // 首先,检查请求的类是否已经被加载过了 Class c = findLoadedClass(name); if (c == null) { try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // 如果父类加载器抛出ClassNotFoundException // 说明父类加载器无法完成加载请求 } if (c == null) { // 在父类加载器无法加载时 // 再调用本身的findClass方法来进行类加载 c = findClass(name); } } if (resolve) { resolveClass©; } return c;}
1.

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Go)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
的类是否已经被加载过了 Class c = findLoadedClass(name); if (c == null) { try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // 如果父类加载器抛出ClassNotFoundException // 说明父类加载器无法完成加载请求 } if (c == null) { // 在父类加载器无法加载时 // 再调用本身的findClass方法来进行类加载 c = findClass(name); } } if (resolve) { resolveClass©; } return c;}
1.

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Go)
[外链图片转存中…(img-G6jcBf8o-1713621516328)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值