JVM之垃圾回收面试总结

文章目录

  • 1.GC概述
    • 1.1 什么是垃圾
    • 1.2 为什么需要GC?
    • 1.3 早期垃圾回收
    • 1.4 Java垃圾回收机制
    • 1.5 评估GC的性能指标
  • 2.垃圾回收相关算法
    • 2.1 垃圾标记阶段的算法
      • 2.1.1 引用计数算法(Java没有使用)
      • 2.1.2 可达性分析算法
    • 2.2 垃圾清除阶段的算法
      • 2.2.1 标记-清除(Mark-Sweep)算法
      • 2.2.2 复制(Copying)算法
      • 2.2.3 标记-压缩(标记-整理Mark-Compact)算法
    • 2.3 分代收集算法
  • 3.垃圾回收相关概念
    • 3.1 System.gc()的理解
    • 3.2 内存溢出(OOM)
    • 3.3 内存泄漏
    • 3.4 Stop The Word(STW)
    • 3.5 垃圾回收的并发与并行
  • 4.强引用、软引用、弱引用和虚引用
  • 5.垃圾回收器
    • 5.1 Serial和Serial Old垃圾收集器:串行回收
      • 5.1.1 Serial垃圾收集器
      • 5.1.2 Serial Old垃圾收集器
    • 5.2 ParNew回收器:并行回收
    • 5.3 Parallel Scavenge回收器、Parallel Old:吞吐量优先
      • 5.3.1 Parallel Scavenge回收器
      • 5.3.2 Parallel Old回收器
      • 5.3.3 jdk8默认使用的是Parallel Garbage Collector (并行垃圾回收器),也就是Parallel Scavenge + Serial Old的组合。
    • 5.4 CMS回收器:低延迟
    • 5.5 G1回收器:区域化分代式

1.GC概述

1.1 什么是垃圾

(1)垃圾是指在运行程序中没有任何指针指向的对象
(2)如果不及时对内存中的垃圾进行清理,这些垃圾对象所占的内存空间就会一直保留到应用程序结束,被保留的空间无法被其他对象使用,甚至可能导致内存溢出

1.2 为什么需要GC?

(1)不进行垃圾回收,内存迟早会被消耗完
(2)垃圾回收也可以清除内存里的记录碎片,碎片整理将所占用的堆内存移到堆的一端,以便JVM将整理出的内存分配给新的对象

1.3 早期垃圾回收

在早期的C/C++时代,垃圾回收基本上是手工进行的,开发人员可以使用new关键字进行内存申请,并使用delete关键字进行内存释放。这种方式可以灵活控制内存释放的时间,但是会给开发人员带来频繁申请和释放内存的管理负担。如果程序员编码时忘记回收则会造成内存泄漏,严重会造成程序崩溃。

1.4 Java垃圾回收机制

(1)Java采用的是自动内存管理,无需开发人员参与内存的分配和回收,这样可以降低内存泄漏和内存溢出的风险。
(2)自动回收会弱化Java开发人员在程序出现内存溢出时的定位问题和解决问题的能力
(3)当出现内存泄漏、内存溢出问题时,我们需要实施必要的监控和调节

1.5 评估GC的性能指标

(1)吞吐量:运行用户代码的时间占总运行时间的比例(总运行时间=程序的运行时间+内存回收的时间)
(2)暂停时间:执行垃圾收集时,程序的工作线程被暂停的时间

2.垃圾回收相关算法

2.1 垃圾标记阶段的算法

(1)在堆里存放着几乎所有的Java对象实例,在GC执行垃圾回收之前,首先需要区分出内存中哪些是存活对象,哪些是已经死亡的对象。只有被标记为已经死亡的对象,GC才会在执行垃圾回收时,释放掉其所占用的内存空间,因此这个过程我们可以称为垃圾标记阶段。
(2)当一个对象已经不再被任何的存活对象继续引用时,JVM中就会标记为死亡对象。
(3)判断对象存活一般有两种方式:引用计数算法和可达性分析算法。

2.1.1 引用计数算法(Java没有使用)

(1)引用计数算法是对每一个对象保存一个整型的引用计数器属性,用于记录对象被引用的情况。
(2)对于一个对象A,只要有任何一个对象引用了A,则A的引用计数器就加1;当引用失效时,引用计数器就减1。只要对象A的引用计数器的值为0,即表示对象A不可能再被使用,可进行回收了。
(3)优点:实现简单,垃圾对象便于辨识;判定效率高,且回收没有延迟性。
(4)缺点:
①它需要单独的字段存储计数器,这样的做法增加了存储空间的开销。
②每次赋值都需要重新计数,伴随着加法和减法操作,增加了时间开销。
③引用计数器无法处理循环引用。这是致命缺陷,导致在Java的垃圾回收器中没有使用这类算法。
(4)python使用了引用计数算法,它是如何解决循环引用的?
①手动解除:在合适的时机,解除引用关系
②使用弱引用weakref,weakref是python提供的标准库,旨在解决循环引用。

2.1.2 可达性分析算法

(1)可达性分析算法也叫,根搜索算法、追踪性垃圾收集。不仅实现简单、执行高效,而且可以解决循环引用问题
(2)可达性分析的基本思路
①可达性分析算法是以根对象集合(GC Roots)为起始点,按照从上至下的方式搜索被根对象集合所连接的目标对象是否可达(所谓GC Roots是一组必须活跃的引用)
②使用可达性分析算法后,内存中的存活对象都会被根对象集合直接或间接连接着,搜索所走过的路径称为引用链
③如果目标对象没有任何引用链相连,则是不可达的,就意味着该对象已经死亡,可以标记为垃圾对象
④在可达性分析算法中,只有能够被根对象集合直接或者间接连接的对象才是存活对象
(4)在Java语言中,GC Roots包括以下几类元素:
①虚拟机栈在引用的对象:比如各个线程被调用的方法中使用到的参数、局部变量等。
②本地方法栈内(通常说的本地方法)引用的对象
③方法区中类静态属性引用的对象,比如:Java类的引用类型静态变量
④方法区中常量引用的对象:比如字符串常量池(String Table)里的引用
⑤所有被同步锁synchronized持有的对象
⑥Java虚拟机内部的引用:基本数据类型对应的Class对象,一些常驻的异常对象(如:NullPointerException、OutOfMemoryError)以及系统类加载器
(5)除了这些固定的GC Roots集合意外,根据用户所选用的垃圾收集器以及当前回收的内存区域不同,还可以有其他对象“临时性”的加入,共同构成完整GC Roots集合。比如:分代收集和局部回收(Partical GC)

2.2 垃圾清除阶段的算法

(1)当成功区分出内存中存活对象和死亡对象后,GC接下来的任务就是执行垃圾回收,释放掉无用对象所占用的内存空间,以便有足够的可用内存空间为新对象分配内存
(2)目前在JVM中比较常见的垃圾收集算法有:标记-清除算法、复制算法、标记-压缩算法

2.2.1 标记-清除(Mark-Sweep)算法

(1)执行过程:当堆中的有效内存空间被耗尽的时候,就会停止整个程序,然后进行两项工作,第一项是标记,第二项则是清除
①标记:Collector从根节点开始遍历,标记所有被引用的对象。一般是在对象的Header中记录为可达对象。
②清除:Collector对堆内存从头到尾进行线性的遍历,如果发现某个对象在其Header中没有标记为可达对象,则将其回收。
(2)优点:简单容易理解
(3)缺点:
①效率不高(从根节点标记,递归遍历可达对象,复杂度O(n),清除阶段需要把堆空间的对象遍历一遍,O(n))
②在进行GC的时候,需要停止整个应用程序,导致用户体验差
③这种方式清理出来的空闲内存是不连续的,产生内存碎片。需要维护一个空闲列表
(3)何为清除?
这里所谓的清除并不是真的置空,而是把需要清除的对象地址保存在空闲的地址列表里。下次有新对象需要加载时,判断垃圾的位置空间是否够,如果够,就直接覆盖原有位置。

2.2.2 复制(Copying)算法

