当前位置: 首页 > 后端技术 > Java

为什么这11道JVM面试题这么重要(附答案)

时间:2023-04-01 21:47:22 Java

本文内容整理自博雪谷野架构师运行时数据区包含哪些内容虚拟机基础面试题程序计数器Java虚拟机栈Native方法栈Java堆方法区程序计数器程序计数器是线程私有的,是JVM中唯一不会溢出的区域。用于保存线程切换时的执行行数。程序计数器(ProgramCounterRegister)是一块很小的内存空间,可以看作是当前线程正在执行的字节码的行号指示器。分支、循环、跳转、异常处理、线程回收等基本功能都需要依赖这个计数器来完成。因为Java虚拟机的多线程是通过轮流切换线程,分配处理器执行时间来实现的。为了在线程切换后回到正确的执行位置,每个线程都需要一个独立的程序计数器,线程间的计数器互不影响,独立存储。如果线程正在执行Java方法,则计数器记录正在执行的虚拟机字节码指令的地址;如果线程正在执行Native方法,则计数器的值为空。程序计数器是唯一未指定任何OutOfMemoryError的区域。虚拟机栈Java虚拟机栈(JavaVirtualMachineStacks)是线程私有的,与线程具有相同的生命周期。虚拟机栈描述了Java方法执行的内存模型:每一个方法执行时,都会创建一个栈帧(StackFrame),用于存储每个方法被调用到执行完成的过程,对应于中的一个栈帧虚拟机栈从入栈到出栈的过程部分局部变量表操作数栈动态链接方法退出异常StackOverflowError:线程请求的栈深度大于虚拟机允许的深度OutOfMemoryError:虚拟机栈扩展到无法申请到足够的内存内存中的Native方法栈Native方法栈(NativeMethodStacks)服务于虚拟机使用的Native方法Java堆Java堆(JavaHeap)是最大的一块内存在Java虚拟机中。Java堆是在虚拟机启动时创建的,由所有线程共享。作用:存放对象实例。垃圾收集器主要管理Java堆。Java堆在物理上不一定是连续的,只要逻辑上是连续的。包含元素对象数组的非静态变量有什么问题?超出开销限制:JDK6增加了一种新的错误类型,当GC花费大量时间释放一个小空间时抛出该错误类型。方法区是所有线程共享的,用于存放类信息、常量、静态变量、即时编译器编译的代码等数据。和Java堆一样,不需要连续内存,可以选择固定大小,也可以选择不实现垃圾回收。什么是垃圾收集算法?有四种常用的垃圾收集算法:标记清除、复制、标记排序和分代收集。Mark-clearalgorithm从算法的名字可以看出,这个算法分为两个部分,mark和clear。先标记所有需要回收的对象,标记完成后统一回收所有标记的对象。该算法简单,但有两个缺点:第一,标记和清除的效率不是很高;第二,标记和清除后,会产生大量的内存碎片,导致可用内存空间不连续。在分配大对象时,没有足够的空间时,不得不提早触发垃圾回收。执行过程如下图所示。复制算法为了解决效率问题,出现了一种称为“复制”的收集算法。它根据容量将可用内存分成大小相等的两块,一次只使用其中一块。当本块内存用完后,将存活的对象复制到另一个块中,然后一次性清理已用内存空间。这样每次都回收了整个半个区域,分配内存时就不用考虑内存碎片等复杂情况。只需要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。只是这个算法的代价是将内存减少到原来的一半大小,有点太高了。优缺点优点:简单高效缺点:成本是将内存减少到原来的一半,成本高。执行过程如下图标记-Collat??ionAlgorithm复制收集算法在对象存活率高的时候会执行更多的复制操作,效率会降低。降低。更重要的是,如果不想浪费50%的空间,就需要使用额外的空间进行分配保证(HandlePromotion在空间不够的时候需要依赖其他内存),以应对极端情况已使用内存中所有对象都100%存活的情况对于“mark-sort”算法,标记过程仍然和“mark-clear”算法一样,只是后面的步骤不直接清理可回收对象,而是让所有存活的对象移动到一端,然后直接清理端边界。“标记排序”算法的示意图如下:标记排序算法解决了复制效率低和空间利用率低的问题复制算法,同时也解决了内存碎片的问题。分代收集算法根据对象生命周期的不同,将内存空间划分为不同的块,然后对不同的块使用不同的回收算法。一般Java堆分为新生代和老年代。新生代中的对象存活时间短,存活的对象数量少,可以采用复制算法,而老年代中的对象存活时间长,对象较多,所以你可以使用Mark-sweep和mark-sort算法。判断对象是否有效引用计数算法为对象添加了一个引用计数器。每当一个地方引用它时,数据寄存器就加1;当引用无效时,计数器减1;优点:实现简单,判断效率高缺点:很难解决对象之间相互循环引用的问题(objA.instance=objB;objB.instance=objA),所以Java语言没有使用引用计数的方式来管理内存根搜索算法Java和C#都使用根搜索算法来确定对象是否存活。以一系列名为“GCRoot”的对象为起点,从这些节点开始向下搜索,搜索到的所有经过的路径称为引用链(ReferenceChain),当一个对象在没有任何引用链的情况下连接到GCRoot当(从图论的角度来说,就是当对象被GCRoot不可达的时候),证明对象是可以被回收的。在Java中,这些对象可以成为GCRoot:虚拟机栈中的引用对象(栈帧中的局部变量表),类静态属性引用的对象方法区的常量引用对象,以及本地方法栈中的JNI(即Native方法)引用对象本文由传智教育博学谷野外架构师教研团队发表。如果本文对您有帮助,请关注并点赞;有什么建议也可以留言或私信。您的支持是我坚持创作的动力。转载请注明出处!