JVM Full GC 频繁问题排查、优化及解决方案

news2025/6/5 0:01:59

引言

  在Java应用程序中,JVM(Java虚拟机)通过垃圾回收机制自动管理内存,确保不再使用的对象能够被及时清理和释放。虽然垃圾回收在大多数情况下运行顺利,但当Full GC频繁发生时,它会严重影响应用性能,导致长时间的停顿(Stop-the-World, STW),从而降低系统的响应速度甚至影响用户体验。

  Full GC是JVM最重的垃圾回收操作,特别是在大规模应用中,频繁Full GC会使应用停顿时间大幅增加,直接影响业务处理。相对于Minor GC(年轻代垃圾回收),Full GC的执行速度至少慢10倍以上,因此在生产环境中应尽量避免频繁的Full GC。

一、JVM垃圾回收基础

1. JVM内存结构

JVM的内存主要分为以下几个区域:

  1. 堆内存(Heap Memory):这是Java对象的主要存储区域,分为:

    • 年轻代(Young Generation)
      • Eden区:对象创建时最先进入的区域
      • Survivor区:Eden中的存活对象会进入Survivor区,分为S0和S1两块
    • 老年代(Old Generation):年轻代中的长生命周期对象会被移到老年代
  2. 非堆内存

    • 方法区/元空间:在JDK 8之前称为永久代(PermGen),JDK 8及以后称为元空间(Metaspace),用于存储类的元数据、常量、静态变量等
    • 程序计数器:当前线程所执行字节码的行号指示器
    • 虚拟机栈:每个线程的栈用于存储局部变量和方法调用的上下文
    • 本地方法栈:为本地方法(Native Method)服务

2. 垃圾回收机制

JVM的垃圾回收主要基于以下原理:

  1. 垃圾识别算法

    • 引用计数法:对每个对象的引用进行计数,计数为0的对象可被回收
    • 可达性分析:从GC Roots开始搜索,不可达的对象被视为垃圾
  2. 垃圾收集算法

    • 标记-清除(Mark-Sweep):标记所有需要回收的对象,然后统一回收
    • 标记-整理(Mark-Compact):标记后将存活对象移到一端,然后清理边界外的内存
    • 复制(Copying):将内存分为两块,每次只使用一块,当这块用完时,将存活对象复制到另一块
    • 分代收集:根据对象的生命周期长短将内存划分为不同的区域,不同区域采用不同的收集算法
  3. 垃圾收集器

    • Serial收集器:单线程收集器,适用于单CPU环境
    • ParNew收集器:Serial的多线程版本
    • Parallel Scavenge收集器:关注吞吐量的多线程收集器
    • CMS(Concurrent Mark Sweep)收集器:以获取最短回收停顿时间为目标的收集器
    • G1(Garbage First)收集器:面向服务端应用的收集器,兼顾吞吐量和停顿时间
    • ZGC/Shenandoah:低延迟垃圾收集器,适用于大内存低延迟应用

3. Full GC与Minor GC的区别

  • Minor GC:只回收年轻代,通常效率较高,且不会影响老年代的内存
  • Full GC:回收整个堆,包括年轻代和老年代,以及方法区(JDK 8前的永久代或JDK 8后的元空间)。Full GC非常耗时,且会触发STW,暂停所有应用线程

二、频繁Full GC的原因分析

1. 老年代空间不足

  老年代空间不足是触发Full GC最常见的原因之一。当老年代中的对象数量持续增长,导致空间不足时,JVM会触发Full GC来尝试回收老年代的内存。如果Full GC无法有效回收内存,可能会抛出OutOfMemoryError错误。

这种情况通常发生在以下场景:

  • 年轻代中的对象不断晋升到老年代,而老年代中的对象又无法及时回收
  • 大对象直接分配到老年代,导致老年代空间快速填满
  • 系统高负载运行,请求量很大,JVM来不及将对象转移到老年代,直接在老年代分配对象

2. 永久代/元空间溢出

  在JDK 8之前,类的元数据存储在永久代(PermGen)中,当永久代空间耗尽时会触发Full GC。JDK 8以后,永久代被元空间(Metaspace)取代,但元空间不足也会导致Full GC。

  当系统中要加载的类、反射的类和调用的方法较多时,永久代/元空间可能会被占满,在未配置为采用CMS GC的情况下也会执行Full GC。如果经过Full GC仍然回收不了,那么JVM会抛出OutOfMemoryError: PermGen space或OutOfMemoryError: Metaspace错误。

3. 晋升失败

  当年轻代对象要晋升到老年代,但老年代空间不足时,会触发Full GC,这种现象称为晋升失败。这种情况通常发生在高并发场景下,大量对象短时间内从年轻代晋升到老年代,而老年代没有足够的空间存放这些对象。

  在CMS垃圾收集器中,这种情况表现为"promotion failed"和"concurrent mode failure"两种状态,当这两种状况出现时可能会触发Full GC。

4. 内存碎片化问题

  老年代中的内存碎片可能导致对象无法晋升,即使老年代有足够的空闲空间,也无法容纳新的大对象,从而触发Full GC。当老年代被频繁分配和释放对象时,可能会导致内存碎片化,最终导致大对象无法被分配。

  CMS垃圾收集器使用的是标记-清除算法,这种算法不会进行内存整理,因此容易产生内存碎片。当碎片过多时,即使总的空闲空间足够,也可能无法找到足够大的连续空间来分配大对象,从而触发Full GC。

5. System.gc()方法的显式调用

显式调用System.gc()方法会建议JVM进行Full GC。虽然只是建议而非一定执行,但在很多情况下它会触发Full GC,从而增加Full GC的频率,也即增加了间歇性停顿的次数。

在RMI应用中,默认情况下会一小时执行一次Full GC,这也可能导致频繁的Full GC问题。

6. 其他常见原因

  1. 统计晋升阈值导致的Full GC:Hotspot为了避免由于新生代对象晋升到旧生代导致旧生代空间不足的现象,在进行Minor GC时,会做一个判断:如果之前统计所得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间,那么就直接触发Full GC。

  2. 堆中分配很大的对象:所谓大对象,是指需要大量连续内存空间的Java对象,例如很长的数组。此种对象会直接进入老年代,而老年代虽然有很大的剩余空间,但是无法找到足够大的连续空间来分配给当前对象,此种情况就会触发JVM进行Full GC。

三、常见导致Full GC的应用场景

1. 内存泄漏场景

场景描述:应用长时间运行后,老年代内存占用持续增长,即使经过多次Full GC也无法有效释放空间,最终可能导致OutOfMemoryError。

