Flink开发环境搭建与提交运行Flink应用程序

Flink开发环境搭建与提交运行Flink应用程序

  • Flink
    • 概述
    • 环境
  • Flink程序开发
    • 项目构建
    • 添加依赖
    • 安装Netcat
    • 实现经典的词频统计
      • 批处理示例
      • 流处理示例
    • Flink Web UI
  • 命令行提交作业
    • 编写Flink程序
    • 打包
    • 上传Jar
    • 提交作业
    • 查看任务
    • 测试
  • Web UI提交作业
    • 提交作业
    • 测试

Flink

概述

Apache Flink 是一个框架和分布式处理引擎,用于在无边界和有边界数据流上进行有状态的计算。Flink 能在所有常见集群环境中运行,并能以内存速度和任意规模进行计算。

在这里插入图片描述
官网:https://flink.apache.org/

GitHub: https://github.com/apache/flink

环境

Flink分别提供了基于Java语言和Scala语言的 API ,如果想要使用Scala语言来开发Flink程序,可以通过在IDEA中安装Scala插件来提供语法提示,代码高亮等功能。

推荐使用Java来作为开发语言,Maven 作为编译和包管理工具进行项目构建和编译。

Flink程序开发

项目构建

1.基于 Maven Archetype 构建

根据交互信息的提示,依次输入 groupId , artifactId 以及包名等信息后等待初始化的完

mvn archetype:generate
-DarchetypeGroupId=org.apache.flink
-DarchetypeArtifactId=flink-quickstart-java 
-DarchetypeVersion=1.17.0

2.使用官方脚本快速构建

官方提供了快速构建脚本,在Linux系统终端,直接通过以下命令来进行调用:

curl https://flink.apache.org/q/quickstart.sh | bash -s 1.17.0

3.使用 IDEA 构建

使用 IDEA开发工具,直接在项目创建页面选择 Maven Flink Archetype 进行项目初始化:

在这里插入图片描述

可以配置一个Flink Archetype,指定groupId 、 artifactId、version。这样就会自动引入pom.xml相关依赖与批处理、流处理demo例子,否则需要手动添加依赖。

在这里插入图片描述

添加依赖

使用使用IDEA 、普通Archetype构建,需要进行添加相关依赖

		<dependency><groupId>org.apache.flink</groupId><artifactId>flink-java</artifactId><version>1.17.0</version></dependency><dependency><groupId>org.apache.flink</groupId><artifactId>flink-streaming-java</artifactId><version>1.17.0</version></dependency><dependency><groupId>org.apache.flink</groupId><artifactId>flink-clients</artifactId><version>1.17.0</version></dependency>

注意:

在打包时,需要将部分依赖的scope标签全部被标识为provided,标记这些依赖不会被打入最终的 JAR 包。

因为Flink的安装包中已经提供了这些依赖,位于其lib目录下,名为flink-dist_*.jar ,它包含了Flink的所有核心类和依赖

安装Netcat

Netcat(又称为NC)是一个计算机网络工具,它可以在两台计算机之间建立 TCP/IP 或 UDP 连接。它被广泛用于测试网络中的端口,发送文件等操作。使用 Netcat 可以轻松地进行网络调试和探测,也可以进行加密连接和远程管理等高级网络操作。因为其功能强大而又简单易用,所以在计算机安全领域也有着广泛的应用。

安装nc命令

yum install -y nc

启动socket端口

[root@node01 bin]# nc -lk 8888

注意:

测试时,先启动端口,后启动程序,否则会报超时连接异常。

实现经典的词频统计

统计一段文本中,每个单词出现的频次。

在项目resources目录下创建words.txt 文件,内容如下:

abc bcd cde 
bcd cde fgh
cde fgh hij

Flink 它可以处理有界的数据集、也可以处理无界的数据集、它可以流式的处理数据、也可以批量的处理数据。

批处理示例

批处理是基于DataSet API操作,对数据的处理转换,可以看作是对数据集的操作,批量的数据集本质上也是流。

