JVM性能监控和调优

news2024/9/20 18:50:05

JVM性能监控和调优

文章目录

  • JVM性能监控和调优
    • 性能优化的步骤
    • GC日志分析
      • GC日志参数
      • 测试
      • GC分类
      • 日志结构
    • OOM:堆溢出
      • 模拟堆溢出
      • dump文件分析
      • gc日志分析
    • OOM:元空间溢出
      • 模拟元空间溢出
      • dump文件分析
      • 原因及解决方案
    • OOM:GC overhead limit exceeded
      • 模拟代码
      • dump文件分析
      • 解决方案
    • OOM:线程溢出
      • 模拟代码
      • 原因和解决方案
    • 性能优化一:合理配置堆内存
      • 分析
      • 总结
    • 性能优化二:JIT优化
    • 性能优化三:分析CPU占用超高

JVM(Java虚拟机)调优是为了优化Java应用程序的性能和稳定性。JVM调优的目的是通过调整JVM的配置参数和优化应用程序代码,使其在给定的硬件和软件环境下达到更好的性能表现。防止出现OOM,进行JVM规划和预调优,解决程序中出现的各种OOM,减少FullGC出现的频率,解决运行慢、卡顿等问题。

性能优化的步骤

第1步:熟悉业务场景

第2步(发现问题):性能监控

  • GC 频繁

  • cpu load过高

  • OOM

  • 内存泄漏

  • 死锁

  • 程序响应时间较长

第3步(排查问题):性能分析

  • 打印GC日志,通过GCviewer或者 http://gceasy.io来分析日志信息

  • 灵活运用 命令行工具,jstack,jmap,jinfo等

  • dump出堆文件,使用内存分析工具分析文件,比如jconsole/ jvisualvm / jprofiler / MAT

  • 使用阿里Arthas,或jconsole,JVisualVM来实时查看JVM状态

  • jstack查看堆栈信息

第4步(解决问题):性能调优

  • 适当增加内存,根据业务背景选择垃圾回收器

  • 优化代码,控制内存使用

  • 增加机器,分散节点压力

  • 合理设置线程池线程数量

  • 使用中间件提高程序效率,比如缓存,消息队列等

GC日志分析

GC日志参数

-verbose:gc 输出gc日志信息,默认输出到标准输出
-XX:+PrintGC 输出GC日志。类似:-verbose:gc
-XX:+PrintGCDetails  在发生垃圾回收时打印内存回收详细的日志,并在进程退出时输出当前内存各区域分配情况
-XX:+PrintGCTimeStamps 输出GC发生时的时间戳
-XX:+PrintGCDateStamps  输出GC发生时的时间戳(以日期的形式,如 2013-05-04T21:53:59.234+0800)
-XX:+PrintHeapAtGC   每一次GC前和GC后,都打印堆信息
-Xloggc:<file>  表示把GC日志写入到一个文件中去,而不是打印到标准输出中

测试

添加运行时参数

-Xms60m -Xmx60m  -XX:+PrintGCDetails

输出

也可以导出日志文件,上传到http://gceasy.io进行在线分析

-Xms60m -Xmx60m  -XX:+PrintGCDetails -Xloggc:D:\testGC.log

在这里插入图片描述

GC分类

  • 部分收集(Partial GC):不是完整收集整个Java堆的垃圾收集。其中又分为:

    • 新生代收集(Minor GC / Young GC):只是新生代(Eden / S0, S1)的垃圾收集
    • 老年代收集(Major GC / Old GC):只是老年代的垃圾收集。目前,只有CMS GC会有单独收集老年代的行为。注意,很多时候Major GC会和Full GC混淆使用,需要具体分辨是老年代回收还是整堆回收。
  • 混合收集(Mixed GC):收集整个新生代以及部分老年代的垃圾收集。目前,只有G1 GC会有这种行为

  • 整堆收集(Full GC):收集整个java堆和方法区的垃圾收集。

日志结构

MinorGC(或young GC或YGC)日志:

[GC (Allocation Failure) [PSYoungGen: 31744K->2192K(36864K)] 31744K->2200K(121856K), 0.0139308 secs] [Times: user=0.05 sys=0.01, real=0.01 secs] 

在这里插入图片描述

在这里插入图片描述

Full GC日志介绍:

[Full GC (Metadata GC Threshold) [PSYoungGen: 5104K->0K(132096K)] [ParOldGen: 416K->5453K(50176K)] 5520K->5453K(182272K), [Metaspace: 20637K->20637K(1067008K)], 0.0245883 secs] [Times: user=0.06 sys=0.00, real=0.02 secs] 

