当前位置: 首页 > news >正文

JVM系列六:JVM性能调优实战指南

🚀 JVM系列六:JVM性能调优实战指南

🌟 引言

JVM性能调优是Java开发者必须掌握的核心技能。本文将从实战角度出发,详细介绍性能调优工具的使用、常见问题的分析方法,以及真实的调优案例,帮助您快速提升JVM调优能力。


文章目录

  • 🚀 JVM系列六:JVM性能调优实战指南
    • 🌟 引言
    • 🔧 六、JVM 性能调优实战
      • 🛠️ 性能调优工具
        • 💻 jps、jstat、jinfo 等命令行工具
          • 🔍 jps - Java进程状态工具
          • 📊 jstat - JVM统计监控工具
          • ⚙️ jinfo - Java配置信息工具
          • 🔧 其他重要命令行工具
        • 🖥️ 图形化工具使用
          • 📊 JConsole - 内置监控工具
          • 🔍 VisualVM - 强大的分析工具
          • 🚀 JProfiler - 商业级分析工具
          • 🔧 Eclipse MAT - 内存分析工具
      • 🔍 常见性能问题分析
        • 💧 内存泄漏排查
          • 🎯 内存泄漏的典型表现
          • 🔍 排查步骤
          • 💡 常见内存泄漏场景
        • 🔒 线程死锁定位
          • 🎯 死锁检测
          • 💡 死锁示例分析
          • 🛠️ 死锁预防策略
        • 📈 CPU 占用过高分析
          • 🔍 分析步骤
          • 💡 常见CPU高使用场景
      • 📊 调优案例分享
        • 🚀 高并发场景下的 JVM 调优
          • 📋 案例背景
          • 🔍 问题分析
          • 🛠️ 调优方案
          • 📈 调优效果
        • 💾 内存不足问题的解决方案
          • 📋 案例背景
          • 🔍 问题分析
          • 🛠️ 解决方案
          • 📈 优化效果
    • 🎯 调优最佳实践
      • 📋 调优流程
      • 🛠️ 调优原则
      • 📊 关键监控指标
      • 🔧 常用JVM参数模板


🔧 六、JVM 性能调优实战

🛠️ 性能调优工具

💻 jps、jstat、jinfo 等命令行工具

命令行工具是JVM调优的基础工具,具有轻量级、实时性强的特点。

🔍 jps - Java进程状态工具

功能:列出正在运行的Java进程

# 基本用法
jps# 显示完整的类名
jps -l# 显示JVM参数
jps -v# 显示传递给main方法的参数
jps -m# 组合使用
jps -lvm

输出示例

12345 com.example.Application -Xmx2g -Xms1g
12346 org.apache.catalina.startup.Bootstrap
12347 sun.tools.jps.Jps -lvm
📊 jstat - JVM统计监控工具

功能:监控JVM运行时状态信息

jstat
类加载统计
垃圾收集统计
堆内存统计
方法区统计
编译统计
-class
-gc
-gccapacity
-gcutil
-heap
-metaspace
-compiler

常用命令

选项功能示例
-gc垃圾收集统计jstat -gc 12345 1s 10
-gcutil垃圾收集利用率jstat -gcutil 12345 5s
-gccapacity各代容量统计jstat -gccapacity 12345
-class类加载统计jstat -class 12345
-compiler编译统计jstat -compiler 12345

实战示例

# 每5秒输出一次GC统计,共输出10次
jstat -gc 12345 5s 10# 输出结果解析
# S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT
# 8704.0 8704.0  0.0   8158.4 69952.0  23401.6   175104.0   2032.6   4864.0 2438.7 512.0  260.4    274    0.298   0      0.000    0.298

字段含义

  • S0C/S1C: Survivor0/1区容量
  • S0U/S1U: Survivor0/1区使用量
  • EC/EU: Eden区容量/使用量
  • OC/OU: 老年代容量/使用量
  • MC/MU: 元空间容量/使用量
  • YGC/YGCT: 年轻代GC次数/时间
  • FGC/FGCT: Full GC次数/时间
⚙️ jinfo - Java配置信息工具

功能:查看和修改JVM配置参数

# 查看所有JVM参数
jinfo 12345# 查看特定参数
jinfo -flag MaxHeapSize 12345
jinfo -flag UseG1GC 12345# 动态修改参数(仅限部分参数)
jinfo -flag +PrintGC 12345
jinfo -flag -PrintGC 12345# 查看系统属性
jinfo -sysprops 12345
🔧 其他重要命令行工具

