-
星空体育网站:面试时常被问及的 JVM 重点是什么?
- 时间:2024-06-22 来源:zoc7RcITctunhMtq7EzA 人气:
本文是从微信公众号「Java极客技术」转载而来,作者是名叫鸭血粉丝 Tang 的人。请先联系Java极客技术公众号,然后转载本文。在面试时,很多面试官都会问及 JVM,作为开发者,我们常常难以抓住面试官的关键要点星空体育网站。因为 JVM 的涵盖范围实在太广,从程序计数器开始,然后是堆和栈。但在面试中往往无法令人满意地回答这个问题。许多情况下,是因为我们没有系统地研究过这方面内容,导致回答时语无伦次,东拉西扯。不要担心,今天我想和大家分享一个 JVM 面试教程,希望对大家有所帮助。如果你觉得有用,请点赞、关注并收藏吧。你对Java虚拟机(JVM)有了解吗?通常来问这个问题的人,一般都已经掌握了一些基础知识。这时候需要你自己重新从头开始讲解JVM。很多人实际上只是想到了垃圾回收机制,这确实没错,但是如果你直接开始谈论垃圾回收机制,可能已经偏离了问题的本质。为什么会这样说呢?因为JVM的垃圾回收机制都是在堆内存中发生的。但是JVM的划分不仅仅局限于堆内存,这种情况下应该如何回答呢?JVM的内部结构中,最重要的部分是什么呢?**
JVM被分为两个部分
1.线程共享区域
2.线程私有区域
线程共享区域包括:堆(Heap)、方法区
线程私有区域包括:程序计数器、虚拟机栈(Stack)、本地方法栈
因为JVM,并不仅仅只有堆(Heap)的存在,其他的部分也是不可或缺的,这是为什么呢,为什么阿粉要这么说呢?有些面试官可能会询问关于JVM的类加载机制,你了解吗星空体育入口?如果你只是对垃圾回收机制有所了解,那么这个问题对你来说可能有些困难,有点难以理解,有点晕了,好像变得有点复杂了。JVM 的类加载机制是怎样的?
回答:
首先,通过类加载器(ClassLoader)将 .class字节码文件加载到内存中,即运行时数据区(Runtime Data Area)。然后,字节码文件只是 JVM 的指令集规范,不能直接交给底层操作系统执行,需要特定的命令解析器执行引擎(Execution Engine),将字节码翻译成底层系统指令,再由 CPU 执行。在这个过程中,需要调用其他语言的本地库接口(Native Interface)来实现整个程序的功能。
偏离了方向,我们继续解答上一个问题。既然你说你已经了解了并且回答了它们的内部结构是什么,那么现在就应该讲一下这些内容的用途是什么了。没错,就是这样回答星空体育官方版。程序计数器:记录线程执行位置,便于线程切换后再次执行 虚拟机栈(Stack):每个线程创建时都会有一个对应的虚拟机栈,内部保存栈帧(Stack Frame),对应Java方法的调用 本地方法栈:用于执行native方法
讲完这些后,如果没有其他事情,最好不要停顿。如果停顿了,面试官可能会继续询问关于栈的特性,尽管你本来可能想谈论垃圾回收机制,但仍要继续讲下去。方法区:多线程共享,用于存储已被虚拟机加载的类信息、常量、静态变量以及即时编译器编译后的代码等
最后,让我们讨论一下堆(Heap)堆是 JVM 中最主要的区域,因为它是 Java 虚拟机管理的最大内存块。
的唯一目标是存储对象实例和数组(JDK7已将字符串常量池和类静态变量移动到Java堆),几乎所有的对象实例都会分配在堆中存储。随着JIT编译器的发展,逃逸分析、栈上分配、标量替换等优化技术导致并非所有对象都必须在堆上分配。这个时候,一般的面试官会开始问问题了,可能会让你详细说明一下内存堆。Java Heap是Java堆的一个主要区域,由垃圾收集器进行管理。内存分为两部分,即新生代和老年代。那么新生代是什么呢?
被用于存储刚创建的实例。一般来说,新生代占据整个堆空间的三分之一。因为会频繁创建对象,所以新生代会频繁触发MinorGC来进行垃圾回收。老年代是Java虚拟机中的一个内存区域。它用于存储长时间存活的对象和已经经过多次垃圾回收仍然存活的对象。在Java虚拟机中,内存被划分为年轻代和老年代两个部分,其中年轻代用于存储新创建的对象,而老年代用于存储长时间存活的对象。老年代的对象一般通过一些特定的条件才会被回收,比如长时间停顿或内存不足的情况下。
老龄化对象的稳定性较高,因此MajorGC执行频率较低。{X} {X}接下来我们分别介绍一下JVM的新生代和老年代,这两个方面的内容足够让你和面试官进行十几分钟的讨论了。JVM的新生代(垃圾回收机制)分为Eden区、ServivorFrom区、ServivorTo区三个部分。在Eden区域是Java新对象的生成地点,如果新创建的对象占用内存较大,就会直接分配给老年代。当Eden区的内存不足时,系统会执行一次MinorGc,对新生代区进行垃圾回收。 作为上一次GC的幸存者,我将成为这一次GC的被扫描者。在JVM无法为新对象分配内存空间时(即Eden区已满),JVM将触发一次MinorGc ,将ServivorTo区
保留了一次MinorGc过程中的幸存者。那么新一代会采用何种样式的垃圾回收机制呢?我们每次创建新对象时,都会先将其放置在新生代的Eden区,也就是开始时是这样的。然后当Eden区使用完毕时会有一些待回收的对象出现。接着,我们会将存活的对象复制到Survivor1(来自)中,待回收的对象等待回收。然后清空并回收Eden区,第一次GC就完成了。往下朝着
Enden填满时将再次进行GC
的过程如下
首先是这样的
然后将Enden和Survoir1的内容复制到Survior中,
接着将Enden和Survior回收
星空体育登录入口
从Enden替换的相当于少数,而从Survior1替换的相当于移动了2次
如此便完成了两次新生代的GC,
待Enden再次使用完成时,将从Survior2复制到Survior1中,
之后是图中的连接
进行回收后,Surior1发生了变化。第一个对象是直接从Enden复制过来的,第二个对象是经过Enden -> Survior2 - > Survior1的路径,第三个对象是经过Enden -> Surivior1 - > Survior2 - > Survior1复制过来的。这样逐步执行下去就是新生代的垃圾回收。这是新一代采取的垃圾回收算法,如果你需要向面试官解释,那么你需要熟练地记住这个图。为什么这么说呢?因为只有你掌握了这个图,你才能清楚地向面试官描述复制算法。
这是新一代使用的垃圾回收算法,如果你需要向面试官解释,你就要对这个图表进行熟练记忆。为什么这么说呢?因为只有你掌握了这个图表,才能把这个复制算法清晰地解释给面试官听。既然我们已经了解了这个复制算法,那么它到底有哪些缺点呢?堆利用效率低,这一点非常明显,空间被分割成两半,每次只能使用其中一半,就需要迁移。递归调用在复制子对象时采用了递归方法,可能导致栈溢出。不过我们也要夸夸复制算法的牛逼之处。
的吞吐量很高,所谓吞吐量就是指搜索活动对象所花费的时间与搜索堆栈的时间之比,比值越高表示有效搜索占比越大。很明显,我们都是从根开始搜索,搜索的对象都是活动对象,并没有浪费时间搜索无用的对象。在更大规模的情况下,这个优势变得更加明显。当活动对象被复制到To空间时,它们都是连续的,没有任何碎片。而在清空From空间时,也会完全清空,不会留下任何碎片。这是新一代使用的垃圾回收算法。JVM 中的老年代(垃圾回收机制)
与新生代不同,老年代的垃圾回收机制采用另一种方式,有些人称之为FullGC。FullGC的出现原因是:在新生代中如果存在某些对象或新创建的对象因某些原因需要移动到老年代,但由于老年代中根本没有足够的内存空间来容纳这些对象,就会触发一次FullGC。如果在执行完FullGC后仍然无法为这些对象分配内存,就会抛出异常,异常类型为OutOfMemoryError。FullGC和MinorGC使用不同的算法。FullGC使用标记清除算法,这个名字听起来很容易理解,下面可以看一张图来解释一下。深入瞭解JVM一書中的圖示呈現如下,
這樣看來圖示相當清晰,先做標記,再進行刪除。在进行标记过程时,需找到所有可访问的对象,并对其进行特定标记。清理(Sweep)过程:遍历堆内存,回收未标记的对象。在理解了这一点之后,我们还需讨论一个概念,即GC Root。Root类似一个根节点,就像{X}{N}{X}{X}{X}{N}{X}图中的a、b、c、d,它们是活跃对象。如果存在引用,比如b引用a,那么a就是一个活跃对象。当我们的老年代内存区中有效的内存空间不足时,整个世界就需要停止(stop the world),然后开始准备进行垃圾回收。
标记:对所有的GC Roots进行遍历,然后将所有GC Roots可到达的对象标记为存活对象。图中所示的是a、b、c、d这几个标记吗?清理:进行清理的操作会检查堆中的所有对象,并清除所有没有标记的对象。换句话说,如果内存不足,GC线程会启动并暂停程序,然后标记尚存活的对象,最后清除未标记的对象,然后程序继续运行。,这个流程图的初始老年代中的对象状态是
。在这个阶段,所有对象都是未标记的。然后内存不足,垃圾回收线程停止,开始标记过程。从根节点开始遍历,标记的abcdeh都是存活的对象,然后继续标记。下一步就是清理数据了。清除完成之后,需要去除标记,以便下次继续进行标记清除。其实老读者肯定看过,因为很早以前我就画过这个图。完成标记清除之后,垃圾回收就可以完成了。在这种情况下,不夸大也不贬低,肯定会存在优点和缺点,否则为什么不选择其他方法呢?毕竟,JVM 肯定会选择最适合自己的方式来执行垃圾收集。堆内存在清除缺点后会产生空间不连续的内存碎片化问题星空体育网页版。如果下一次需要分配对象的内存大于碎片空间,就会提前触发GC。当提前触发的GC回收后,如果空间仍然不足,就会出现OOM等错误。时间问题:由于需要分为标记和清除两个步骤,当堆内有大量可回收对象时,该算法需要大量的时间来进行标记和清除。因此随着可回收对象的增加,标记和清除的效率就会下降。另外,由于空间不连续的情况,每次再次分配都需要遍历空闲列表,导致效率降低。做
稍微
有一些挑战,与保守型的GC算法兼容,这方面真的无法说出其他优点了,除了能够解决引用计数算法无法清除循环引用的问题之外,其他优点实在不明显。关于JVM的重要知识要点,你学会了吗?
星空体育手机版
星空体育官方版
星空体育官方版