在这里插入图片描述

在这里插入图片描述

OOM:堆溢出

模拟堆溢出

@RequestMapping("/add")
public void addObject(){
    System.err.println("add"+peopleSevice);
    ArrayList<People> people = new ArrayList<>();
    while (true){
        people.add(new People());
    }
}

初始参数配置:

-Xms30M -Xmx30M
// 发生oom会导出一个dump文件
-XX:+PrintGCDetails -XX:MetaspaceSize=64m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heap/heapdump.hprof   
-XX:+PrintGCDateStamps -Xms200M -Xmx200M -Xloggc:log/gc-oomHeap.log

报错信息

java.lang.OutOfMemoryError: Java heap space

dump文件分析

jvisualvm工具分析堆内存文件heapdump.hprof

在这里插入图片描述

使用MAT工具查看,能找到对应的线程及相应线程中对应实例的位置和代码:

在这里插入图片描述

gc日志分析

将日志文件导入http://gceasy.io 上面讲了

运行时参数如果没设置堆转储,Jmap命令导出一个即可

jmap -dump:format=b,file=<filename.hprof> <pid>

jmap -dump:live,format=b,file=<filename.hprof> <pid>

OOM:元空间溢出

元空间存储数据类型

方法区(Method Area)与 Java 堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、即时编译器编译后的代码等数据。虽然Java 虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做 Non-Heap(非堆),目的应该是与 Java 堆区分开来。

Java 虚拟机规范对方法区的限制非常宽松,除了和 Java 堆一样不需要连续的内存和可以选择固定大小或者可扩展外,还可以选择不实现垃圾收集。垃圾收集行为在这个区域是比较少出现的,其内存回收目标主要是针对常量池的回收和对类型的卸载。当方法区无法满足内存分配需求时,将抛出 OutOfMemoryError 异常。

模拟元空间溢出

@RequestMapping("/metaSpaceOom")
public void metaSpaceOom(){
    ClassLoadingMXBean classLoadingMXBean = ManagementFactory.getClassLoadingMXBean();
    while (true){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(People.class);
          enhancer.setUseCache(false);
        enhancer.setUseCache(true);
        enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
            System.out.println("我是加强类,输出print之前的加强方法");
            return methodProxy.invokeSuper(o,objects);
        });
        People people = (People)enhancer.create();
        people.print();
        System.out.println(people.getClass());
        System.out.println("totalClass:" + classLoadingMXBean.getTotalLoadedClassCount());
        System.out.println("activeClass:" + classLoadingMXBean.getLoadedClassCount());
        System.out.println("unloadedClass:" + classLoadingMXBean.getUnloadedClassCount());
    }
}

初始参数

-XX:+PrintGCDetails -XX:MetaspaceSize=60m -XX:MaxMetaspaceSize=60m -Xss512K -XX:+HeapDumpOnOutOfMemoryError 
-XX:HeapDumpPath=heap/heapdumpMeta.hprof  -XX:SurvivorRatio=8 -XX:+TraceClassLoading -XX:+TraceClassUnloading 
-XX:+PrintGCDateStamps  -Xms60M  -Xmx60M -Xloggc:log/gc-oomMeta.log

dump文件分析

在这里插入图片描述

JDK8后,元空间替换了永久代,元空间使用的是本地内存

原因及解决方案

原因:

  1. 运行期间生成了大量的代理类,导致方法区被撑爆,无法卸载

  2. 应用长时间运行,没有重启

  3. 元空间内存设置过小

解决方法

因为该 OOM 原因比较简单,解决方法有如下几种:

  1. 检查是否永久代空间或者元空间设置的过小

  2. 检查代码中是否存在大量的反射操作

  3. dump之后通过mat检查是否存在大量由于反射生成的代理类

OOM:GC overhead limit exceeded

模拟代码

public class OOMTest {
    public static void main(String[] args) {
        test1();

//        test2();
    }

    public static void test1() {
        int i = 0;
        List<String> list = new ArrayList<>();
        try {
            while (true) {
                list.add(UUID.randomUUID().toString().intern());
                i++;
            }
        } catch (Throwable e) {
            System.out.println("************i: " + i);
            e.printStackTrace();
            throw e;
        }
    }