jstack - 线程堆栈分析

# 生成线程堆栈快照
jstack 12345 > thread_dump.txt# 检测死锁
jstack -l 12345# 强制生成堆栈(进程无响应时)
jstack -F 12345

jmap - 内存映像工具

# 生成堆转储文件
jmap -dump:format=b,file=heap.hprof 12345# 查看堆内存使用情况
jmap -heap 12345# 查看对象统计信息
jmap -histo 12345# 查看类加载器统计
jmap -clstats 12345

jhsdb - 服务性代理调试器

# 分析堆转储文件
jhsdb jmap --heap --pid 12345# 分析核心转储文件
jhsdb jmap --heap --core core.12345 --exe $JAVA_HOME/bin/java
🖥️ 图形化工具使用

图形化工具提供了更直观的监控界面和强大的分析功能。

📊 JConsole - 内置监控工具

特点

  • ✅ JDK内置,无需额外安装
  • ✅ 实时监控多个JVM指标
  • ✅ 支持远程连接
  • ❌ 功能相对简单

使用步骤

# 启动JConsole
jconsole# 连接远程JVM(需要开启JMX)
# 在目标JVM启动时添加参数:
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=9999
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false

监控面板

JConsole
概览
内存
线程
VM摘要
MBeans
堆内存使用
非堆内存使用
内存池详情
线程数量
线程状态
死锁检测
🔍 VisualVM - 强大的分析工具

特点

  • ✅ 功能丰富的性能分析
  • ✅ 支持插件扩展
  • ✅ 堆转储分析
  • ✅ CPU和内存采样

核心功能

功能模块描述适用场景
监视器实时监控JVM状态日常监控
线程线程状态和堆栈分析死锁排查
采样器CPU和内存采样分析性能瓶颈定位
分析器详细的性能分析深度性能调优
堆转储内存快照分析内存泄漏排查

使用示例

// 示例应用 - 内存泄漏演示
public class MemoryLeakDemo {private static List<byte[]> memoryLeak = new ArrayList<>();public static void main(String[] args) throws InterruptedException {while (true) {// 模拟内存泄漏memoryLeak.add(new byte[1024 * 1024]); // 1MBThread.sleep(100);if (memoryLeak.size() % 100 == 0) {System.out.println("已分配内存: " + memoryLeak.size() + "MB");}}}
}
🚀 JProfiler - 商业级分析工具

特点

  • ✅ 专业的性能分析功能
  • ✅ 直观的用户界面
  • ✅ 强大的内存和CPU分析
  • ❌ 商业软件,需要许可证

核心功能

JProfiler
实时监控
CPU分析
内存分析
线程分析
数据库分析
调用树
热点方法
调用图
堆遍历
分配记录
垃圾收集
🔧 Eclipse MAT - 内存分析工具

特点

  • ✅ 专门用于堆转储分析
  • ✅ 强大的内存泄漏检测
  • ✅ 免费开源
  • ✅ 支持大型堆转储文件

分析流程

应用程序 JVM Eclipse MAT 运行应用 生成堆转储 加载.hprof文件 自动分析 生成泄漏报告 显示分析结果 应用程序 JVM Eclipse MAT

🔍 常见性能问题分析

💧 内存泄漏排查

内存泄漏是Java应用中最常见的性能问题之一。

🎯 内存泄漏的典型表现
内存泄漏症状
内存使用持续增长
频繁Full GC
应用响应变慢
最终OutOfMemoryError
堆内存利用率持续上升
GC时间占比过高
请求处理时间增加
应用崩溃
🔍 排查步骤

1. 监控内存使用趋势

# 持续监控堆内存使用情况
jstat -gc 12345 10s# 观察指标:
# - 老年代使用率是否持续增长
# - Full GC频率是否过高
# - GC后内存是否能有效回收

2. 生成堆转储文件

# 手动生成堆转储
jmap -dump:format=b,file=heap_$(date +%Y%m%d_%H%M%S).hprof 12345# 自动生成(OOM时)
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/path/to/dumps/

3. 分析堆转储文件

使用Eclipse MAT分析:

1. 打开MAT,导入.hprof文件
2. 查看Leak Suspects报告
3. 分析Dominator Tree
4. 检查GC Roots
5. 查找可疑的大对象
💡 常见内存泄漏场景

1. 集合类未清理

// 问题代码
public class CacheManager {private static Map<String, Object> cache = new HashMap<>();public void addToCache(String key, Object value) {cache.put(key, value); // 只添加,从不清理}
}// 解决方案
public class ImprovedCacheManager {private static Map<String, Object> cache = new ConcurrentHashMap<>();private static final int MAX_SIZE = 1000;public void addToCache(String key, Object value) {if (cache.size() >= MAX_SIZE) {// 清理最老的条目cache.entrySet().iterator().remove();}cache.put(key, value);}// 或使用LRU缓存private static final Map<String, Object> lruCache = new LinkedHashMap<String, Object>(16, 0.75f, true) {@Overrideprotected boolean removeEldestEntry(Map.Entry<String, Object> eldest) {return size() > MAX_SIZE;}};
}

2. 监听器未注销

// 问题代码
public class EventPublisher {private List<EventListener> listeners = new ArrayList<>();public void addListener(EventListener listener) {listeners.add(listener);}// 缺少removeListener方法
}// 解决方案
public class ImprovedEventPublisher {private List<EventListener> listeners = new CopyOnWriteArrayList<>();public void addListener(EventListener listener) {listeners.add(listener);}public void removeListener(EventListener listener) {listeners.remove(listener);}// 使用弱引用避免内存泄漏private List<WeakReference<EventListener>> weakListeners = new ArrayList<>();
}

3. 线程局部变量未清理

// 问题代码
public class ThreadLocalDemo {private static ThreadLocal<List<String>> threadLocal = new ThreadLocal<>();public void processRequest() {List<String> data = new ArrayList<>();threadLocal.set(data);// 处理完成后未清理}
}// 解决方案
public class ImprovedThreadLocalDemo {private static ThreadLocal<List<String>> threadLocal = new ThreadLocal<>();public void processRequest() {try {List<String> data = new ArrayList<>();threadLocal.set(data);// 业务处理} finally {threadLocal.remove(); // 确保清理}}
}
🔒 线程死锁定位

死锁是多线程应用中的严重问题,会导致应用完全停止响应。

🎯 死锁检测

1. 使用jstack检测

# 生成线程转储
jstack 12345 > thread_dump.txt# 查找死锁信息
grep -A 20 "Found Java-level deadlock" thread_dump.txt

2. 使用JConsole检测

1. 连接到目标JVM
2. 切换到"线程"标签
3. 点击"检测死锁"按钮
4. 查看死锁详情
💡 死锁示例分析

经典死锁场景

public class DeadlockDemo {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: 获得lock1");try { Thread.sleep(100); } catch (InterruptedException e) {}synchronized (lock2) {System.out.println("Thread 1: 获得lock2");}}});Thread t2 = new Thread(() -> {synchronized (lock2) {System.out.println("Thread 2: 获得lock2");try { Thread.sleep(100); } catch (InterruptedException e) {}synchronized (lock1) {System.out.println("Thread 2: 获得lock1");}}});t1.start();t2.start();}
}

jstack输出分析

Found Java-level deadlock:
=============================
"Thread-1":waiting to lock monitor 0x00007f8b1c005208 (object 0x000000076ab62208, a java.lang.Object),which is held by "Thread-0"
"Thread-0":waiting to lock monitor 0x00007f8b1c007008 (object 0x000000076ab62218, a java.lang.Object),which is held by "Thread-1"Java stack information for the threads listed above:
===================================================
"Thread-1":at DeadlockDemo.lambda$main$1(DeadlockDemo.java:23)- waiting to lock <0x000000076ab62208> (a java.lang.Object)- locked <0x000000076ab62218> (a java.lang.Object)
🛠️ 死锁预防策略

1. 锁顺序一致

public class DeadlockPrevention {private static final Object lock1 = new Object();private static final Object lock2 = new Object();// 确保所有线程以相同顺序获取锁public void method1() {synchronized (lock1) {synchronized (lock2) {// 业务逻辑}}}public void method2() {synchronized (lock1) { // 与method1相同的顺序synchronized (lock2) {// 业务逻辑}}}
}

2. 使用超时锁

public class TimeoutLockDemo {private final ReentrantLock lock1 = new ReentrantLock();private final ReentrantLock lock2 = new ReentrantLock();public boolean transferMoney(Account from, Account to, double amount) {try {if (lock1.tryLock(1, TimeUnit.SECONDS)) {try {if (lock2.tryLock(1, TimeUnit.SECONDS)) {try {// 执行转账操作return true;} finally {lock2.unlock();}}} finally {lock1.unlock();}}} catch (InterruptedException e) {Thread.currentThread().interrupt();}return false; // 获取锁失败}
}
📈 CPU 占用过高分析

CPU占用过高通常表明存在性能瓶颈或无限循环等问题。

🔍 分析步骤

1. 确定高CPU使用的线程

# 查看进程CPU使用情况
top -p 12345# 查看线程级别的CPU使用
top -H -p 12345# 或使用ps命令
ps -mp 12345 -o THREAD,tid,time | sort -k2 -nr

2. 获取线程堆栈

# 将线程ID转换为16进制
printf "%x\n" 12345# 生成线程转储
jstack 12345 > thread_dump.txt# 在转储文件中查找对应线程
grep -A 20 "nid=0x3039" thread_dump.txt

3. 分析CPU热点

使用async-profiler进行CPU采样:

# 下载async-profiler
wget https://github.com/jvm-profiling-tools/async-profiler/releases/download/v2.9/async-profiler-2.9-linux-x64.tar.gz# 开始采样
java -jar async-profiler.jar -e cpu -d 30 -f profile.html 12345# 生成火焰图
java -jar async-profiler.jar -e cpu -d 30 -o flamegraph 12345
💡 常见CPU高使用场景

1. 无限循环

// 问题代码
public class InfiniteLoopDemo {public void processData() {while (true) {// 没有适当的退出条件或休眠doSomeWork();}}
}// 解决方案
public class ImprovedProcessing {private volatile boolean running = true;public void processData() {while (running) {try {doSomeWork();Thread.sleep(100); // 适当休眠} catch (InterruptedException e) {Thread.currentThread().interrupt();break;}}}public void stop() {running = false;}
}

2. 频繁的GC

# 监控GC情况
jstat -gc 12345 1s# 如果发现频繁GC,检查:
# - 堆内存配置是否合理
# - 是否存在内存泄漏
# - 对象创建是否过于频繁

3. 低效的算法

// 问题代码 - O(n²)复杂度
public List<String> findDuplicates(List<String> list) {List<String> duplicates = new ArrayList<>();for (int i = 0; i < list.size(); i++) {for (int j = i + 1; j < list.size(); j++) {if (list.get(i).equals(list.get(j))) {duplicates.add(list.get(i));}}}return duplicates;
}// 优化后 - O(n)复杂度
public List<String> findDuplicatesOptimized(List<String> list) {Set<String> seen = new HashSet<>();Set<String> duplicates = new HashSet<>();for (String item : list) {if (!seen.add(item)) {duplicates.add(item);}}return new ArrayList<>(duplicates);
}

📊 调优案例分享

🚀 高并发场景下的 JVM 调优
📋 案例背景

系统概况