(1)为了解决标记-清除算法在垃圾收集效率方面的缺陷,引入了复制算法
(2)核心思想:将活着的内存空间分为两块,每次只使用其中一块,在垃圾回收时将正在使用的内存中的存活对象复制到未被使用的内存块中,之后清除正在使用的内存块中的所有对象,交换两个内存的角色,最后完成垃圾回收。
在这里插入图片描述
(3)优点:
①没有标记和清除过程,实现简单,运行高效
②复制过去以后保证空间的连续性,不会出现“碎片”问题
(4)缺点:
①需要两倍的内存空间
②对于G1垃圾收集器这种拆成大量r egion的GC,复制而不是移动,意味着GC需要维护region之间对象引用关系,不管是内存占用或者时间开销也不小
(5)复制算法需要复制的存活对象数量不太多的情况。在新生代,对常规应用的垃圾回收,一次通常可以回收70%-99%的内存空间。回收性价比很高。所以现在的商业虚拟机都是用这种收集算法回收新生代。

2.2.3 标记-压缩(标记-整理Mark-Compact)算法

(1)标记-压缩算法的最终效果等同于标记-清除算法执行完成后,再进行一次碎片整理。因此,也可以把它称为标记-清除-压缩算法。
(2)执行过程
①第一阶段和标记-清除算法一样,从根节点开始标记所有被引用对象
②第二阶段将所有的存活对象压缩到内存的一端,按顺序排放。之后,清理边界外所有的空间
(3)优点:
①清除了标记-清除算法中,内存区域分散的缺点,我们需要给新对象分配内存时,JVM只需要持有一个内存的起始地址即可。
②消除了复制算法当中,内存减半的高额代价
(4)缺点:
①从效率上说,标记-整理算法要低于复制算法
②移动对象的同时,如果对象被其他对象引用,则还需要调整引用的地址
③移动过程中,需要全程暂停用户应用程序。

2.3 分代收集算法

(1)分代收集算法是基于这样一个事实:不同的对象的生命周期是不一样的。因此,不同生命周期的对象可以采取不同的收集方式,以便提高回收效率。一般是把Java堆分为新生代和老年代,这样就可以根据各个年代的特点使用不同的回收算法,以提高垃圾回收的效率。
(2)目前几乎所有的GC都是采用分代收集算法执行垃圾回收的。在HotSpot( SUN的JDK版本从1.3.1开始运用HotSpot虚拟机)中,基于分代的概念,GC所使用的内存回收算法必须结合年轻代和老年代各自的特点:
①年轻代:区域相对老年代较小,对象生命周期短、存活率低,回收频繁。这种情况复制算法是最快的。
②老年代:区域较大,对象生命周期长、存活率高。一般是标记-清除或者是标记-清除与标记-整理的混合实现。

3.垃圾回收相关概念

3.1 System.gc()的理解

(1)在默认情况下,通过System.gc()或者Runtime.getRuntime().gc()的调用,会显示触发Full GC,同时对老年代和新生代进行回收,尝试释放被丢弃对象占用的内存。System.gc()的底层就是Runtime.getRuntime().gc()
在这里插入图片描述
(2)System.gc()只是尝试调用垃圾收集器,但是不能保证对垃圾收集器一定调用
(3)JVM实现者可以通过System.gc()调用来决定JVM的GC行为。而一般情况下,垃圾回收应该是自动进行的,无需手动触发。但在一些特殊情况下,如我们正在编写一个性能基准,我们就可以在运行之间调用System.gc()

3.2 内存溢出(OOM)

(1)javadoc中对OutOfMemoryError的解释是:没有空闲内存,并且垃圾收集器也无法提供更多内存。
(2)没有空闲内存,说明Java虚拟机的堆内存不够,原因有二:
①Java虚拟机的堆内存设置不够。比如说:可能存在内存泄漏问题;也有可能就是堆的大小不合理,比如我们要处理比较可观的数据量,但是没有显式指定JVM堆大小或者指定数值偏小。我们可以通过-Xms、-Xmx来调整
②代码中创建了大量大对象,并且长时间不能被垃圾收集器收集(存在被引用)。
(3)在抛出OOM之前,通常垃圾收集器会被触发,尽其所能去清理出空间。例如:在引用机制分析中,涉及到JVM会去尝试回收软引用指向的对象等。当然,也不是任何情况下垃圾收集器都会被触发,比如:我们去分配一个超大数组超过堆的最大值,JVM会判断出垃圾收集并不能解决这个问题,会直接抛出OOM

