《企业实战分享 · 内存溢出分析》

📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗
🌻 近期刚转战 CSDN,会严格把控文章质量,绝不滥竽充数,如需交流,欢迎留言评论。👍

文章目录

  • 写在前面的话
    • 书写背景
    • 知识补充
    • 常见原因
    • 排查方式
  • 使用 JProfiler 排查
    • 具体步骤
  • 使用 VisualVM 排查
    • 监视视图
    • 线程视图
    • 抽样器
    • Visual GC插件
  • 使用Eclipse Memory Analyzer排查
    • 工具介绍
    • Overview组件
    • Top consumers组件
    • Histogram组件
    • Thread Overview组件
    • Domainator Tree组件
    • Leak Suspects组件
    • 常用分析技巧


写在前面的话

书写背景

博主所在公司采用的是技术栈为:后端 SpringCloud,前端 Nuxt,部署 K8S。(看过前面文章的应该知道)
由于涉及服务较多,代码量也随着需求研发不断增加,难免出现一些臭鱼烂虾的隐患代码。
最终导致的是,线上服务的内存溢出问题屡见不鲜,每次排查都要依靠架构等少数人员,现着手整理了一份分析内存溢出问题的流程文档,方便研发主管人员可以自行排查,而不需要依赖架构部门。
此篇博文将以上述文档为基础略为调整,发布到博客,若出现一些未及时调整的话术,希望理解。

知识补充

Tips:这部分纯属知识补充,可以跳过。

关于内存泄漏和内存溢出
一般来说内存异常问题分为内存泄漏和内存溢出。
内存泄漏:对象已经不使用了,但是还占用着内存空间,没有被释放。
内存溢出:堆空间不够用了,通常表现为 OutOfMemoryError,内存泄漏通常会导致内存溢出。
再看一下“内存泄漏”的定义:一个不再被程序使用的对象或变量还在内存中占有存储空间。
一次内存泄漏似乎不会有大的影响,但内存泄漏堆积后的后果就是内存溢出。
“内存溢出”:指程序申请内存时,没有足够的内存供申请者使用,或者说,给了你一块存储 int 类型数据的存储空间,但是你却存储 long 类型的数据,那么结果就是内存不够用,此时就会报错OOM,即所谓的内存溢出。
二者的关系:内存泄漏的堆积最终会导致内存溢出
**内存泄漏 :**是指你向系统申请分配内存进行使用(new),可是使用完了以后却不归还(delete),结果你申请到的那块内存你自己也不能再访问(也许你把它的地址给弄丢了),而系统也不能再次将它分配给需要的程序。就相当于你租了个带钥匙的柜子,你存完东西之后把柜子锁上之后,把钥匙丢了或者没有将钥匙还回去,那么结果就是这个柜子将无法供给任何人使用,也无法被垃圾回收器回收,因为找不到他的任何信息。
**内存溢出:就是你要的内存空间超过了系统实际分配给你的空间,此时系统相当于没法满足你的需求,就会报内存溢出的错误。

内存溢出种类与解决
JVM 管理的内存大致包括三种区域:Heap space(堆区域)、Java Stacks(Java 栈)、Permanent Generation space(永久保存区域)。由此,OOM 简单的分为堆溢出、栈溢出、永久代溢出(常量池/方法区)。Java 程序的每个线程中都有一个独立的堆栈。

PS:我们日常开发中,遇到最后的就是堆内存溢出,这里重点介绍这块,其他两块自行了解。

Java 堆内存溢出
Java 堆是线程共有的区域,主要用来存放对象实例,几乎所有的 Java 对象都在这里分配内存,也是 JVM 内存管理最大的区域。Java堆内存分年轻代和年老代,堆内存溢出一般是年老代溢出。当程序不断地创建大量对象实例并且没有被GC回收时,就容易产生内存溢出。当一个对象产生时,主要过程是这样的:
1、JVM首先在年轻代的Eden区为它分配内存;
2、若分配成功,则结束,否则JVM会触发一次Young GC,试图释放Eden区的不活跃对象;
3、如果释放后还没有足够的内存空间,则将Eden区部分活跃对象转移到Survivor区,Survivor区长期存活的对象会被转移到老年代;
4、当老年代空间不够,会触发Full GC,对年老代进行完全的垃圾回收;
5、回收后如果Suvivor和老年代仍没有充足的空间接收从Eden复制过来的对象,使得Eden区无法为新产生的对象分配内存,即溢出。
由此可见,当程序不断地创建大量对象实例并且没有被GC回收时,就容易产生内存溢出。如下:

public static void main(String[] args){ArrayList list = new ArrayList();while(true){list.add(new heap());}
}

堆内存溢出很可能伴随内存泄漏,应首先排查可能泄露的对象,再通过工具检查 GC roots 引用链,从而发现泄露对象是由于何种引用关系使得GC无法回收他们;若不存在内存泄漏,换句话说就是内存中的对象还都需要继续存活,则可通过修改虚拟机的堆参数将堆内存增大。

补充说明:
堆区域用来存放 Class 的实例(即对象),对象需要存储的内容主要是非静态属性。每次用 new 创建一个对象实例后,对象实例存储在堆区域中,这部分空间也被 JVM 的垃圾回收机制管理。
java.lang.OutOfMemoryError: Java heap space 此种情况最常见,一般由于内存泄露或者堆的大小设置不当引起。原因是 JVM 创建的对象太多,在进行垃圾回收之间,虚拟机分配的到堆内存空间已经用满了,与 Heap space 有关。
解决这类问题有两种思路:
1、对于内存泄露,可以通过内存监控软件查找程序中的泄露代码。检查程序,看是否有死循环或不必要地重复创建大量对象。找到原因后,修改程序和算法。
2、增加 JVM 中 Xms(初始堆大小)和 Xmx(最大堆大小)参数的大小。如:set JAVA_OPTS= -Xms256m -Xmx1024
PS:JVM大小通常运维人员会考虑一个合适的值,研发要注意的就是第一点。

常见原因

首先排除Java程序的JVM配置问题,代码有哪些常见问题会导致内存溢出:
1、内存中加载的数据量过大,一次从数据库取出过多数据导致内存溢出;
2、集合类中有对对象的引用,使用完后没有及时清空,使得 JVM 不能回收;
3、代码中存在死循环或循环产生过多重复的实体对象;
4、未完待续。。。

排查方式

凭经验肉眼扫描代码
针对上述列的常见导致内存溢出的代码,日常排查代码可以遵循如下轨迹:
1、检查对数据库查询中,是否有一次获得全部数据的查询,一般来说,如果一次取十万条记录到内存,就可能引起内存溢出。这个问题比较隐蔽且有潜伏性,在上线前,数据库中数据较少,不容易出问题,上线后,数据库中数据多了,一次查询就有可能引起内存溢出。所以可以使用分页查询数据库;
2、检查代码中是否有死循环或递归调用导致有大循环重复产生新对象实体;
3、检查 List、MAP 等集合对象是否有使用完后未清除的问题,集合中存在对对象的引用会导致这些对象不能被 GC 回收;

借助工具定位问题
1、服务启动的时候,设置其参数,使其支持在发生内存溢出时自动dump内存快照;

PS:也就是通常说的dump文件,这步运维那边会做好。

2、内存快照文件是后缀为 .hprof 的文件,内存溢出的时候产生,可以从服务器下载到本地;

PS:找运维拿到这个文件,或者从SpringBootAdmin尝试下载。

3、借助内存溢出排查工具 VisualVM、Jprofiler 等,直接加载hprof文件,按步骤定位问题代码;

PS:安装一款你顺手的工具,排查一下。

研发人员如何手动操作
1、进入到对应的容器里面去
docker exec -it 容器id /bin/bash
2、ps -ef | grep java 查看java进程id
3、保存堆和栈的现场快照 ,jstack命令用于打印指定Java进程、核心文件或远程调试服务器的Java线程的Java堆栈跟踪信息
jstack pid > stack.log
jmap -dump:format=b,file=heap.hprof pid
4、退出容器
5、看情况是否重启容器
docker restart 容器id
6、将文件从容器导出到宿主机
docker cp 容器id:要导出的文件路径 宿主机路径
7、再次进入容器删掉 相关文件
8、下载文件导本地进行分析并且删除
9、接下来使用具体工具分析


使用 JProfiler 排查

具体步骤

选择指定的 dump 文件(后缀是hprof),使用 Jprofiler 加载,第一次可能较慢。

文件范例:D:\cjwmy1013\devlop\zoe-optimus-dia-dc-8157.hprof

image.png
如图点击大对象
image.png
image.png
image.png
image.png
image.png

补充说明
若软件提示内存不够,可以加大内存,如下:
C:\Users\cjwmy1013.jprofiler11\jprofiler.vmoptions
-Xmx4036m
-Xss2m


使用 VisualVM 排查

VisualVM是jdk自带的jvm监测工具,主要用来监测cpu、线程和堆内存的使用情况,使用简单,不需要额外配置,可以支持本地和远程环境,软件的位置在jdk的bin目录下,找到jvisualvm.exe,双击打开。
image.png
image.png
image.png

监视视图

选择本地的运行的某个进程双击,选择监视tab按钮就可以查询到当前进程的cpu、内存、类、线程运行信息
image.png

点击堆Dump用来生成某一时刻的堆转储文件快照(.hprof),并且把堆转储信息转换成堆转储标签内,我们可以看到摘要、类、实例数等信息以及通过 OQL 控制台执行查询语句功能,帮助我们分析对象的引用关系、是否有内存泄漏情况的发生。
image.png
堆转储的概要包括转储的文件大小、路径等基本信息,运行的系统环境信息,也可以显示所有的线程信息。
image.png
从类视图可以获得各个类的实例数和占用堆大小数,分析出内存空间的使用情况,找出内存的瓶颈,避免内存的过度使用。
image.png
对两个堆转储文件进行比较,通过比较我们能够分析出两个时间点哪些对象被大量创建或销毁。
image.png
image.png
打开实例数视图需要指定一个类,即在类视图中双击某一个类进入该类的实例数视图,通过下图可以看出主要的信息包含该类的实例(对象)总数,单个实例、总实例占堆内存的大小,单个实例在堆内存中的所有字段、值,以及对象与引用对象之间的依赖关系。
image.png

线程视图

线程视图显示当前进程包含的所有线程,类型分为User Thread(用户线程)、Daemon Thread(守护线程),任何一个守护线程都是整个JVM中所有非守护线程的保姆,Daemon的作用是为其他线程的运行提供便利服务,守护线程最典型的应用就是 GC (垃圾回收器)
image.png
image.png

  • 运行状态:线程正在运行
  • 休眠状态:线程在休眠
  • 等待状态:调用Object.wait的线程,此处要注意,condtion.await并不是此状态,而是下面的状态。
  • 驻留状态:调用了LockSupport.park的线程就是此状态,常见的有如下:
Lock lock = new ReentrantLock();
lock.lock();
Condition condition = lock.newCondition();
condition.await();
  • 监视状态:synchrnoiezed获取锁被阻塞时的状态

如果想保存某个时间节点的线程快照信息,就点击右上角的线程Dump,其实就是执行jstack命令,jstack命令可以生成JVM当前时刻的线程快照。线程快照是当前JVM内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。
image.png

抽样器

抽样器可以对该进程的某个时间段的CPU使用情况进行抽样,即点击CPU样例的快照按钮。
image.png
image.png
image.png
抽样器同样可以对内存使用情况进行实时监测,以及保存快照信息,根据内存大小(字节数)降序排序就能快速定位出占用内存较多的类,下图可以看出是byte[]数组占用了大量内存,如果此时还不能定位到程序哪里的问题,那就通过保存内存的快照,然后分析这个dump。
image.png
根据下图可以看出byte[]数组实例很少,但是占用的内存很多,继续向上查找引用它的实例,最后发现是arrayList中引用了byte[],既然问题找到了,后续的解决方法就是在程序中进行定位,优化现有代码。
image.png
image.png

Visual GC插件

visual gc插件可以清晰的看到堆的使用情况以垃圾收集信息,工具栏选择插件,可用插件搜索visual gc点击安装,
image.png
image.png
安装完毕后,重启visualVM,会出现如下图:
image.png
visual gc分为两个板块:
1、visual gc window(即spaces区域)
Metaspace :方法区的内存,如果JDK1.8之前的版本,就是Perm,JDK7和之前的版本都是以永久
Old:老年代
新生代:由Eden、S0、S1组成

每个方框都使用不同的颜色表示,有颜色的区域是占用的空间,空白的是剩余的空间,当程序运行时,会动态的显示
image.png

2、Graph区域,包含了以时间轴为横坐标的状态面板
Compile Time:编译情况,1407 compoles - 2.644s 表示编译总数为1407,编译总耗时为2.644s。
一个脉冲表示一次JIT编译,脉冲越宽表示编译时间越长。
Class Loader Time:类加载情况,1709 loaded,0 unloaded - 696.184ms表示已加载的数量为1709,卸载的数量为0,耗时为696.184ms。
GC Time:总的(包含新生代和老年代)gc情况记录 52 collections,708.580ms Last Cause:Allocation Failure表示一共经历了52次gc,总共耗时708.580ms。
Eden Space:新生代Eden区内存使用情况
Survivor 0和Survivor 1:新生代的两个Survivor区内存使用情况
Old Gen:老年代内存使用情况
Metaspace:方法区内存使用情况(最大容量,当前容量)
image.png


使用Eclipse Memory Analyzer排查

工具介绍

Java VisualVM只提供了一些基本的功能,所以我们一般不使用Java VisualVM来分析,而是使用Eclipse Memory Analyzer(MAT)来分析,MAT工具是一款强大的Java堆内存分析工具,特点是免费使用,无需安装解压即用,可用于查找内存泄露以及查看内存消耗情况,便于开发或运维人员快速定位内存溢出或内存泄露问题。
需要注意必须安装jdk1.8环境才能使用
下载地址:https://www.eclipse.org/mat/previousReleases.php
解压后,双击MemoryAnalyzer.exe打开
image.png
界面效果如下:
image.png

这边写一个小案例来演示内存不断增加的场景:

public class Main {public static void main(String[] args) {List<Demo> list = new ArrayList<>();while (true) {list.add(new Demo());}}
}

然后设置启动参数,让程序内存溢出时自动生成Dump文件
image.png

-Xmx30m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\dump

-Xmx30m:最大堆内存为30m -XX:+HeapDumpOnOutOfMemoryError:当JVM发生OOM时,自动生成DUMP文件。 -XX:HeapDumpPath:指定文件路径,例如:-XX:HeapDumpPath=${目录}/java_heapdump.hprof。如果不指定文件名,默认为:java_pid.hprof

image.png
当内存溢出的时候自动在指定的目录下生成了一个hprof文件,然后用Eclipse Memory Analyzer打开这个文件:
image.png

点击File-》Open Heap Dump,选择内存溢出生产的.hprof文件,默认选择Leak Suspects Report泄漏可疑报告
image.png
导入成功之后首先来看这个界面,我们先不着急如何分析问题,先快速了解下面板上各个组件的用法,方便后面的学习。
image.png

Overview组件