    public static void test2() {
        String str = "";
        Integer i = 1;
        try {
            while (true) {
                i++;
                str += UUID.randomUUID();
            }
        } catch (Throwable e) {
            System.out.println("************i: " + i);
            e.printStackTrace();
            throw e;
        }
    }

}

代码解析

第一段代码:运行期间将内容放入常量池的典型案例

intern()方法

  • 如果字符串常量池里面已经包含了等于字符串X的字符串,那么就返回常量池中这个字符串的引用;

  • 如果常量池中不存在,那么就会把当前字符串添加到常量池并返回这个字符串的引用

第二段代码:不停的追加字符串str

为什么第二个没有报GC overhead limit exceeded呢?以上两个demo的区别在于:

  • Java heap space的demo每次都能回收大部分的对象(中间产生的UUID),只不过有一个对象是无法回收的,慢慢长大,直到内存溢出

  • GC overhead limit exceeded的demo由于每个字符串都在被list引用,所以无法回收,很快就用完内存,触发不断回收的机制。

报错信息:

[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7110K->7095K(7168K)] 9158K->9143K(9728K), [Metaspace: 3177K->3177K(1056768K)], 0.0479640 secs] [Times: user=0.23 sys=0.01, real=0.05 secs] 
java.lang.OutOfMemoryError: GC overhead limit exceeded
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7114K->7096K(7168K)] 9162K->9144K(9728K), [Metaspace: 3198K->3198K(1056768K)], 0.0408506 secs] [Times: user=0.22 sys=0.01, real=0.04 secs] 
通过查看GC日志可以发现,系统在频繁性的做FULL GC,但是却没有回收掉多少空间,那么引起的原因可能是因为内存不足,也可能是存在内存泄漏的情况,接下来我们要根据堆DUMP文件来具体分析

dump文件分析

和上面两个相同,可自行分析

解决方案

原因:

这个是JDK6新加的错误类型,一般都是堆太小导致的。Sun 官方对此的定义:超过98%的时间用来做GC并且回收了不到2%的堆内存时会抛出此异常。本质是一个预判性的异常,抛出该异常时系统没有真正的内存溢出

解决方法:

  1. 检查项目中是否有大量的死循环或有使用大内存的代码,优化代码。

  2. 添加参数 -XX:-UseGCOverheadLimit 禁用这个检查,其实这个参数解决不了内存问题,只是把错误的信息延后,最终出现 java.lang.OutOfMemoryError: Java heap space。

  3. dump内存,检查是否存在内存泄漏,如果没有,加大内存。

OOM:线程溢出

模拟代码

public class TestNativeOutOfMemoryError {
    public static void main(String[] args) {
        for (int i = 0; ; i++) {
            System.out.println("i = " + i);
            new Thread(new HoldThread()).start();
        }
    }
}

class HoldThread extends Thread {
    CountDownLatch cdl = new CountDownLatch(1);

    @Override
    public void run() {
        try {
            cdl.await();
        } catch (InterruptedException e) {
        }
    }
}

报错信息

java.lang.OutOfMemoryError : unable to create new native Thread

原因和解决方案

出现这种异常,基本上都是创建了大量的线程导致的

解决一

通过 -Xss 设置每个线程栈大小的容量

  • JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。

  • 正常情况下,在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。

  • 能创建的线程数的具体计算公式如下:

(MaxProcessMemory - JVMMemory - ReservedOsMemory) / (ThreadStackSize) = Number of threads

——————————————————————————————————————

MaxProcessMemory 指的是进程可寻址的最大空间

JVMMemory JVM内存

ReservedOsMemory 保留的操作系统内存

ThreadStackSize 线程栈的大小

——————————————————————————————————————

解决二

  • 在Java语言里, 当你创建一个线程的时候,虚拟机会在JVM内存创建一个Thread对象同时创建一个操作系统线程,而这个系统线程的内存用的不是JVMMemory,而是系统中剩下的内存(MaxProcessMemory - JVMMemory - ReservedOsMemory)。

  • 由公式得出结论:你给JVM内存越多,那么你能创建的线程越少,越容易发生java.lang.OutOfMemoryError: unable to create new native thread

综上,在生产环境下如果需要更多的线程数量,建议使用64位操作系统,如果必须使用32位操作系统,可以通过调整Xss的大小来控制线程数量。

线程总数也受到系统空闲内存和操作系统的限制,检查是否该系统下有此限制:

  • /proc/sys/kernel/pid_max 系统最大pid值,在大型系统里可适当调大

  • /proc/sys/kernel/threads-max 系统允许的最大线程数

  • maxuserprocess(ulimit -u) 系统限制某用户下最多可以运行多少进程或线程

  • /proc/sys/vm/max_map_count

