【JVM从入门到实战】(八)垃圾回收(1)
内存泄漏:指的是不再使用的对象在系统中未被回收,内存泄漏的积累可能会导致内存溢出
什么是垃圾回收
Java中为了简化对象的释放,引入了自动的垃圾回收(Garbage Collection
简称GC
)机制。通过垃
圾回收器来对不再使用的对象完成自动的回收,垃圾回收器主要负责对【堆】上的内存进行回收。其他
很多现代语言比如C#、Python、Go
都拥有自己的垃圾回收器。
自动垃圾回收 java
自动根据对象是否使用由虚拟机来回收对象
• 优点:降低程序员实现难度、降低对象回收bug的可能性
• 缺点:程序员无法控制内存回收的及时性
手动垃圾回收 C\C++
由程序员编程实现对象的删除
• 优点:回收及时性高,由程序员把控回收的时机
• 缺点:编写不当容易出现悬空指针、重复释放、内存泄漏等问题
Java虚拟机在运行Java程序过程中管理的内存区域,称之为运行时数据区。线程不共享的部分(程序计数器、虚拟机栈、本地方法区),都是伴随着线程的创建而创建,线程的销毁而销毁。而方法的栈帧在执行完方法之后就会自动弹出栈并释放掉对应的内存。
一、方法区的回收
方法区中能回收的内容主要就是不再使用的类。判定一个类可以被卸载,需要同时满足三个条件:
-
此类所有实例对象都已被回收,在堆中不存在任何该类的实例对象以及子类对象。
-
加载该类的类加载器已被回收。
-
该类对应的 java.lang.Class 对象没有在任何地方被引用。
开发中此类场景一般很少出现,主要在如
OSGi、JSP
的热部署等应用场景中。每个jsp文件对应一个唯一的类加载器,当一个jsp
文件修改了,就直接卸载这个jsp类加载器。重新创建类加载器,重新加载jsp文件。
手动触发回收
如果需要手动触发垃圾回收,可以调用System.gc()
方法。
语法: System.gc()
注意事项:调用
System.gc()
方法并不一定会立即回收垃圾,仅仅是向Java虚拟机
发送一个垃圾回收的请求,具体是否需要执行垃圾回收Java虚拟机会自行判断。
二、堆回收
1. 如何判断堆上的对象可以回收?
Java中的对象是否能被回收,是根据对象是否被引用来决定的。
如果对象被引用了,说明该对象还在使用,不允许被回收。
图中A的实例对象要回收,有两个引用要去除:
- 栈中
a1
变量到对象的引用 2.B
对象到A
对象的引用
即a1 = null; b1.a = null;
2. 如何判断堆上的对象没有被引用?
常见的有两种方法:引用计数法、可达性分析法
2.1 引用计数法
会为每个对象维护一个引用计数器,当对象被引用时加1,取消引用时减1。
优点:实现简单,C++中的智能指针就采用了引用计数法
缺点:
1.每次引用和取消引用都需要维护计数器,对系统性能会有一定的影响
2.存在循环引用问题,所谓循环引用就是当A引用B,B同时引用A时会出现对象无法回收的问题。
查看垃圾回收日志,可以使用-verbose:gc参数。
语法: -verbose:gc
2.2 可达性分析法
Java使用的是可达性分析算法来判断对象是否可以被回收。可达性分析将对象分为两类:垃圾回收的根对象(GC Root
)和普通对象,对象与对象之间存在引用关系。
下图中A到B再到C和D,形成了一个引用链,可达性分析算法指的是如果从某个到GC Root对象是可达的,对象就不可被回收。
哪些对象被称之为GC Root
对象呢?
1)线程Thread对象。引用线程栈帧中的方法参数、局部变量等
2)系统类加载器加载的java.lang.Class
对象。引用类中的静态变量
3)监视器对象,用来保存同步锁synchronized
关键字持有的对象。
4)本地方法调用时使用的全局对象。
查看GC Root
通过arthas
和eclipse Memory Analyzer (MAT)
工具可以查看GC Root
,MAT
工具是eclipse
推出的Java堆内存
检测工具。具体操作步骤如下:
1、使用arthas
的heapdump
命令将堆内存快照保存到本地磁盘中。
2、使用MAT
工具打开堆内存快照文件。
3、选择GC Roots
功能查看所有的GC Root
。
2.3 几种常见的对象引用
可达性算法中描述的对象引用,一般指的是强引用,即是GCRoot
对象对普通对象有引用关系,只要这层关系存在,普通对象就不会被回收。
除了强引用之外,Java中还设计了几种其他引用方式:软引用、弱引用、虚引用、终结器引用
1)软引用:
相对于强引用是一种比较弱的引用关系,如果一个对象只有软引用关联到它,当程序内存不足时,就会将软引用中的数据进行回收。在JDK 1.2
版之后提供了SoftReference
类来实现软引用,软引用常用于缓存中
软引用的执行过程如下:
1 将对象使用软引用包装起来,new SoftReference<对象类型>(对象)
。
2 内存不足时,虚拟机尝试进行垃圾回收。
3 如果垃圾回收仍不能解决内存不足的问题,回收软引用中的对象。
4 如果依然内存不足,抛出OutOfMemory
异常。
软引用中的对象如果在内存不足时回收,SoftReference
对象本身也需要被回收。如何知道哪些SoftReference
对象需要回收呢?SoftReference
提供了一套队列机制:
1 软引用创建时,通过构造器传入引用队列
2 在软引用中包含的对象被回收时,该软引用对象会被放入引用队列
3 通过代码遍历引用队列,将SoftReference
的强引用删除
2)弱引用:
整体机制和软引用基本一致,区别在于弱引用包含的对象在垃圾回收时,不管内存够不够都会直接被回收。在JDK 1.2
版之后提供了WeakReference
类来实现弱引用,弱引用主要在ThreadLocal
中使用。弱引用对象本身也可以使用引用队列进行回收。
3)虚引用和终结器引用
- 这两种引用在常规开发中不会使用到,仅了解。
- 虚引用也叫幽灵引用/幻影引用,不能通过虚引用对象获取到包含的对象。虚引用唯一的用途是当对象被垃圾回收器回收时可以接收到对应的通知。Java中使用
PhantomReference
实现了虚引用,直接内存中为了及时知道直接内存对象不再使用,从而回收内存,使用了虚引用来实现。 - 终结器引用指的是在对象需要被回收时,终结器引用会关联对象并放置在
Finalizer
类中的引用队列中,在稍后由一条由FinalizerThread
线程从队列中获取对象,然后执行对象的finalize
方法,在对象第二次被回收时,该对象才真正的被回收。在这个过程中可以在finalize
方法中再将自身对象使用强引用关联上,但是不建议这样做。
三、垃圾回收算法
垃圾回收要做的有两件事:
1)找到内存中存活的对象
2)释放不再存活对象的内存,使得程序能再次利用这部分空间
四种算法:标记-清除算法、复制算法、标记-整理算法、分代GC
垃圾回收算法的评价标准
Java垃圾回收过程会通过单独的GC线程来完成,但是不管使用哪一种GC算法,都会有部分阶段需要停止所有的用户线程。这个过程被称之为
Stop The World
简称STW
,如果STW时间过长则会影响用户的使用。
判断GC算法是否优秀,可以从三个方面来考虑:
1)吞吐量:指的是 CPU 用于执行用户代码的时间
与 CPU 总执行时间的比值
即吞吐量
= 执行用户代码时间
/(执行用户代码时间 + GC时间)
。吞吐量数值越高,垃圾回收的效率就越高。比如:虚拟机总共运行了 100 分钟,其中GC花掉 1 分钟,那么吞吐量就是 99%
2)最大暂停时间:最大暂停时间指的是所有在垃圾回收过程中的STW时间最大值
。最大暂停时间越短,用户使用系统时受到的影响就越短。
比如如下的图中,黄色部分的STW就是最大暂停时间,显而易见上面的图比下面的图拥有更少的最大暂停时间。
3)堆使用效率:不同垃圾回收算法,对堆内存的使用方式是不同的。比如标记清除算法,可以使用完整的堆内存。而复制算法会将堆内存一分为二,每次只能使用一半内存。从堆使用效率上来说,标记清除算法要优于复制算法。
上述三种评价标准:堆使用效率、吞吐量,以及最大暂停时间不可兼得。
一般来说,堆内存越大,最大暂停时间就越长。想要减少最大暂停时间,就会降低吞吐量。
不同的垃圾回收算法,适用于不同的场景
1. 标记清除算法
标记清除算法的核心思想分为两个阶段
1)标记阶段,将所有存活的对象进行标记。Java中使用可达性分析算法,从GC Root
开始通过引用链遍历出所有存活对象。
2)清除阶段,从内存中删除没有被标记也就是非存活对象
优点:实现简单,只需要在第一阶段给每个对象维护标志位,第二阶段删除对象即可。
缺点:1)碎片化问题:由于内存是连续的,所以在对象被删除之后,内存中会出现很多细小的可用内存单元。如果我们需要的是一个比较大的空间,很有可能这些内存单元的大小过小无法进行分配。
2)分配速度慢:由于内存碎片的存在,需要维护一个空闲链表,极有可能发生每次需要遍历到链表的最后才能获得合适的内存空间。
2. 复制算法
复制算法的核心思想是:
1)准备两块空间From
空间和To
空间,每次在对象分配阶段,只能使用其中一块空间(From
空间)。
2)在垃圾回收GC阶段,将From
中存活对象复制到To
空间。
3)将两块空间的From
和To
名字互换。
完整的复制算法的例子:
1.将堆内存分割成两块From
空间To
空间,对象分配阶段,创建对象。
2.GC
阶段开始,将GC Root
搬运到To
空间
3.将GC Root
关联的对象,搬运到To
空间
4.清理From
空间,并把名称互换
优点:
1)吞吐量高:复制算法只需要遍历一次存活对象复制到To
空间即可,比标记-整理算法少了一次遍历的过程,因而性能较好,但是不如标记-清除算法,因为标记清除算法不需要进行对象的移动
2)不会发生碎片化:复制算法在复制之后就会将对象按顺序放入To
空间中,所以对象以外的区域都是可用空间,不存在碎片化内存空间。
缺点:内存使用效率低,每次只能让一半的内存空间来为创建对象使用
3. 标记整理算法
标记整理算法也叫标记压缩算法,是对标记清理算法中容易产生内存碎片问题的一种解决方案。
核心思想分为两个阶段:
1)标记阶段,将所有存活的对象进行标记。Java中使用可达性分析算法,从GC Root
开始通过引用链遍历出所有存活对象。
2)整理阶段,将存活对象移动到堆的一端。清理掉存活对象的内存空间。
优点:
1)整个堆内存都可以使用,不会像复制算法只能使用半个堆内存
2)不会发生碎片化,在整理阶段可以将对象往内存的一侧进行移动,剩下的空间都是可以分配对象的有效空间
缺点:整理阶段的效率不高。整理算法有很多种,比如Lisp2
整理算法需要对整个堆中的对象搜索3次,整体性能不佳。可以通过Two Finger
、表格算法
、ImmixGC
等高效的整理算法优化此阶段的性能
4. 分代垃圾回收算法
分代垃圾回收将整个内存区域划分为年轻代和老年代:年轻代(新生代) Young区存放存活时间比较短的对象,Old区老年代存放存活时间比较长的对象.
1)分代回收时,创建出来的对象,首先会被放入Eden
伊甸园区。随着对象在Eden
区越来越多,如果Eden
区满,新创建的对象已经无法放入,就会触发年轻代的GC,称为Minor GC
或者Young GC
。Minor GC
会把eden
区和From
区(S0
)中需要回收的对象回收,把没有回收的对象放入To
区(S1
)。
2)接下来,S0
会变成To
区,S1变成From区。当eden
区满时再往里放入对象,依然会发生Minor GC
。此时会回收eden
区和S1(from)
中的对象,并把eden
和from
区中剩余的对象放入S0
。每次Minor GC
中都会为对象记录他的年龄,初始值为0,每次GC完加1。
3)如果Minor GC
后对象的年龄达到阈值(最大15,默认值和垃圾回收器有关),对象就会被晋升至老年代。当老年代中空间不足,无法放入新的对象时,先尝试minor gc
如果还是不足,就会触发Full GC
,Full GC
会对整个堆进行垃圾回收。如果Full GC
依然无法回收掉老年代的对象,那么当对象继续放入老年代时,就会抛出Out Of Memory
异常。
下图中的程序为什么会出现OutOfMemory?
从上图可以看到,Full GC
无法回收掉老年代的对象,那么当对象继续放入老年代时,就会抛出Out Of Memory
异常。
arthas查看分代之后的内存情况:
在JDK8
中,添加-XX:+UseSerialGC
参数使用分代回收的垃圾回收器,运行程序。
在arthas
中使用memory
命令查看内存,显示出三个区域的内存情况。 图2
调整内存区域的大小:通过添加jvm启动参数修改各个区域大小和比例。注意加上-XX:+UseSerialGC
为什么分代GC算法要把堆分成年轻代和老年代?
- 系统中的大部分对象,都是创建出来之后很快就不再使用可以被回收,比如用户获取订单数据,订单数据返回给用户之后就可以释放了。
- 老年代中会存放长期存活的对象,比如
Spring
的大部分bean
对象,在程序启动之后就不会被回收了。- 在虚拟机的默认设置中,新生代大小要远小于老年代的大小。
答:分代GC算法将堆分成年轻代和老年代主要原因有:
1.可以通过调整年轻代和老年代的比例来适应不同类型的应用程序,提高内存的利用率和性能。
2)新生代和老年代使用不同的垃圾回收算法,新生代一般选择“复制算法”,老年代可以选择“标记-清除”和“标记-整理”算法,由程序员来选择灵活度较高。
3)分代的设计中允许只回收新生代(minor gc
),如果能满足对象分配的要求就不需要对整个堆进行回收(full gc
),STW
时间就会减少。
相关文章:

