1. Java面试题基础系列228道(2)概述
2019年的这套Java面试题基础系列共包含228道题目,主要覆盖Java基础、JVM原理等核心知识点。作为Java开发者技术能力检测的重要参考,这套题目能有效帮助求职者查漏补缺,特别适合准备面试的初中级开发者系统复习。
这套题目有几个显著特点:
- 问题设计由浅入深,从基础语法到JVM底层实现都有涉及
- 包含大量实际开发中容易忽视的细节问题
- 题目来源真实企业面试,具有很强代表性
- 对JVM内存模型、垃圾回收机制等难点有深入探讨
2. JVM核心知识点解析
2.1 JVM内存区域划分
Java虚拟机运行时数据区主要分为:
- 方法区(Method Area):存储类信息、常量、静态变量等
- 堆(Heap):对象实例分配区域
- 虚拟机栈(VM Stack):方法调用栈帧
- 本地方法栈(Native Method Stack)
- 程序计数器(Program Counter Register)
其中堆和方法区是线程共享的,其他区域为线程私有。
2.2 对象内存布局
一个Java对象在内存中通常包含:
-
对象头(Header)
- Mark Word:存储对象运行时数据(哈希码、GC分代年龄等)
- 类型指针:指向类元数据的指针
- 实例数据(Instance Data)
- 对齐填充(Padding)
示例代码:
class ObjectLayout {
private int x; // 实例数据
private long y; // 实例数据
}
2.3 垃圾回收机制
2.3.1 可达性分析算法
JVM通过GC Roots对象作为起点,向下搜索引用链。不在引用链上的对象即为可回收对象。GC Roots包括:
- 虚拟机栈中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中JNI引用的对象
2.3.2 垃圾回收算法
常见GC算法对比:
| 算法类型 | 特点 | 适用场景 | 缺点 |
|---|---|---|---|
| 标记-清除 | 简单直接 | 老年代 | 产生内存碎片 |
| 标记-整理 | 无碎片 | 老年代 | 移动对象成本高 |
| 复制算法 | 高效无碎片 | 新生代 | 内存利用率低 |
| 分代收集 | 综合优势 | 实际应用 | 实现复杂 |
3. 常见面试题深度解析
3.1 对象分配过程
对象优先在Eden区分配,当Eden区没有足够空间时,触发Minor GC。大对象直接进入老年代。
示例代码:
// -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails
public class ObjectAllocation {
public static void main(String[] args) {
byte[] allocation1 = new byte[2 * 1024 * 1024]; // Eden区分配
byte[] allocation2 = new byte[2 * 1024 * 1024];
byte[] allocation3 = new byte[2 * 1024 * 1024];
byte[] allocation4 = new byte[4 * 1024 * 1024]; // 触发Minor GC
}
}
3.2 内存溢出场景
3.2.1 堆内存溢出
// -Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError
public class HeapOOM {
public static void main(String[] args) {
List<byte[]> list = new ArrayList<>();
while(true) {
list.add(new byte[1024 * 1024]);
}
}
}
3.2.2 栈内存溢出
// -Xss128k
public class StackSOF {
private int stackLength = 1;
public void stackLeak() {
stackLength++;
stackLeak();
}
public static void main(String[] args) {
StackSOF oom = new StackSOF();
try {
oom.stackLeak();
} catch (Throwable e) {
System.out.println("stack length:" + oom.stackLength);
throw e;
}
}
}
3.3 GC日志分析
通过添加JVM参数获取GC日志:
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc.log
典型GC日志示例:
2023-07-20T14:23:45.731+0800: [GC (Allocation Failure)
[PSYoungGen: 6144K->640K(9216K)] 6144K->4744K(19456K), 0.0028285 secs]
关键字段说明:
- PSYoungGen:Parallel Scavenge收集器
- 6144K->640K:GC前->GC后年轻代使用量
- 9216K:年轻代总容量
- 0.0028285 secs:GC耗时
4. JVM调优实战
4.1 常用JVM参数
| 参数 | 说明 | 示例 |
|---|---|---|
| -Xms | 初始堆大小 | -Xms1g |
| -Xmx | 最大堆大小 | -Xmx4g |
| -Xmn | 年轻代大小 | -Xmn2g |
| -XX:SurvivorRatio | Eden/Survivor比例 | -XX:SurvivorRatio=8 |
| -XX:MaxTenuringThreshold | 晋升老年代年龄 | -XX:MaxTenuringThreshold=15 |
4.2 内存泄漏排查步骤
-
获取堆转储文件
jmap -dump:format=b,file=heap.hprof <pid> - 使用MAT工具分析
- 查看支配树和大对象
- 分析引用链
4.3 GC选择建议
根据应用特点选择GC:
- 吞吐量优先:Parallel Scavenge + Parallel Old
- 低延迟优先:G1或ZGC
- 中小堆:CMS(JDK8及之前)
- 大堆(>4G):G1或ZGC
5. 高频面试题解答
5.1 finalize()方法的作用
finalize()是对象被回收前的最后机会,但不建议依赖它进行资源释放。因为:
- 执行时机不确定
- 可能造成对象复活
- 影响GC性能
5.2 四种引用类型
| 引用类型 | GC时表现 | 用途 |
|---|---|---|
| 强引用 | 不回收 | 普通对象引用 |
| 软引用 | 内存不足时回收 | 缓存 |
| 弱引用 | 下次GC时回收 | 缓存/监听器 |
| 虚引用 | 随时可能回收 | 跟踪对象回收 |
5.3 类加载过程
- 加载:获取类的二进制字节流
- 验证:确保Class文件符合规范
- 准备:为静态变量分配内存
- 解析:符号引用转直接引用
- 初始化:执行静态代码块
6. 面试准备建议
- 理解JVM内存模型图示
- 掌握常见OOM错误场景
- 熟悉GC日志格式
- 了解常用JVM参数
- 准备项目中的调优案例
建议通过以下命令实践:
# 查看默认JVM参数
java -XX:+PrintFlagsFinal -version
# 监控JVM状态
jstat -gcutil <pid> 1000
这套面试题的价值在于它系统性地覆盖了Java基础知识的各个方面,特别是JVM相关原理。在实际面试中,面试官往往会根据候选人的回答深度进行追问,因此建议不仅要记住答案,更要理解背后的原理。

2120

被折叠的 条评论
为什么被折叠?