public class WordCountBatch {public static void main(String[] args) throws Exception {// 创建执行环境ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();// 获取文件路径String path = WordCountBatch.class.getClassLoader().getResource("word.txt").getPath();// 从文件中读取数据DataSource<String> lineDS = env.readTextFile(path);// 切分、转换,例如: (word,1)FlatMapOperator<String, Tuple2<String, Integer>> wordAndOne = lineDS.flatMap(new MyFlatMapper());// 按word分组 按照第一个位置的word分组UnsortedGrouping<Tuple2<String, Integer>> wordAndOneGroupby = wordAndOne.groupBy(0);// 分组内聚合统计 将第二个位置上的数据求和AggregateOperator<Tuple2<String, Integer>> sum = wordAndOneGroupby.sum(1);// 输出sum.print();}/*** 自定义MyFlatMapper类,实现FlatMapFunction接口* 输出: String 元组Tuple2<String, Integer>>  Tuple2是flink提供的元组类型*/public static class MyFlatMapper implements FlatMapFunction<String, Tuple2<String, Integer>> {@Override//value是输入,out就是输出的数据public void flatMap(String value, Collector<Tuple2<String, Integer>> out) throws Exception {// 按空格切分单词String[] words = value.split(" ");// 遍历所有word,包成二元组输出 将单词转换为 (word,1)for (String word : words) {Tuple2<String, Integer> wordTuple2 = Tuple2.of(word, 1);//  使用Collector向下游发送数据out.collect(wordTuple2);}}}
}

本机不需要配置其他任何的 Flink 环境,直接运行 Main 方法即可

输出结果:

(bcd,2)
(cde,3)
(abc,1)
(hij,1)
(fgh,2)

流处理示例

DataSet API是基于批处理的API,从Flink 1.12开始,官方推荐使用DataStream API,它是流批统一处理的API

对于Flink而言,流才是整个处理逻辑的底层核心,所以流批统一之后的DataStream API
更加强大,可以直接处理批处理和流处理的所有场景。

1.有界流之读取文件

