JVM 是小白 Java 程序员发展路上的一道坎也是很多工作2到3年程序员的一个重点面试问题之一,学习JVM之前,只需要知道代码编译成 Class,捏造机加载 Class 运行就可以了,学习 JVM 之后,可以深入理解代码从编译到加载的过程、内存中对象的创建与垃圾接纳、日常开辟中快速定位性能问题等,固然也是面试不可缺少的加分项。 在这我整理20道精选的JVM面试题分享给各人,希望能帮助到各人同时可以给此文点个喜好,后续会更新Java面试常问的一些问题,有爱好的朋友可以连续关注下我,多谢支持!
面试问题如下(文末有我的一些JVM学习笔记):
1.Java 类加载过程?
答:Java 类加载需要经历一下 7 个过程:
1. 加载
加载是类加载的第一个过程,在这个阶段,将完成一下三件事情:
- 通过一个类的全限定名获取该类的二进制流。
- 将该二进制流中的静态存储结构转化为方法去运行时数据结构。
- 在内存中天生该类的 Class 对象,作为该类的数据访问入口。
2. 验证
验证的目标是为了确保 Class 文件的字节流中的信息不回危害到捏造机.在该阶段重要完成以下四钟验证:
- 文件格式验证:验证字节流是否符合 Class 文件的规范,如主次版本号是否在当前捏造机范围内,常量池中的常量是否有不被支持的范例.
- 元数据验证:对字节码描述的信息举行语义分析,如这个类是否有父类,是否集成了不被继承的类等。
- 字节码验证:是整个验证过程中最复杂的一个阶段,通过验证数据流和控制流的分析,确定程序语义是否正确,重要针对方法体的验证。如:方法中的范例转换是否正确,跳转指令是否正确等。
- 符号引用验证:这个动作在反面的剖析过程中发生,重要是为了确保剖析动作能正确实行。
3. 准备
准备阶段是为类的静态变量分配内存并将其初始化为默认值,这些内存都将在方法区中举行分配。准备阶段不分配类中的实例变量的内存,实例变量将会在对象实例化时随着对象一起分配在 Java 堆中。
public static int value =123;//在准备阶段value初始值为0 。在初始化阶段才会变为123 。
4. 剖析
该阶段重要完成符号引用到直接引用的转换动作。剖析动作并不一定在初始化动作完成之前,也有大概在初始化之后。
5. 初始化
初始化时类加载的末了一步,前面的类加载过程,除了在加载阶段用户应用程序可以通过自界说类加载器加入之外,别的动作完全由捏造机主导和控制。到了初始化阶段,才真正开始实行类中界说的 Java 程序代码。
6. 利用
7. 卸载
2.描述一下 JVM 加载 Class 文件的原理机制?
答:Java 语言是一种具有动态性的解释型语言,类(Class)只有被加载到 JVM 后才能运行。当运行指定程序时,JVM 会将编译天生的 .class 文件按照需求和一定的规则加载到内存中,并组织成为一个完整的 Java 应用程序。这个加载过程是由类加载器完成,详细来说,就是由 ClassLoader 和它的子类来实现的。类加载器本身也是一个类,其实质是把类文件从硬盘读取到内存中。
类的加载方式分为隐式加载和显示加载。隐式加载指的是程序在利用 new 等方式创建对象时,会隐式地调用类的加载器把对应的类加载到 JVM 中。显示加载指的是通过直接调用 class.forName() 方法来把所需的类加载到 JVM 中。
任何一个工程项目都是由许多类组成的,当程序启动时,只把需要的类加载到 JVM 中,其他类只有被利用到的时候才会被加载,采用这种方法一方面可以加快加载速率,另一方面可以节约程序运行时对内存的开销。此外,在 Java 语言中,每个类或接口都对应一个 .class 文件,这些文件可以被当作是一个个可以被动态加载的单元,因此当只有部分类被修改时,只需要重新编译变革的类即可,而不需要重新编译所有文件,因此加快了编译速率。
在 Java 语言中,类的加载是动态的,它并不会一次性将所有类全部加载后再运行,而是包管程序运行的底子类(比方基类)完全加载到 JVM 中,至于其他类,则在需要的时候才加载。
类加载的重要步调:
- 装载,根据查找路径找到相应的 class 文件,然后导入。
- 链接,链接又可分为 3 个小步:
- 检查,检查待加载的 class 文件的正确性。
- 准备,给类中的静态变量分配存储空间。
- 剖析,将符号引用转换为直接引用(这一步可选)
- 初始化。对静态变量和静态代码块实行初始化工作。
3. Java 内存分配。
- 寄存器: 我们无法控制。
- 静态域 :static界说的静态成员。
- 常量池: 编译时被确定并保存在 .class 文件中的(final)常量值和一些文本修饰的符号引用(类和接口的全限定名,字段的名称和描述符,方法和名称和描述符)。
- 非 RAM 存储: 硬盘等永世存储空间。
- 堆内存 :new 创建的对象和数组,由 Java 捏造机自动垃圾接纳器管理,存取速率慢。
- 栈内存: 基本范例的变量和对象的引用变量(堆内存空间的访问地址),速率快,可以共享,但是巨细与生存期必须确定,缺乏灵活性。
4.Java 堆的结构是什么样子的?什么是堆中的永世代(Perm Gen space)?
答:JVM 的堆是运行时数据区,所有类的实例和数组都是在堆上分配内存。它在 JVM 启动的时候被创建。对象所占的堆内存是由自动内存管理系统也就是垃圾网络器接纳。
堆内存是由存活和死亡的对象组成的。存活的对象是应用可以访问的,不会被垃圾接纳。死亡的对象是应用不可访问尚且还没有被垃圾网络器接纳掉的对象。一直到垃圾网络器把这些 对象接纳掉之前,他们会一直占据堆内存空间。
5.GC 是什么? 为什么要有 GC?
答:GC 是垃圾网络的意思(GabageCollection),内存处理处罚是编程人员容易出现问题的地方,忘记或者错误的内存接纳会导致程序或系统的不稳定甚至崩溃,Java 提供的 GC 功能可以自动监测对象是否凌驾作用域从而到达自动接纳内存的目标,Java 语言没有提供开释已分配内存的显示操纵方法。
6.简述 Java 垃圾接纳机制?
答:在 Java 中,程序员是不需要显示的去开释一个对象的内存的,而是由捏造机自行实行。在 JVM 中,有一个垃圾接纳线程,它是低优先级的,在正常情况下是不会实行的,只有在捏造机空闲或者当前堆内存不敷时,才会触发实行,扫面那些没有被任何引用的对象,并将它们添加到要接纳的聚集中,举行接纳。
7. 怎样判定一个对象是否存活?(或者 GC 对象的判定方法)
答:判定一个对象是否存活有两种方法:
1. 引用计数法
所谓引用计数法就是给每一个对象设置一个引用计数器,每当有一个地方引用这个对象时,就将计数器加一,引用失效时,计数器就减一。当一个对象的引用计数器为零时,阐明此对象没有被引用,也就是“死对象”,将会被垃圾接纳.
引用计数法有一个缺陷就是无法解决循环引用问题,也就是说当对象 A 引用对象 B,对象 B 又引用者对象 A,那么此时 A、B 对象的引用计数器都不为零,也就造成无法完成垃圾接纳,所以主流的捏造机都没有采用这种算法。
2. 可达性算法(引用链法)
该算法的头脑是:从一个被称为 GC Roots 的对象开始向下搜索,如果一个对象到 GC Roots 没有任何引用链相连时,则阐明此对象不可用。
在 Java 中可以作为 GC Roots 的对象有以下几种:
- 捏造机栈中引用的对象
- 方法区类静态属性引用的对象
- 方法区常量池引用的对象
- 当地方法栈JNI引用的对象
固然这些算法可以判定一个对象是否能被接纳,但是当满足上述条件时,一个对象比不一定会被接纳。当一个对象不可达 GC Root 时,这个对象并不会立马被接纳,而是出于一个死缓的阶段,若要被真正的接纳需要经历两次标志.
如果对象在可达性分析中没有与 GC Root 的引用链,那么此时就会被第一次标志并且举行一次筛选,筛选的条件是是否有必要实行 finalize() 方法。当对象没有覆盖 finalize() 方法或者已被捏造机调用过,那么就认为是没必要的。 如果该对象有必要实行 finalize() 方法,那么这个对象将会放在一个称为 F-Queue 的对队列中,捏造时机触发一个 Finalize() 线程去实行,此线程是低优先级的,并且捏造机不会答应一直期待它运行完,这是因为如果 finalize() 实行迟钝或者发生了死锁,那么就会造成 F-Queue 队列一直期待,造成了内存接纳系统的崩溃。GC 对处于 F-Queue 中的对象举行第二次被标志,这时,该对象将被移除” 即将接纳” 聚集,期待接纳。
8.垃圾接纳的优点和原理。并考虑 2 种接纳机制?
答:Java 语言中一个显著的特点就是引入了垃圾接纳机制,使 C++ 程序员最头疼的内存管理的问题迎刃而解,它使得 Java 程序员在编写程序的时候不再需要考虑内存管理。由于有个垃圾接纳机制,Java 中的对象不再有“作用域”的概念,只有对象的引用才有"作用域"。垃圾接纳可以有效的防止内存泄漏,有效的利用可以利用的内存。垃圾接纳器通常是作为一个单独的低级别的线程运行,不可预知的情况下对内存堆中已经死亡的或者长时间没有利用的对象举行清楚和接纳,程序员不能及时的调用垃圾接纳器对某个对象或所有对象举行垃圾接纳。
接纳机制有分代复制垃圾接纳和标志垃圾接纳,增量垃圾接纳。
9.什么是分布式垃圾接纳(DGC)?它是怎样工作的?
答:DGC 叫做分布式垃圾接纳。RMI 利用 DGC 来做自动垃圾接纳。因为 RMI 包含了跨捏造机的远程对象的引用,垃圾接纳是很困难的。DGC 利用引用计数算法来给远程对象提供自动内存管理。
10.在 Java 中,对象什么时候可以被垃圾接纳?
答:当对象对当前利用这个对象的应用程序变得不可触及的时候,这个对象就可以被接纳了。
11.简述 Java 内存分配与接纳策率以及 Minor GC 和 Major GC。
- 对象优先在堆的 Eden 区分配
- 大对象直接进入老年代
- 恒久存活的对象将直接进入老年代
当 Eden 区没有充足的空间举行分配时,捏造时机实行一次 Minor GC。Minor GC 通常发生在新生代的 Eden 区,在这个区的对象生存期短,通常发生 Gc 的频率较高,接纳速率比力快;Full GC/Major GC 发生在老年代,一般情况下,触发老年代 GC 的时候不会触发 Minor GC,但是通过配置,可以在 Full GC 之前举行一次 Minor GC 这样可以加快老年代的接纳速率。
12.串行(serial)网络器和吞吐量(throughput)网络器的区别是什么?
答:吞吐量网络器利用并行版本的新生代垃圾网络器,它用于中等规模和大规模数据的应用程序。 而串行网络器对大多数的小应用(在现代处理处罚器上需要大概 100M 左右的内存)就充足了。
13.Java 中垃圾网络的方法有哪些?
可以参考之前我发过的一篇文章 JVM系列(四):浅谈常用四种垃圾接纳算法总结
14.什么是类加载器,类加载器有哪些?
答:实现通过类的权限定名获取该类的二进制字节流的代码块叫做类加载器。
重要有一下四种类加载器:
- 启动类加载器(Bootstrap ClassLoader)用来加载 Java 焦点类库,无法被 Java 程序直接引用。
- 扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 捏造机的实现会提供一个扩展库目次。该类加载器在此目次里面查找并加载 Java 类。
- 系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader() 来获取它。
- 用户自界说类加载器,通过继承 java.lang.ClassLoader 类的方式实现。
15.类加载器双亲委派模型机制?
可以参考之前我发过的一篇文章 JVM系列(九):Java类加载机制之双亲委派模型
16.垃圾接纳器的基本原理是什么?垃圾接纳器可以立刻接纳内存吗?有什么办法主动通知捏造机举行垃圾接纳?
答:对于 GC 来说,当程序员创建对象时,GC 就开始监控这个对象的地址、巨细以及利用情况。通常,GC 采用有向图的方式记录和管理堆(heap)中的所有对象。通过这种方式确定哪些对象是”可达的”,哪些对象是”不可达的”。当 GC 确定一些对象为“不可达”时,GC 就有责任接纳这些内存空间。可以。程序员可以手动实行 System.gc(),通知 GC 运行,但是 Java 语言规范并不包管 GC 一定会实行。
17.System.gc() 和 Runtime.gc() 会做什么事情?
答:这两个方法用来提示 JVM 要举行垃圾接纳。但是,立即开始还是耽误举行垃圾接纳是取决于 JVM 的。
18.finalize() 方法什么时候被调用?析构函数 (finalization) 的目标是什么?
答:垃圾接纳器(garbage colector)决定接纳某对象时,就会运行该对象的 finalize() 方法 但是在 Java 中很不幸,如果内存总是充足的,那么垃圾接纳大概永远不会举行,也就是说 filalize() 大概永远不被实行,显然指望它做收尾工作是靠不住的。 那么 finalize() 究竟是做什么的呢? 它最重要的用途是接纳特殊渠道申请的内存。Java 程序有垃圾接纳器,所以一般情况下内存问题不用程序员费心。但有一种 JNI(Java Native Interface)调用 non-Java 程序(C 或 C++), finalize() 的工作就是接纳这部分的内存。
19.Java 中会存在内存泄漏吗,请简单描述。
此题我反面会单独写篇文章,有爱好的朋友可以关注下和点个喜好,谢谢支持!
20.怎样判定对象是否已死
可以参考之前我发过的一篇文章 JVM系列(三):JVM的判定对象是否已死分析总结!
JVM与性能优化学习导图笔记:
(此Xmind图是平时学习时做的一些笔记和重点缩略未睁开)
知识笔记导图
末了
我自己网络了一些Java资料,里面就包涵了一些BAT面试资料,以及一些 Java 高并发、分布式、微服务、高性能、源码分析、JVM等技术资料
点赞支持一下吧。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |