加群联系作者vx:xiaoda0423
仓库地址:webvueblog.github.io/JavaPlusDoc...
1. 减少对象创建:使用对象复用(如线程池)
频繁创建和销毁对象会加剧 GC 压力。
            
            
              ini
              
              
            
          
          // 替代频繁 new Thread
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
    executor.submit(() -> {
        // 执行业务逻辑
        System.out.println("处理任务:" + Thread.currentThread().getName());
    });
}
executor.shutdown();2. 使用 StringBuilder 替代字符串拼接
字符串拼接容易产生大量中间对象。
            
            
              ini
              
              
            
          
          // 差的写法(每次拼接都会创建新对象)
String result = "";
for (int i = 0; i < 1000; i++) {
    result += i;
}
// 优化写法
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
    sb.append(i);
}
String result = sb.toString();3. 缓存热点对象,避免重复计算(局部变量表优化)
比如计算经常使用的日期格式化器等。
            
            
              typescript
              
              
            
          
          // 线程不安全(会创建很多 SimpleDateFormat)
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// 优化方法:使用 ThreadLocal 缓存
private static final ThreadLocal<SimpleDateFormat> dateFormatThreadLocal =
        ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
public static String formatDate(Date date) {
    return dateFormatThreadLocal.get().format(date);
}4. 手动触发 Full GC 做性能基准测试前清场
例如你在做某些性能测试之前:
            
            
              scss
              
              
            
          
          System.gc();  // 请求 JVM 执行一次 GC(注意不能频繁用,生产不建议)5. JVM 参数优化(启动参数)
这些是 JVM 层的设置,在运行时配置。例如:
            
            
              ruby
              
              
            
          
          java -Xms512m -Xmx2048m -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError解释:
- -Xms、- -Xmx:设置初始/最大堆大小
- -XX:+UseG1GC:使用 G1 垃圾收集器
- -XX:MaxGCPauseMillis=200:最大 GC 停顿时间
- -XX:+PrintGCDetails:打印 GC 详情
- -XX:+HeapDumpOnOutOfMemoryError:OOM 时 dump 堆
6. 避免在循环中频繁创建对象
            
            
              ini
              
              
            
          
          // 差写法
for (int i = 0; i < 10000; i++) {
    Pattern pattern = Pattern.compile("\d+");
    Matcher matcher = pattern.matcher("12345");
    matcher.matches();
}
// 优化写法
Pattern pattern = Pattern.compile("\d+");
for (int i = 0; i < 10000; i++) {
    Matcher matcher = pattern.matcher("12345");
    matcher.matches();
}7. 使用懒加载 + 单例模式(减少类加载和对象创建)
            
            
              csharp
              
              
            
          
          public class ConfigManager {
    private ConfigManager() {}
    private static class Holder {
        private static final ConfigManager INSTANCE = new ConfigManager();
    }
    public static ConfigManager getInstance() {
        return Holder.INSTANCE;
    }
}为什么优化:
- 类加载是惰性加载的,只有在调用 getInstance()时才会加载。
- 避免了提前创建对象,减少内存浪费。
8. 使用 -XX:+PrintGCDetails 分析 GC,优化代码定位内存泄漏
示例:定位哪里内存泄露:
            
            
              javascript
              
              
            
          
          Map<String, Object> cache = new HashMap<>();
while (true) {
    Object value = new byte[1024 * 1024]; // 模拟大对象
    cache.put(UUID.randomUUID().toString(), value); // 没有限制大小,容易内存泄漏
}优化方式:加缓存上限 + LRU
            
            
              typescript
              
              
            
          
          LinkedHashMap<String, Object> cache = new LinkedHashMap<>(1000, 0.75f, true) {
    protected boolean removeEldestEntry(Map.Entry<String, Object> eldest) {
        return size() > 1000;
    }
};9. 避免频繁的 System.currentTimeMillis() 调用(热点调用)
            
            
              scss
              
              
            
          
          // 差写法:每次调用都陷入到系统调用
