Java 函数式编程、并发编程与 JVM 原理学习总结
一、Java 函数式编程
1. 核心概念
- 函数式接口:仅包含一个抽象方法的接口
@FunctionalInterface interface MyFunction { int apply(int a, int b); }
- Lambda表达式:
(参数) -> 表达式
MyFunction add = (a, b) -> a + b;
- 方法引用:
List<String> names = Arrays.asList("Alice", "Bob"); names.forEach(System.out::println); // 实例方法引用
2. Stream API
List<Integer> numbers = Arrays.asList(1,2,3,4,5);
List<Integer> squares = numbers.stream()
.map(n -> n * n)
.collect(Collectors.toList());
二、Java 并发编程
1. 线程创建方式对比
方式 | 优点 | 缺点 |
---|---|---|
继承Thread | 简单直接 | 无法继承其他类 |
实现Runnable | 支持多继承 | 无返回值 |
实现Callable | 支持返回值/异常 | 需要配合FutureTask使用 |
2. 线程安全问题
// 使用AtomicInteger保证原子性
AtomicInteger counter = new AtomicInteger(0);
counter.incrementAndGet();
// 同步代码块示例
synchronized(lock) {
// 临界区代码
}
三、JVM 原理深度解析
1. 内存模型(重点)
典型内存溢出场景
// 堆内存溢出示例
List<byte[]> list = new ArrayList<>();
while(true) {
list.add(new byte[1024*1024]); // 不断创建1MB数组
}
// 元空间溢出示例(JDK8+)
List<Class<?>> classes = new ArrayList<>();
while(true) {
classes.add(new ClassLoader(){}.defineClass("MyClass", new byte[1024], 0, 1024));
}
2. 类加载机制
双亲委派流程:
- 应用程序类加载器 → 扩展类加载器 → 启动类加载器
- 父加载器无法加载时,才由子加载器加载
破坏双亲委派的场景:
- SPI 服务加载(如JDBC)
- OSGi 模块化系统
3. 垃圾回收机制
垃圾判定算法对比
算法 | 优点 | 缺点 |
---|---|---|
引用计数法 | 实时回收 | 循环引用问题 |
可达性分析 | 解决循环引用 | 需要STW暂停 |
GC日志分析示例
[GC (Allocation Failure) [PSYoungGen: 65536K->1024K(76288K)]
65536K->1124K(251392K), 0.0023456 secs]
PSYoungGen
:Parallel Scavenge收集器- Allocation Failure:触发GC的原因
- 前后堆内存变化:年轻代回收效果
四、线上问题排查实战
1. 常见问题排查工具链
2. 内存泄漏排查流程
- 生成堆转储文件
jmap -dump:format=b,file=heap.bin <pid>
- 使用MAT分析
- 查找Dominator Tree中的大对象
- 分析GC Roots引用链
3. CPU飙高问题定位
top -Hp <pid> # 查看高CPU线程
printf "%x\n" 12345 # 转换线程ID为16进制
jstack <pid> | grep 0x3039 -A 30 # 查看线程栈
五、实战问题解析
问题1:内存溢出代码分析
public static void main(String[] args) {
List<String> list = new ArrayList<>();
while (true) {
list.add(new String(new char[1024 * 1024]));
}
}
问题原因:在无限循环中不断创建1MB大小的字符串对象,导致堆内存耗尽
解决方案:
- 增加JVM堆大小:
-Xmx4g
- 检查业务逻辑是否存在内存泄漏
- 使用内存分析工具定位大对象
问题2:并发文件处理
ExecutorService executor = Executors.newFixedThreadPool(4);
List<String> sharedData = Collections.synchronizedList(new ArrayList<>());
// 使用函数式接口处理文件
Function<Path, List<String>> fileProcessor = path -> {
try {
return Files.readAllLines(path);
} catch (IOException e) {
return Collections.emptyList();
}
};
// 内存监控线程
new Thread(() -> {
while(true) {
if (sharedData.size() > 100_000) {
System.out.println("内存警告!");
}
Thread.sleep(1000);
}
}).start();
六、关键总结
- 函数式编程:通过Lambda+Stream实现声明式编程,提高代码可读性
- 并发安全:合理使用synchronized/Lock/原子类保证线程安全
- JVM核心:
- 堆内存管理是性能调优重点
- 类加载机制保障Java生态安全
- GC算法选择直接影响系统吞吐
- 线上排查:
- 内存问题优先使用MAT分析堆转储
- CPU问题通过线程栈定位热点代码
- 结合Arthas实现动态诊断