根本原因

  • 程序中存在未正确关闭的资源,如数据库连接、文件流等
  • 使用了不当的缓存策略,缓存无限增长且没有淘汰机制
  • 集合类(如HashMap、ArrayList等)持续增长但从未清理
  • 使用ThreadLocal但未正确移除,导致内存泄漏
  • 监听器或回调注册后未注销

典型表现

  • 老年代内存使用率持续上升,Full GC后回收效果不明显
  • 应用运行时间越长,Full GC频率越高
  • 内存分析工具显示某些对象实例数量异常增长

2. 高并发服务场景

场景描述:在高并发、高吞吐量的服务中,大量对象在短时间内被创建并快速晋升到老年代,导致老年代空间不足,触发频繁Full GC。

根本原因

  • 服务接收请求速率过高,超过系统处理能力
  • Minor GC频繁发生,导致对象提前晋升到老年代
  • 大量临时对象在高并发场景下快速创建,但JVM回收不及时
  • 线程池配置不合理,创建过多线程导致内存压力增大

典型表现

  • 系统负载突然增高时Full GC频率明显增加
  • GC日志中出现"promotion failed"或"concurrent mode failure"
  • 老年代空间使用率波动较大

3. 大对象分配场景

场景描述:应用程序中频繁创建大对象或大数组,这些对象直接分配在老年代,导致老年代空间不足,触发Full GC。

根本原因

  • 一次性读取大文件到内存中
  • 大批量数据查询未做分页处理
  • 图片、视频等大型媒体文件处理不当
  • 大型缓存结构一次性初始化

典型表现

  • GC日志中显示老年代空间突然增长
  • 内存分析显示有大对象直接进入老年代
  • 应用在处理特定类型数据时Full GC频率增加

4. 内存碎片化场景

场景描述:老年代中虽然有足够的空闲空间总量,但由于空间碎片化,无法找到足够大的连续空间来分配对象,导致Full GC。

根本原因

  • 使用CMS收集器但未合理设置压缩策略
  • 应用中存在大小不一的对象频繁创建和回收
  • 老年代空间设置过小,导致碎片问题更加明显

典型表现

  • GC日志中显示老年代仍有较多空闲空间,但仍然触发Full GC
  • 使用CMS收集器时出现"concurrent mode failure"
  • 内存分析工具显示老年代空间碎片化严重

4. 显式调用GC场景

场景描述:应用代码中直接调用System.gc()或Runtime.getRuntime().gc()方法,或者RMI等机制定期触发Full GC。

根本原因

  • 开发人员错误地认为手动触发GC可以提高性能
  • 使用了RMI等技术,其默认配置会定期执行Full GC
  • 第三方库中包含显式GC调用

典型表现

  • GC日志中出现"System.gc()"相关信息
  • Full GC以固定的时间间隔发生
  • 即使系统负载较低,仍有规律性的Full GC

5. 元空间溢出场景

场景描述:在JDK 8及以上版本中,Metaspace空间不足导致频繁Full GC,最终可能引发OutOfMemoryError: Metaspace。

根本原因

  • 动态类加载过多,如使用大量动态代理
  • 使用JSP的应用重新部署多次但未重启
  • 使用字节码增强库如cglib、javassist等过度生成类
  • OSGi等动态模块系统频繁加载和卸载类

典型表现

  • GC日志中显示Metaspace区域使用率高
  • 应用重新部署或热加载后Full GC频率增加
  • 内存分析显示加载的类数量异常增多

6. JVM参数配置不当场景

场景描述:由于JVM参数配置不合理,导致GC策略不适合当前应用特性,引发频繁Full GC。

根本原因

  • 堆内存大小设置不合理(过小或过大)
  • 新生代与老年代比例设置不当
  • 选择了不适合应用特性的垃圾收集器
  • GC触发阈值设置不合理

典型表现

  • 系统资源利用率不均衡(如内存使用率低但CPU使用率高)
  • GC日志中显示GC暂停时间异常长
  • 内存分配与回收模式不符合应用实际需求

7. 对象晋升阈值设置不当场景

场景描述:由于对象晋升年龄阈值设置不当,导致对象过早晋升到老年代或在新生代停留时间过长,引发GC问题。

根本原因

  • MaxTenuringThreshold参数设置过小,对象过早进入老年代
  • 新生代空间设置过小,导致对象提前晋升
  • Survivor空间比例设置不合理,导致对象直接进入老年代

典型表现

  • GC日志中显示对象晋升率异常高
  • 新生代GC后,老年代使用率明显上升
  • 内存分析显示老年代中存在大量应该在新生代的短生命周期对象

8. 数据库或外部系统交互场景

场景描述:应用与数据库或其他外部系统交互时,由于连接管理、数据处理方式不当导致内存问题,引发Full GC。

根本原因

  • 数据库连接未正确关闭或连接池配置不当
  • 一次性查询过多数据到内存中
  • 网络IO阻塞导致线程堆积,创建过多临时对象
  • 序列化/反序列化大对象时内存使用不当

典型表现

  • 在执行特定数据库操作或外部调用后Full GC频率增加
  • 应用日志中出现数据库或网络相关异常的同时伴随GC问题
  • 内存分析显示与IO相关的对象占用异常内存

四、Full GC问题排查方法

1. 开启并分析GC日志

GC日志是排查Full GC问题的最基本和最重要的工具。通过分析GC日志,可以了解GC的频率、持续时间、内存使用情况等关键信息。

开启GC日志的JVM参数

-XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -Xloggc:/path/to/gc.log

对于JDK 9及以上版本,可以使用:

-Xlog:gc*=debug:file=/path/to/gc.log:time,uptime,level,tags

GC日志轮转配置

-XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=20M

GC日志分析要点

  1. Full GC的频率:正常情况下,Full GC应该很少发生,如果频繁出现,需要重点关注
  2. Full GC的持续时间:Full GC时间过长会导致应用停顿,影响用户体验
  3. 内存使用情况:关注各代内存使用率,特别是老年代的使用情况
  4. 对象晋升情况:关注对象从年轻代晋升到老年代的速率
  5. 特殊GC事件:如"concurrent mode failure"、"promotion failed"等

GC日志分析工具

  • GCViewer
  • GCEasy
  • GCPlot
  • IBM Pattern Modeling and Analysis Tool for Java Garbage Collector (PMAT)

2. 使用监控工具

除了GC日志,还可以使用各种监控工具实时观察JVM的运行状况。