System.currentTimeMillis();优化:使用缓存时间更新线程
            
            
              csharp
              
              
            
          
          public class TimeUtil {
    private static volatile long currentTime = System.currentTimeMillis();
    static {
        ScheduledExecutorService timer = Executors.newSingleThreadScheduledExecutor();
        timer.scheduleAtFixedRate(() -> currentTime = System.currentTimeMillis(), 0, 1, TimeUnit.MILLISECONDS);
    }
    public static long currentTimeMillis() {
        return currentTime;
    }
}10. Zero-copy 技术:避免 IO 缓冲拷贝(NIO)
传统方式:
            
            
              arduino
              
              
            
          
          // 传统 IO 每次读写都会复制缓冲区
FileInputStream fis = new FileInputStream("file.txt");
byte[] buffer = new byte[1024];
while (fis.read(buffer) != -1) {
    // 处理数据
}优化方式:Java NIO(零拷贝)
            
            
              ini
              
              
            
          
          FileChannel srcChannel = new FileInputStream("file.txt").getChannel();
FileChannel destChannel = new FileOutputStream("file_copy.txt").getChannel();
srcChannel.transferTo(0, srcChannel.size(), destChannel);  // 零拷贝方式11. 使用 G1/ZGC 等新型 GC,结合代码优化垃圾生成
JVM 启动参数:
            
            
              ruby
              
              
            
          
          java -XX:+UseG1GC -Xms512m -Xmx2g -XX:+PrintGCDetails -XX:+UseStringDeduplication代码配合优化:
            
            
              ini
              
              
            
          
          // 避免大量临时字符串重复创建(开启 G1 的字符串去重)
List<String> list = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
    list.add(("用户" + i).intern()); // intern 可以帮助字符串去重(慎用)
}12. 使用 ByteBuffer.allocateDirect 避免堆内存占用
            
            
              ini
              
              
            
          
          // 默认是在堆内存
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 优化:使用直接内存(减少 GC 压力,但分配成本略高)
ByteBuffer directBuffer = ByteBuffer.allocateDirect(1024);13. 避免频繁装箱/拆箱操作
            
            
              csharp
              
              
            
          
          // 差写法:频繁装箱
for (int i = 0; i < 10000; i++) {
    list.add(i); // i 自动装箱成 Integer
}
// 优化:使用基本类型数组或专用集合类(如 Trove、fastutil)
int[] arr = new int[10000];使用对象池、线程池,减少对象频繁创建
            
            
              ini
              
              
            
          
          ExecutorService executor = Executors.newFixedThreadPool(10);  // 重用线程,避免频繁创建销毁
// 比如数据库连接池使用 HikariCP
HikariDataSource ds = new HikariDataSource();
ds.setMaximumPoolSize(20); // 限制连接数量避免重复创建大对象 / 使用缓存
            
            
              arduino
              
              
            
          
          // 缓存 DateFormat、Pattern、等重量对象
private static final ThreadLocal<SimpleDateFormat> dateFormat =
        ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));使用 G1 GC 配置,适用于大堆和低延迟需求
JVM 启动参数:
            
            
              ruby
              
              
            
          
          -XX:+UseG1GC
-XX:+PrintGCDetails
-XX:MaxGCPauseMillis=200
-XX:+UseStringDeduplication内存泄漏定位示例
            
            
              arduino
              
              
            
          
          // 模拟内存泄漏:不断创建对象加入静态集合
private static final List<byte[]> list = new ArrayList<>();
public static void main(String[] args) {
    while (true) {
        byte[] b = new byte[1024 * 1024];
        list.add(b);
    }
}开启 JVM 参数 -XX:+HeapDumpOnOutOfMemoryError,使用 jvisualvm 或 MAT 分析堆。
使用并发容器代替同步
            
            
              arduino
              
              
            
          
          Map<String, String> map = new ConcurrentHashMap<>();避免类加载过程中执行大量静态代码
            
            
              arduino
              
              
            
          
          // 不建议在类加载时就执行重量级逻辑
static {
    // 连接数据库、大文件加载等操作不要放在这里
}(模拟内存使用 + 线程池)
            
            
              java
              
              
            
          
          import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