OverView功能是导入了dump文件之后,对可能出现的问题做了一个整体性的分析概述,首先呈现在眼前的是以一个形状图的方式,快速展现了该文件中dump文件大小,以及类、对象和类加载器的数量,当光标移动到蓝色区域时候,会呈现该日志文件的主要线程,类加载器信息。
image.png
![4E3B`I)FS4I4}B3NN%Z_OQ.png
总共使用的内存为24M Thread对象占用了23M,类加载器占用了206kb,其他对象占用了752kb。

Top consumers组件

展示的是占用内存比较多的对象的分布,下面是具体的一些类和占用
![MSN(4598`T$JEKW~H4RT_]5.png](https://img-blog.csdnimg.cn/img_convert/9b754529578285828253081bd16d22f1.png)

Histogram组件

Histogram 可以列出内存中的对象,对象的个数以及大小
Objects:对象的个数
Shallow Heap:对象所占用的本身内存大小(不包含引用对象)
Retained Heap:对象以及它所持有的其它引用(包括直接和间接)所占的总内存大小

当点击进去之后,为我们呈现出dump文件中,已经创建的主要的对象信息,默认按照对象的个数进行排序,而这个排序,多少也反映出在当前的dump文件中,那些排在前面的数量最多的对象可能是我们分析问题的关键入口
image.png
顶部的可以支持对象名称的模糊匹配,比如搜索Picture,可以快速找到我们需要的对象名称。
image.png
MAT还提供了分组查看功能,方便快速定位类的信息,默认视图就是Group by class根据类分组。
image.png
Group by superclass根据父类分组,所有类的父类都是Object,所以会生成树形结构。
![F`3TDHG8CHI5J3`ZLLPSVM.png
Group by class loader根据类加载器分组
2)3VWXHT23V{~HXP1)N%5Y.png
Group by package根据包目录分组
image.png
除了分组功能,还支持按照字段排序,右键某个对象可以选择按照类名、对象个数、浅堆数量、深堆数量排序,这也是快速定位到需要查找到的对象的一种方式
![(`X]0DS7UYQ4H163JJJ7HE5.png

这里需要了解浅堆、深堆的概念,因为这两个参数是分析OOM的一个关键点。
浅堆:指的是一个对象所消耗的内存(不包括内部引用的对象大小)
深堆:指的是一个对象直接访问或者间接访问到的所有对象的浅堆之和,即对象被回收后,可以释放的真实空间。
浅堆、深堆总结:对象的深堆个数总是多于浅堆,实际开发过程中,可能因为编码的习惯不好,导致某些类中,对象的引用链条特别长,层级也很深,搞不清楚那些对象是实际在使用的,假如正好有那么一些对象实际上并没有使用,但是在某些循环中大量创建,尤其是大对象,在这种情况下,很容易造成GC过程的失败最终引发OOM,所以分析这两个参数对于快速定位那些数量较多的对象还是很有帮助的。

对象引用链:
查看引用当前对象的外部对象可以点击这个右键—》show objects by class-》by incomming references
![KJ(783)WBOKYAUZ4VV({~7.png
查看当前对象引用的外部对象可以点击这个右键—》show objects by class-》by outgoing references
![3[1)Q}RY_M]R}LKQ)KaTeX parse error: Expected 'EOF', got '}' at position 190: …问题的一个很好的点。 ![PP}̲EW~X44IIRB7O03)…W.png

Thread Overview组件

Thread Overview展示出当前dump文件中,所有的线程信息,展示出了所有的线程,主线程,以及各个线程中的浅堆和深堆占用的大小,类加载器,是否守护线程等信息。
![9PZQ%HKT$Y2FYPSGMQ84AM.png
以main线程为例,列举出了里面的成员变量的信息,比如在当前的main线程中,有一个成员变量包含了很多对象,并且浅堆和深堆的大小都很大。
@`_300N$3Q7(VTH4{54)77P.png

Domainator Tree组件

Domainator Tree指的是支配树的对象图,支配树体现了对象实例之间的支配关系,理解支配树的目的是,在我们分析dump文件时,可以通过支配树,清楚的知道某个对象引用的对象情况。
N2OV1)MOJLALB0WC@1OD~4B.png
Retained Heap表示这个对象以及它所持有的其它引用(包括直接和间接)所占的总内存,因此从上图中看,前两行的Retained Heap是最大的,我们分析内存泄漏时,内存最大的对象也是最应该去怀疑的。

Leak Suspects组件

点击Leak Suspects查看具体的内存泄露报告,MAT分析工具会根据你导入的dump文件,快速生成一份怀疑报告,将可能出现内存泄露的点展示出来,便于开发或运维人员进行问题定位。
O_B4E(Q0%(FEJ8VPONHD)NY.png
~M8D57C2KA[9]CC.png
在内存泄漏报告中Thread Stack可以清晰的看到内存溢出代码的位置,对于快速定位程序问题非常方便友好
YW69@($6G}KL5VZPSB1WQ.png

常用分析技巧

第一种:通过Thread Overview线程视图,按照Retained Heap深堆大小排序,展开线程查看,从调用栈中找到当前服务的代码。
第二种:通过histogram内存大小直方图,选择group by package按照包路径分组,再按照Retained Heap深堆大小排序,找到当前服务的代码。
第三种:通过Dominator Tree 支配树,按照Retained Heap深堆大小排序,然后查看深堆最大的线程在持有哪些大对象,接着在该对象右键查看线程详细堆栈信息。

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

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

相关文章

用PyQt5打造炫酷界面:深入解析pyqt5-custom-widgets

在PyQt5中&#xff0c;使用自定义小部件可以为应用程序增添更多实用性和时尚感。pyqt5-custom-widgets是一个开源项目&#xff0c;提供了一系列有用且时尚的自定义小部件&#xff0c;如开关按钮、动画按钮等。本文将详细介绍pyqt5-custom-widgets的安装和使用方法。 安装 可以…

权限维持Linux---监控功能Strace后门命令自定义Alias后门

免责声明:本文仅做技术交流与学习... 目录 监控功能Strace后门 1、记录 sshd 明文 监控 筛选查看 2、记录sshd私钥 命令自定义Alias后门 1、简单粗鲁实现反弹&#xff1a; 靶机替换命令 攻击机监听上线 2.升级(让命令正常) 将反弹命令进行base64编码 替换alias命令 …

【Linux】--help,man page , info page

我们知道Linux有很多的命令&#xff0c;那LInux要不要背命令&#xff1f; 答案是背最常用的那些就行了 那有的时候我们想查询一些命令的详细用法该怎么办呢&#xff1f; 这里我给出3种方法 1.--help --help的使用方法很简单啊 要查询的命令 --help 我们看个例子 这里我只…

java版企业工程管理系统源码:全方位的项目管理解决方案

工程管理系统是一款专注于建设工程项目全生命周期管理的软件。它覆盖了项目从策划、设计、施工到竣工的每一个阶段&#xff0c;提供全方位的管理功能。系统采用模块化设计&#xff0c;包括系统管理、系统设置、项目管理、合同管理、预警管理、竣工管理、质量管理、统计报表和工…

6月30日功能测试Day10

3.4.4拼团购测试点 功能位置&#xff1a;营销-----拼团购 后台优惠促销列表管理可以添加拼团&#xff0c;查看拼团活动&#xff0c;启动活动&#xff0c;编辑活动&#xff0c;删除活动。 可以查看拼团活动中已下单的订单以状态 需求分析 功能和添加拼团 商品拼团活动页 3…

python使用pywebview集成vue3和element-plus开发桌面系统框架

随着web技术越来越成熟&#xff0c;就连QQ的windows客户端都用web技术来开发&#xff0c;所以在未来&#xff0c;web技术来开发windows桌面软件也会越来越多&#xff0c;所以在此发展驱动之下&#xff0c;将最近流程的python与web技术相结合&#xff0c;使用vue3和element-plus…

图像增强 目标检测 仿射变换 图像处理 扭曲图像

1.背景 在目标检测中&#xff0c;需要进行图像增强。这里的代码模拟了旋转、扭曲图像的功能&#xff0c;并且在扭曲的时候&#xff0c;能够同时把标注的结果也进行扭曲。 这里忽略了读取xml的过程&#xff0c;假设图像IMG存在对应的标注框&#xff0c;且坐标为左上、右下两个…

[C++初阶]vector的初步理解

一、标准库中的vector类 1.vector的介绍 1. vector是表示可变大小数组的序列容器 &#xff0c; 和数组一样&#xff0c;vector可采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问&#xff0c;和数组一样高效。但是又不像数组&#xff0c;它的大…

Java学习高级一

修饰符 static 类变量的应用场景 成员方法的分类 成员变量的执行原理 成员方法的执行原理 Java之 main 方法 类方法的常见应用场景 代码块 设计模式 单例设计模式 饿汉式单例设计模式 懒汉式单例设计模式 继承 权限修饰符

小红书 达芬奇:生活问答 AI 机器人

小红书去年 9 月开始内测的生活问答 AI 机器人&#xff1a;达芬奇&#xff0c;现在可以在小红书 APP 上用了 得益于小红书平台的特性&#xff0c;该助手擅长吃、住、宠、喝、学等等各类生活知识&#xff0c;目前还在搞活动&#xff0c;写评测笔记最高得 666 元

为什么不能在foreach中删除元素

文章目录 快速失败机制&#xff08;fail-fast&#xff09;for-each删除元素为什么报错原因分析逻辑分析 如何正确的删除元素remove 后 breakfor 循环使用 Iterator 总结 快速失败机制&#xff08;fail-fast&#xff09; In systems design, a fail-fast system is one which i…

网络基础:EIGRP

EIGRP&#xff08;Enhanced Interior Gateway Routing Protocol&#xff09;是由思科开发的一种高级距离矢量路由协议&#xff0c;结合了距离矢量和链路状态路由协议的优点&#xff1b;EIGRP具有快速收敛、高效带宽利用、负载均衡等特点&#xff0c;适用于各种规模的网络。EIGR…

python sklearn机械学习-数据预处理

&#x1f308;所属专栏&#xff1a;【机械学习】✨作者主页&#xff1a; Mr.Zwq✔️个人简介&#xff1a;一个正在努力学技术的Python领域创作者&#xff0c;擅长爬虫&#xff0c;逆向&#xff0c;全栈方向&#xff0c;专注基础和实战分享&#xff0c;欢迎咨询&#xff01; 您…

【设计模式】策略模式(定义 | 特点 | Demo入门讲解)

文章目录 定义策略模式的结构 QuickStart | DemoStep1 | 策略接口Step2 | 策略实现Step3 | 上下文服务类Step4 | 客户端 策略模式的特点优点缺点 定义 策略模式Strategy是一种行为模式&#xff0c;它能定义一系列算法&#xff0c;并将每种算法分别放入到独立的类中&#xff0c…

负载均衡类型和算法解析

假如你正在设计和开发一个分布式服务系统&#xff0c;系统中存在一批能够独立运行的服务&#xff0c;而在部署上也采用了集群模式以防止出现单点故障。所谓集群&#xff0c;就是指将多个服务实例集中在一起&#xff0c;对外提供同一业务功能&#xff0c;也就是任意请求都可以由…

吉利银河L6 AQS空气质量监控系统

结论 顶配才有AQS 开启空调且auto模式 则默认开启AQS 无法关闭AQS AQS的作用 银河L6 AQS触发 和 图标 AQS官方配置参数 官方文档 吉利用户手册

开源即正义,3D软件Blender设计指南

在当今数字化时代&#xff0c;开源软件的崛起不仅代表着技术的发展&#xff0c;更象征着一种信息自由和技术民主的理念。其本质是集众人之智&#xff0c;共同去完善一个软件&#xff0c;最终使双方互惠共赢。具体来说&#xff0c;开源的价值&#xff0c;在于打破资源垄断&#…

苹果AI的国产大模型之争,没有悬念

文 | 智能相对论 作者 | 陈泊丞 苹果终于公布了最新的AI进程。 一个月前&#xff0c;正如此前预期的那样&#xff0c;人工智能是今年 WWDC 发布会的焦点。全程105分钟的主题演讲&#xff0c;就有40多分钟用于介绍苹果的AI成果。 苹果似乎还有意玩了一把“谐音梗”&#xff…

用机器改变人类方向

1800 世纪初&#xff0c;美国迎来了工业革命&#xff0c;这是一个由技术进步推动的变革时代。新机器和制造技术的引入重塑了经济格局&#xff0c;提高了生产效率&#xff0c;同时减少了某些领域对手工劳动的需求。因此&#xff0c;这种转变导致了失业。 如今&#xff0c;我们看…

实现点击按钮导出页面pdf

在Vue 3 Vite项目中&#xff0c;你可以使用html2canvas和jspdf库来实现将页面某部分导出为PDF文档的功能。以下是一个简单的实现方式&#xff1a; 1.安装html2canvas和jspdf&#xff1a; pnpm install html2canvas jspdf 2.在Vue组件中使用这些库来实现导出功能&#xff1a;…