3.3 内存泄漏

(1)也叫存储泄漏,指对象不会再被程序用到,但是GC又不能回收他们的情况。一旦发生内存泄漏,程序中的可用内存最终会被耗尽,出现OOM。
(2)举例
①单例模式:单例的生命周期和应用程序是一样长的,所以单例程序中,如果持有对外部对象的引用的话,那么这个外部对象是不能被回收的,则会导致内存泄漏的产生
②一些提供close的资源未关闭导致内存泄漏。数据库连接,网络连接(socket)以及IO连接必须手动close,否则是不能被回收的。

3.4 Stop The Word(STW)

(1)Stop The Word(简称STW),指的是GC事件发生过程中,会产生应用程序的停顿。停顿产生时整个应用程序线程都会被暂停,没有任何响应,有点像卡死的感觉,这个停顿称为STW。
(2)SWT是JVM在后台自动发起和自动完成的。在用户不可见的情况下,把用户正常的工作线程全部停掉。被STW中断的应用程序线程会在完成GC之后恢复,频繁中断降低用户体验感,我们要减少STW的发生。在开发中尽量不要用System.gc(),会导致SWT的发生。
(3)可达性分析算法中枚举根节点(GC Roots)会导致所以Java执行线程停顿
(4)STW事件和采用哪款GC无关,所有的GC都会有这个事件。哪怕是G1也不能完全避免Stop The Word情况发生,只能说垃圾回收器越来越优秀,回收率越高,尽可能的缩短了暂停时间。

3.5 垃圾回收的并发与并行

(1)并行:指多条垃圾收集线程并行工作,但此时用户线程仍处于等待状态
(2)串行:单线程执行。如果内存不够,则程序暂停,启动JVM垃圾收集器进行垃圾回收,回收完,再启动程序的线程。
(3)并发:指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行),垃圾回收线程在执行时不会停顿用户程序的运行。用户线程在继续执行,而垃圾收集程序线程运行于另一个CPU上。

4.强引用、软引用、弱引用和虚引用

(1)强引用:是指在程序代码中普遍存在的引用赋值,即类似“Object obj = new Object()”这种引用关系。无论任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回收掉被引用的对象。
(2)软引用:在系统将要发生内存溢出之前,将会把这些对象列入回收范围之中进行二次回收。如果这次回收后还没有足够的内存,才会抛出内存溢出异常。
(3)弱引用:被弱引用关联的对象只能生存到下一次垃圾收集之前。当垃圾收集器工作时,无论内存空间是否足够,都会回收掉被软引用关联的对象。
(4)虚引用:一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来获得一个对象的实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象被垃圾收集器回收时收到一个系统通知。

5.垃圾回收器

(1)垃圾收集器与垃圾分代之间的关系
在这里插入图片描述
①新生代收集器:Serial、ParNew、Parallel Scavenge
②老年代收集器:Serial Old、Parallel Old、CMS
③整堆收集器:G1

5.1 Serial和Serial Old垃圾收集器:串行回收

5.1.1 Serial垃圾收集器

(1)Serial垃圾收集器是最基本、历史最悠久的垃圾收集器。JDK1.3之前回收新生代唯一的选择
(2)Serial垃圾收集器是Client模式下的默认新生代垃圾收集器
(3)Serial垃圾收集器采用复制算法、串行回收和“Stop-the-Word”的机制执行回收

5.1.2 Serial Old垃圾收集器

(1)Serial Old垃圾收集器同样也采用了串行回收和“Stop-the-Word”机制,只不过内存回收算法使用的是标记-压缩算法。
(2)Serial Old是Client模式下默认的老年代垃圾回收器
(3)Serial Old在Server模式下主要有两个用途:
①与新生代的Parallel Scavenge配合使用
②作为老年代CMS收集器的后备垃圾收集方案

5.2 ParNew回收器:并行回收

