Java 后端高频面试题解析:JVM、集合、并发与类加载

一、JVM 内存结构与垃圾回收

Java 面试必问。运行时数据区分几块:

区域 存什么 线程
堆 Heap 对象实例,GC 主战场 共享
方法区/元空间 类信息、常量、静态变量 共享
虚拟机栈 栈帧、局部变量、方法调用 私有
本地方法栈 native 方法 私有
程序计数器 当前字节码行号 私有

GC 思路:用可达性分析(从 GC Roots 出发,不可达即回收)替代引用计数,避免循环引用问题。堆分代:年轻代(Eden + 两个 Survivor)放新对象、老年存放长期存活对象,Minor GC 频繁、Full GC 慢。现代默认 G1,把堆分成 Region 做增量回收,兼顾吞吐与停顿。

二、集合框架:HashMap 与 ConcurrentHashMap

  • HashMap(JDK 1.8):数组 + 链表 + 红黑树。key 的 hash 做扰动后定位桶;链表长度超 8 且数组容量 ≥ 64 转红黑树;负载因子 0.75,扩容翻倍并 rehash;
  • ConcurrentHashMap(1.8):放弃分段锁,改成”数组 + 链表/红黑树 + CAS + synchronized(只锁头节点)”,并发度更高;读基本无锁。
// 线程池是并发题的高频考点
ThreadPoolExecutor pool = new ThreadPoolExecutor(
    4, 8, 60, TimeUnit.SECONDS,
    new LinkedBlockingQueue<>(100),
    new ThreadPoolExecutor.CallerRunsPolicy());

三、并发与多线程

对比 synchronized ReentrantLock
实现 JVM 内置,自动释放 API 层,需手动 unlock
功能 基础互斥 可中断、公平锁、多条件
性能 1.6 后锁升级优化,够用 高竞争下更灵活

要点:volatile 保证可见性但不保证原子性;线程池核心参数(核心数 / 最大数 / 空闲时间 / 队列 / 拒绝策略)要会算;Executors.newFixedThreadPool 等便捷方法可能因无界队列堆积任务,生产推荐手动 ThreadPoolExecutor 并配有界队列 + 合理拒绝策略。

四、String、equals 与 hashCode

  • String 不可变,常量池复用;
  • equals 相等则 hashCode 必须相等(反之不要求);
  • 重写 equals 必须重写 hashCode,否则 HashMap 当 key 会出问题。

五、类加载:双亲委派

加载顺序:自定义加载器 → 应用类加载器 → 扩展类加载器 → 启动类加载器,自底向上委托,自顶向下尝试加载。好处是防止核心类被篡改(如自己写个 java.lang.String 不会被加载)。打破委派的场景:SPI(JDBC)、热部署。

六、答题要点

Java 面试题重”机制 + 版本演进 + 代价”:HashMap 要讲 1.7 头插与 1.8 尾插/红黑树的区别,GC 要讲分代与 G1,并发要讲 volatile 的边界。能说清演进和取舍,比背结论更加分。