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

【Java进阶】GC友好的编程方式

Java 开发进阶:GC 友好编程实践指南

  • Java 开发进阶:GC 友好编程实践指南
    • 一、GC 友好编程核心目标
    • 二、对象创建与生命周期优化
      • 1. 减少不必要的对象创建
      • 2. 对象复用技术(对象池)
    • 三、数据结构与内存布局优化
      • 1. 选择低内存占用的集合类
      • 2. 扁平化嵌套数据结构
      • 3. 预分配集合容量
    • 四、引用类型的高级应用
      • 1. 软引用(SoftReference)缓存
      • 2. 弱引用(WeakReference)监听器
      • 3. 虚引用(PhantomReference)资源清理
    • 五、并发场景的 GC 优化
      • 1. 线程安全的对象池(ThreadLocal 优化)
      • 2. 避免并发下的临时对象爆炸
    • 六、GC 友好代码的检查与监控
      • 1. 代码审查清单(GC 友好)
      • 2. JVM 工具监控 GC 行为
    • 七、JVM 参数与 GC 友好编程的配合
      • 1. 根据 GC 算法调整代码
      • 2. 关键 JVM 参数示例
    • 八、总结:GC 友好编程最佳实践
    • 相关文献

Java 开发进阶:GC 友好编程实践指南

一、GC 友好编程核心目标

核心目标:通过代码层面的优化,减少 GC 频率、缩短 GC 停顿时间,提升应用吞吐量与响应速度。
关键指标

  • 降低 Young GC 频率(目标:< 1 次/秒)
  • 控制 Full GC 触发(目标:数小时/天一次)
  • 减少对象分配速率(目标:< 100MB/s)
  • 避免内存泄漏(老年代稳定增长)

二、对象创建与生命周期优化

1. 减少不必要的对象创建

反例:循环中重复创建对象

// 每次循环创建新 String 对象(浪费)
for (int i = 0; i < 10000; i++) {String log = new String("User " + i + " logged in"); 
}// 优化:复用 StringBuilder(减少对象分配)
StringBuilder sb = new StringBuilder(128); // 预分配容量
for (int i = 0; i < 10000; i++) {sb.setLength(0); // 重置内容sb.append("User ").append(i).append(" logged in");log(sb.toString());
}

反例:自动装箱产生大量包装类对象

// 自动装箱:每次循环创建 Integer 对象
Integer sum = 0;
for (int i = 0; i < 10000; i++) {sum += i; // 等价于 sum = Integer.valueOf(sum.intValue() + i)
}// 优化:使用基本类型 int
int primitiveSum = 0;
for (int i = 0; i < 10000; i++) {primitiveSum += i;
}

2. 对象复用技术(对象池)

适用场景:创建成本高的对象(如数据库连接、线程、大对象)。
实现示例

// 通用对象池(线程安全)
public class ObjectPool<T> {private final Supplier<T> creator; // 对象创建函数private final Consumer<T> resetter; // 对象重置函数private final Queue<T> pool = new ConcurrentLinkedQueue<>();public ObjectPool(Supplier<T> creator, Consumer<T> resetter) {this.creator = creator;this.resetter = resetter;}// 借用对象(无则创建)public T borrow() {T obj = pool.poll();return obj != null ? obj : creator.get();}// 归还对象(重置状态)public void release(T obj) {resetter.accept(obj); // 重置对象状态(如清空集合)pool.offer(obj);}
}// 使用示例:StringBuilder 池
ObjectPool<StringBuilder> sbPool = new ObjectPool<>(StringBuilder::new, sb -> sb.setLength(0) // 归还前清空内容
);// 业务中使用
StringBuilder sb = sbPool.borrow();
try {sb.append("Data: ").append(data);process(sb.toString());
} finally {sbPool.release(sb); // 必须归还
}

三、数据结构与内存布局优化

1. 选择低内存占用的集合类

集合类内存对比(以存储 100 万个整数为例):

集合类型内存占用(MB)特点
ArrayList<Integer>~40连续内存,随机访问快
LinkedList<Integer>~160节点多,内存分散
TIntArrayList(Trove)~8原始类型数组,无装箱开销