(1)ParNew收集器是Serial收集器的多线程版本。ParNew也是采用复制算法、“Stop-the-Word”机制。
(2)ParNew收集器运行在多CPU的环境下,可以充分利用多CPU、多核心等物理硬件资源的优势,更快的完成垃圾收集,提升程序的吞吐量。但是在单个CPU的环境下,ParNew收集器没有Serial收集器效率高。虽然Serial收集器是基于串行回收,但是由于CPU不需要频繁的做任务切换,因此可以有效的避免多线程交互过程中产生的一些额外开销。
(3)除了Serial外,目前只有ParNew能与CMS收集器配合工作。

5.3 Parallel Scavenge回收器、Parallel Old:吞吐量优先

5.3.1 Parallel Scavenge回收器

(1)Parallel Scavenge和ParNew一样是基于并行回收,采用复制算法、“Stop-the-Word”机制。Parallel Scavenge是吞吐量优先的垃圾收集器。
(2)自适应调节策略(在JVM运行过程中,根据当前的运行情况进行一个性能的监控,动态调整内存中的分配的情况,以达到最优策略,比如吞吐量最优)也是Parallel Scavenge和ParNew一个重要区别

5.3.2 Parallel Old回收器

Parallel Old收集器,是用来代替老年代的Serial Old收集器。Parallel Old和Serial Old一样采用标记-压缩算法和“Stop-the-Word”机制。但是是基于并行回收。

5.3.3 jdk8默认使用的是Parallel Garbage Collector (并行垃圾回收器),也就是Parallel Scavenge + Serial Old的组合。

5.4 CMS回收器:低延迟

(1)CMS即Concurrent-Mark-Sweep收集器,实现了让垃圾收集线程与用户线程同时工作
(2)CMS缩短了垃圾收集时用户线程的停顿时间,即低延迟。
(3)CMS采用 标记-清除 算法,并且也会“Stop-the-Word”

5.5 G1回收器:区域化分代式

在延迟可控的情况下获得尽可能高的吞吐量。G1是一个并行回收器,他把堆内存分割为很多不相干的区域,每次根据允许的收集时间,优先回收价值最大的Region

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

今在推特发一个推特立马推特账户被删除了

咨询 Google Contacts 是如何 获取我的苹果手机通讯录的电话号码清单的?不到一分钟,我的账户之间被删除了,比停用、冻结还令人可怕。 立马推特账户被删除了。

阿里云搭建物联网平台+MQTT.fx接入阿里云

文章目录 本篇介绍一、阿里云物联网平台搭建二 、MQTT客户端接入阿里云物联网平台总结 本篇介绍 本篇搭建了阿里云物联网平台,使用MQTT.fx接入阿里云,上传温湿度数据 使用到的软件:阿里云、MQTT.fx 一、阿里云物联网平台搭建 首先创建一个物…

Codeforces Round 949 (Div. 2)(A,B题解)