public class JvmTuningDemo {
    // 模拟一个缓存,未做清理,易导致内存泄漏
    private static final List<byte[]> memoryLeakList = new ArrayList<>();
    // 模拟线程池处理任务
    private static final ExecutorService executor = new ThreadPoolExecutor(
            4,                  // 核心线程数
            8,                  // 最大线程数
            60L, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(100),  // 有界队列
            new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
    );
    public static void main(String[] args) throws InterruptedException {
        System.out.println("模拟 JVM 压力启动...");
        for (int i = 0; i < 100_000; i++) {
            // 模拟大对象分配,占用堆内存
            memoryLeakList.add(new byte[1024 * 10]); // 10KB
            // 提交任务给线程池(模拟异步请求)
            final int taskId = i;
            executor.submit(() -> {
                try {
                    // 模拟业务逻辑执行
                    Thread.sleep(10);
                    if (taskId % 10_000 == 0) {
                        System.out.println("处理任务: " + taskId);
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
            // 控制内存增长速度
            if (i % 1000 == 0) {
                Thread.sleep(100);
            }
        }
        System.out.println("任务提交完毕,等待执行完成...");
        executor.shutdown();
        executor.awaitTermination(10, TimeUnit.MINUTES);
    }
}内存泄漏模拟 + 自动释放优化
            
            
              java
              
              
            
          
          import java.util.Map;
import java.util.WeakHashMap;
public class MemoryLeakFixedDemo {
    // 会被 GC 回收的缓存实现
    private static final Map<Object, byte[]> safeCache = new WeakHashMap<>();
    public static void main(String[] args) throws InterruptedException {
        System.out.println("开始缓存填充测试...");
        for (int i = 0; i < 100000; i++) {
            Object key = new Object(); // key 不保留引用
            safeCache.put(key, new byte[1024 * 100]); // 100KB per entry
            if (i % 1000 == 0) {
                System.gc(); // 手动触发 GC 观察 WeakHashMap 是否被清理
                System.out.printf("第 %d 次插入,缓存大小:%d\n", i, safeCache.size());
            }
        }
        System.out.println("测试结束");
    }
}✅ JVM 启动参数建议
            
            
              ruby
              
              
            
          
          -Xmx256m -Xms256m -Xss512k -XX:+UseG1GC -XX:+PrintGCDetails -Xloggc:gc.log📌 调优点说明:
- 使用 WeakHashMap避免强引用导致的内存泄漏
- 用于模拟和验证"缓存未清理"导致 OOM 的问题
线程池配置不当导致内存暴涨 + 有界线程池优化
            
            
              java
              
              
            
          
          import java.util.concurrent.*;
public class ThreadPoolMemoryDemo {
    public static void main(String[] args) throws InterruptedException {
        // 错误的做法:不限制任务队列大小(容易 OOM)
        ExecutorService badPool = new ThreadPoolExecutor(
                2, 4,
                60, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>() // 无限队列
        );
        // 推荐做法:限制队列大小 + 拒绝策略
        ExecutorService goodPool = new ThreadPoolExecutor(
                2, 4,
                60, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(1000),
                new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝时在主线程跑任务
        );
        for (int i = 0; i < 10_000; i++) {
            final int taskId = i;
            try {
                goodPool.submit(() -> {
                    try {
                        Thread.sleep(100); // 模拟 IO/业务耗时
                        if (taskId % 1000 == 0) {
                            System.out.println("执行任务:" + taskId);
                        }
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                });
            } catch (RejectedExecutionException e) {
                System.out.println("任务被拒绝:" + taskId);
            }
        }
        goodPool.shutdown();
        goodPool.awaitTermination(5, TimeUnit.MINUTES);
    }
}JVM 调优常见场景汇总
| 类型 | 场景问题 | 典型表现 | 常用工具 | 优化思路 | 
|---|
🚀 1. 内存溢出(OOM)问题
| 表现 | OutOfMemoryError: Java heap space | 
| 原因 | 对象创建过多、GC 无法及时回收 | 
| 工具 | jmap,MAT,VisualVM,HeapDump | 
| 优化 | 排查大对象、检查缓存、内存泄露、增加堆大小(-Xmx) | 
🧱 2. Metaspace(类元数据)溢出
| 表现 | OutOfMemoryError: Metaspace | 
| 原因 | 动态加载太多类(如反射、动态代理、频繁热部署) | 
| 工具 | jcmd,jmap,VisualVM | 
| 优化 | 合理设置 -XX:MaxMetaspaceSize,排查类加载器泄漏 | 
🧵 3. 线程过多,无法创建新线程
| 表现 | OutOfMemoryError: unable to create new native thread | 
| 原因 | 线程创建无上限,系统线程资源耗尽 | 
| 工具 | jstack,top -H,jconsole | 
| 优化 | 使用线程池,限制最大线程数,优化业务阻塞逻辑 | 
⏱ 4. 频繁 Full GC / GC 暴涨
| 表现 | GC 日志频繁出现 Full GC,应用卡顿、吞吐低 | 
| 原因 | 堆老年代频繁满,Survivor 区调优不合理 | 
| 工具 | jstat -gcutil,GClog,Arthas,VisualGC | 
| 优化 | 
- 调整堆内存大小:-Xmx,-Xms
- 调整新生代大小:-Xmn
- 优化对象生命周期和回收代策略
🐢 5. 应用响应慢、卡顿、吞吐低
| 表现 | RT(响应时间)变高,系统 TPS 下降 | 
| 原因 | GC 频繁、线程阻塞、IO 等待、锁竞争 | 
| 工具 | jstack,jfr,perf,Arthas,BTrace | 
| 优化 | 
- 减少大对象创建
- 优化锁(如 synchronized → ReentrantLock)
- 异步处理、限流、线程池调整
📦 6. 直接内存(DirectMemory)泄漏
| 表现 | OutOfMemoryError: Direct buffer memory | 
| 原因 | Netty 或 NIO 使用的堆外内存未及时释放 | 
| 工具 | jcmd VM.native_memory summary,Netty leak detector | 
| 优化 | 增加 -XX:MaxDirectMemorySize,手动释放 ByteBuffer,升级框架版本 | 
📈 7. 内存使用高但 GC 不回收
| 表现 | 应用占用大量内存,GC 没触发或没效果 | 
| 原因 | 持有对象引用、内存泄露、Soft/Weak 引用被滥用 | 
| 工具 | jmap,MAT,HeapDump | 
| 优化 | 定位强引用链,清理缓存对象,避免静态变量泄漏 | 
🧬 8. 类加载过多 / 类卸载失败
| 表现 | Metaspace 持续增长,PermGen 泄漏(Java 7) | 
| 原因 | 动态加载类没有被正确卸载(如 Web 容器热部署) | 
| 工具 | jvisualvm,jcmd VM.class_histogram | 
| 优化 | 确保类加载器能被 GC 回收,使用统一的加载器 | 
🧪 9. JVM 参数不合理
| 表现 | GC 效率低、内存空间浪费、OOM | 
| 原因 | 堆太小、线程栈太大、GC 参数不匹配 | 
| 工具 | -XX:+PrintGCDetails,jstat,Arthas | 
| 优化 | 调整参数: -Xmx,-Xms,-Xss,-Xmn,GC 算法选择 | 
🧨 10. 服务崩溃 / JVM Crash
| 表现 | JVM 崩溃、core dump、hs_err_pid.log 文件生成 | 
| 原因 | 本地方法调用异常、native 内存溢出、JNI 错误 | 
| 工具 | hs_err_pid.log,gdb,jfr,perf | 
| 优化 | 分析 core dump,检查 native 库,避免 JNI 滥用 | 
🚧 JVM 调优常用工具列表
| 工具 | 作用 | 
|---|---|
| jmap | 导出堆快照、对象统计 | 
| jstack | 查看线程栈 | 
| jstat | GC 监控 | 
| VisualVM/JMC | 图形化调试内存、CPU | 
| GClog + GCViewer | 分析垃圾回收日志 | 
| Arthas | 热诊断神器,线程、堆、方法调用监控 | 
| MAT | 内存泄漏分析工具 | 
| jcmd | 启动诊断命令、元空间统计 | 
| jfr | Java Flight Recorder,分析运行轨迹 |