【JVM从入门到实战】(八)垃圾回收(1)
内存泄漏:指的是不再使用的对象在系统中未被回收,内存泄漏的积累可能会导致内存溢出 什么是垃圾回收 Java中为了简化对象的释放,引入了自动的垃圾回收(Garbage Collection简称GC)机制。通过垃 圾回收器来对不再使用的…...
LeeCode前端算法基础100题(12)-删除有序数组中的重复项
一、问题详情: 给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。 考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题…...

MATLAB图解傅里叶变换(初学者也可以理解)
1、概述 相信很多人对于傅里叶变换可能觉得比较复杂和有点难懂,其实不难,它只是一种积分变换。 傅里叶变换,表示能将满足一定条件的某个函数表示成三角函数(正弦和/或余弦函数)或者它们的积分的线性组合。也就是说&qu…...

uni-app 用于开发H5项目展示饼图,使用ucharts 饼图示例
先下载ucharts H5示例源码: uCharts: 高性能跨平台图表库,支持H5、APP、小程序(微信小程序、支付宝小程序、钉钉小程序、百度小程序、头条小程序、QQ小程序、快手小程序、360小程序)、Vue、Taro等更多支持canvas的框架平台&#…...

回归预测 | MATLAB实现SABO-LSTM基于减法平均优化器优化长短期记忆神经网络的多输入单输出数据回归预测模型 (多指标,多图)
回归预测 | MATLAB实现SABO-LSTM基于减法平均优化器优化长短期记忆神经网络的多输入单输出数据回归预测模型 (多指标,多图) 目录 回归预测 | MATLAB实现SABO-LSTM基于减法平均优化器优化长短期记忆神经网络的多输入单输出数据回归预测模型 &a…...