这场真是给我打的汗流浃背了,这场真的巨难(可能是因为我二进制根本就没学好的原因吧) 反正总共就搞了两道题,第一道题注重于思维,第二道题纯二进制,第三道题看着也是二进制(最后时间不够了&…

【Microelectronic Systems】

1 background&introduction 2 analog to Digital Conversion 3 Digital to Anolog Conversion 4 introduction to CMOS outlook ! introduction to semiconductors siemens,西门子 properties of semiconductors types of semiconductors $ PN junction 5 Mathematic s…

Llama(二):Open WebUI作为前端界面,使用本机的llama3

目录 背景 Open WebUI是什么 工程能力特性 产品功能特性 用户体验特性 Open WebUI安装并使用 背景 Mac M1芯片,16G 内存 llama3 8B的部署参考Llama(一):Mac M1芯片运行Llama3-CSDN博客在Mac M1 16G内存环境中,…

【数据结构】二叉树的层序遍历~动画超详解

目录 1 什么是层序遍历2 二叉树层序遍历的基本思路3 二叉树层序遍历的实现 1 什么是层序遍历 我们从字面意思就明白,所谓层序,就是一层一层按顺序去遍历一个二叉树,这和我们之前了解的按前中后序遍历方式完全不同 比方说这颗二叉树: 前序遍历: 层序遍历: 2 二叉树层序遍历的…

Zabbix安装:构建高效可靠的Zabbix监控系统

目录 引言 一、zabbix基本介绍 (一)什么是zabbix (二)zabbix结构体系 (三)zabbix监控对象 (四)zabbix进程 (五)zabbix监控模式 (六&#…

VRTK4教程 二:基本追踪

文章目录 untiyXR和UnityXRPluginFramwork使用方法: TrackedAlias使用方法使用技巧 untiyXR和UnityXRPluginFramwork 这两个用于跟踪头盔位置,其中UnityXR使用的是旧版API,另一个是新版API,两个我我们选一个即可 使用方法&#…

git使用流程

1.下载git 搜索下载 2.注册github账号(打开爬墙工具) 创建一个仓库 3.配置邮箱和密码 4.所以找一个文件夹 鼠标右键 选择 open Git Bash here(当前文件夹下打开命令行) 输入命令 配置用户名和邮箱 5.将建的仓库克隆下来 …

鸿蒙Ability Kit(程序框架服务)【UIAbility组件与UI的数据同步】

UIAbility组件与UI的数据同步 基于当前的应用模型,可以通过以下几种方式来实现UIAbility组件与UI之间的数据同步。 [使用EventHub进行数据通信]:在基类Context中提供了EventHub对象,可以通过发布订阅方式来实现事件的传递。在事件传递前&am…

响应式UI组件DevExtreme中文教程 - 工具栏的自适应模式

DevExtreme拥有高性能的HTML5 / JavaScript小部件集合,使您可以利用现代Web开发堆栈(包括React,Angular,ASP.NET Core,jQuery,Knockout等)构建交互式的Web应用程序。从Angular和Reac&#xff0c…

【算法】在?复习一下快速排序?

基本概念 快速排序是一种基于交换的排序算法,该算法利用了分治的思想。 整个算法分为若干轮次进行。在当前轮次中,对于选定的数组范围[left, right],首先选取一个标志元素pivot,将所有小于pivot的元素移至其左侧,大于…

Java实战:文本文件复制

任务目标 本实战任务的目标是创建一个Java程序,用于复制指定的文本文件到另一个位置,并在控制台中显示复制结果。 任务步骤 创建源文件:在指定的路径D:\love.txt创建源文件。创建文件复制类:在net.huawei.student.test包中创建…

上位机图像处理和嵌入式模块部署(f407 mcu中的单独烧录方法)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 前面我们说过,stm32有三种烧录方法,一种是st-link v2,一种是dap,一种是j-link。不过我们在实际操作…

数电课设:电动机转速测量控制电路

电动机转速测量控制电路设计 摘要 本文设计的电动机转速测量控制电路通过数字电路核心实现对电机转速的测量和显示。与市面上基于单片机的电机转速测量相比,该电路无需要注重复杂的软件设计,功耗小,稳定性高,实现了更好的底层封装…

【C++】C++入门1.0

鼠鼠最近在学C,那么好,俺来做笔记了! 目录 1.C关键字(C98) 2.命名空间 2.1.命名空间定义 2.2.命名空间的使用 3.C的输入&&输出 4.缺省参数 4.1缺省参数概念 4.2.缺省参数的分类 5.函数重载 5.1.函数重载概念 5.2.C支持函数…

URL路由基础

本书1-7章样章及配套资源下载链接: https://pan.baidu.com/s/1OGmhHxEMf2ZdozkUnDkAkA?pwdnanc 源码、PPT课件、教学视频等,可以从前言给出的下载信息下载,大家可以评估一下。 对于高质量的Web应用来讲,使用简洁、优雅的URL设计模式非常…

Vue进阶之Vue无代码可视化项目(三)

Vue无代码可视化项目 项目初始化store的使用DataSourceView.vuestores/counter.ts开发模式按钮store/editor.tsLayoutView.vue导航条安装图标iconpackage.jsonstore/debug.tssrc/components/AppNavigator.vueAppNavigator.ts:AppNavigator.vue:theme样式theme/reset.csstheme/v…

浅谈正向代理和反向代理(案例介绍)

公司一般主要以反向代理为主(最典型的Nginx负载均衡) 一、正向代理 客户端Client不直接访问服务器Server,通过代理服务器Proxy访问 正向代理是客户主动使用的代理 正向代理:最典型的案例就是通过爬虫爬取网络数据,如果请求次数过多该网站会…