JVM 内存结构主要包含堆 (Heap)、栈 (Stack)、方法区 (Method Area)、本地方法栈 (Native Method Stack) 和程序计数器 (PC Register)。其中,堆是 GC 的主要区域,可通过以下参数进行调优:
# JVM启动参数示例(以生产环境常用配置为例)
java -Xms2048m -Xmx2048m -Xmn768m -XX:MetaspaceSize=256m \
-XX:MaxMetaspaceSize=256m -XX:+UseG1GC \
-XX:SurvivorRatio=8 -XX:MaxGCPauseMillis=200 \
-jar your-application.jar
关键参数解析:
不同垃圾回收器适用于不同场景,常见选择如下:
# 选择CMS垃圾回收器(适合响应时间敏感的Web应用)
java -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled \
-XX:CMSInitiatingOccupancyFraction=70 -jar your-application.jar
# 选择ZGC垃圾回收器(适合超大堆内存、极低延迟场景)
java -XX:+UseZGC -XX:MaxHeapSize=16g -jar your-application.jar
各 GC 对比:
垃圾回收器 | 适用场景 | 特点 |
---|---|---|
Serial GC | 单线程、小内存应用 | 简单高效,停顿时间长 |
Parallel GC | 吞吐量优先的批处理任务 | 多线程并行收集 |
CMS | 响应时间敏感的 Web 应用 | 低停顿,并发标记 |
G1 | 大内存、混合负载 | 分区收集,可预测停顿 |
ZGC | 超大堆内存(TB 级) | 亚毫秒级停顿,实验性 |
案例描述:某电商系统频繁出现 OutOfMemoryError,堆内存持续增长。
排查步骤:
获取 Heap Dump:
# 使用jmap命令获取堆转储文件
jmap -dump:format=b,file=heapdump.hprof <pid>
分析工具:使用 Eclipse MAT 或 VisualVM 分析内存快照,发现大量java.util.ArrayList对象占用 90% 堆内存。
代码问题:
// 错误示例:未限制缓存大小导致内存溢出
private static final List<Order> orderCache = new ArrayList<>();
public void processOrders(List<Order> orders) {
orderCache.addAll(orders); // 持续添加,无清理机制
// ...
}
解决方案:
// 使用LRU缓存替代ArrayList
private static final LoadingCache<Long, Order> orderCache = Caffeine.newBuilder()
.maximumSize(10000) // 限制最大容量
.expireAfterWrite(10, TimeUnit.MINUTES) // 过期策略
.build(key -> orderService.getOrder(key));
JVM 将堆内存分为新生代 (Young Generation) 和老年代 (Old Generation):
对象晋升流程:
// 示例:对象在新生代和老年代之间的晋升
public class GCExample {
public static void main(String[] args) {
// 循环创建大对象,触发GC
for (int i = 0; i < 10000; i++) {
byte[] data = new byte[1024 * 1024]; // 1MB对象
// 当Eden区满时触发Minor GC,存活对象进入Survivor
// 多次GC后仍存活的对象晋升到老年代
}
}
}
常见内存泄漏场景:
检测工具:
# 使用jstat监控GC情况
jstat -gc <pid> 1000 10 # 每1秒输出一次,共10次
# 使用jstack分析线程状态(排查死锁等问题)
jstack <pid> > threaddump.txt
修复示例:
// 错误示例:静态集合导致内存泄漏
public class LeakExample {
private static final List<Object> cache = new ArrayList<>();
public void addToCache(Object obj) {
cache.add(obj); // 对象永久存活,不会被GC
}
}
// 正确示例:使用弱引用避免内存泄漏
public class SafeCache {
private static final ReferenceQueue<Object> queue = new ReferenceQueue<>();
private static final Set<WeakReference<Object>> cache =
Collections.newSetFromMap(new WeakHashMap<>());
public void addToCache(Object obj) {
cache.add(new WeakReference<>(obj, queue)); // 弱引用对象可被GC回收
}
}
案例:某数据分析系统处理 TB 级数据,堆内存配置为 32GB。
优化方案:
使用 G1 GC 并调整分区大小:
java -Xms32g -Xmx32g -XX:+UseG1GC -XX:G1HeapRegionSize=32m \
-XX:MaxGCPauseMillis=500 -jar data-processing.jar
对象指针压缩:
-XX:+UseCompressedOops # 启用指针压缩,减少内存占用
内存预分配:
-XX:NativeMemoryTracking=detail # 启用本地内存跟踪
合理配置线程池参数:
// 创建自定义线程池,避免OOM和资源耗尽
public class ThreadPoolConfig {
private static final int CORE_POOL_SIZE = Runtime.getRuntime().availableProcessors() * 2;
private static final int MAX_POOL_SIZE = CORE_POOL_SIZE * 2;
private static final long KEEP_ALIVE_TIME = 60L;
private static final BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(1000);
private static final ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setNameFormat("custom-thread-%d")
.setDaemon(true)
.build();
public static ExecutorService createThreadPool() {
return new ThreadPoolExecutor(
CORE_POOL_SIZE,
MAX_POOL_SIZE,
KEEP_ALIVE_TIME,
TimeUnit.SECONDS,
workQueue,
threadFactory,
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略:调用者执行
);
}
}
减少锁竞争的策略:
// 1. 分段锁优化(替代synchronized)
public class SegmentLockMap<K, V> {
private static final int SEGMENT_COUNT = 16;
private final List<Lock> locks;
private final List<Map<K, V>> segments;
public SegmentLockMap() {
locks = new ArrayList<>(SEGMENT_COUNT);
segments = new ArrayList<>(SEGMENT_COUNT);
for (int i = 0; i < SEGMENT_COUNT; i++) {
locks.add(new ReentrantLock());
segments.add(new ConcurrentHashMap<>());
}
}
public V get(K key) {
int segmentIndex = key.hashCode() % SEGMENT_COUNT;
locks.get(segmentIndex).lock();
try {
return segments.get(segmentIndex).get(key);
} finally {
locks.get(segmentIndex).unlock();
}
}
}
// 2. 无锁编程(使用Atomic类)
public class Counter {
private final AtomicLong count = new AtomicLong(0);
public long increment() {
return count.incrementAndGet(); // CAS操作,无锁化
}
}
死锁示例:
// 可能导致死锁的代码
public class DeadlockExample {
private static final Object lock1 = new Object();
private static final Object lock2 = new Object();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
synchronized (lock1) {
System.out.println("Thread 1: Holding lock 1...");
try { Thread.sleep(100); } catch (InterruptedException e) {}
System.out.println("Thread 1: Waiting for lock 2...");
synchronized (lock2) {
System.out.println("Thread 1: Holding lock 1 & 2");
}
}
});
Thread t2 = new Thread(() -> {
synchronized (lock2) {
System.out.println("Thread 2: Holding lock 2...");
try { Thread.sleep(100); } catch (InterruptedException e) {}
System.out.println("Thread 2: Waiting for lock 1...");
synchronized (lock1) {
System.out.println("Thread 2: Holding lock 2 & 1");
}
}
});
t1.start();
t2.start();
}
}
死锁检测工具:
# 使用jstack分析死锁
jstack <pid> | grep -A 20 "found one Java-level deadlock"
预防策略:
案例:使用 Disruptor 框架实现高性能事件处理
// 基于Disruptor的高性能生产者-消费者模式
public class DisruptorExample {
private static final int BUFFER_SIZE = 1024;
public static void main(String[] args) {
// 创建EventFactory
EventFactory<OrderEvent> factory = OrderEvent::new;
// 创建RingBuffer
RingBuffer<OrderEvent> ringBuffer = RingBuffer.createSingleProducer(
factory, BUFFER_SIZE, new YieldingWaitStrategy());
// 创建SequenceBarrier
SequenceBarrier barrier = ringBuffer.newBarrier();
// 创建消费者数组
WorkHandler<OrderEvent>[] handlers = new WorkHandler[4];
for (int i = 0; i < handlers.length; i++) {
handlers[i] = event -> {
// 处理订单事件
System.out.println("Consumer " + i + " processed: " + event.getOrderId());
};
}
// 创建WorkerPool
WorkerPool<OrderEvent> workerPool = new WorkerPool<>(
ringBuffer, barrier, new FatalExceptionHandler(), handlers);
// 启动消费者
ringBuffer.addGatingSequences(workerPool.getWorkerSequences());
workerPool.start(Executors.newFixedThreadPool(4));
// 生产者发布事件
for (long i = 0; i < 1000; i++) {
long sequence = ringBuffer.next();
OrderEvent event = ringBuffer.get(sequence);
event.setOrderId(i);
ringBuffer.publish(sequence);
}
}
// 订单事件类
static class OrderEvent {
private long orderId;
public long getOrderId() { return orderId; }
public void setOrderId(long orderId) { this.orderId = orderId; }
}
}
工具 | 用途 | 示例命令 / 操作 |
---|---|---|
jstat | 监控 GC 和内存使用情况 | jstat -gc 1000 |
jmap | 生成堆转储文件 | jmap -dump:format=b,file=heap.hprof |
jstack | 生成线程快照 | jstack > threaddump.txt |
VisualVM | 图形化监控工具 | 启动后连接目标 JVM |
YourKit | 商业级性能分析工具 | 集成到 IDE 或独立运行 |
发现问题:某电商系统高峰期响应时间过长
收集数据:
# 收集JVM性能数据
jstat -gcutil <pid> 1000 60 > gc_stat.log
jstack <pid> > threaddump_$(date +%s).txt
jmap -heap <pid> > heap_info.txt
分析数据:
定位问题:
// 问题代码:线程池队列满导致任务阻塞
ExecutorService executor = Executors.newFixedThreadPool(10);
// 应使用有界队列替代无界队列
优化方案:
// 优化后的线程池配置
ExecutorService executor = new ThreadPoolExecutor(
10, 20, 60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1000), // 有界队列
new ThreadPoolExecutor.CallerRunsPolicy()
);
通过以上全方位的性能优化策略,可以显著提升 Java 应用的响应速度、吞吐量和稳定性,打造高效可靠的生产系统。
关注公众号:搜 架构研究站,回复:资料领取,即可获取全部面试题以及1000+份学习资料