  • 电商平台订单处理系统
  • 日均订单量:100万+
  • 峰值QPS:5000+
  • 服务器配置:16核32GB内存

遇到的问题

  • 高峰期响应时间过长
  • 频繁的Full GC
  • 系统偶发性不可用
🔍 问题分析

1. 监控数据收集

# GC日志分析
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintGCApplicationStoppedTime
-Xloggc:/path/to/gc.log# 关键指标:
# - Full GC频率:每5分钟1次
# - GC停顿时间:平均2秒
# - 堆内存使用率:持续在85%以上

2. 堆内存分析

# 生成堆转储
jmap -dump:format=b,file=heap.hprof 12345# MAT分析结果:
# - 订单缓存占用60%内存
# - 大量临时对象未及时回收
# - 字符串常量池过大
🛠️ 调优方案

1. 堆内存优化

# 调优前配置
-Xms4g -Xmx8g
-XX:NewRatio=3
-XX:+UseParallelGC# 调优后配置
-Xms16g -Xmx16g                    # 增大堆内存
-XX:NewRatio=1                     # 调整新生代比例
-XX:SurvivorRatio=8                # 优化Survivor区
-XX:+UseG1GC                       # 切换到G1收集器
-XX:MaxGCPauseMillis=200           # 设置GC停顿目标
-XX:G1HeapRegionSize=16m           # 设置Region大小

2. 应用层优化

// 优化前 - 频繁创建临时对象
public String formatOrderInfo(Order order) {return "订单号:" + order.getId() + ",客户:" + order.getCustomer() + ",金额:" + order.getAmount();
}// 优化后 - 使用StringBuilder
public String formatOrderInfo(Order order) {StringBuilder sb = new StringBuilder(128);return sb.append("订单号:").append(order.getId()).append(",客户:").append(order.getCustomer()).append(",金额:").append(order.getAmount()).toString();
}// 进一步优化 - 使用对象池
public class StringBuilderPool {private static final ThreadLocal<StringBuilder> BUILDER_POOL = ThreadLocal.withInitial(() -> new StringBuilder(256));public static StringBuilder get() {StringBuilder sb = BUILDER_POOL.get();sb.setLength(0); // 重置长度return sb;}
}

3. 缓存优化

// 优化前 - 无限制缓存
public class OrderCache {private static final Map<String, Order> cache = new ConcurrentHashMap<>();public void putOrder(String id, Order order) {cache.put(id, order);}
}// 优化后 - 使用Caffeine缓存
public class OptimizedOrderCache {private static final Cache<String, Order> cache = Caffeine.newBuilder().maximumSize(10000).expireAfterWrite(30, TimeUnit.MINUTES).recordStats().build();public void putOrder(String id, Order order) {cache.put(id, order);}public Order getOrder(String id) {return cache.getIfPresent(id);}
}
📈 调优效果
指标调优前调优后改善幅度
平均响应时间800ms200ms75%
99%响应时间3000ms500ms83%
Full GC频率5分钟/次30分钟/次83%
GC停顿时间2000ms150ms92%
系统可用性99.5%99.9%0.4%
💾 内存不足问题的解决方案
📋 案例背景

系统概况

