别背“堆栈方法区”,画一张内存图
面试官问内存模型,不是考你记忆力,是考你脑子里有没有一幅图。你可以说:“我习惯把JVM内存想象成一栋楼。程序计数器是每层楼的门牌号,记录线程执行到哪一行;虚拟机栈是每个线程的独立工作台,方法调用时压入栈帧,里面放着局部变量表和操作数栈;堆是整栋楼的大仓库,所有对象都住在这里;方法区是图纸间,存着类元信息、常量池和静态变量。”这样答,面试官立刻知道你不是背的,是理解的。能把抽象概念翻译成生活场景,才是真懂。如果追问JDK8之后方法区去哪了,你可以说:“方法区的实现从永久代换成了元空间,移到本地内存,避免了永久代OOM,也减少了Full GC的压力。”一句话带出版本演进,加分。
GC不是背参数,是讲清楚对象的一生
“新生代用复制算法,老年代用标记清除或标记整理。”这句话谁都会背。但面试官更想听:“一个对象刚出生在Eden区,Minor GC时如果还活着,被复制到Survivor区,年龄加一。熬过15次GC,或者Survivor装不下,就晋升到老年代。老年代满了触发Full GC,STW停顿长,能避免就避免。”GC的本质是对象的生命周期管理,不是算法名词的堆砌。接着你可以主动提:“我们线上用G1,因为它能预测停顿时间,通过-XX:MaxGCPauseMillis设定目标,把堆分成Region,优先回收垃圾最多的区域。相比CMS,G1不会产生大量碎片,JDK9之后成了默认。”面试官听到“我们线上”,就知道你有实战。
类加载别只背双亲委派,说说打破它的场景
双亲委派机制,AppClassLoader找父类,父类找不到再自己找。背完这个,面试官会问:“有没有打破双亲委派的场景?”这时你可以说:“有,比如JDBC。DriverManager在rt.jar里,由BootstrapClassLoader加载,但它要加载各数据库厂商的Driver实现,那些jar在classpath里,父类加载器够不着。于是线程上下文类加载器登场,让父类加载器委托子类去加载,这就是双亲委派的打破。”能说出打破的案例,比背一百遍定义都有用。再补一句:“Tomcat也打破了,每个webapp一个WebAppClassLoader,实现应用隔离。”面试官会觉得你不仅读过源码,还想过为什么。
调优不是报数字,是讲一个故事
“Xmx4G,Xmn2G,SurvivorRatio=8。”报完这些,面试官面无表情。换个说法:“我们有个订单服务,大促前频繁Full GC,接口响应从50ms涨到800ms。用jstat看,老年代每秒涨1%,对象晋升太快。查代码发现有个查询一次性加载了十万条订单明细,大对象直接进老年代。后来改成分批查询,又调大了Survivor区,让短命对象在新生代就被回收。Full GC从一天五次降到一周一次,接口恢复到60ms。”调优不是参数竞赛,是问题诊断、假设验证、数据对比的完整故事。有场景、有工具、有动作、有结果,面试官自然点头。
加分的关键:主动暴露你的思考边界
遇到不会的,别硬编。面试官问ZGC,你如果没在生产用过,可以说:“ZGC我了解原理,染色指针加读屏障,停顿在1ms以内,适合超大堆。但我们堆只有8G,G1够用,贸然换ZGC吞吐量会降,所以没上。我本地测过,确实停顿极低,但生态兼容性还要观察。”诚实加上判断逻辑,比编造完美答案更让人信任。JVM被问烂了,但回答的深度永无止境。别人停在“是什么”,你走到“为什么”和“怎么选”。这一层之差,就是offer的分水岭。