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

java培训JVM基础面试题分享

时间:2023-04-02 02:03:26 Java

为什么要学JVM?深入理解JVM可以帮助我们提高从平台角度解决问题的能力,例如有效防止内存泄漏(Memoryleak)、优化线程锁(ThreadLock)的使用、更高效地进行垃圾回收(Garbagecollection),提高系统吞吐量throughput(吞吐量),减少延迟(Delay),提高其性能(performance)等。你是怎么理解JVM的?JVM是Java虚拟机的缩写。顾名思义,它是一台虚拟计算机,是硬件计算机的抽象(虚构)实现,是JAVA平台的一部分,如图(见下图):JVM是一个Java程序它可以实现跨平台的基础(Java的跨平台本质上是通过不同平台的JVM实现的),它的作用是加载Java程序,将字节码(bytecode)翻译成机器码然后交给CPU执行。如图:在程序执行之前,必须将Java代码(.java)转换为字节码(.class),JVM通过类加载器(ClassLoader)将字节码加载到内存中,但是字节码文件是是JVM的一套指令集规范,不能直接交给底层操作系统执行。因此需要特定的命令解析器执行引擎(ExecutionEngine)将字节码翻译成底层机器码,然后交给CPU执行java。培训机构。市面上主流的JVM有哪些?JVM是一种规范。基于这个规范,不同的公司做出了具体的实现。BEA开发了JRockitVM,2008年被甲骨文收购;IBM开发了J9VM,仅在IBM内部使用。Sun开发的HotSpotVM,2010年被甲骨文收购,目前是甲骨文公司最主流的JVM虚拟机,也是最常用的。JVM的架构是怎样的?JVM的架构,如图:ClassLoaderSystem负责将类加载到内存中;RuntimeDataArea负责存储对象数据信息;ExecutionEngine负责调用对象执行业务;原生接口(NativeInterface)负责Java不同编程语言的整合。JVM有哪些运行模式?JVM有两种运行模式Server和Client。两种模式的区别在于Client模式的启动速度较快,Server模式的启动速度较慢;但是在启动进入稳定期之后,Server模式下程序的运行速度要比Client模式快很多。这是因为Server模式启动的JVM使用了重量级虚拟机,对程序使用了更多的优化;而Client模式启动的JVM使用的是轻量级虚拟机。所以Server启动比较慢,但是稳定后速度比Client快很多。现在64位的jdk默认是server模式(可以通过java-version查看)。当虚拟机以-client模式运行时,使用代号为C1的轻量级编译器,而以server模式启动的虚拟机使用代号为C2的相对重量级的编译器。c1和c2都是JITCompiler,C2比C1编译器编译得比较彻底,服务后性能更高。JVM运行时的内存结构是怎样的?不同的虚拟机实现可能略有不同,但都遵循Java虚拟机规范。Java8虚拟机规范规定,Java虚拟机管理的内存将包括以下几个区域,如图:Java堆(Heap)Java堆(JavaHeap)是JVM中最大的一块内存。它由所有线程共享,并在虚拟机启动时创建。主要用来存放对象实例,大部分对象实例也是在这里分配的。随着JIT编译器的发展和逃逸分析技术的逐渐成熟,栈分配和标量替换优化的技术会发生一些微妙的变化,所有对象都分配在堆上的情况会逐渐变得不那么绝对。小对象也可以直接在栈上分配,不用转义。如果堆中没有内存来完成实例分配,堆不能再扩展,系统底层运行时会抛出OutOfMemoryError。Java虚拟机规范规定,Java堆可以在物理上不连续的内存空间中,只要逻辑上连续即可,就像我们的磁盘空间一样。在实现上,也可以是固定大小,也可以是可扩展的,但是目前主流的虚拟机都是可扩展的,堆内存大小是通过-Xmx和-Xms参数定义的,用于java训练。方法区(MethodArea)方法区(MethedArea)是存放虚拟机已经加载的类信息、常量、静态变量、即时编译代码等数据的规范。不同的JDK有不同的方法区实现。HotSpot虚拟机使用JDK8中的NativeMemory来实现方法区。当方法不能满足内存分配要求时抛出OutOfMemoryError异常。Java虚拟机栈(VMStack)Java虚拟机栈(JavaVirtualMachineStacks)描述了Java方法执行时的内存模型。每个方法在被线程调用时都会创建一个栈帧(StackFrame),用于存储局部变量表、操作数栈、动态链接、方法退出等信息,每个方法从调用到执行完成的过程对应流程堆栈帧被推入虚拟机堆栈以弹出堆栈。如果线程请求的栈深度大于虚拟机允许的栈深度,则会抛出StackOverflowError异常。如果虚拟机可以动态扩容,如果扩容申请到足够的内存失败,会抛出OutOfMemoryError异常。JVM原生方法栈(NativeMethodStack)与虚拟机栈类似,只是虚拟机栈是为Java方法服务的,而原生方法栈是为虚拟机调用Native方法服务的。在Java虚拟机规范中,对本地方法栈没有特殊要求,虚拟机可以自由实现。所以SunHotSpot虚拟机直接将本地方法栈和虚拟机栈合二为一。JVM程序计数器(ProgramCounterRegister)程序计数器寄存器(ProgramCounterRegister)是一块很小的内存空间,可以看作是当前线程执行的字节码的行号指示器。在虚拟机的概念模型中,字节码解析器的工作是通过改变计数器的值来选择下一条要执行的字节码指令。分支、循环、跳转、异常处理、线程回收等基本功能都需要依赖这个计数器来完成。由于JVM的多线程是通过轮流切换线程,分配处理器执行时间来实现的,也就是说,在任何时刻,一个处理器(或一个核)都只会在一个线程中执行指令。因此,为了在线程切换后恢复正确的执行位置,每个线程都有一个独立的程序计数器。如果线程正在执行Java中的方法,程序计数器记录正在执行的虚拟机字节码指令的地址。如果是Native方法,这个计数器为空(undefined),所以这个内存区域在Java虚拟机中是唯一的。OutOfMemoryError字段在规范中没有指定。如何理解JVM中的GC系统?跟踪所有仍在使用的对象,将剩余的对象标记为垃圾,然后进行回收。这个过程称为GC(垃圾收集)。所有GC系统都可以从GC(如引用计数、对象可达性分析)、GC收集算法(标记-清除、标记-清除-排序、标记-复制-清除、分代)、GC收集器(如Serial、Parallel,CMS,G1)等方面的学习可以作为JVM引用链对象中的Root吗?Java虚拟机栈中的引用对象;本地方法栈中JNI引用的对象(一般称为Native方法);方法区中类静态常量的引用对象;方法区常量的引用对象。JVM中常见的垃圾回收算法有哪些?引用计数器算法该算法是为每个对象设置一个引用计数器。每当有对该对象的引用时,计数器就加1。相反,每当引用无效时,它就减1。也就是用计数来判断对象是否是垃圾。例如:引用计数方式的一大缺陷就是循环引用。比如可达性分析算法的核心思想就是以一系列“GCRoots”对象为起点,从这些对象开始向下搜索。搜索遵循的路径称为“引用链”。当一个对象没有任何引用链连接到GCRoots时,证明该对象可以被回收。例如:copyalgorithm这个算法就是把内存分成两个大小相同的块。当这个块用完后,将当前存活的对象复制到另一个块中,然后一次性清除当前块。该算法的缺点是只能利用一半的内存空间。例如:mark-clearalgorithm这个算法分两个阶段执行,第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆清除未标记的对象。该算法需要暂停整个应用程序,同时会产生内存碎片。例如:Mark-SortingAlgorithm该算法结合了“Mark-Sweep”和“Copy”算法的优点。第一阶段从根节点开始标记所有被引用的对象,第二阶段遍历整个堆,将存活的对象“压缩”复制到其中一个堆空间,并按顺序排出。该算法避免了“mark-clear”碎片问题,也避免了“copy”算法的空间问题,例如:JVM对象引用有哪些类型?无论是引用计数方法还是可达性分析算法,都与对象的“引用”有关【说说Java中的四大引用类型。],可见对象的引用决定了对象的生死,对象的引用关系如下。强引用在代码中无处不在,比如Objectobj=newObject(),只要强引用还在,垃圾回收器就永远不会回收被引用的对象。软引用是一种比强引用弱的引用。它可以使对象免于某些垃圾收集。只有当JVM认为内存不足时,才会尝试回收软引用指向的对象。JVM会确保在抛出OutOfMemoryError之前,清理掉软引用指向的对象。弱引用不是必需的对象,但是它们的强度比软引用要弱,弱引用关联的对象只能存活到下一次垃圾回收发生。幻象引用,也称为幽灵引用或幽灵引用,是最弱的一种引用关系。不可能通过幻象引用获得一个对象实例。到系统通知。JVM垃圾收集器的分类有哪些?新生代收集器Serial,ParNew,ParallelScavenge老年代收集器SerialOld,ParallelOld,CMSwholeheapcollectorG1garbagecollector分代垃圾收集器有哪些组件?分代垃圾收集器由新生代(YoungGeneration)和老年代(TenuredGeneration)组成。默认情况下,新生代与老年代的内存比例为1:2。新一代的组成部分是什么?:新生代由三个区域组成:Eden、FormSurvivor、ToSurvivor。它们内存的默认比例是8:1:1,如图:新生代垃圾回收是如何执行的?第一步,将Eden和FromSurvivor的活体对象复制到ToSurvivor区;第二步是清除Eden和FromSurvivor分区;第三步,交换FromSurvivor和ToSurvivor分区(FromtoTo,TotoFrom)。当新生代有两个Survivor分区时,空间利用率和程序运行效率都达到最优。说说JVM中的CMS垃圾收集器?CMS(ConcurrentMarkandSweep)是一个并发标记和清除垃圾收集器。它会使用free-lists来管理内存空间的回收,不会组织老年代。它的优点是标记和清除阶段的大部分工作与应用程序线程并发执行。它可以减少延迟,缩短暂停时间,并提高服务响应时间。当然也有缺陷,主要是对CPU资源需求敏感,无法清除浮动垃圾(浮动垃圾是指CMS清理垃圾时,用户线程产生新的垃圾,这部分未标记的垃圾称为“浮动垃圾”)garbage”,只能在下次GC时清除),会产生大量的空间碎片。说说JVM中的G1垃圾回收器?G1(垃圾优先GC)是一个实时收集器,旨在使STW暂停时间和分布可预测和可配置。可以说是一种兼顾了吞吐量和停顿时间的GC实现。G1可以直观地设定停顿时间的目标。与CMS相比,G1最好的情况下可能无法实现CMS的延迟暂停,但最坏的情况下要好很多。使用G1收集器时,Java堆的内存布局与其他收集器有很大不同。它将整个Java堆划分为多个大小相等的独立区域(Regions)。虽然它仍然保留了新生代和老年代的概念,但是,新生代和老年代在物理上已经不再分离。它们是区域的一部分(可以是不连续的)的集合。例如,这样的划分使得GC不必每次都收集整个堆空间,而是以增量的方式来处理,每次只处理小堆区域的一部分,称为收集集。每次停顿都会把新生代的小堆区全部收集起来,可能只包含一部分老年代的小堆区。G1的另一个创新是在并发阶段估计每个小堆中存活对象的总数。用于构建收集集的原则是:首先收集垃圾最多的小堆区域。这也是G1名字的由来:garbage-first。G1解决了CMS中的各种痛点,包括暂停时间的可预测性,并杜绝了堆碎片。对于对单服务延迟非常敏感的系统,如果不限制CPU资源,那么G1可以说是HotSpot中的最佳选择,尤其是在最新版本的Java虚拟机中。当然,这种减少延迟的优化并非没有代价:由于额外的写入障碍和更积极的守护线程,G1将更加昂贵。因此,如果系统是吞吐量优先类型,或者CPU持续占用100%,但不关心单次GC的停顿时间,那么CMS是更好的选择。JVM垃圾回收的调优参数是什么?-Xmx:512设置最大堆内存为512M;-Xms:256设置初始堆内存(最小堆)为256M;-XX:MaxNewSize设置最大新生代内存;-XX:MaxTenuringThreshold=6设置新生代对象通过6次GC提升到老年代;-XX:PretrnureSizeThreshold设置大对象的值,超过这个值的大对象直接进入老年代;-XX:NewRatio设置分代垃圾收集器的新生代和老年代的内存比例;-XX:SurvivorRatio设置新生代Eden、FormSurvivor、ToSurvivor的比例。现代JVM并发GC的调优原则是什么?首先是以空间换取时间和效率。对于G1&ZGC,增加堆内存(更多空闲空间)配置往往更有利于GC达到目标停顿时间。其次要知道的是,低暂停并不意味着高吞吐量。并发GC是为了保证GC在并发阶段的同时,业务线程仍然有机会获得CPU时间片,但是也意味着GC会和业务线程一起抢占计算资源,而且往往是为了处理更多的同步问题在并发阶段,也会占用更多的计算资源。三是GC调优要始终考虑机器资源、对应的系统应用场景等,至少目前没有灵丹妙药。文章来自jason

猜你喜欢