JVM基础50道经典面试题(一)

JVM基础50道经典面试题(一)

      • [💡 内存区域与模型(14题)](#💡 内存区域与模型(14题))
      • [💡 垃圾回收机制(20题)](#💡 垃圾回收机制(20题))
      • [💡 类加载机制(8题)](#💡 类加载机制(8题))
      • [💡 执行引擎与性能调优(8题)](#💡 执行引擎与性能调优(8题))
      • [💎 从理论到实践的建议](#💎 从理论到实践的建议)

围绕JVM学习框架,构建了50道高质量的面试题及深度分析,旨在帮助从"记忆"走向"理解",直至"调优"。

为了方便系统性地掌握和复习,将这50道题目整理到了下面的表格中,它们覆盖了JVM的四大核心模块:

类别 考察要点 关键题目编号
内存区域与模型 运行时数据区划分、核心原理、常见异常与配置 1 - 14
垃圾回收机制 对象回收判定、GC算法、各类收集器原理与调优实战 15 - 34
类加载机制 类加载过程、双亲委派模型及其破坏、应用 35 - 42
执行与性能调优 JIT编译器、性能监控工具、综合调优思路 43 - 50

💡 内存区域与模型(14题)

这部分是理解JVM的基石,面试官会通过它判断对程序运行底层环境的掌握程度。

  1. 请详细描述JVM的运行时数据区(Runtime Data Areas)。哪些是线程共享的,哪些是线程私有的?

    • 核心分析 :必须准确区分。线程共享 :堆(Heap)、方法区(Method Area,JDK8后为元空间 Metaspace)。线程私有:程序计数器(PC Register)、Java虚拟机栈(VM Stack)、本地方法栈(Native Method Stack)。需能阐述每个区域的核心作用。
  2. 程序计数器(Program Counter Register)的作用是什么?为什么它是线程私有的?

    • 核心分析 :① 作用:指向当前线程正在执行的字节码指令地址(或native方法的undefined)。② 线程私有原因:Java多线程通过CPU时间片轮转实现,一个CPU核心在任意时刻只能执行一个线程的指令。线程切换后需能恢复到正确位置,因此必须每个线程独立存储自己的执行地址。
  3. Java虚拟机栈(VM Stack)中存储的是什么?什么是栈帧(Stack Frame)?

    • 核心分析 :虚拟机栈存储栈帧。栈帧 是用于支持方法调用和执行的数据结构,每个方法从调用到完成对应一个栈帧的入栈和出栈。栈帧内部包含局部变量表 (基本类型和对象引用)、操作数栈动态链接方法返回地址等信息。
  4. 什么情况下会抛出 StackOverflowErrorOutOfMemoryError(在栈方面)?

    • 核心分析StackOverflowError:线程请求的栈深度超过 虚拟机栈所允许的最大深度(如无限递归)。OutOfMemoryError:虚拟机栈动态扩展时无法申请到足够内存(如创建过多线程,每个线程都需要独立的栈空间)。两者触发条件不同。
  5. 堆(Heap)为什么要分代(年轻代、老年代)?永久代(PermGen)和元空间(Metaspace)有什么区别?

    • 核心分析分代 基于"弱分代假说",让不同生命周期的对象处于不同区域,从而采用最合适的GC算法(如年轻代用复制算法)。永久代 vs 元空间:永久代在堆内,大小固定易OOM;元空间使用本地内存,默认无上限,且将字符串常量池等移入堆中,降低了OOM风险。
  6. 方法区(Method Area)和运行时常量池(Runtime Constant Pool)的关系是什么?

    • 核心分析 :方法区用于存储已被加载的类信息、常量、静态变量、即时编译器编译后的代码 等数据。运行时常量池是方法区的一部分,用于存放编译期生成的各种字面量和符号引用(在类加载后进入)。JDK8后,运行时常量池移到了堆中。
  7. 直接内存(Direct Memory)是什么?它和堆内存有什么区别?使用 ByteBuffer.allocateDirect() 有什么优缺点?

    • 核心分析 :直接内存不是JVM运行时数据区的一部分,而是通过Native函数库在堆外 直接分配的内存。区别 :不受JVM堆大小限制,但受本机总内存限制;避免了Java堆和Native堆间的数据复制(零拷贝),提升性能(如NIO)。优点 :性能高。缺点 :分配回收成本高,管理不当易导致OutOfMemoryError
  8. String 常量池在 JDK 1.7 和 JDK 1.8 中分别位于哪里?String.intern() 方法的行为是怎样的?

    • 核心分析 :JDK 1.7起,字符串常量池从方法区移到了堆中intern()是一个本地方法:如果池中已包含等于此String对象的字符串,则返回池中字符串的引用;否则,将此String对象包含的字符串添加到常量池中,并返回此String对象的引用(JDK7+会复制引用到池,而非复制整个字符串对象)。
  9. 对象在堆内存中的布局是怎样的?

    • 核心分析 :分为三块:对象头 (包含Mark Word和类型指针)、实例数据 (对象真正存储的有效信息)、对齐填充(非必需,仅起占位作用,保证对象大小是8字节的整数倍)。了解Mark Word对于理解锁升级至关重要。
  10. 一个对象从创建到被回收,在内存中是如何流转的?(结合新生代Eden, Survivor区说明)

    • 核心分析 :对象优先在Eden区 分配。Eden满后触发Minor GC,存活对象移到Survivor0 ("To"区)。再次Minor GC, Eden和Survivor0存活对象复制到Survivor1 ("From"区),并交换From/To指针。对象每熬过一次Minor GC,年龄+1。达到阈值(默认15)后,晋升到老年代。大对象可能直接进入老年代。
  11. 如何判断一个对象是否是"垃圾",可以被回收?引用计数法和可达性分析算法各有什么优缺点?

    • 核心分析引用计数法 :简单,但无法解决循环引用 问题。可达性分析(Java采用):从一系列"GC Roots"对象出发,向下搜索,不可达的对象即为垃圾。GC Roots包括:虚拟机栈中引用的对象、方法区静态属性引用的对象、方法区常量引用的对象、本地方法栈JNI引用的对象等。
  12. Java中的四种引用类型(强、软、弱、虚)对垃圾回收有什么影响?分别有什么使用场景?

    • 核心分析 :这是内存敏感应用的关键。强引用 :永不回收。软引用 :内存不足时回收,适合缓存。弱引用 :下次GC即回收,适合WeakHashMap等。虚引用 :无法通过它获取对象,用于追踪对象被GC的活动(与ReferenceQueue配合)。这体现了JVM内存管理的精细度。
  13. 什么是Stop-The-World(STW)?为什么GC过程中会发生STW?

    • 核心分析 :STW指在GC过程中,整个Java应用程序线程会被暂停 ,就像世界停止一样。原因:为了保证可达性分析的一致性GC期间对象引用关系不会发生变化,就像拍照时需要瞬间静止。所有GC器都有STW,只是暂停时间和频率不同,低延迟GC器(如ZGC)的核心目标就是尽可能缩短STW时间。
  14. 逃逸分析(Escape Analysis)是什么?JVM基于它可以做什么优化?

    • 核心分析 :分析对象动态作用域,判断对象是否会"逃逸"出方法或线程。基于此,JIT编译器可以进行:栈上分配 (对象在栈上创建销毁,减轻GC压力)、锁消除 (对线程局部对象移除同步锁)、标量替换 (将对象分解为基本类型在寄存器/栈上分配)。这些是重要的即时编译优化

💡 垃圾回收机制(20题)

这部分是JVM面试的核心和难点,需要深入理解算法、收集器及其调优。

  1. 常见的垃圾收集算法有哪些?请对比标记-清除(Mark-Sweep)、复制(Copying)、标记-整理(Mark-Compact)算法的优缺点。

    • 核心分析标记-清除 :产生内存碎片。复制 :无碎片,但浪费一半空间,适合对象存活率低的新生代。标记-整理:无碎片,但移动对象成本高,适合对象存活率高的老年代。这是选择收集器的基础。
  2. 什么是"卡表"(Card Table)?它如何优化跨代引用时的垃圾回收效率?

    • 核心分析 :一种解决跨代引用 (老年代对象引用新生代对象)问题的数据结构。将老年代划分为大小为512字节的"卡",维护一个卡表(字节数组)。当老年代对象引用新生代对象时,JVM将对应卡标记为"脏"。Minor GC时,只需扫描脏卡而非整个老年代,大大加快了速度。
  3. Serial、ParNew、Parallel Scavenge收集器各自的特点和适用场景是什么?

    • 核心分析Serial :单线程,简单高效,适用于客户端或小内存单核环境。ParNew :Serial的多线程并行版本,是CMS在新生代的默认搭档。Parallel Scavenge("吞吐量优先"收集器):目标是达到可控制的吞吐量(运行用户代码时间/总时间),适用于后台计算任务。
  4. CMS(Concurrent Mark-Sweep)收集器的工作流程是怎样的?它有什么优点和缺点?

    • 核心分析 :流程:① 初始标记(STW快)② 并发标记 ③ 重新标记(STW)④ 并发清除。优点 :并发,低停顿。缺点:对CPU敏感;无法处理浮动垃圾;会产生内存碎片;可能引发"Concurrent Mode Failure"导致Full GC。
  5. G1(Garbage-First)收集器是如何工作的?相比CMS有哪些改进?

    • 核心分析 :G1将堆划分为多个大小相等的Region ,并跟踪每个Region的垃圾价值(回收所得空间及所需时间)。工作流程:初始标记 -> 并发标记 -> 最终标记 -> 筛选回收(STW,选择价值最高Region回收)。改进 :① 整体基于"标记-整理",局部基于"复制",避免内存碎片 。② 可预测的停顿时间模型 (通过-XX:MaxGCPauseMillis设定)。③ 能独立管理整个堆。
  6. ZGC和Shenandoah作为新一代低延迟收集器,其核心设计目标是什么?它们是如何实现极低停顿时间的(如读屏障、染色指针等技术思想)?

    • 核心分析 :核心目标:将STW停顿时间控制在10ms以内 ,且几乎与堆大小无关。关键技术染色指针 (将GC信息直接存储在对象指针中,而非对象头,使得某些阶段无需访问对象即可完成操作)、读屏障 (在读取指针时触发一些操作,如对象转移的转发)。它们实现了并发标记、并发转移,大大减少了STW。
  7. 如何选择适合的垃圾收集器?(从应用场景、停顿要求、吞吐量等角度分析)

    • 核心分析 :① 小型应用/单核:Serial。② 追求吞吐量(如数据分析):Parallel Scavenge + Parallel Old。③ 追求低延迟(如Web应用):JDK8可选ParNew + CMS;JDK11+推荐G1;超大堆或超低延迟(<10ms)选ZGCShenandoah。需结合-XX:+Use*GC参数和具体硬件考量。
  8. 什么情况下会触发Minor GC、Major GC(Full GC)?

    • 核心分析Minor GC :Eden区空间不足时触发。Major GC/Full GC :定义较混乱,通常指回收整个堆(包括年轻代和老年代)。触发条件:老年代空间不足;方法区空间不足;调用System.gc()(建议);CMS GC时出现"Concurrent Mode Failure";堆内存分配超大对象时空间不足等。
  9. GC日志中,[GC (Allocation Failure) ...][Full GC (System.gc()) ...] 分别表示什么?如何分析GC日志?

    • 核心分析Allocation Failure表示对象分配失败触发的GC,最常见。System.gc()表示由代码显式调用触发。分析GC日志需关注:时间戳、GC类型、回收前后各区域容量变化、耗时 。关键指标:GC频率、停顿时间、吞吐量、内存使用率。可使用gceasy等在线工具辅助分析。
  10. 常用的GC调优参数有哪些?(如堆大小、新生代比例、晋升阈值等)

    • 核心分析 :基础:-Xms/-Xmx(堆初始/最大)、-Xmn(新生代大小)、-XX:SurvivorRatio(Eden/Survivor比例)、-XX:MaxTenuringThreshold(晋升阈值)。CMS:-XX:CMSInitiatingOccupancyFraction(触发回收阈值)。G1:-XX:MaxGCPauseMillis(目标暂停时间)、-XX:G1HeapRegionSize(Region大小)。
  11. 如何排查和解决Java应用中的内存泄漏(Memory Leak)问题?

    • 核心分析 :步骤:① 监控发现异常(如GC后堆内存不降、OOM)。② 使用jpsjstat初步定位。③ 使用jmap -dump:format=b,file=heap.hprof <pid> 生成堆转储。④ 使用MAT、JProfiler等工具分析堆快照,查找大对象、支配树、GC Roots引用链,定位泄漏对象和代码。
  12. 什么是"并发模式失败"(Concurrent Mode Failure)?如何避免?

    • 核心分析 :发生在CMS收集器并发清理阶段,此时应用线程仍在运行并产生新垃圾(浮动垃圾),如果老年代空间无法满足这些新对象,就会发生CMF,此时JVM会启用Serial Old收集器进行Full GC (长时间STW)。避免 :合理设置-XX:CMSInitiatingOccupancyFraction,预留足够空间。
  13. 什么是"分配担保"(Handle Promotion Failure)?

    • 核心分析 :发生在Minor GC前,JVM会检查老年代最大可用连续空间 是否大于新生代所有对象总空间历次晋升到老年代对象的平均大小。如果条件不成立,则可能触发一次Full GC以确保安全。这是担保Minor GC后存活对象能顺利进入老年代的机制。
  14. -XX:+DisableExplicitGC 参数有什么作用?为什么生产环境有时会启用它?

    • 核心分析 :禁用System.gc()调用。启用原因:① System.gc()会触发Full GC,造成不可预测的停顿。② 一些框架/库(如NIO的Direct Buffer清理)或RMI会隐式调用它。启用后需确保应用不依赖显式GC来管理内存(如堆外内存),或使用-XX:+ExplicitGCInvokesConcurrentSystem.gc()触发并发GC。
  15. 如何理解"大对象直接进入老年代"?相关参数是什么?

    • 核心分析 :大对象(如长数组、大字符串)需要连续内存空间,容易导致提前触发GC且产生大量内存拷贝。通过-XX:PretenureSizeThreshold参数(只对Serial和ParNew收集器有效),可以设定对象大小阈值,超过此阈值的对象直接在老年代分配,避免在新生代反复复制。
  16. 什么是"动态对象年龄判定"?

    • 核心分析 :HotSpot并非永远要求对象年龄达到MaxTenuringThreshold才晋升。如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半 ,那么年龄大于或等于该年龄的对象就可以直接进入老年代。这是一种自适应的优化策略。
  17. G1收集器中的"Mixed GC"是什么?

    • 核心分析 :Mixed GC是G1特有的阶段。在并发标记完成后,G1知道哪些Region垃圾最多。Mixed GC会同时回收一部分年轻代Region和一部分标记为垃圾多的老年代Region。它是介于Minor GC(只回收年轻代)和Full GC(回收整个堆)之间的一种回收。
  18. ZGC中的"染色指针"(Colored Pointers)技术具体是如何工作的?它有什么优势?

    • 核心分析 :ZGC利用指针的64位中未使用的位(在Linux下是42-45位)来存储GC状态信息(如标记、转发、重映射)。优势:① 使得某些GC阶段(如标记、转移)可以仅通过操作指针完成,无需访问对象内存,更快 。② 支持并发转移,极大地减少了STW时间。
  19. 如何监控JVM的GC状态和性能?你用过哪些工具?

    • 核心分析 :命令行:jstat -gc <pid> 实时查看各区域容量和GC次数/时间。可视化:jconsoleVisualVMJMC(Java Mission Control)。生产级APM:Arthas(阿里)、Prometheus + Grafana(监控指标)。需结合应用日志和系统监控综合判断。
  20. 什么是"安全点"(Safepoint)和"安全区域"(Safe Region)?它们对GC有何意义?

    • 核心分析安全点 :代码中一些特定的位置(如方法调用、循环跳转),线程执行到这些位置时,其状态(如栈帧、寄存器)是确定的,可以安全地进行GC等操作。安全区域 :在一段代码片段中,引用关系不会发生变化,线程在此区域任意点开始GC都是安全的。它们是实现所有GC器STW的协作机制

💡 类加载机制(8题)

这部分考察你对Java动态性的底层支持的理解。

  1. 类加载的过程分为哪几个步骤?

    • 核心分析 :五个阶段:① 加载 :获取字节流,生成Class对象。② 验证 :确保字节流安全合规。③ 准备 :为静态变量分配内存并设初始值(零值)。④ 解析 :将符号引用转为直接引用。⑤ 初始化 :执行<clinit>方法(静态块和静态变量赋值)。其中加载、验证、准备、解析属于连接阶段。
  2. 什么是双亲委派模型(Parents Delegation Model)?请描述其工作流程及其好处。

    • 核心分析 :工作流程:一个类加载器收到加载请求时,先委派给父加载器 ,依次向上。只有当所有父加载器都无法完成时,自己才尝试加载。好处 :① 保证核心类安全 :防止用户自定义类篡改核心类(如java.lang.String)。② 避免重复加载:保证类的全局唯一性。
  3. 有哪些场景破坏了双亲委派模型?是如何破坏的?(如JDBC、Tomcat)

    • 核心分析 :破坏场景:① 历史原因 :JDK1.2前无该模型,为了兼容,loadClass()方法可被子类重写。② 自身缺陷 :基础类(如java.sql.Driver)需调用由应用类加载器实现的SPI代码(如数据库驱动),使用线程上下文类加载器 反向委派。③ 模块化热部署:如Tomcat为每个Web应用提供独立类加载器,实现应用隔离。
  4. 如何自定义一个类加载器?需要重写哪些方法?

    • 核心分析 :继承ClassLoader类。通常只需重写findClass(String name)方法(遵循双亲委派),在该方法中根据名称找到或生成字节码,最后调用defineClass()将字节数组转换为Class对象。不应轻易重写loadClass()方法,除非明确要破坏双亲委派
  5. <clinit><init> 方法有什么区别?

    • 核心分析<clinit>类构造器 ,由编译器自动收集所有类变量的赋值动作和静态语句块 合并而成,在类初始化 时执行,只执行一次。<init>实例构造器 ,在对象实例化时执行,可能有多份(对应不同构造器)。两者都是线程安全的。
  6. 什么情况下会触发一个类的初始化?(主动引用 vs 被动引用)

    • 核心分析主动引用 (触发初始化):new、读写静态字段(非常量)、调用静态方法、反射、初始化子类(先初始化父类)、作为主类。被动引用 (不触发初始化):通过子类引用父类静态字段;通过数组定义引用类;引用编译期常量(static final常量,值已在编译期确定,存入常量池)。
  7. Tomcat等Web容器是如何实现Web应用隔离的?(从类加载器角度)

    • 核心分析 :Tomcat为每个Web应用创建一个**WebAppClassLoader实例。它 优先加载自己/WEB-INF/classes/WEB-INF/lib下的类**(打破双亲委派,实现应用隔离),对于基础类库(如java.*javax.*)则委派给共同的父加载器(如Common ClassLoader)加载,实现共享。这保证了不同应用间类库的独立与共享。
  8. 什么是"命名空间"(Namespace)?两个类"相等"的条件是什么?

    • 核心分析 :每个类加载器实例都有独立的命名空间。一个类的唯一性由其全限定名和加载它的类加载器实例 共同决定。即使两个类来自同一个.class文件,被不同的类加载器加载,在JVM看来也是两个完全不同的类instanceofequals()isAssignableFrom()等都会返回false

💡 执行引擎与性能调优(8题)

这部分将知识与实际性能问题关联,考察综合能力。

  1. 解释执行和即时编译(JIT)有什么区别?HotSpot虚拟机是如何结合这两种方式的?

    • 核心分析解释执行 :逐条翻译执行字节码,启动快,执行慢。JIT编译 :将"热点代码"(频繁执行的代码)编译成本地机器码,执行快,但需要编译时间。HotSpot采用混合模式:程序启动时以解释器为主,随着运行,热点代码被编译,后续执行直接使用编译后的本地代码。
  2. 什么是热点代码?JVM如何探测热点代码?

    • 核心分析 :热点代码指被频繁执行的方法或代码块 。探测方式:基于计数器 (方法调用计数器和回边计数器)。当一个方法被调用的次数达到阈值,或一个循环体的循环次数达到阈值,就会触发JIT编译。-XX:CompileThreshold可设置阈值。
  3. JIT编译器有哪些常见的优化技术?(如方法内联、逃逸分析、公共子表达式消除等)

    • 核心分析 :① 方法内联 :将目标方法代码"复制"到发起调用的方法中,消除调用开销。② 逃逸分析 (见第14题)。③ 公共子表达式消除 :如果一个表达式之前已经计算过,且变量值未变,则直接用结果替换。④ 数组边界检查消除 :在安全情况下省略数组下标检查。⑤ 锁消除/锁粗化
  4. -client-server 模式(已过时但体现思想)下JVM的行为有什么主要区别?

    • 核心分析 :体现不同的编译策略取舍。-client(C1编译器):启动速度快 ,编译优化较少。-server(C2编译器):启动较慢 ,但进行更多的激进优化 (如深度内联、逃逸分析),追求峰值性能。现代多核服务器环境默认使用服务端模式。
  5. 什么是"代码缓存"(Code Cache)?它满了会怎样?

    • 核心分析 :JVM用于存放JIT编译生成的本地机器码 的内存区域。如果满了,JVM会停止JIT编译,后续代码只能解释执行,导致性能下降。可以调整大小:-XX:ReservedCodeCacheSize-XX:InitialCodeCacheSize。可以使用-XX:+PrintCodeCache监控使用情况。
  6. 你常用的JVM性能监控和故障处理工具有哪些?分别用在什么场景?

    • 核心分析 :需形成工具箱思维。① 基础诊断jps(查进程)、jstat(GC/类加载统计)、jmap(堆转储)、jstack(线程快栈查死锁)。② 可视化/分析jconsoleVisualVMMAT(分析堆转储)。③ 线上诊断Arthas (阿里,功能强大,动态跟踪、热更新等)。④ 监控Prometheus + Grafana
  7. 如果发现系统CPU使用率很高,且是Java进程导致的,你的排查思路是什么?

    • 核心分析 :① top找到高CPU的Java进程及其线程。② top -Hp <pid>找到该进程中高CPU的线程ID。③ 将线程ID转为16进制(printf "%x\n" <tid>)。④ jstack <pid> > stack.log抓取线程快照。⑤ 在stack.log中搜索16进制线程ID,定位线程堆栈,分析是计算密集型代码死循环 还是GC问题 (结合jstat -gc)。
  8. 给你一个正在运行的、存在性能问题(如周期性卡顿)的Java应用,你的一般性调优思路和步骤是怎样的?

    • 核心分析 :体现系统性方法论。① 确立指标 :明确问题(吞吐量下降?延迟增高?)。② 监控收集 :收集GC日志、线程快照、堆转储、系统指标(CPU、内存、IO)。③ 分析定位 :分析数据,定位瓶颈(如频繁Full GC、锁竞争、内存泄漏)。④ 制定方案 :调整JVM参数(堆大小、收集器)、优化代码(避免内存泄漏、减少锁粒度)。⑤ 验证迭代 :在预发环境测试,对比指标,持续优化。强调"数据驱动"和"假设验证"

💎 从理论到实践的建议

你已经掌握了JVM面试的知识图谱。为了将这些知识内化为能力,我建议:

  1. 动手实验 :在本地或测试环境,使用 -XX:+PrintGCDetails-Xlog:gc* 等参数运行程序,亲自分析GC日志。尝试用 jmapMAT 分析一个简单的内存泄漏程序。
  2. 参数调优:为一个简单的Spring Boot Web应用,尝试使用G1、ZGC等不同收集器,并通过压测工具观察其吞吐量和停顿时间的变化,理解参数的实际影响。
  3. 源码关联(高阶):如果感兴趣,可以阅读《深入理解Java虚拟机》中关于HotSpot源码的章节(如垃圾收集器的实现),这将极大提升你的原理深度。

JVM的学习是一个长期过程。如果你在后续的学习或实践中,对 "生产环境JVM调优案例"、"特定中间件(如Kafka, Elasticsearch)的JVM配置"或"容器化(Docker/K8s)环境下的JVM挑战" 等更具体的方向有疑问,我们可以继续深入探讨。

相关推荐
程序猿202310 小时前
MAT(memory analyzer tool)主要功能
jvm
期待のcode13 小时前
Java虚拟机的非堆内存
java·开发语言·jvm
jmxwzy17 小时前
JVM(java虚拟机)
jvm
Maỿbe17 小时前
JVM中的类加载&&Minor GC与Full GC
jvm
人道领域18 小时前
【零基础学java】(等待唤醒机制,线程池补充)
java·开发语言·jvm
小突突突19 小时前
浅谈JVM
jvm
饺子大魔王的男人20 小时前
远程调试总碰壁?局域网成 “绊脚石”?Remote JVM Debug与cpolar的合作让效率飙升
网络·jvm
天“码”行空1 天前
java面向对象的三大特性之一多态
java·开发语言·jvm
独自破碎E1 天前
JVM的内存区域是怎么划分的?
jvm
期待のcode2 天前
认识Java虚拟机
java·开发语言·jvm