推荐

  • 小数据集:Arrays.asList()(无额外对象开销)
  • 高频增删:ArrayDeque(优于 LinkedList
  • 原始类型存储:使用 Trove/HPPC 等第三方库(如 TIntHashMap

2. 扁平化嵌套数据结构

反例:多层嵌套 Map 导致内存碎片

// 嵌套 Map:内存分散,访问效率低
Map<String, Map<String, List<User>>> userMap = new HashMap<>();// 优化:使用复合键扁平化结构
public record UserKey(String department, String id) {} // Java 16+ 记录类Map<UserKey, User> flatUserMap = new HashMap<>();

3. 预分配集合容量

避免集合扩容时的内存复制:

// 预分配 ArrayList 容量(减少扩容次数)
List<String> users = new ArrayList<>(expectedSize); // 预分配 HashMap 容量(负载因子 0.75)
Map<String, Integer> counts = new HashMap<>(expectedSize * 4 / 3); 

四、引用类型的高级应用

1. 软引用(SoftReference)缓存

适用场景:内存敏感的缓存(如图片、计算结果),在内存不足时自动回收。

public class SoftCache<K, V> {private final Map<K, SoftReference<V>> cache = new HashMap<>();public void put(K key, V value) {cache.put(key, new SoftReference<>(value));}public V get(K key) {SoftReference<V> ref = cache.get(key);return (ref != null && ref.get() != null) ? ref.get() : null;}
}// 使用示例:缓存大图片
SoftCache<String, BufferedImage> imageCache = new SoftCache<>();
imageCache.put("avatar", loadImage("avatar.png"));

2. 弱引用(WeakReference)监听器

适用场景:避免监听器未注销导致的内存泄漏。

public class EventSource {private final List<WeakReference<EventListener>> listeners = new ArrayList<>();// 添加监听器(弱引用)public void addListener(EventListener listener) {listeners.add(new WeakReference<>(listener));}// 触发事件(自动清理无效监听器)public void fireEvent(Event event) {Iterator<WeakReference<EventListener>> it = listeners.iterator();while (it.hasNext()) {EventListener listener = it.next().get();if (listener != null) {listener.onEvent(event);} else {it.remove(); // 清理已回收的监听器}}}
}

3. 虚引用(PhantomReference)资源清理

适用场景:跟踪对象被回收的时机(如释放堆外内存)。

public class DirectBufferCleaner {private static final ReferenceQueue<DirectBuffer> QUEUE = new ReferenceQueue<>();private static final Set<PhantomReference<DirectBuffer>> REFERENCES = new HashSet<>();// 包装 DirectBuffer 并注册虚引用public static DirectBuffer wrap(DirectBuffer buffer) {PhantomReference<DirectBuffer> ref = new PhantomReference<>(buffer, QUEUE);REFERENCES.add(ref);return buffer;}// 清理线程(定期检查虚引用)static {Thread cleanerThread = new Thread(() -> {while (!Thread.interrupted()) {try {PhantomReference<?> ref = (PhantomReference<?>) QUEUE.remove();REFERENCES.remove(ref);// 释放堆外内存(如通过 Unsafe)cleanNativeMemory(ref);} catch (InterruptedException e) {break;}}});cleanerThread.setDaemon(true);cleanerThread.start();}
}

五、并发场景的 GC 优化

1. 线程安全的对象池(ThreadLocal 优化)

问题:全局对象池在并发时竞争激烈。
优化:使用 ThreadLocal 为每个线程维护本地池。

public class ThreadLocalObjectPool<T> {private final Supplier<T> creator;private final Consumer<T> resetter;private final ThreadLocal<Stack<T>> threadLocalPool = ThreadLocal.withInitial(Stack::new);public ThreadLocalObjectPool(Supplier<T> creator, Consumer<T> resetter) {this.creator = creator;this.resetter = resetter;}// 借用对象(优先本地池)public T borrow() {Stack<T> stack = threadLocalPool.get();return stack.isEmpty() ? creator.get() : stack.pop();}// 归还对象(本地池)public void release(T obj) {resetter.accept(obj);threadLocalPool.get().push(obj);}
}

2. 避免并发下的临时对象爆炸

反例:多线程下频繁创建临时对象(如 SimpleDateFormat)。
优化:使用线程安全的替代类或对象池。

// 反例:SimpleDateFormat 非线程安全(每次调用创建新实例)
public String formatDate(Date date) {return new SimpleDateFormat("yyyy-MM-dd").format(date); // 产生临时对象
}// 优化1:使用线程安全的 DateTimeFormatter(Java 8+)
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
public String formatDate(LocalDate date) {return date.format(FORMATTER); // 无临时对象
}// 优化2:对象池(兼容旧代码)
private static final ObjectPool<SimpleDateFormat> DATE_FORMAT_POOL = new ObjectPool<>(SimpleDateFormat::new, sdf -> sdf.setTimeZone(TimeZone.getDefault())
);public String formatDate(Date date) {SimpleDateFormat sdf = DATE_FORMAT_POOL.borrow();try {return sdf.format(date);} finally {DATE_FORMAT_POOL.release(sdf);}
}

六、GC 友好代码的检查与监控

1. 代码审查清单(GC 友好)

检查项问题示例优化方案
循环中创建对象new String(...) 循环内复用 StringBuilder
自动装箱Integer sum = 0; sum += i;使用基本类型 int
静态集合持有对象static List<User> cache;改用 WeakHashMap 或定期清理
未关闭的资源FileInputStream 未关闭使用 try-with-resources
监听器未注销事件源未移除监听器使用弱引用监听器
大对象未拆分单个 100MB 字节数组拆分为 8KB 小块或流式处理

2. JVM 工具监控 GC 行为

常用工具

  • jstat:实时监控 GC 统计(jstat -gcutil <pid> 1000
  • VisualVM:图形化查看堆内存、对象分布(jvisualvm
  • JFR(Java Flight Recorder):低开销记录 GC 事件(jcmd <pid> JFR.start
  • MAT(Memory Analyzer Tool):分析堆转储(jmap -dump:format=b,file=heap.bin <pid>

示例:通过 JFR 分析 GC 停顿

  1. 启动 JFR:jcmd <pid> JFR.start name=myrecording duration=60s
  2. 分析 GC 事件:使用 JMC(Java Mission Control)打开 myrecording.jfr,查看 GarbageCollection 事件。

七、JVM 参数与 GC 友好编程的配合

1. 根据 GC 算法调整代码

GC 算法特点代码优化建议
G1分 Region 收集,低延迟避免大对象(> Region 大小)
ZGC超低延迟(< 10ms)减少对象分配速率
Serial单线程收集,适合小内存避免频繁 Full GC
Parallel吞吐量优先允许更多 Young GC

2. 关键 JVM 参数示例

# G1 GC 优化(低延迟)
java -Xms4g -Xmx4g \-XX:+UseG1GC \-XX:MaxGCPauseMillis=200 \    # 目标停顿时间-XX:InitiatingHeapOccupancyPercent=45 \  # 触发并发标记的堆占用率-XX:G1ReservePercent=15 \     # 预留内存防止晋升失败-jar your-app.jar# ZGC 优化(超低延迟)
java -Xms8g -Xmx8g \-XX:+UseZGC \-XX:ConcGCThreads=4 \         # 并发 GC 线程数-XX:ParallelGCThreads=8 \     # 并行 GC 线程数-XX:ZCollectionInterval=5 \   # 收集间隔(秒)-jar your-app.jar

八、总结:GC 友好编程最佳实践

  1. 减少对象分配:复用对象、避免循环内创建、使用基本类型。
  2. 优化数据结构:选择低内存占用的集合、扁平化嵌套结构、预分配容量。
  3. 控制对象生命周期:使用对象池、软/弱引用缓存、及时释放资源。
  4. 避免内存泄漏:清理无效监听器、注销资源、检查静态集合。
  5. 监控与调优:使用 JFR/MAT 分析 GC 行为,配合 JVM 参数优化。

最终目标:让 GC 成为“隐形助手”,而非性能瓶颈,支撑高吞吐、低延迟的应用场景。

相关文献

java基础知识-JVM知识详解
【Java知识】手把手教你使用JVM参数配置以及优化技巧
【Java进阶】常见的JVM调试工具介绍

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

相关文章:

  • 甘肃肃第八建设集团网站福州市高速公路建设指挥部网站
  • 鸿蒙NEXT媒体开发全栈解析:从播放器到录屏的一站式解决方案
  • 郑州做网站排名dede网站首页
  • python 做网站很快吗广州自助网站推广建站
  • AD22 热风焊盘在哪设置
  • CMake进阶:生成器表达式
  • 从 Vite 到现代构建范式:一个关于“快”的技术哲学
  • 2025世界智能制造大会(南京)将带来那些新技术与新体验?
  • 杭州网站建设杭州上海哪个网站好用
  • 做网站的文案是指网站怎么做才能赚钱吗
  • 完善企业能力等级评价体系 构建高质量发展新标尺
  • Vue2 封装二维码弹窗组件
  • 哪里有做网站较好的公司龙华做网站怎么样
  • 在1688做公司网站前端开发语言的特点是
  • 少儿教育网站建设价格免费看电视剧的网站在线观看
  • (四)从零学 React Props:数据传递 + 实战案例 + 避坑指南
  • 上传自己做的网站吗关键词优化百家号
  • 连云港做网站公司校园网的规划与设计
  • DeepSeek-OCR:视觉压缩的革命性突破——当OCR遇上LLM的“降维打击“
  • 盐城网站开发市场做网站怎么去工信部缴费
  • ps做游戏网站伊宁网站建设优化
  • 【高等数学笔记-极限(7)】函数连续
  • !for_each_process 命令详解
  • 住房与城乡建设网站引流客户的最快方法是什么
  • 江西网站开发的公司wordpress插件看访问者数量
  • 10.21
  • SharedFlow和StateFlow的方案选择-屏幕旋转设计
  • ps做素材下载网站有哪些wordpress用什么框架
  • 网站建设除凡科外还有哪些wordpress主题 添加自定义菜单
  • 怎么做视频网站教程什么是网络营销报价