JDK自带工具

  1. jstat:监控JVM的GC情况

    jstat -gcutil <pid> 1000
    
  2. jmap:生成堆转储文件或查看内存使用情况

    jmap -heap <pid>
    jmap -dump:live,format=b,file=heap.hprof <pid>
    
  3. jstack:生成线程转储,分析线程状态

    jstack -l <pid> > thread_dump.txt
    
  4. jinfo:查看和修改JVM参数

    jinfo -flags <pid>
    
  5. jcmd:执行JVM诊断命令

    jcmd <pid> GC.heap_info
    jcmd <pid> GC.class_histogram
    

第三方监控工具

  1. JVisualVM:图形化JVM监控工具,可以监控内存、CPU、线程等
  2. Java Mission Control (JMC):Oracle提供的性能监控工具
  3. Arthas:阿里巴巴开源的Java诊断工具
  4. JProfiler:商业Java性能分析工具
  5. YourKit:商业Java性能分析工具

3. 堆转储分析

堆转储(Heap Dump)是JVM堆内存的快照,通过分析堆转储,可以了解当前内存中有哪些对象占用了大量空间,从而定位哪些对象导致了内存泄漏或过度的老年代占用。

生成堆转储的方法

  1. 使用jmap命令:

    jmap -dump:live,format=b,file=heap.hprof <pid>
    
  2. 使用JVisualVM:通过界面操作生成堆转储

  3. 在OOM时自动生成堆转储:

    -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/to/dump
    

堆转储分析工具

  1. Eclipse Memory Analyzer (MAT):功能强大的堆分析工具,可以检测内存泄漏
  2. JVisualVM:可以打开和分析堆转储文件
  3. JProfiler:提供更详细的堆分析功能
  4. YourKit:提供堆分析和内存泄漏检测功能

堆转储分析要点

  1. 大对象分析:找出占用内存最多的对象
  2. 对象实例数分析:找出实例数异常多的类
  3. GC Root分析:分析对象的引用链,找出内存泄漏的根源
  4. 对象年龄分析:分析对象在堆中的存活时间

4. 线程分析

线程状态和线程堆栈信息对于分析Full GC问题也很重要,特别是在高并发场景下。

生成线程转储的方法

  1. 使用jstack命令:

    jstack -l <pid> > thread_dump.txt
    
  2. 使用JVisualVM:通过界面操作生成线程转储

线程分析要点

  1. 线程状态分布:关注BLOCKED、WAITING状态的线程数量
  2. 锁竞争情况:分析是否存在严重的锁竞争
  3. 线程堆栈:分析线程执行的代码路径,找出可能的问题点
  4. 死锁检测:检查是否存在死锁情况

5. 系统性能指标监控

除了JVM内部的监控,系统级别的性能指标也对分析Full GC问题很有帮助。

关键系统指标

  1. CPU使用率:高CPU使用率可能导致GC线程无法及时执行
  2. 内存使用情况:系统内存不足可能导致JVM内存分配问题
  3. 磁盘IO:高磁盘IO可能影响GC性能
  4. 网络IO:网络IO问题可能导致线程堆积,间接影响GC

系统监控工具

  1. top/htop:监控CPU和内存使用情况
  2. vmstat:监控系统资源使用情况
  3. iostat:监控磁盘IO情况
  4. netstat:监控网络连接情况
  5. Prometheus + Grafana:构建完整的监控系统

五、优化策略与解决方案