    public static void main(String[] args) throws Exception {// 创建执行环境StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();// 从项目根目录下的data目录下的word.txt文件中读取数据DataStreamSource<String> source = env.readTextFile("data/word.txt");// 处理数据: 切分、转换SingleOutputStreamOperator<Tuple2<String, Integer>> wordAndOneDS = source.flatMap(new FlatMapFunction<String, Tuple2<String, Integer>>() {@Overridepublic void flatMap(String value, Collector<Tuple2<String, Integer>> out) throws Exception {// 按空格切分String[] words = value.split(" ");for (String word : words) {// 转换成二元组 (word,1)Tuple2<String, Integer> wordsAndOne = Tuple2.of(word, 1);// 通过采集器向下游发送数据out.collect(wordsAndOne);}}});// 处理数据:分组KeyedStream<Tuple2<String, Integer>, String> wordAndOneKS = wordAndOneDS.keyBy(new KeySelector<Tuple2<String, Integer>, String>() {@Overridepublic String getKey(Tuple2<String, Integer> value) throws Exception {return value.f0;}});// 处理数据:聚合SingleOutputStreamOperator<Tuple2<String, Integer>> sumDS = wordAndOneKS.sum(1);// 输出数据sumDS.print();// 执行env.execute();}

输出结果如下:

10> (bcd,1)
10> (cde,1)
10> (cde,2)
3> (fgh,1)
3> (fgh,2)
11> (abc,1)
10> (bcd,2)
10> (cde,3)
8> (hij,1)

注意:

1.前面编号:并行度,与电脑线程数相关2.(cde,1)、(cde,2)、(cde,3):切分、转换、分组、聚合,是有状态的计算

2.无界流之读取socket文本流

在实际的生产环境中,真正的数据流其实是无界的,有开始却没有结束,这就需要持续地处理捕获的数据。为了模拟这种场景,可以监听socket端口,然后向该端口不断的发送数据。

Flink的流处理是事件驱动的,当前程序会一直处于监听状态,只有接收到数据才会执行任务、输出统计结果。

DataStream API支持从Socket套接字读取数据。只需要指定要从其中读取数据的主机和端口号即可。

    public static void main(String[] args) throws Exception {// 创建执行环境StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();// 指定并行度,默认电脑线程数env.setParallelism(3);// 读取数据socket文本流 指定监听 IP 端口 只有在接收到数据才会执行任务DataStreamSource<String> socketDS = env.socketTextStream("IP", 8080);// 处理数据: 切换、转换、分组、聚合 得到统计结果SingleOutputStreamOperator<Tuple2<String, Integer>> sum = socketDS.flatMap((String value, Collector<Tuple2<String, Integer>> out) -> {String[] words = value.split(" ");for (String word : words) {out.collect(Tuple2.of(word, 1));}}).setParallelism(2)// // 显式地提供类型信息:对于flatMap传入Lambda表达式,系统只能推断出返回的是Tuple2类型,而无法得到Tuple2<String, Long>。只有显式设置系统当前返回类型,才能正确解析出完整数据.returns(new TypeHint<Tuple2<String, Integer>>() {})
//                .returns(Types.TUPLE(Types.STRING,Types.INT)).keyBy(value -> value.f0).sum(1);// 输出sum.print();// 执行env.execute();}

注意:

Flink具有一个类型提取系统,可以分析函数的输入和返回类型,自动获取类型信息,从而获得对应的序列化器和反序列化器。

但是,由于Java中泛型擦除的存在,在某些特殊情况下(如Lambda表达式中),自动提取的信息是不够准确的。因此,就需要显式地提供类型信息,才能使应用程序正常工作或提高其性能。

执行以下命令,发送测试数据

[root@master ~]# nc -l 8080
abc bcd cde
bcd cde fgh
cde fgh hij

输出结果内容

3> (abc,1)
3> (bcd,1)
3> (cde,1)
1> (fgh,1)
3> (bcd,2)
3> (cde,2)
1> (fgh,2)
2> (hij,1)
3> (cde,3)

Flink Web UI

在本地开发环境中,可以添加flink-runtime-web依赖,启动Flink Web UI界面,方便开发测试使用 。

添加flink-runtime-web依赖

<dependency><groupId>org.apache.flink</groupId><artifactId>flink-runtime-web</artifactId><version>1.17.0</version><scope>provided</scope>
</dependency>
 /*** 并行度优先级:算子 > 全局env > 提交指定 > 配置文件* @param args* @throws Exception*/public static void main(String[] args) throws Exception {// 本地模式Configuration conf = new Configuration();// 指定端口conf.setString(RestOptions.BIND_PORT, "7777");//  创建执行环境StreamExecutionEnvironment env = StreamExecutionEnvironment.createLocalEnvironmentWithWebUI(conf);// 全局指定并行度,默认是电脑的线程数env.setParallelism(2);// 读取socket文本流DataStreamSource<String> socketDS = env.socketTextStream("node01", 8888);//  处理数据: 切割、转换、分组、聚合 得到统计结果SingleOutputStreamOperator<Tuple2<String, Integer>> sum = socketDS.flatMap((String value, Collector<Tuple2<String, Integer>> out) -> {String[] words = value.split(" ");for (String word : words) {out.collect(Tuple2.of(word, 1));}})// 局部设置算子并行度.setParallelism(3).returns(Types.TUPLE(Types.STRING, Types.INT)).keyBy(value -> value.f0).sum(1)// 局部设置算子并行度.setParallelism(4);//  输出sum.print();//  执行env.execute();}

启动Netcat

[root@node01 ~]# nc -l 8888

启动Flink程序,访问:http://localhost:7777/

若出现如下提示,需要在pom.xml中将依赖flink-runtime-web的指定<scope>provided</scope>作用域标签去掉

在这里插入图片描述
注释后再次启动项目,访问:http://localhost:7777/

在这里插入图片描述
查看任务执行详情,可以看出开发Flink应用程序时指定的并发度与此执行流程图上的并发度一致。
在这里插入图片描述

命令行提交作业

编写Flink程序

编写一个读取socket发送单词并统计单词个数的程序

    public static void main(String[] args) throws Exception {// 创建执行环境StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();// 指定并行度,默认电脑线程数env.setParallelism(3);// 读取数据socket文本流 指定监听 IP 端口 只有在接收到数据才会执行任务DataStreamSource<String> socketDS = env.socketTextStream("IP", 8080);// 处理数据: 切换、转换、分组、聚合 得到统计结果SingleOutputStreamOperator<Tuple2<String, Integer>> sum = socketDS.flatMap((String value, Collector<Tuple2<String, Integer>> out) -> {String[] words = value.split(" ");for (String word : words) {out.collect(Tuple2.of(word, 1));}}).setParallelism(2)// // 显式地提供类型信息:对于flatMap传入Lambda表达式,系统只能推断出返回的是Tuple2类型,而无法得到Tuple2<String, Long>。只有显式设置系统当前返回类型,才能正确解析出完整数据.returns(new TypeHint<Tuple2<String, Integer>>() {})
//                .returns(Types.TUPLE(Types.STRING,Types.INT)).keyBy(value -> value.f0).sum(1);// 输出sum.print();// 执行env.execute();}

打包

在项目pom.xml文件添加打包插件配置

<build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-shade-plugin</artifactId><version>3.2.4</version><executions><execution><phase>package</phase><goals><goal>shade</goal></goals><configuration><artifactSet><excludes><exclude>com.google.code.findbugs:jsr305</exclude></excludes></artifactSet><filters><filter><!-- Do not copy the signatures in the META-INF folder.Otherwise, this might cause SecurityExceptions when using the JAR. --><artifact>*:*</artifact><excludes><exclude>META-INF/*.SF</exclude><exclude>META-INF/*.DSA</exclude><exclude>META-INF/*.RSA</exclude></excludes></filter></filters><transformers><transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"><!-- Replace this with the main class of your job --><mainClass>my.programs.main.clazz</mainClass></transformer><transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/></transformers></configuration></execution></executions></plugin></plugins>
</build>

注意:

Flink集群内部依赖实则包含了Flink相关依赖,在打包Flink应用程序时,可以在pom.mxl中指定Flink相关依赖作用域。

1.使用内置在Flink集群内部依赖

# 作用在编译和测试时,同时没有传递性
# 项目打包发布,不包含该依赖
<scope>provided</scope>

2.不使用内置在Flink集群内部依赖

# 使用默认作用域,即scope标签可省略
# 作用在所有阶段,会传递到依赖项目中,项目打包发布,含该依赖
<scope>compile</scope>

上传Jar

将编写好的Flink程序打包后上传到服务器的/root目录

提交作业

进入到flink的bin目录,使用flink run命令提交作业

[root@node01 flink]# ./bin/flink run -m node01:8081 -c cn.ybzy.demo.WordCountStreamUnboundedDemo demo-1.0-SNAPSHOT.jar 
Job has been submitted with JobID 33a87b974d19880887ffe9b34efc8ac8
-m:指定提交到的JobManager-c:指定入口类

查看任务

浏览器中打开Web UI,访问http://node01:8081查看任务

在这里插入图片描述
点击任务查询详情
在这里插入图片描述

测试

在socket端口,发送测试数据

[root@node01 bin]# nc -lk 8888
abc bcd cdf

在TaskManagers列表中寻找执行节点,并查看执行日志。

这里很明显node01节点有数据接收,故应该查看它,否则应该在其他TaskManager节点查看

在这里插入图片描述
在TaskManager的标准输出(Stdout)看到对应的统计结果。
在这里插入图片描述

Web UI提交作业

除了通过命令行提交任务之外,也可以直接通过WEB UI界面提交任务。

提交作业

打开Flink的WEB UI页面,选择上传运行的JAR 包

在这里插入图片描述
JAR包上传完成

在这里插入图片描述
点击该 JAR 包,出现任务配置页面,进行相应配置。

配置程序入口主类的全类名,任务运行的并行度,任务运行所需的配置参数和保存点路径等

在这里插入图片描述
配置完成后,即可点击按钮“Submit”,将任务提交到集群运行,默认显示任务运行的具体情况
在这里插入图片描述

测试

在socket端口,发送测试数据

[root@node01 bin]# nc -lk 8888
abc bcd cdf

在TaskManagers列表中寻找执行节点,并查看执行日志。

这里很明显node02节点有数据接收,故应该查看它,否则应该在其他TaskManager节点查看

在这里插入图片描述
在这里插入图片描述

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

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

相关文章

MinIO的安装与使用

文章目录 1.MINIO是什么&#xff1f;2.MINIO安装3.启动脚本4.打开MINIO页面5.MC命令6.MINIO备份脚本 1.MINIO是什么&#xff1f; MinIO 是一款高性能、分布式的对象存储系统. 它是一款软件产品, 可以100%的运行在标准硬件。即X86等低成本机器也能够很好的运行MinIO。 MinIO与…

alsa音频pcm设备之i2c调试

i2cdetect 列举 I2C bus i2cdetect -l ls /dev/i2c* 列出I2C bus i2c-7 上面连接的所有设备,并得到i2c设备地址 i2cdetect -y 7 发现i2c设备的位置显示为UU或表示设备地址的数值,UU表示设备在driver中被使用. I2cdump i2c设备大量register的值 i2cdump -y 7 0x40 I2cset设置…

ICPC 2019-2020 North-Western Russia Regional Contest

A (codeforces.com) 这题在移动不被挡板挡住以及不超过边界的情况下&#xff0c;每次走的越多那么次数就越少 只要两个每次都走b-a步&#xff08;已经是不被挡板挡住走的最多了&#xff09;&#xff0c;就不用考虑被挡板挡住的情况&#xff0c;只用单独考虑了&#xff0c;如果…

微服务09-Sentinel的入门

文章目录 微服务中的雪崩现象解决办法&#xff1a;1. 超时处理2. 舱壁模式3. 熔断降级4.流量控制 Sentinel1.介绍2.使用操作3.限流规则4.实战&#xff1a;流量监控5.高级选项功能的使用1.关联模式2.链路模式3.总结 流控效果1.预热模式2.排队等待模式3.总结4.热点参数限流5.实战…

【业务功能篇 131】23种设计模式介绍

第一章 设计模式概述 1.1 代码质量好坏如何评价? 要想学习设计模式呢 我们就必须搞清楚设计模式到底在我们的编程过程中起到了怎样的作用,在编程世界中它处在一个什么样的位置,它到底是一种抽象的设计思想,还是一套具体的落地方案. 在学习设计模式之前呢 我们需要了解一下 代…

【数据结构C/C++】顺序与链式二叉树创建与前中后、层序遍历

文章目录 顺序存储结构二叉树链式存储结构二叉树刷题推荐408考研各数据结构C/C代码&#xff08;Continually updating&#xff09; 顺序存储结构二叉树 顺序存储结构的二叉树的特点在于&#xff0c;其使用数组存放二叉树中的每一个节点。 我们设定根节点的数组索引下标为n&…

MYSQL的日志管理

MySQL中有几种类型的日志记录&#xff0c;分别用于记录不同的操作和事件。以下是MySQL中常见的日志类型 错误日志 错误日志是 MySQL 中最重要的日志之一&#xff0c;它记录了当 mysqld 启动和停止时&#xff0c;以及服务器在运行过程中发生任何严重错误时的相关信息。当数据…

linux后台运行java项目/ jar包:nohup 命令

1.提出问题 我们把一个 SpringBoot 工程导出为 jar 包&#xff0c;jar 包上传到阿里云 ECS 服务器上&#xff0c;使用 java -jar xxx-xxx.jar 命令启动这个 SpringBoot 程序。此时我们本地的 xshell 客户端必须一直开着&#xff0c;一旦 xshell 客户端关闭&#xff0c;java -j…

Jenkins对应java版本

官网地址&#xff1a;Java Support Policy 运行jenkins时,需要使用下列Java版本:

Jenkins安装多个jdk版本,并在项目中选择对应jdk版本

下载jdk版本&#xff1a;进入oracle官网下载官方jdk Java Downloads | Oracle 例&#xff1a;比如项目需要使用java8.341的版本&#xff0c;而jenkins用的是java11的版本&#xff0c;这里就需要下载多个jdk版本。进入下载网址&#xff0c;Java Archive Downloads - Java SE 8u…

MySQL数据库技术笔记(3)

概述 学习MySQL数据库技术其实只需要安装mysql服务器就可以使用了。只不过对于初学者来说直接操作dos窗口方式比较麻烦&#xff0c;命令不熟悉&#xff0c;导致经常写错。在真实的开发当中直接操作dos窗口效率比较慢&#xff0c;企业中也会经常使用一些mysql数据库支持的可视化…

学习记忆——数学篇——案例——代数——方程——一元二次方程

重点记忆法 a x 2 b x c 0 ax^2bxc0 ax2bxc0 整体可以由&#xff1a; 根&#xff08;多少&#xff0c;正负&#xff0c;区间&#xff09; ⟹ \Longrightarrow ⟹ △ △ △ ⟹ \Longrightarrow ⟹ 求根公式 x 1 , 2 x_{1,2} x1,2​ − b △ 2 a \frac{-b\sqrt{△}}{2a} 2…

Transformer模型 | Python实现TransformerCPI模型(pytorch)

文章目录 效果一览文章概述程序设计参考资料效果一览 文章概述 Python实现TransformerCPI模型(tensorflow) Dependencies: python 3.6 pytorch >= 1.2.0 numpy RDkit = 2019.03.3.0 pandas Gensim >=3.4.0 程序设计 import torch import numpy as np import random …

TensorFlow入门(十九、softmax算法处理分类问题)

softmax是什么? Sigmoid、Tanh、ReLU等激活函数,输出值只有两种(0、1,或-1、1或0、x),而实际现实生活中往往需要对某一问题进行多种分类。例如之前识别图片中模糊手写数字的例子,这个时候就需要使用softmax算法。 softmax的算法逻辑 如果判断输入属于某一个类的概率大于属于其…

线性代数 --- 矩阵的QR分解,A=QR

矩阵的QR分解&#xff0c;格拉姆施密特过程的矩阵表示 首先先简单的回顾一下Gram-Schmidt正交化过程的核心思想&#xff0c;如何把一组线性无关的向量构造成一组标准正交向量&#xff0c;即&#xff0c;如何把矩阵A变成矩阵Q的过程。 给定一组线性无关的向量a,b,c&#xff0c;我…

Hazelcast系列(三):hazelcast集成(服务器/客户端)

系列文章 Hazelcast系列(一)&#xff1a;初识hazelcast Hazelcast系列(二)&#xff1a;hazelcast集成&#xff08;嵌入式&#xff09; Hazelcast系列(三)&#xff1a;hazelcast集成&#xff08;服务器/客户端&#xff09; Hazelcast系列(四)&#xff1a;hazelcast管理中心 …

ubuntu下使用gcc编译c程序: “error: stray ‘\357’ in program“

现象&#xff1a; ubuntu下使用gcc编译c程序: “error: stray ‘\357’ in program“ 尝试查找原因&#xff1a;打开从windos直接粘贴c程序到ubuntu的c代码&#xff0c;发现多了 <200b>&#xff1a; 方案&#xff1a;尝试在vim编辑器删除&#xff0c;多出来的字符后编译…

长沙建筑模板生产厂家有哪些?

在湖南长沙地区&#xff0c;建筑施工企业寻找一家可信赖的建筑模板供应商是非常重要的。在长沙地区&#xff0c;有多家建筑模板生产厂家&#xff0c;其中值得一提的是能强优品木业&#xff0c;他们是长沙地区建筑模板生产的领先供应商之一。 能强优品木业位于广西贵港市&#x…

Linux 部署1Panel 现代化运维管理面板进行公网远程访问

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏:《速学数据结构》 《C语言进阶篇》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 前言1. Linux 安装1Panel2. 安装cpolar内网穿透2.1 使用一键脚本安装命令 2.2向系统添加服务2.3 启动cpolar服务…

【Java】 DirectByteBuffer堆外内存回收

PhantomReference虚引用 在分析堆外内存回收之前&#xff0c;先了解下PhantomReference虚引用。 PhantomReference需要与ReferenceQueue引用队列结合使用&#xff0c;在GC进行垃圾回收的时候&#xff0c;如果发现一个对象只有虚引用在引用它&#xff0c;则认为该对象需要被回…