  • 数据分析平台
  • 处理大量CSV文件
  • 单文件大小:1-5GB
  • 服务器配置:8核16GB内存

遇到的问题

  • 频繁OutOfMemoryError
  • 处理大文件时系统崩溃
  • 内存使用率持续高位
🔍 问题分析

1. 内存使用分析

# 堆内存配置
-Xms8g -Xmx12g# 问题现象:
# - 处理3GB文件时OOM
# - 堆转储显示大量String对象
# - 老年代快速填满

2. 代码分析

// 问题代码 - 一次性加载整个文件
public List<String[]> parseCSV(String filePath) {List<String[]> records = new ArrayList<>();try (BufferedReader reader = Files.newBufferedReader(Paths.get(filePath))) {String line;while ((line = reader.readLine()) != null) {records.add(line.split(",")); // 所有数据保存在内存中}}return records;
}
🛠️ 解决方案

1. 流式处理

// 优化后 - 流式处理
public void processCSVStream(String filePath, Consumer<String[]> processor) {try (BufferedReader reader = Files.newBufferedReader(Paths.get(filePath))) {String line;while ((line = reader.readLine()) != null) {String[] record = line.split(",");processor.accept(record); // 逐行处理,不保存在内存中}} catch (IOException e) {throw new RuntimeException("文件处理失败", e);}
}// 使用示例
processCSVStream("large_file.csv", record -> {// 处理单行数据processRecord(record);
});

2. 分批处理

public class BatchCSVProcessor {private static final int BATCH_SIZE = 1000;public void processBatch(String filePath) {List<String[]> batch = new ArrayList<>(BATCH_SIZE);try (BufferedReader reader = Files.newBufferedReader(Paths.get(filePath))) {String line;while ((line = reader.readLine()) != null) {batch.add(line.split(","));if (batch.size() >= BATCH_SIZE) {processBatch(batch);batch.clear(); // 清空批次,释放内存}}// 处理最后一批if (!batch.isEmpty()) {processBatch(batch);}} catch (IOException e) {throw new RuntimeException("文件处理失败", e);}}private void processBatch(List<String[]> batch) {// 处理一批数据batch.forEach(this::processRecord);}
}

3. 内存映射文件

public class MemoryMappedCSVProcessor {public void processLargeFile(String filePath) {try (RandomAccessFile file = new RandomAccessFile(filePath, "r");FileChannel channel = file.getChannel()) {long fileSize = channel.size();long position = 0;int bufferSize = 64 * 1024 * 1024; // 64MB缓冲区while (position < fileSize) {long remaining = fileSize - position;int mapSize = (int) Math.min(bufferSize, remaining);MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, position, mapSize);processBuffer(buffer);position += mapSize;}} catch (IOException e) {throw new RuntimeException("文件处理失败", e);}}private void processBuffer(MappedByteBuffer buffer) {// 处理映射的内存缓冲区StringBuilder line = new StringBuilder();while (buffer.hasRemaining()) {char c = (char) buffer.get();if (c == '\n') {processLine(line.toString());line.setLength(0);} else {line.append(c);}}}
}

4. JVM参数调优

# 调优后配置
-Xms4g -Xmx4g                     # 减少堆内存,避免过度分配
-XX:+UseG1GC                      # 使用G1收集器
-XX:MaxGCPauseMillis=100          # 降低GC停顿时间
-XX:+UseStringDeduplication       # 启用字符串去重
-XX:MaxDirectMemorySize=2g        # 设置直接内存大小
-XX:+DisableExplicitGC            # 禁用显式GC调用
📈 优化效果
指标优化前优化后改善幅度
最大处理文件大小2GB10GB+400%+
内存使用峰值12GB4GB67%
处理速度50MB/s200MB/s300%
OOM发生率30%0%100%

🎯 调优最佳实践

📋 调优流程

性能基线测试
问题识别
监控数据收集
根因分析
制定调优方案
实施调优
效果验证
是否达到目标?
文档记录
持续监控

🛠️ 调优原则

  1. 测量优先

    • 建立性能基线
    • 使用科学的测量方法
    • 关注关键性能指标
  2. 渐进式调优

    • 一次只调整一个参数
    • 验证每次调整的效果
    • 避免过度优化
  3. 全栈考虑

    • 不仅关注JVM层面
    • 考虑应用代码优化
    • 关注系统资源配置

📊 关键监控指标

类别指标正常范围告警阈值
内存堆内存使用率< 70%> 85%
内存老年代使用率< 60%> 80%
GCFull GC频率< 1次/小时> 1次/10分钟
GCGC停顿时间< 100ms> 500ms
线程活跃线程数< 200> 500
CPUCPU使用率< 70%> 90%

🔧 常用JVM参数模板

生产环境推荐配置

# 基础内存配置
-Xms8g
-Xmx8g
-XX:MetaspaceSize=256m
-XX:MaxMetaspaceSize=512m# G1垃圾收集器配置
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:G1HeapRegionSize=16m
-XX:G1NewSizePercent=30
-XX:G1MaxNewSizePercent=40# GC日志配置
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintGCApplicationStoppedTime
-Xloggc:/path/to/gc-%t.log
-XX:+UseGCLogFileRotation
-XX:NumberOfGCLogFiles=5
-XX:GCLogFileSize=100M# 异常处理
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/path/to/dumps/
-XX:+ExitOnOutOfMemoryError# JIT编译优化
-XX:+TieredCompilation
-XX:+UseStringDeduplication
-XX:+OptimizeStringConcat

如果这篇文章对您有帮助,不要忘记点赞、收藏和分享哦!

http://www.dtcms.com/a/269616.html

相关文章:

  • Java基础回顾(1)
  • 7 种简单方法将三星文件传输到电脑
  • 瞄准Win10难民,苹果正推出塑料外壳、手机CPU的MacBook
  • 用户生命周期与改进型RFM模型
  • C#读取modbus值,C#读写modbus,支持读写uint32值,Modbus TCP工具类
  • HTTPS工作原理
  • java获取文件的消息摘要APP进行文件完整性校验
  • JavaScript基础篇——第二章 类型转换与常见错误解析
  • 二分查找篇——搜索二维矩阵【LeetCode】遍历法
  • qt-C++笔记之setCentralWidget的使用
  • Visual Studio Code 中统一配置文件在团队协作中的应用
  • 论文略读:Prefix-Tuning: Optimizing Continuous Prompts for Generation
  • Git 安装避坑指南:从环境检查到高级配置的全流程解析
  • EXCEL转html,含图片
  • Linux下SPHinXsys源码编译安装及使用
  • Flutter基础(前端教程③-跳转)
  • Wend看源码-RAGFlow(上)
  • nvm npm nrm 使用教程
  • 台式电脑如何连wifi 快速连接方法
  • synchronized 的使用和特性
  • 算法学习笔记:11.冒泡排序——从原理到实战,涵盖 LeetCode 与考研 408 例题
  • VBA经典应用69例应用8:取消预设任务
  • (三)C#使用yolo
  • 在教育领域中,如何通过VRM分片错序对视频进行加密?
  • git学习:首次创建仓库
  • ubuntu 运行脚本打开WIFI adb
  • YOLO在自动驾驶交通标志识别中的应用与优化【附代码】
  • Qt:图片切割
  • 代码详细注释:演示如何使用dup()系统调用复制文件描述符
  • Linux操作系统:再谈虚拟地址空间