max_map_count文件包含限制一个进程可以拥有的VMA(虚拟内存区域)的数量。虚拟内存区域是一个连续的虚拟地址空间区域。在进程的生命周期中,每当程序尝试在内存中映射文件,链接到共享内存段,或者分配堆空间的时候,这些区域将被创建。调优这个值将限制进程可拥有VMA的数量。限制一个进程拥有VMA的总数可能导致应用程序出错,因为当进程达到了VMA上线但又只能释放少量的内存给其他的内核进程使用时,操作系统会抛出内存不足的错误。如果你的操作系统在NORMAL区域仅占用少量的内存,那么调低这个值可以帮助释放内存给内核用。

性能优化一:合理配置堆内存

增加内存可以提高系统的性能而且效果显著,那么随之带来的一个问题就是,我们增加多少内存比较合适?如果内存过大,那么如果产生FullGC的时候,GC时间会相对比较长,如果内存较小,那么就会频繁的触发GC,在这种情况下,我们该如何合理的适配堆内存大小呢?

分析

依据的原则是根据Java Performance里面的推荐公式来进行设置。

Java整个堆大小设置,Xmx 和 Xms设置为老年代存活对象的3-4倍,即FullGC之后的老年代内存占用的3-4倍。 方法区(永久代 PermSize和MaxPermSize 或 元空间 MetaspaceSize 和 MaxMetaspaceSize)设置为老年代存活对象的1.2-1.5倍。 年轻代Xmn的设置为老年代存活对象的1-1.5倍。 老年代的内存大小设置为老年代存活对象的2-3倍。

但是,上面的说法也不是绝对的,也就是说这给的是一个参考值,根据多种调优之后得出的一个结论,大家可以根据这个值来设置一下我们的初始化内存,在保证程序正常运行的情况下,我们还要去查看GC的回收率,GC停顿耗时,内存里的实际数据来判断,Full GC是基本上不能有的,如果有就要做内存Dump分析,然后再去做一个合理的内存分配。

我们还要注意到一点就是,上面说的老年代存活对象怎么去判定

判定:

JVM参数中添加GC日志,GC日志中会记录每次FullGC之后各代的内存大小,观察老年代GC之后的空间大小。可观察一段时间内(比如2天)的FullGC之后的内存情况,根据多次的FullGC之后的老年代的空间大小数据来预估FullGC之后老年代的存活对象大小(可根据多次FullGC之后的内存大小取平均值)。

总结

在内存相对紧张的情况下,可以按照上述的方式来进行内存的调优, 找到一个在GC频率和GC耗时上都可接受的一个内存设置,可以用较小的内存满足当前的服务需要。

但当内存相对宽裕的时候,可以相对给服务多增加一点内存,可以减少GC的频率,GC的耗时相应会增加一些。 一般要求低延时的可以考虑多设置一点内存, 对延时要求不高的,可以按照上述方式设置较小内存。

如果在垃圾回收日志中观察到OutOfMemoryError,尝试把Java堆的大小扩大到物理内存的80%~90%。尤其需要注意的是堆空间导致的OutOfMemoryError以及一定要增加空间。

  • 比如说,增加-Xms和-Xmx的值来解决old代的OutOfMemoryError

  • 增加-XX:PermSize和-XX:MaxPermSize来解决permanent代引起的OutOfMemoryError(jdk7之前);增加-XX:MetaspaceSize和-XX:MaxMetaspaceSize来解决Metaspace引起的OutOfMemoryError(jdk8之后)

记住一点Java堆能够使用的容量受限于硬件以及是否使用64位的JVM。在扩大了Java堆的大小之后,再检查垃圾回收日志,直到没有OutOfMemoryError为止。如果应用运行在稳定状态下没有OutOfMemoryError就可以进入下一步了,计算活动对象的大小。

性能优化二:JIT优化

可见这篇文章的逃逸分析内容

性能优化三:分析CPU占用超高

如果是生产环境的话,是怎么样才能发现目前程序有问题呢?我们可以推导一下,如果线程死锁,那么线程一直在占用CPU,这样就会导致CPU一直处于一个比较高的占用率。所示我们解决问题的思路应该是:

1.查看所有java进程 ID

jps -l

2.根据进程 ID 检查当前使用异常线程的pid

top -Hp 1456

在这里插入图片描述

