JVM 架构与执行流程
JVM 的执行链路通常遵循 class 文件加载、类装载、内存分配再到执行引擎的顺序。类装载器采用双亲委派模型,每个加载请求会先向父类传递,直到顶层。若顶层无法加载,再逐层向下检查,这种机制有效避免了重复加载。主要包含启动类加载器、扩展类加载器、应用程序类加载器以及自定义类加载器。
内存区域方面,主要分为静态内存(如方法区、虚拟机栈)和动态内存(堆)。GC 主要针对动态内存进行回收。虚拟机栈服务于 Java 代码,而本地方法栈则专门处理 Native 方法。
当 class 文件进入类装载器后,JVM 栈与线程绑定。每创建一个线程,JVM 都会为其分配一个独立的 Java 栈。栈中包含多个栈帧,每调用一个方法就生成一个栈帧,用于存储局部变量表、操作数栈、动态链接和方法出口等信息。方法从调用到完成的过程,对应着栈帧的入栈与出栈。因此,Java 栈是线程私有的,实现了线程隔离。
内存模型与可见性
堆内存中存放主内存数据,每个线程维护自己的本地内存副本。普通代码无法保证多线程环境下的可见性,即线程操作本地内存后,主存不会立即更新,其他线程也无法立刻同步。
使用 volatile 关键字可以禁止指令重排序,强制访问主存,从而保证可见性,但它并不能保证操作的原子性。强引用会导致内存溢出时不回收,软引用在内存溢出时会回收,弱引用则在 GC 时回收,虚引用随时可被回收。代码执行过程本质上是压栈入栈,栈保存基本类型和引用地址,堆保存具体对象。
垃圾回收机制
检测算法
引用计数法通过计数器追踪对象引用,引用失效即减一,但无法解决循环引用问题。目前主流采用可达性分析算法,从 GC Roots 出发检测引用链,未引用的对象将被回收。根集通常包括 Java 栈中引用的对象、方法区常量池中引用的对象等。
收集算法
标记 - 清除算法先标记待回收对象再清理,容易产生内存碎片。复制算法将存活对象复制到另一块内存,清空原区域,无碎片但消耗双倍内存。标记 - 整理算法在回收的同时整理内存空间。
现代 JVM 采用分代收集理论。堆分为年轻代和年老代。年轻代通常采用复制算法,年老代则多采用标记 - 整理或标记 - 清除。对象在年轻代出生,经过多次 GC 未被回收便会晋升到年老代。不同代采取不同的回收策略,参数 n 决定了对象晋升的阈值,这通常由内存大小和人工配置共同决定。
调优实践
调优的核心目标是减少 Full GC 频率,提升系统吞吐量。我们可以借助 JConsole、Java VisualVM 等工具监控内存和 CPU 曲线,或者通过线程 PID 查询堆栈信息。
在实际操作中,可以通过修改各内存区域的大小和比例来优化性能。同时需要结合内存曲线分析,排查死锁、死循环等异常代码。必要时导出 Dump 文件进行深入分析,定位具体的内存泄漏点。