JDK bug:ciObjectFactory::create_new_metadata
文章目录 1、问题2.详细日志3.JDK:bug最终bug链接: 京东遇到过类似bug各位大佬如果有更详细的解答可以留言。 1、问题 Problematic frame: V [libjvm.so0x438067] ciObjectFactory::create_new_metadata(Metadata*)0x327 关键字还是ciObjectFactory::cr…...
Flink系列之:Over聚合
Flink系列之:Over聚合 一、Over聚合二、ORDER BY三、PARTITION BY四、范围(RANGE)定义五、RANGE 间隔六、ROW 间隔 适用于流、批 一、Over聚合 OVER 聚合通过排序后的范围数据为每行输入计算出聚合值。和 GROUP BY 聚合不同, OV…...

Java开发工具积累(符合阿里巴巴手册规范)
文章目录 一、命名规约二、代码格式三、集合篇1. 栈、队列、双端队列2. List的升序倒序3. Map的升序降序4. 二维数组排序5. 集合之间的转换6. Map键值对遍历7. 重写equal与hashCode8. ArrayList的subList9. keySet()/values()/ent…...

SiLM5350MDBCA-DG车规级隔离驱动芯片,我们能为汽车智能提供什么?
SiLM5350MDBCA-DG是一款适用于IGBT、MOSFET的单通道 隔离门极驱动器,具有10A拉电流和10A灌电流驱动能 力。提供内部钳位功能,可单独控制 上升时间和下降时间。 在 SOP8 封 装 中 具 有 3000VRMS 隔 离 耐 压 ( 符 合 UL1577)。 与…...
【开题报告】基于SpringBoot的企业财务管理系统的设计与实现
1.研究背景 随着全球经济的发展和市场竞争的加剧,企业财务管理变得越来越重要。企业需要一个高效、精确、安全的财务管理系统来管理企业的财务事务,提供准确的财务数据支持决策制定。传统的手工财务管理方式已经无法满足企业的需求,因此&…...