3.当前占用cpu比较高的线程 ID 是1465

接下来把 线程 PID 转换为16进制为

# 10 进制线程PId 转换为 16 进制
1465   ------->    5b9
# 5b9 在计算机中显示为   
0x5b9

4.最后我们把线程信息打印出来:

jstack 1456 > jstack.log

5.打开jstack.log文件 查找一下刚刚我们转换完的16进制ID是否存在

在这里插入图片描述

jstack命令生成的thread dump信息包含了JVM中所有存活的线程,里面确实是存在我们定位到的线程 ID ,在thread dump中每个线程都有一个nid,在nid=0x5b9的线程调用栈中,我们发现两个线程在互相等待对方释放资源

xxx大厂问题排查过程:
...省略;
4、ps aux | grep java  查看到当前java进程使用cpu、内存、磁盘的情况获取使用量异常的进程
5、top -Hp 进程pid  检查当前使用异常线程的pid
6、把线程pid变为16进制如 31695 -7bcf  然后得到0x7bcf
7、jstack 进程的pid | grep -A20  0x7bcf  得到相关进程的代码

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

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

相关文章

python学习第二节:安装开发工具pycharm

python学习第二节&#xff1a;安装开发工具pycharm 1.下载安装包&#xff1a;https://www.jetbrains.com/pycharm/ 点击 Download 下滑页面到下面的社区版&#xff0c;&#xff08;上面的专业版是收费的&#xff0c;下面的社区版是免费的&#xff09; 2.安装 点击安装文件 …

计算机基础知识复习9.5

数据交换 电路交换&#xff1a;交换信息的两个主机之间简历专用通道&#xff0c;传输时延小&#xff0c;实时性强&#xff0c;效率低&#xff0c;无法纠正错误。 报文交换&#xff1a;信息拆分成小包(报文&#xff09;大小无限制&#xff0c;有目的/源等信息提高利用率。有转…

C++入门项目:Linux下C++轻量级Web服务器 跑通|运行|测试(小白进)

TinyWebServer是一个开源的项目&#xff0c;适合小白入门C网络编程&#xff0c;注意该项目是在linux系统下。 Linux下C轻量级Web服务器&#xff0c;助力初学者快速实践网络编程&#xff0c;搭建属于自己的服务器. 使用 线程池 非阻塞socket epoll(ET和LT均实现) 事件处理(R…

时钟分频流程

具体流程&#xff1a; 外部晶振&#xff08;XTIpll&#xff09;和 外部时钟&#xff08;EXTCLK&#xff09;经过OM【3&#xff1a;2】选择进入时钟模块 我们可以看到&#xff0c;在不同的模式下会选择不同的时钟源和状态。 时钟信号进入锁相环(PLL)后&#xff0c;会改…

Java项目:139 springboot基于SpringBoot的论坛系统设计与实现

项目介绍 论坛系统设置的角色有管理员&#xff0c;版主&#xff0c;用户等。 管理员管理论坛&#xff0c;主要是对论坛帖子增删改查以及对论坛帖子回复进行查看&#xff0c;对版主和用户进行管理&#xff0c;管理新闻等。 版主可以发布帖子&#xff0c;可以查询论坛帖子&…

Stage 模型应用程序包的结构

AppScope 目录是工具自动生成的 Module 则是工具自动生成的 entry、library&#xff0c;也可以自定义。 Module 类型 Module 有两种类型&#xff1a; Ability 类型的 Module&#xff1a;用来实现应用功能和特性&#xff0c;每一个 Ability 类型的 Module 编译以后。生成一个…

【C++ Primer Plus习题】11.5

问题: 解答: main.cpp #include <iostream> #include "Stonewt.h" using namespace std;int main() {Stonewt incognito 275;cout << "incognito: " << incognito << endl;Stonewt wolfe(285.7);cout << "wolfe: &qu…

Spring Boot技术在医疗排班系统开发中的创新

4系统概要设计 4.1概述 本系统采用B/S结构(Browser/Server,浏览器/服务器结构)和基于Web服务两种模式&#xff0c;是一个适用于Internet环境下的模型结构。只要用户能连上Internet,便可以在任何时间、任何地点使用。系统工作原理图如图4-1所示&#xff1a; 在这里插入图片描述 …

Web入门-08.Tomcat-基本使用

一.Tomcat的基本使用 二.Tomcat使用时的常见问题 Tomcat默认占用8080端口 如果占用8080端口的进程不方便被关闭掉&#xff0c;那么便配置Tomcat端口号(conf/server.xml) 三.Tomcat部署项目