1. 内存泄漏问题的解决方案

  1. `规范资源管理

    • 使用try-with-resources语法确保资源自动关闭
    try (Connection conn = dataSource.getConnection()) {
        // 使用连接
    } // 自动关闭连接
    
    • 在finally块中显式关闭资源
    Connection conn = null;
    try {
        conn = dataSource.getConnection();
        // 使用连接
    } finally {
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                logger.error("关闭连接失败", e);
            }
        }
    }
    
    • 使用连接池技术管理数据库连接等资源
  2. 优化缓存策略

    • 使用WeakHashMap实现缓存,允许垃圾回收
    Map<Key, Value> cache = new WeakHashMap<>();
    
    • 为缓存设置合理的大小限制和过期策略
    // 使用Guava Cache
    LoadingCache<Key, Value> cache = CacheBuilder.newBuilder()
        .maximumSize(1000)
        .expireAfterWrite(10, TimeUnit.MINUTES)
        .build(new CacheLoader<Key, Value>() {
            @Override
            public Value load(Key key) throws Exception {
                return createValue(key);
            }
        });
    
    • 使用成熟的缓存框架如Guava Cache、Caffeine或Ehcache
  3. 合理使用集合类

    • 为集合预设合理的初始容量,避免频繁扩容
    // 预设容量为1000
    List<String> list = new ArrayList<>(1000);
    Map<String, Object> map = new HashMap<>(1000);
    
    • 及时清理不再使用的集合元素
    // 使用完毕后清理
    list.clear();
    map.clear();
    
    • 考虑使用软引用或弱引用持有对象
    Map<Key, SoftReference<Value>> cache = new HashMap<>();
    
  4. 正确使用ThreadLocal

    • 在不再需要ThreadLocal变量时调用remove()方法
    ThreadLocal<User> userThreadLocal = new ThreadLocal<>();
    try {
        userThreadLocal.set(user);
        // 使用ThreadLocal
    } finally {
        userThreadLocal.remove(); // 防止内存泄漏
    }
    
    • 使用ThreadLocal.withInitial()创建,避免内存泄漏
    ThreadLocal<User> userThreadLocal = ThreadLocal.withInitial(() -> new User());
    
  5. 监控与预警

    • 建立内存使用监控,设置合理的告警阈值
    • 定期分析GC日志,及时发现内存异常
    • 在关键应用中添加内存泄漏检测机制

2. 高并发服务问题的解决方案

  1. 调整JVM内存参数

    • 增加年轻代空间,减少对象晋升
    -Xmn2g 或 -XX:NewRatio=2
    
    • 调整Survivor区比例,避免对象过早进入老年代
    -XX:SurvivorRatio=8
    
    • 调整对象晋升年龄阈值
    -XX:MaxTenuringThreshold=15
    
  2. 优化GC策略

    • 对于CMS收集器,调整触发阈值
    -XX:CMSInitiatingOccupancyFraction=70
    -XX:+UseCMSInitiatingOccupancyOnly
    
    • 考虑使用G1收集器替代CMS
    -XX:+UseG1GC
    -XX:MaxGCPauseMillis=200
    
  3. 实施流量控制

    • 使用限流技术如Guava RateLimiter
    RateLimiter limiter = RateLimiter.create(100.0); // 每秒100个请求
    if (limiter.tryAcquire()) {
        // 处理请求
    } else {
        // 请求被限流
    }
    
    • 实现服务降级机制,在高负载时保护核心功能
    • 使用队列缓冲请求,避免瞬时高并发
  4. 优化线程池配置

    • 根据CPU核心数和任务特性设置合理的线程池大小
    int corePoolSize = Runtime.getRuntime().availableProcessors() + 1;
    int maxPoolSize = corePoolSize * 2;
    ExecutorService executor = new ThreadPoolExecutor(
        corePoolSize,
        maxPoolSize,
        60L, TimeUnit.SECONDS,
        new LinkedBlockingQueue<>(1000),
        new ThreadPoolExecutor.CallerRunsPolicy());
    
    • 使用有界队列,避免任务堆积导致内存溢出
    • 实现自适应线程池,根据系统负载动态调整
  5. 代码层面优化

    • 减少临时对象创建,重用对象
    // 避免在循环中创建对象
    StringBuilder sb = new StringBuilder();
    for (String item : items) {
        sb.append(item);
    }
    String result = sb.toString();
    
    • 使用对象池技术管理重复使用的对象
    • 避免在循环中创建大量临时对象

3. 大对象分配问题的解决方案

  1. 优化文件处理

    • 使用流式处理替代一次性读取
    try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
        String line;
        while ((line = reader.readLine()) != null) {
            // 处理每一行
        }
    }
    
    • 实现分块读取和处理大文件
    byte[] buffer = new byte[8192]; // 8KB缓冲区
    int bytesRead;
    try (FileInputStream fis = new FileInputStream(file)) {
        while ((bytesRead = fis.read(buffer)) != -1) {
            // 处理buffer中的数据
        }
    }
    
    • 使用NIO的内存映射文件处理大文件
    try (FileChannel channel = new RandomAccessFile(file, "r").getChannel()) {
        MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
        // 处理buffer中的数据
    }
    
  2. 优化数据库操作

    • 实现分页查询,避免一次加载大量数据
    // JDBC分页查询示例
    String sql = "SELECT * FROM users LIMIT ? OFFSET ?";
    try (PreparedStatement stmt = conn.prepareStatement(sql)) {
        stmt.setInt(1, pageSize);
        stmt.setInt(2, (pageNum - 1) * pageSize);
        ResultSet rs = stmt.executeQuery();
        // 处理结果集
    }
    
    • 使用游标方式处理大结果集
    // 使用游标处理大结果集
    try (Statement stmt = conn.createStatement(
            ResultSet.TYPE_FORWARD_ONLY,
            ResultSet.CONCUR_READ_ONLY)) {
        stmt.setFetchSize(Integer.MIN_VALUE); // MySQL特定设置,启用流式结果集
        ResultSet rs = stmt.executeQuery("SELECT * FROM large_table");
        while (rs.next()) {
            // 处理每一行数据
        }
    }
    
    • 优化SQL查询,只选择必要的字段
    -- 避免使用SELECT *
    SELECT id, name, email FROM users WHERE ...
    
  3. 调整JVM参数

    • 设置大对象直接进入老年代的阈值
    -XX:PretenureSizeThreshold=3M
    
    • 增加老年代空间,适应大对象分配
    -XX:NewRatio=2
    
  4. 代码层面优化

    • 拆分大对象,使用组合模式管理
    • 实现延迟加载,按需创建对象
    // 延迟加载示例
    class LazyHolder {
        private static class ResourceHolder {
            static final Resource INSTANCE = new Resource();
        }
        
        public static Resource getInstance() {
            return ResourceHolder.INSTANCE;
        }
    }
    
    • 使用对象池管理大对象,重用而非重建

4. 内存碎片化问题的解决方案

  1. 调整CMS收集器参数

    • 启用碎片整理
    -XX:+UseCMSCompactAtFullCollection
    
    • 设置多少次Full GC后进行一次碎片整理
    -XX:CMSFullGCsBeforeCompaction=5
    
    • 调低CMS触发阈值,提前回收
    -XX:CMSInitiatingOccupancyFraction=70
    
  2. 考虑使用其他垃圾收集器

    • 使用G1收集器,它具有更好的碎片处理能力
    -XX:+UseG1GC
    
    • 对于Java 11+,考虑使用ZGC
    -XX:+UseZGC
    
  3. 优化对象分配模式

    • 尽量使用大小相近的对象,减少碎片产生
    • 实现对象池,重用固定大小的对象
    // 使用Apache Commons Pool2
    GenericObjectPoolConfig<MyObject> config = new GenericObjectPoolConfig<>();
    config.setMaxTotal(100);
    config.setMaxIdle(50);
    ObjectPool<MyObject> pool = new GenericObjectPool<>(new MyObjectFactory(), config);
    
    // 使用对象
    MyObject obj = pool.borrowObject();
    try {
        // 使用对象
    } finally {
        pool.returnObject(obj);
    }
    
    • 避免频繁创建和销毁临时对象
  4. 增加内存空间

    • 适当增加堆内存大小,减轻碎片影响
    -Xms4g -Xmx4g
    
    • 调整老年代与新生代比例,为大对象分配预留空间
    -XX:NewRatio=3
    

5. 显式GC调用问题的解决方案

  1. 禁用显式GC

    • 添加JVM参数禁止响应显式GC请求
    -XX:+DisableExplicitGC
    
  2. 优化RMI配置

    • 调整RMI GC间隔时间
    -Dsun.rmi.dgc.client.gcInterval=3600000
    -Dsun.rmi.dgc.server.gcInterval=3600000
    
    • 或完全禁用RMI的显式GC
    -XX:+DisableExplicitGC
    
  3. 代码修改

    • 移除代码中的显式GC调用
    // 避免使用
    System.gc();
    Runtime.getRuntime().gc();
    
    • 使用更精确的内存管理方法替代显式GC
    • 对于DirectByteBuffer等特殊情况,考虑使用替代方案
  4. 第三方库替换

    • 识别并替换包含显式GC调用的第三方库
    • 或通过包装和代理方式拦截显式GC调用

6. 元空间溢出问题的解决方案

  1. 调整元空间参数

    • 增加元空间初始大小
    -XX:MetaspaceSize=256M
    
    • 设置元空间最大值
    -XX:MaxMetaspaceSize=512M
    
  2. 优化类加载机制

    • 减少动态生成的类数量
    • 使用类卸载机制,及时释放不再使用的类
    -XX:+ClassUnloadingWithConcurrentMark
    
    • 优化自定义类加载器,避免类加载器泄漏
    // 确保自定义类加载器可以被GC
    class MyClassLoader extends ClassLoader {
        private final WeakReference<ClassLoader> parent;
        
        public MyClassLoader(ClassLoader parent) {
            super(parent);
            this.parent = new WeakReference<>(parent);
        }
        
        // 实现类加载逻辑
    }
    
  3. 框架使用优化

    • 减少使用CGLib等动态代理技术
    // 使用JDK动态代理替代CGLib
    MyService proxy = (MyService) Proxy.newProxyInstance(
        MyService.class.getClassLoader(),
        new Class[] { MyService.class },
        new MyInvocationHandler(target));
    
    • 优化ORM框架配置,减少动态类生成
    • 避免频繁重新部署应用,特别是在使用JSP的环境中
  4. 监控与预警

    • 建立元空间使用监控
    jstat -gcmetacapacity <pid> 1000
    
    • 设置合理的告警阈值,提前发现问题

7. JVM参数优化方案

  1. 堆内存配置优化

    • 根据应用特性设置合理的堆大小
    -Xms4g -Xmx4g
    
    • 调整新生代与老年代比例
    -XX:NewRatio=2
    
    • 优化Survivor空间比例
    -XX:SurvivorRatio=8
    
  2. 选择合适的垃圾收集器

    • 对于注重响应时间的应用,使用CMS或G1
    -XX:+UseConcMarkSweepGC 或 -XX:+UseG1GC
    
    • 对于注重吞吐量的批处理应用,使用Parallel GC
    -XX:+UseParallelGC
    
    • 对于Java 11+的低延迟应用,考虑ZGC
    -XX:+UseZGC
    
  3. 调整GC触发策略

    • 优化CMS触发阈值
    -XX:CMSInitiatingOccupancyFraction=70
    -XX:+UseCMSInitiatingOccupancyOnly
    
    • 调整G1区域大小和目标暂停时间
    -XX:G1HeapRegionSize=4M
    -XX:MaxGCPauseMillis=200
    
  4. 启用GC日志和监控

    • 配置详细的GC日志
    -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/path/to/gc.log
    
    • 使用GC日志轮转避免单个文件过大
    -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=20M
    

8. 对象晋升问题的解决方案

  1. 调整晋升参数

    • 优化对象晋升年龄阈值
    -XX:MaxTenuringThreshold=15
    
    • 调整动态年龄计算策略
    -XX:+NeverTenure 或 -XX:+AlwaysTenure
    
  2. 优化新生代空间

    • 增加新生代大小,减少晋升压力
    -Xmn2g 或 -XX:NewRatio=2
    
    • 调整Survivor空间比例
    -XX:SurvivorRatio=8
    
  3. 代码层面优化

    • 减少长生命周期对象的创建
    • 优化对象复用策略,避免频繁创建临时对象
    // 使用对象池
    ObjectPool<ExpensiveObject> pool = new GenericObjectPool<>(new ExpensiveObjectFactory());
    
    • 使用对象池管理频繁使用的对象
  4. 考虑使用G1收集器

    • G1具有更智能的对象晋升策略
    -XX:+UseG1GC
    
    • 调整G1的区域大小和收集目标
    -XX:G1HeapRegionSize=4M
    -XX:MaxGCPauseMillis=200
    

9. 数据库或外部系统交互问题的解决方案

  1. 优化数据库交互

    • 使用合理配置的连接池,如HikariCP、Druid
    HikariConfig config = new HikariConfig();
    config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
    config.setUsername("user");
    config.setPassword("password");
    config.setMaximumPoolSize(10);
    config.setMinimumIdle(5);
    
    HikariDataSource dataSource = new HikariDataSource(config);
    
    • 实现分页查询,避免一次加载大量数据
    • 优化SQL查询,只选择必要的字段
    • 使用批处理减少交互次数
    // 批处理插入
    try (Connection conn = dataSource.getConnection();
         PreparedStatement stmt = conn.prepareStatement("INSERT INTO users VALUES (?, ?, ?)")) {
        conn.setAutoCommit(false);
        for (User user : users) {
            stmt.setLong(1, user.getId());
            stmt.setString(2, user.getName());
            stmt.setString(3, user.getEmail());
            stmt.addBatch();
        }
        stmt.executeBatch();
        conn.commit();
    }
    
  2. 优化网络IO

    • 实现异步非阻塞IO,减少线程等待
    // 使用CompletableFuture实现异步调用
    CompletableFuture<Response> future = CompletableFuture.supplyAsync(() -> {
        // 执行远程调用
        return client.call();
    });
    
    // 处理其他任务
    
    // 获取结果
    Response response = future.get();
    
    • 使用NIO或Netty等高性能网络框架
    • 实现请求合并,减少网络交互次数
  3. 优化序列化/反序列化

    • 使用高效的序列化框架如Protobuf、Kryo
    // 使用Kryo序列化
    Kryo kryo = new Kryo();
    kryo.register(MyObject.class);
    
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    Output output = new Output(baos);
    kryo.writeObject(output, object);
    output.close();
    
    byte[] bytes = baos.toByteArray();
    
    • 避免序列化整个对象图,只序列化必要字段
    • 实现增量序列化,减少数据传输量
  4. 实现数据流式处理

    • 使用流式API处理大数据集
    // 使用Java 8 Stream API
    List<User> result = users.stream()
        .filter(user -> user.getAge() > 18)
        .map(User::getName)
        .collect(Collectors.toList());
    
    • 实现数据分块处理,避免一次加载全部数据
    • 考虑使用响应式编程模型,如Reactor、RxJava
  5. 异常处理优化

    • 确保在异常情况下正确关闭资源
    • 使用try-with-resources语法自动管理资源
    • 实现优雅降级,在外部系统异常时保持核心功能可用

六、最佳实践与注意事项

1. JVM参数配置最佳实践

  1. 堆内存配置

    • 设置初始堆大小等于最大堆大小,避免运行时堆大小调整
    -Xms4g -Xmx4g
    
    • 根据应用特性和可用物理内存合理设置堆大小
    • 避免设置过大的堆,可能导致长时间GC暂停
  2. 垃圾收集器选择

    • 对于服务器应用,推荐使用CMS或G1收集器
    • 对于Java 11+的应用,考虑使用ZGC
    • 根据应用对延迟和吞吐量的需求选择合适的收集器
  3. GC日志配置

    • 始终开启GC日志,便于问题排查
    • 配置GC日志轮转,避免单个日志文件过大
    • 定期分析GC日志,及时发现潜在问题
  4. 内存分代配置

    • 根据对象生命周期特性调整新生代和老年代比例
    • 对于短生命周期对象多的应用,增大新生代比例
    • 对于长生命周期对象多的应用,增大老年代比例
  5. 其他重要参数

    • 设置合理的元空间大小
    -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=512M
    
    • 对于CMS收集器,调整并发收集线程数
    -XX:ConcGCThreads=4
    
    • 对于G1收集器,设置合理的暂停时间目标
    -XX:MaxGCPauseMillis=200
    

2. 代码层面优化建议

  1. 对象创建与管理

    • 避免频繁创建临时对象,特别是在循环中
    • 使用对象池管理重复使用的对象
    • 避免创建过大的对象,考虑分块处理
  2. 集合类使用

    • 为集合类预设合理的初始容量
    • 使用更高效的集合实现,如ArrayList替代LinkedList(随机访问场景)
    • 及时清理不再使用的集合元素
  3. 资源管理

    • 使用try-with-resources语法自动关闭资源
    • 确保在finally块中关闭资源
    • 使用连接池管理数据库连接等资源
  4. 并发编程

    • 合理配置线程池,避免创建过多线程
    • 使用非阻塞算法和数据结构,减少锁竞争
    • 避免长时间持有锁,减少线程阻塞
  5. IO操作

    • 使用缓冲IO,减少系统调用
    • 实现异步IO,避免线程阻塞
    • 分块处理大文件,避免一次加载全部内容

3. 监控与预警体系建设

  1. JVM监控

    • 监控GC频率、持续时间和内存使用情况
    • 监控线程状态和数量
    • 监控类加载情况
  2. 系统监控

    • 监控CPU使用率
    • 监控系统内存使用情况
    • 监控磁盘IO和网络IO
  3. 应用监控

    • 监控请求响应时间
    • 监控错误率和异常情况
    • 监控业务指标
  4. 告警设置

    • 设置合理的告警阈值,避免误报
    • 实现多级告警,区分紧急程度
    • 建立告警升级机制,确保问题得到及时处理
  5. 监控工具选择

    • JVM层面:JMX、jstat、JVisualVM
    • 系统层面:Prometheus、Grafana、Zabbix
    • 应用层面:APM工具如Pinpoint、SkyWalking

4. 常见误区与注意事项

  1. 过度调优

    • 不要盲目追求最优参数,应根据实际需求调整
    • 避免频繁修改JVM参数,每次修改后需充分测试
    • 记录每次调整的参数和效果,便于回溯
  2. 忽视业务特性

    • JVM调优应考虑应用的业务特性和对象生命周期
    • 不同类型的应用需要不同的调优策略
    • 调优目标应与业务需求一致(延迟敏感vs吞吐量优先)
  3. 过分关注GC

    • GC只是性能问题的一个方面,不要忽视其他因素
    • 有时应用代码优化比GC调优更有效
    • 系统瓶颈可能在数据库、网络或磁盘IO
  4. 参数设置误区

    • 避免设置过大的堆内存,可能导致长时间GC暂停
    • 避免禁用新生代GC,可能导致更多对象进入老年代
    • 避免过度调整GC线程数,可能导致CPU竞争
  5. 监控与分析误区

    • 不要只关注单次GC事件,应分析长期趋势
    • 不要孤立地分析GC问题,应结合系统整体状况
    • 避免过度依赖单一监控指标,应综合多方面数据

七、案例分析

案例一:内存泄漏导致的频繁Full GC

问题描述:某电商系统在运行数天后,开始出现频繁Full GC,每次Full GC后内存回收效果不明显,最终导致系统响应变慢,甚至出现超时。

排查过程

  1. 分析GC日志:发现Full GC频率逐渐增加,且每次Full GC后老年代内存占用率仍然很高。

  2. 生成堆转储:使用jmap命令生成堆转储文件。

    jmap -dump:live,format=b,file=heap.hprof <pid>
    
  3. 分析堆转储:使用MAT分析堆转储文件,发现有大量的Session对象未被释放,这些对象通过某个静态集合被引用。

  4. 代码审查:检查代码发现,系统中有一个用于缓存用户会话的静态HashMap,但没有设置大小限制和过期机制。

    // 问题代码
    public class SessionManager {
        private static final Map<String, UserSession> sessions = new HashMap<>();
        
        public static void addSession(String id, UserSession session) {
            sessions.put(id, session);
        }
        
        public static UserSession getSession(String id) {
            return sessions.get(id);
        }
        
        // 缺少移除会话的方法
    }
    

解决方案

  1. 修改缓存实现:使用带过期时间和大小限制的缓存替代无限增长的HashMap。

    // 修复后的代码
    public class SessionManager {
        private static final Cache<String, UserSession> sessions = CacheBuilder.newBuilder()
            .maximumSize(10000)
            .expireAfterAccess(30, TimeUnit.MINUTES)
            .build();
        
        public static void addSession(String id, UserSession session) {
            sessions.put(id, session);
        }
        
        public static UserSession getSession(String id) {
            return sessions.getIfPresent(id);
        }
        
        public static void removeSession(String id) {
            sessions.invalidate(id);
        }
    }
    
  2. 添加会话清理机制:在用户登出或会话超时时主动清理会话。

    public void logout(String sessionId) {
        // 其他登出逻辑
        SessionManager.removeSession(sessionId);
    }
    
  3. 增加监控:添加缓存大小监控,设置告警阈值。

效果:修复后,系统内存使用稳定,Full GC频率恢复正常,系统响应时间明显改善。

案例二:高并发下的Full GC问题

问题描述:某支付系统在双11活动期间,随着交易量激增,系统开始频繁出现Full GC,导致部分支付请求超时。

排查过程

  1. 监控系统状态:发现系统CPU使用率高,GC频繁,且老年代空间使用率波动较大。

  2. 分析GC日志:发现大量"promotion failed"错误,表明年轻代对象无法晋升到老年代。

    2024-11-11T10:15:30.123+0800: [GC (Allocation Failure) 2024-11-11T10:15:30.123+0800: [ParNew (promotion failed): 629120K->629120K(629120K), 0.1234567 secs]
    
  3. 分析线程状态:使用jstack发现大量线程处于RUNNABLE状态,且主要在处理支付请求。

    jstack -l <pid> > thread_dump.txt
    
  4. 代码审查:发现支付处理逻辑中创建了大量临时对象,且线程池配置不合理,允许无限制创建线程。

    // 问题代码
    ExecutorService executor = Executors.newCachedThreadPool(); // 无限制线程池
    
    public void processPayment(Payment payment) {
        // 每个请求创建大量临时对象
        List<TransactionRecord> records = new ArrayList<>();
        for (Item item : payment.getItems()) {
            records.add(new TransactionRecord(item));
        }
        // 处理逻辑
    }
    

解决方案

  1. 优化JVM参数:增加年轻代空间,调整GC策略。

    -Xms8g -Xmx8g -Xmn3g -XX:SurvivorRatio=8 -XX:+UseG1GC -XX:MaxGCPauseMillis=200
    
  2. 优化线程池配置:使用有界线程池,避免线程数无限增长。

    // 修复后的代码
    int corePoolSize = Runtime.getRuntime().availableProcessors() * 2;
    int maxPoolSize = corePoolSize * 2;
    ExecutorService executor = new ThreadPoolExecutor(
        corePoolSize,
        maxPoolSize,
        60L, TimeUnit.SECONDS,
        new LinkedBlockingQueue<>(1000),
        new ThreadPoolExecutor.CallerRunsPolicy());
    
  3. 实现流量控制:添加限流机制,避免系统过载。

    RateLimiter limiter = RateLimiter.create(1000.0); // 每秒1000个请求
    
    public void handlePaymentRequest(PaymentRequest request) {
        if (limiter.tryAcquire()) {
            // 处理支付请求
        } else {
            // 请求被限流,返回友好提示
        }
    }
    
  4. 优化对象创建:减少临时对象创建,重用对象。

    // 修复后的代码
    public void processPayment(Payment payment) {
        // 使用对象池
        List<TransactionRecord> records = recordPool.borrowList();
        try {
            for (Item item : payment.getItems()) {
                TransactionRecord record = recordPool.borrowRecord();
                record.setItem(item);
                records.add(record);
            }
            // 处理逻辑
        } finally {
            recordPool.returnList(records);
        }
    }
    

效果:优化后,系统在高并发下GC频率明显降低,支付请求处理更加稳定,超时率大幅下降。

案例三:大对象分配引起的Full GC

问题描述:某数据分析系统在处理大批量数据时,频繁出现Full GC,且每次GC后老年代空间使用率变化较大。

排查过程

  1. 分析GC日志:发现Full GC通常发生在数据处理任务开始时,且老年代空间使用率在GC前后变化明显。

  2. 堆转储分析:发现老年代中存在大量大型数组对象,这些对象与数据处理任务相关。

  3. 代码审查:发现数据处理逻辑中,一次性读取整个数据文件到内存,创建了大型数组。

    // 问题代码
    public List<DataRecord> processDataFile(File file) throws IOException {
        // 一次性读取整个文件内容
        byte[] fileContent = Files.readAllBytes(file.toPath());
        
        // 解析数据
        List<DataRecord> records = new ArrayList<>();
        // 解析fileContent并填充records
        
        return records;
    }
    

解决方案

  1. 实现分块处理:改为分块读取和处理数据,避免一次加载全部内容。

    // 修复后的代码
    public void processDataFile(File file, DataProcessor processor) throws IOException {
        try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
            String line;
            List<DataRecord> batch = new ArrayList<>(1000);
            
            while ((line = reader.readLine()) != null) {
                DataRecord record = parseLine(line);
                batch.add(record);
                
                // 达到批处理大小时处理一批数据
                if (batch.size() >= 1000) {
                    processor.processBatch(batch);
                    batch.clear();
                }
            }
            
            // 处理最后一批数据
            if (!batch.isEmpty()) {
                processor.processBatch(batch);
            }
        }
    }
    
  2. 调整JVM参数:增加老年代空间,设置大对象直接进入老年代的阈值。

    -XX:NewRatio=2 -XX:PretenureSizeThreshold=3M
    
  3. 使用内存映射文件:对于超大文件,使用内存映射文件技术。

    // 使用内存映射文件处理大文件
    public void processLargeFile(File file, DataProcessor processor) throws IOException {
        try (FileChannel channel = new RandomAccessFile(file, "r").getChannel()) {
            long fileSize = channel.size();
            long position = 0;
            long chunkSize = 10 * 1024 * 1024; // 10MB chunks
            
            while (position < fileSize) {
                long size = Math.min(chunkSize, fileSize - position);
                MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, position, size);
                
                // 处理buffer中的数据
                processor.processBuffer(buffer);
                
                position += size;
            }
        }
    }
    

效果:优化后,系统在处理大数据时Full GC频率显著降低,内存使用更加稳定,数据处理效率提高。

八、总结

  JVM频繁Full GC问题是Java应用性能调优中常见且重要的挑战。本文系统地分析了导致频繁Full GC的各种原因,包括内存泄漏、高并发、大对象分配、内存碎片化等,并提供了详细的排查方法和优化建议。

解决JVM频繁Full GC问题需要综合考虑多个方面:

  1. JVM参数调优:合理配置堆内存大小、分代比例、垃圾收集器等参数,使其适合应用特性。

  2. 代码层面优化:减少不必要的对象创建,避免内存泄漏,优化数据结构和算法,合理管理资源。

  3. 架构层面优化:实现流量控制,优化线程模型,改进数据处理方式,提高系统整体效率。

  4. 监控与预警:建立完善的监控体系,及时发现潜在问题,防患于未然。

  有效的JVM调优应该是持续的过程,包括监控、分析、优化和验证的循环。通过建立完善的监控体系,及时发现潜在问题,并采取预防措施,可以大大减少Full GC带来的性能影响,提升应用的稳定性和用户体验。

  最后,需要强调的是,JVM调优不是孤立的工作,它应该结合应用特性、业务需求和系统架构进行综合考虑。有时,最好的解决方案可能不是调整JVM参数,而是重新设计应用架构或优化业务流程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2396576.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

rsync服务的搭建

目录 一、rsync介绍 rsync的安装 二、rsync的语法 三、rsync命令使用 1. 本机同步 2. 远程同步 四、rsync作为服务使用 1、尝试启动rsync程序 2、rsync的配置文件介绍 注意事项&#xff1a; 3. rsyncinotify实时同步 3.依赖服务托管xinetd&#xff08;CentOS 6中rs…

vscode 配置 QtCreat Cmake项目

1.vscode安装CmakeTool插件并配置QT中cmake的路径&#xff0c;不止这一处 2.cmake生成器使用Ninja&#xff08;Ninja在安装QT时需要勾选&#xff09;&#xff0c;可以解决[build] cc1plus.exe: error: too many filenames given; type ‘cc1plus.exe --help’ for usage 编译时…

HTML实现端午节主题网站:龙舟争渡,凭吊祭江诵君赋。

名人说&#xff1a;龙舟争渡&#xff0c;助威呐喊&#xff0c;凭吊祭江诵君赋。——苏轼《六幺令天中节》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 一、项目概览&#xff1a;传统与现代的技术碰撞1. 核心特…

uniapp uni-id 如果是正式项目,需自行实现发送邮件的相关功能

(3) 使用云对象sendEmailCode 发送邮箱验证码&#xff0c;报错送邮箱验证码失败 Error: 已启动测试模式&#xff0c;直接使用&#xff1a;123456作为邮箱验证码即可。 如果是正式项目&#xff0c;需自行实现发送邮件的相关功能 - DCloud问答 uni-id 没有实现邮箱验证码逻辑&am…

C++学习-入门到精通【12】文件处理

C学习-入门到精通【12】文件处理 目录 C学习-入门到精通【12】文件处理一、文件和流二、创建顺序文件三、从顺序文件读取数据文件定位指针对之前的程序进行修改&#xff1a;贷款查询程序 四、更新顺序文件五、随机存取文件1.创建随机存取文件2.修改程序&#xff1a;贷款处理程序…

记一次 Starrocks be 内存异常宕机

突发性 be 内存飙高&#xff0c;直至被系统 kill 掉&#xff0c;be 内存如下&#xff1a;其中 starrocks_be_update_mem_bytes 指标打满&#xff0c;重启也是如此 [rootlocalhost bin]# curl -XGET -s http://192.168.1.49:8040/metrics | grep "^starrocks_be_.*_mem_b…

LangChain-结合GLM+SQL+函数调用实现数据库查询(一)

业务流程 实现步骤 1. 加载数据库配置 在项目的根目录下创建.env 文件&#xff0c;设置文件内容&#xff1a; DB_HOSTxxx DB_PORT3306 DB_USERxxx DB_PASSWORDxxx DB_NAMExxx DB_CHARSETutf8mb4 加载环境变量&#xff0c;从 .env 文件中读取数据库配置信息 使用 os.getenv…

2025年渗透测试面试题总结-匿名[校招]安全工程师(甲方)(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 匿名[校招]安全工程师(甲方) 1. 介绍自己熟悉的渗透领域 2. 编程语言与开发能力 3. 实习工作内容与流程 …

PySide6 GUI 学习笔记——常用类及控件使用方法(地址类QUrl)

文章目录 地址类QUrl主要功能URL 格式介绍常见 scheme&#xff08;协议&#xff09;类型QUrl 类常用方法常用方法示例典型应用场景 地址类QUrl QUrl 是 PySide6.QtCore 模块中的一个类&#xff0c;用于处理和操作 URL&#xff08;统一资源定位符&#xff09;。它可以解析、构建…

任务23:创建天气信息大屏Django项目

任务描述 知识点&#xff1a; Django 重 点&#xff1a; Django创建项目Django视图函数Django路由Django静态文件Django渲染模板 内 容&#xff1a; 使用PyCharm创建大屏项目渲染大屏主页 任务指导 1. 使用PyCharm创建大屏项目。 创建weather项目配置虚拟环境创建ch…

数学分析——一致性(均匀性)和收敛

目录 1. 连续函数 1.1 连续函数的定义 1.2 连续函数的性质 1.2.1 性质一 1.2.2 性质二 1.2.3 性质三 1.2.4 性质四 2. 一致连续函数 2.1 一致连续函数的定义 2.2 一致连续性定理(小间距定理)(一致连续函数的另一种定义) 2.3 一致连续性判定法 2.4 连…

Flutter GridView网格组件

目录 常用属性 GridView使用配置 GridView.count使用 GridView.extent使用 GridView.count Container 实现列表 GridView.extent Container 实现列表 GridView.builder使用 GridView网格布局在实际项目中用的也是非常多的&#xff0c;当我们想让可以滚动的元素使用矩阵…

【深度学习】18. 生成模型:Variational Auto-Encoder(VAE)详解

Variational Auto-Encoder&#xff08;VAE&#xff09;详解 本节内容完整介绍 VAE 的模型结构、优化目标、重参数化技巧及其生成机制。 回顾&#xff1a;Autoencoder&#xff08;自编码器&#xff09; Autoencoder 是一种无监督学习模型&#xff0c;旨在从未标注的数据中学习压…

解决Window10上IP映射重启失效的问题

问题 在实际网络搭建过程中&#xff0c;大家有可能会遇到在局域网范围内&#xff0c;在自己本机上搭建一个网站或者应用时&#xff0c;其他设备通过本机的IP地址无法访问的问题,这个问题可以通过设置IP映射来解决&#xff0c;但是通过netsh interface命令设置的IP映射&#xf…

python h5py 读取mat文件的<HDF5 object reference> 问题

我用python加载matlab的mat文件 mat文件&#xff1a; 加载方式&#xff1a; mat_file h5py.File(base_dir str(N) _nodes_dataset_snr- str(snr) _M_ str(M) .mat, r) Signals mat_file["Signals"][()] Tp mat_file["Tp"][()] Tp_list mat_fil…

linux命令 systemctl 和 supervisord 区别及用法解读

目录 基础与背景服务管理范围配置文件和管理方式监控与日志依赖管理适用场景常用命令对照表实际应用场景举例优缺点对比小结参考链接 1. 基础与背景 systemctl 和 supervisord 都是用于管理和控制服务&#xff08;进程&#xff09;的工具&#xff0c;但它们在设计、使用场景和…

Spring Boot + MyBatis 实现的简单用户管理项目的完整目录结构示例

&#x1f4c1; 示例项目结构&#xff08;基于 Maven&#xff09; user-management/ ├── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/example/usermanagement/ │ │ │ ├── controller/ │ │ │ │ └── UserC…

stm32 + ads1292心率检测报警设置上下限

这个项目是在做心率检测的时候一个小伙伴提出来的&#xff0c;今年五一的时候提出来的想法&#xff0c;五一假期的时候没时间&#xff0c;也没心情做这个&#xff0c;就把这个事情搁置了&#xff0c;在月中做工作计划的时候&#xff0c;就把这个小项目排进来了&#xff0c;五一…

项目练习:element ui 的icon放在button的右侧

文章目录 一、需求描述二、左侧实现三、右侧实现 一、需求描述 我们知道&#xff0c;element ui的button一般都会配置一个icon 这个icon默认是放在左侧的。 如何让它放在右侧了&#xff1f; 二、左侧实现 <el-buttontype"primary"plainicon"el-icon-d-arr…

性能诊断工具AWR配置策略与报告内容解析

AWR&#xff08;Automatic Workload Repository&#xff09;是 Oracle 数据库中的一个重要性能诊断工具。AWR 会按照固定的时间间隔自动收集数据库系统的性能统计信息。这些信息涵盖了数据库运行状态的方方面面&#xff0c;像SQL 执行情况、系统资源利用率、等待事件等。AWR抓取…