java类加载采用三层双亲委派模型(bootstrap、extension、appclassloader),按需分阶段加载;堆与方法区(metaspace)动态可调,回收策略与线程可见性不同;解释器与jit共存并动态反馈编译热点代码;gc策略需依停顿敏感度、堆大小等权衡选择。

类加载器不是单个组件,而是分层协作的三套机制
Java 类加载不是一次性把所有类塞进内存,而是按需、分阶段、有优先级地加载。核心是 BootstrapClassLoader、ExtensionClassLoader 和 AppClassLoader 这三层,它们构成双亲委派模型——子加载器先问父加载器“你能不能 load 这个类”,父不认才自己动手。
常见错误现象:NoClassDefFoundError 表面是类没找到,实际常因父加载器已加载过一个不兼容版本,子加载器拒绝再加载;或自定义类加载器没正确实现委派逻辑,导致 ClassNotFoundException。
- 不要在
AppClassLoader里直接 new 自定义类加载器并绕过委派——除非你明确要隔离类(如插件系统) -
BootstrapClassLoader用 C++ 实现,不继承ClassLoader,所以不能被 Java 代码直接引用或重写 - 如果用
Thread.currentThread().getContextClassLoader()加载资源,注意它默认是AppClassLoader,但框架(如 Tomcat、Spring Boot)可能中途替换成自己的
运行时数据区里,堆和方法区最容易被误读为“静态划分”
堆(Heap)和方法区(Method Area)看起来像固定分区,其实 JVM 8+ 后方法区基本等价于 Metaspace,而堆也支持动态扩容收缩。真正关键的是它们的回收策略和线程可见性完全不同。
使用场景:频繁生成代理类(如 Spring AOP)、大量反射调用、或使用 String.intern()(JDK 7+ 后 intern 字符串进堆,不是永久代)都会直接影响 Metaspace 大小;大对象分配可能触发 DirectMemory 溢出,但它不属于堆,也不归 GC 管理。
立即学习“Java免费学习笔记(深入)”;
-
-XX:MaxMetaspaceSize必须显式设置,否则只受系统内存限制,容易撑爆容器内存(尤其 OOM killer 杀进程) - 堆内新生代用
-Xmn控制,但更推荐用-XX:NewRatio或让 G1/CMS 自动调节——硬编码-Xmn在不同机器上反而引发 GC 频繁 -
java.lang.OutOfMemoryError: Metaspace和java.lang.OutOfMemoryError: Compressed class space是两种独立错误,后者说明压缩类指针空间不足,需调-XX:CompressedClassSpaceSize
执行引擎的解释器与 JIT 编译器不是“开关切换”,而是共存且动态反馈的
JVM 不是先全解释跑一遍再整体编译。HotSpot 默认采用分层编译(-XX:+TieredStopAtLevel=1 可关),解释器快速启动,同时记录方法调用频次;当某个方法被高频执行(如循环体、热点方法),C1 或 C2 编译器就把它编译成本地机器码,缓存起来复用。
性能影响:JIT 编译本身耗 CPU 和内存,刚启动时吞吐低;但长期运行后,热点代码性能远超纯解释执行。不过 JIT 优化依赖运行时 profile,比如分支预测、虚函数去虚化,一旦实际行为偏离 profile(如某 if 分支突然从 99% 变成 1%),就会触发去优化(deoptimization),回退到解释执行。
-
-XX:+PrintCompilation能打印哪些方法被编译、何时被去优化,比看 GC 日志更能定位“为什么这段代码越跑越慢” - C2 编译器对循环展开、逃逸分析等激进优化可能导致
StackOverflowError(栈帧变大)或对象意外未被 GC(逃逸分析后栈上分配,但某些反射操作会强制逃逸) - 使用
-XX:TieredStopAtLevel=1强制只用 C1(client 模式风格),适合短生命周期应用(如 CLI 工具),避免 JIT 预热开销
GC 策略选择本质是权衡停顿时间、吞吐量与内存 footprint
选 G1、ZGC 还是 Parallel GC,不是看“新不新”,而是看你的应用对 pause time 是否敏感、堆是否大于 4GB、是否允许使用实验性特性。ZGC 的 sub-millisecond 停顿代价是更高的内存占用(多保留一份对象副本)和 JDK 版本绑定(JDK 11+)。
容易踩的坑:给小堆(-Xmx 却忽略 -XX:MaxRAMPercentage,导致 JVM 无视 cgroup 限制疯狂吃内存。
- 容器环境必须加
-XX:+UseContainerSupport(JDK 10+ 默认开启),否则 JVM 看不到内存限制,-Xmx无效 - G1 的
-XX:MaxGCPauseMillis是目标值,不是保证值;实际停顿超时往往因为 Humongous 对象分配失败触发 Full GC - ZGC 要求 Linux kernel 4.14+ 且启用
mem=参数(避免大页 fallback),否则启动报Failed to allocate backing memory
made not entrant 其实意味着什么。