机器学习之 PCA降维

1.PCA 降维简介 主成分分析&#xff08;Principal Component Analysis, PCA&#xff09;是一种统计方法&#xff0c;用于在数据集中寻找一组线性组合的特征&#xff0c;这些特征被称为主成分。PCA 的目标是通过变换原始特征空间到新的特征空间&#xff0c;从而减少数据的维度&…

AGV行业遇冷,叉车AGV逆风崛起:180家企业掀起血战

导语 大家好&#xff0c;我是社长&#xff0c;老K。专注分享智能制造和智能仓储物流等内容。 在自动化物流领域&#xff0c;一场悄然发生的变革正引领着行业风向的转折。尽管2024年以来&#xff0c;整体AGV&#xff08;自动引导车&#xff09;行业因下游市场需求疲软而遭遇增速…

打造安心宠物乐园:EasyCVR平台赋能猫咖/宠物店的智能视频监控解决方案

随着宠物经济的蓬勃发展&#xff0c;宠物店与猫咖等场所对顾客体验、宠物安全及健康管理的需求日益提升。然而&#xff0c;如何确保这些场所的安全与秩序&#xff0c;同时提升顾客体验&#xff0c;成为了经营者们关注的焦点。引入高效、智能的视频监控方案&#xff0c;不仅能够…

uniapp交互反馈

页面交互反馈可以通过:uni.showToast(object)实现,常用属性有 ioc值说明 值说明success显示成功图标&#xff0c;此时 title 文本在小程序平台最多显示 7 个汉字长度&#xff0c;App仅支持单行显示。error显示错误图标&#xff0c;此时 title 文本在小程序平台最多显示 7 个汉字…

【Linux】多线程:POSIX库、线程管理、线程ID

目录 一、POSIX线程库 二、线程ID 三、动态库加载 四、再谈线程ID 一、POSIX线程库 原生库&#xff1a;指的是操作系统自带的库&#xff0c;如POSIX线程库&#xff0c;在类Unix系统中通常是原生支持的。这些库是操作系统的一部分&#xff0c;提供了系统级的线程管理功能。 …

基于人工智能的植物病害检测系统

目录 引言项目背景环境准备 硬件要求软件安装与配置系统设计 系统架构关键技术代码示例 数据预处理模型训练模型预测应用场景结论 1. 引言 植物病害的早期检测对于农业生产至关重要&#xff0c;它有助于及时采取措施防止病害扩散&#xff0c;减少作物损失。通过人工智能技术…

css问题:display:flex布局+justify-content: space-between; 最后一行不能左对齐

解决方法1&#xff1a; display: flex;margin: 10px var(--leftRight); --leftRight&#xff1a; 动态计算一行减去item的宽度后剩下的间距 解决方法2&#xff1a;网格布局 display: grid;grid-template-columns: repeat(5, 1fr);margin: 10px auto; 完整代码&#xff1a; &l…

C++ 继承学习笔记

1.继承概念 继承 (inheritance) 机制是面向对象程序设计 使代码可以复用 的最重要的手段&#xff0c;它允许程序员在 保 持原有类特性的基础上进行扩展 &#xff0c;增加功能&#xff0c;这样产生新的类&#xff0c;称派生类。继承 呈现了面向对象 程序设计的层次结构 &#xf…

奥威让您更懂现金流情况

企业现金流一旦出了问题都是大问题&#xff0c;会直接影响到企业的日常运作&#xff0c;甚至直接关系到企业能不能继续存活&#xff0c;因此现金流量表是企业财务分析中重要报表之一&#xff0c;也是企业监控财务监控情况的重要手段之一。那么这么重要的一份现金流量表该怎么做…

科研绘图系列:R语言折线图(linechart plots)

文章目录 介绍加载R包导入数据数据预处理画图组合图形介绍 在R语言中,折线图(Line Plot)是一种常用的数据可视化类型,用于展示数据随时间或有序类别变化的趋势。折线图通过连接数据点来形成一条或多条线,这些线条可以清晰地表示数据的变化方向、速度和模式。 加载R包 k…

基于Spring Boot的宠物领养系统的设计与实现

基于Spring Boot的宠物领养系统的设计与实现 springboot138宠物领养系统的设计与实现 摘 要 如今社会上各行各业&#xff0c;都在用属于自己专用的软件来进行工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。互联网的发展&#xff0c;离不开一…