一、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 的边界。能说清演进和取舍,比背结论更加分。