【C盘清理】Jetbrains全家桶(PyCharm、Clion……)更改 IDE 特定文件(配置、缓存、插件、日志等)存储位置
文章目录 一、官网说明二、更改 IDE 目录的位置1. 转到“帮助”|“编辑自定义属性”2. 各文件位置3. 以PyCharm系统目录为例4. 修改idea.properties 三、清理旧的 IDE 目录 一、官网说明 IDE 使用的目录官网说明 二、更改 IDE 目录的位置 默认情况下,PyCharm 将每…...
nginx部署vue项目
nginx部署vue 解决nginx中vue项目刷新报404问题解决nginx转发后端服务隐藏部分url访问url路径/prod-api/api经过nginx反向代理后,到达后端服务的实际url地址为/api。 解决nginx中vue项目刷新报404问题 location / { …...

Relocations for this machine are not implemented,IDA版本过低导致生成汇编代码失败
目录 1、问题描述 2、安卓app发生崩溃,需要查看汇编代码上下文去辅助分析 3、使用IDA打开.so动态库文件,提示Relocations for this machine are not implemented 4、IDA版本较老,不支持ARM64的指令集,使用7.0版本就可以了 5、…...

[ CTF ]【天格】战队WriteUp-第七届“强网杯”全国安全挑战赛
第七届“强网杯”全国安全挑战赛 2023.12.16~2023.12.17 文章目录 【Misc】Pyjail ! Its myFILTER !!!easyfuzz谍影重重2.0签到Pyjail ! Its myRevenge !!!server_8F6C72124774022B.py 问卷调查 【Reverse】ezre 【Web】happygame 【强网先锋】石头剪刀布TrieSpeedUpezreez_fmt…...

Android13音频录制适配
Android13音频录制适配 前言: 之前写过一篇音频录制的文章,当时是在Android10以下的手机可以成功录制和播放,但是Android10及以上手机提示创建文件失败,最近做过Android13的适配,索性一起把之前的录音也适配了&#…...

【Python】—— 如果使用matplotlib做数据可视化
matplotlib做数据可视化 相关知识掌握matplotlib的基本使用方法1. 折线图2. 散点图3. 柱状图4. 饼图5. 直方图6. 等高线图7. 图形定制 掌握数据处理的基本方法1. 数据筛选2. 缺失值处理3. 异常值处理 理解数据可视化的原则和方法1. 选择合适的图表类型2. 避免数据混淆3. 突出重…...
【MyBatis-Plus】多数据源分页配置(低版本暂时就支持一种(可选),高版本多支持)
【转载】一、Mybatis Plus 3.4 版本之后分页插件的变化 1、地址 Mybatis Plus 3.4版本之后分页插件的变化 2、内容 1、MybatisPlusInterceptor 从 Mybatis Plus 3.4.0 版本开始,不再使用旧版本的 PaginationInterceptor,而是使用 MybatisPlusInterce…...

Linux 特殊符号
目录 1. # 注释 2. ;命令分隔符 3. .. 上级目录 4. . 当前目录 5. " " 换行,解析变量 6. 换行,不解析变量 7. \ 和 / 8. !历史命令调用,取反 9. * 通配符 10. $ 调用变量 11. | 管道 12. || …...
TDengine 签约中船九院,助力航运业智能化转型升级
在大数据时代背景下,船舶智能化已经成为船舶制造与航运领域发展的必然趋势。智能船舶作为《中国制造 2025》中明确重点发展的领域,代表了船舶未来的方向,对于航运业的转型升级至关重要。其中,大数据的处理和运用成为船舶智能化转型…...

upload-labs笔记
简介 upload-labs是一个使用php语言编写的,专门收集渗透测试和CTF中遇到的各种上传漏洞的靶场。旨在帮助大家对上传漏洞有一个全面的了解。目前一共21关,每一关都包含着不同上传方式。 文件上传漏洞是指: Web 服务器允许用户将文件上传至其…...

wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...
IGP(Interior Gateway Protocol,内部网关协议)
IGP(Interior Gateway Protocol,内部网关协议) 是一种用于在一个自治系统(AS)内部传递路由信息的路由协议,主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...

高频面试之3Zookeeper
高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个?3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制(过半机制࿰…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...

高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...

mac 安装homebrew (nvm 及git)
mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用: 方法一:使用 Homebrew 安装 Git(推荐) 步骤如下:打开终端(Terminal.app) 1.安装 Homebrew…...
全面解析数据库:从基础概念到前沿应用
在数字化时代,数据已成为企业和社会发展的核心资产,而数据库作为存储、管理和处理数据的关键工具,在各个领域发挥着举足轻重的作用。从电商平台的商品信息管理,到社交网络的用户数据存储,再到金融行业的交易记录处理&a…...
【FTP】ftp文件传输会丢包吗?批量几百个文件传输,有一些文件没有传输完整,如何解决?
FTP(File Transfer Protocol)本身是一个基于 TCP 的协议,理论上不会丢包。但 FTP 文件传输过程中仍可能出现文件不完整、丢失或损坏的情况,主要原因包括: ✅ 一、FTP传输可能“丢包”或文件不完整的原因 原因描述网络…...