1. Looking Closer at the Scene: Multiscale Representation Learning


Information

在这里插入图片描述

论文地址https://ieeexplore.ieee.org/document/9298485
论文代码https://github.com/hw2hwei/SKAL

Abstract

  遥感图像场景分类因其广泛的应用而备受关注。尽管基于卷积神经网络(CNN)的场景分类方法取得了优异的效果,但遥感图像中特征和目标的大规模变化限制了分类性能的进一步提高。为解决该问题,我们提出了一种基于全局-局部双流架构的场景分类多尺度表示方法。该体系结构分为全局流和局部流两个分支,分别从整个图像和最重要的区域提取全局特征和局部特征。为了仅使用图像级标签定位整个图像中最重要的区域,专门设计了一种弱监督的结构化关键区域定位关键区域检测策略(SKAL),将全局流和局部流连接起来。为验证所提出的基于 SKAL 的双流架构的有效性,我们基于 AlexNet、GoogleNet 和 ResNet18 这三种广泛使用的 CNN 模型,在4个公共遥感图像场景分类数据集上进行了对比实验,均达到了 SOTA 水平。

keywords: Convolutional neural network (CNN), multiscale representation, remote sensing, scene classification, structured key area localization (SKAL).

1. Introduction

  近年来,受益于遥感成像设备和技术的发展,遥感图像的许多语义级任务发展迅速,如目标检测[1]、图像检索[2]、图像字幕[3]、[4]、道路提取[5]等。作为这些任务的基础,遥感图像场景分类[6]-[10]已成为研究热点,它根据遥感图像中的特征和对象将遥感图像划分为一组场景类。

  遥感图像中存在大量相似和混淆的特征和目标,因此提取遥感场景的判别特征至关重要。特征提取包含两种监督特征:手工特征(handcrafted features)和深度特征(deep features)。与手工特征相比,深度特征包含更多的高级语义信息,可通过 CNN 自动学习。由于 CNN 强大的特征提取能力,基于 CNN 的方法[6]、[8]、[9]、[11]-[14]已成为遥感图像场景分类领域的主流方法,并达到了 SOTA 水平。

  尽管基于 CNN 的方法在性能上有了明显的提高,但遥感图像的场景分类仍然受到图像中特征和对象的大规模变化的困扰。如图1所示,最重要的区域只占整个图像的一小部分,并且通常被大量无用的特征和对象包围,这降低了提取特征的识别率。
在这里插入图片描述

  为了解决这一问题,我们提出了一种用于遥感图像场景分类的全局和局部特征联合表示方法。如图2所示,我们设计了一个全局-局部双流架构。该架构中,蓝色分支网络为全局特征提取流,绿色分支网络为局部特征提取流。全局区域包含更多的全局特征,如轮廓和纹理信息,而关键局部区域扩大了最重要的目标,可以提供更细粒度的特征并降低背景噪声。我们的全局-局部双流架构可以分别从不同尺度的输入图像中提取全局特征和局部特征,最后融合它们的分类分数。
在这里插入图片描述
  为了定位整个图像中最重要的局部区域,我们提出了一种弱监督的结构化关键区域定位(SKAL)关键区域检测策略,如图2中的黄色路线。该算法基于全局特征映射中补丁的特征响应程度,定义了一种明确的局部关键区域定位过程。仅使用图像级标签即可准确定位最重要的区域,即其边界[x1, x2, y1, y2]。

  为了验证所提出的基于 SKAL 的全局-局部双流架构在遥感图像场景分类中的有效性,我们在UC Merced (UCM)数据集[18]、RSSCN7数据集[19]、AID数据集[20]、 NWPU-RESISC45 数据集[21]等4个公开的大型遥感场景数据集上,基于三种被广泛使用的 CNN 模型(Alexnet[15]、ResNet18[16]、GoogleNet[17])进行了大量对比实验,在所有数据集上均达到了 SOTA 水平。

  本文的贡献可以概括为以下四个方面。

  (1)针对遥感图像的大尺度变化问题,从多尺度特征的角度提出了遥感图像场景分类的全局和局部特征联合表示。相应地,设计了一种全局-局部双流架构,分别从不同尺度的输入图像中提取全局特征和局部特征。

  (2)为了定位整个遥感场景图像中最重要的区域,特别提出了一种连接全局和局部流的 SKAL 策略。SKAL 能够以边界框 [x1, x2, y1, y2] 的形式精确计算出最重要的局部区域。

  (3)为了验证基于 SKAL 的全局-局部双流架构在遥感图像中的有效性,在4个公开的遥感图像场景分类数据集上,基于几种广泛使用的 CNN 模型进行了大量的对比实验。研究结果表明,该方法可以显著提高遥感场景分类的性能。

  (4)作为一种多尺度表示学习方法,我们的全局-局部双流架构可以很容易地应用于各种CNN模型,具有实现简单、运行速度快、可解释性强的优点。

2. Related Work

  本节简要回顾了遥感图像场景分类和弱监督关键目标检测方法的相关工作。

  A.遥感图像场景分类:

  根据特征提取的不同,用于遥感图像场景分类的方法大致可分为以下三类。

  (1)手工特征(Handcrafted Features):基于手工特征的方法首次应用于遥感图像场景分类。这些方法依赖于一系列手动设计的特征描述符,包括全局特征描述符(如颜色直方图和纹理描述符[22]、[23])和局部特征描述符(如定向梯度直方图(HOG)[24]、[25]和尺度不变特征变换(SIFT)[26]、[27])。全局特征描述符可以生成遥感图像的完整表示,可以直接发送到分类器中。局部特征描述符通常是中级特征描述符,需要通过特征组合技术(如BoVW)将其集成到全局表示中[28]。此外,Zhu等[29]提出了直方图级别的局部-全局特征融合操作。这些手工制作的功能都是精心设计的;然而,它们不能有效地应对类内多样性、类间相似性和尺度变化的挑战。

  (2)无监督特征(Unsupervised Features):分类本质上是一个监督任务,但研究人员也找到了将无监督学习结果解释为类的方法。研究人员已经尝试了基于无监督特征学习的方法[30]-[34],旨在学习特征编码函数。对于这些基于无监督特征学习的方法,它们将手工制作的特征描述符(如SIFT)作为输入,并生成融合的特征。通过使用一些编码技术来组合多个特征是至关重要的。用于遥感图像场景分类的典型编码方法包括主成分分析(PCA)、k-means聚类、稀疏编码[32]和自动编码器[35]。此外,BoVW[25]可以基于k-means聚类从手工制作的特征生成视觉字典(码本),也是最流行的基于无监督特征学习的方法之一。然而,总体而言,由于缺乏标签,无监督方法无法生成与有监督特征相同的不同场景类别的区分特征。

  (3)深度特征(Deep Features):近年来,深度学习方法,尤其是cnn[6]、[8]、[9]、[11]-[14]、[36]-[38],由于其强大的大规模特征提取能力,在自然图像的大部分领域占据了主导地位。同样,基于深度特征的方法也成为遥感图像场景分类的主流,其分类性能优于手工特征和无监督特征。与通常由特征工程技能和领域识别决定的基于手工特征的方法相比,基于深度特征的方法可以从原始图像中自动学习最具判别性的语义级特征。此外,CNN模型是端到端的可训练架构,而不是复杂的多阶段架构,这是基于手工制作特征方法的主要工作流程。尽管基于深度特征的方法已经取得了优异的表现,但大规模的变化仍然是最难解决的问题之一。

  B.弱监督对象检测

  弱监督目标检测是一项有意义的研究课题,它仅使用图像级标签来定位最重要的局部区域。不仅可以提高我们的场景分类模型的可解释性,而且可以用于进一步提高其性能。Pandey和Lazebnik[39]基于可变形零件模型(dpm)的手工特征实现了弱监督对象检测。为了充分利用卷积特征的优势,Bilen和Vedaldi[40]提出了弱监督深度检测网络来定位关键目标。Cinbis等人[41]将多实例学习引入弱监督目标检测。此外,Zhang等[42]将显著性检测引入了这一领域。此外,Fu等[43]实现了一种可学习的循环注意CNN关键目标定位网络(RA-CNN),用于细粒度图像识别,并取得了显著的成果。聚类学习技术在文献[44]中也有尝试。最近,Yang等[45]提出了联合目标检测和动作分类中对象依赖的空间先验。

  然而,对于遥感图像来说,存在大量含有背景噪声的大尺度特征和目标。为了处理这些复杂的场景图像,受 RA-CNN 中多尺度特征表示思想的启发,我们设计了一种 SKAL 关键区域定位策略,生成最小区域边界,对整个图像中最重要的区域进行采样。提出的 SKAL 和 RA-CNN 之间有三个主要区别:
  (1)所提出的 SKAL 在一定程度上是一种相对可解释的关键区域定位策略,而 RA-CNN 利用同样属于黑箱模型的注意建议子网(attention proposal subnetwork, ATN)来预测关键区域。
  (2)SKAL定位的关键区域大小可以通过调节超参数进行人为控制,而RA-CNN难以实现。由于遥感图像场景分类是其他进一步遥感图像处理任务的基础,可控的尺寸可能更适合于进一步的图像处理。
  (3)RA-CNN需要设置一个额外的尺度间成对排序损失来约束定位过程和一个微妙的替代训练策略。对于我们的SKAL,两个流的训练是独立的,更容易实现。

  提出的 SKAL 与区域定位需求下的一系列区域建议目标检测方法[46]-[48]之间存在一定的联系。不同的是,这些对象检测方法需要检测对象的精确边界框,而提出的 SKAL 不需要。

3. Method

  在本节中,首先介绍 CNN 的组成。然后,图3详细介绍了基于全局多层特征映射的 SKAL 策略。最后,如图2所示,我们提出了基于 SKAL 的全局-局部双流架构,分别提取全局和局部特征并融合它们的分类分数。
在这里插入图片描述
  A. CNN:

  一般来说,用于图像分类的整个 CNN 模型可以大致分为三个部分:堆叠的卷积层、全局平均池化(GAP)层和全连接(FC)层。

  (1)堆叠卷积层:在 CNN 中,多个堆叠卷积层是提取从底层纹理和颜色特征到高层语义信息的最重要部分。每个卷积层通常由卷积核和非线性激活函数组成(在某些情况下,也有批归一化[49])。由于不同模型的卷积层以不同的方式堆叠,因此很难分别描述它们的体系结构。因此,本文将它们描述为一个统一的 ConvBlocks 来大致表示特征提取的过程。这些堆叠卷积层的输入为RGB图像 I∈R3×224×224,输出为多层特征映射 M∈RC×H×W (C为通道数,H为空间高度,W为空间宽度)。记作:
在这里插入图片描述
  (2)全局平均池化(GAP)层:由于多层特征映射 Mpatch 为单位分布在空间中,因此需要将特征映射M 池化到对应的特征向量 V∈RC 中,便于连接下一个分类器(FC层)。因此,全局池化层用于连接卷积层和 FC 层,其计算公式为:
在这里插入图片描述
  GAP不仅可以改变特征的空间维度,还可以减少可训练参数的过拟合。
  (3)全连接(FC)层:FC 层在 CNN 中起到分类器的作用,基于高维特征向量 V 给出每个类的分类分数。它取V作为输入,取分类置信度的评分向量作为输出,记为S∈RN ,其中 N 为数据集中的类别数。计算公式为:
在这里插入图片描述
其中,W∈RC×N ,b∈RN 分别为特征 V 的权值和偏置。S的元素是每个类的分类分数。为了将S的和缩放为1,在FC层之后增加 softmax 的操作。它的公式为:
在这里插入图片描述
其中 Si∈ RN 为类别分数。
  卷积层、GAP层和FC层的级联是分类任务的整个CNN模型。它也是我们全局-局部双流架构中每个流的结构。

  B. SKAL:
  最关键的一步是对全局图像中的关键区域进行定位,它在全局流和局部流之间起着桥梁的作用。如图3所示,我们在本节中详细提出了SKAL策略。基于从全局图像经过式 (1)-(3) 计算得到的全局图像的多层特征映射 Mg ,本文提出的 SKAL 生成一个[x1,x2,y1,y2]的边界框来指导采样过程。SKAL包括三个子步骤:能量聚合、能量图构建和贪婪边界搜索。

  (1)能量聚集:定量描述每个 Patch 在 Mg 中的重要程度是前提。能量聚合操作以 Mg 为输入,以 ME ∈ RH×W 的能量图为输出。
在这里插入图片描述
  这里值得注意的是,能量聚集可以看作是一种不需要训练的显性注意机制。需要用最小-最大缩放法将 ME 的所有元素缩放到[0,1]范围内,这样可以消除负元素的干扰:
在这里插入图片描述其中 max(ME )和 min(ME )分别是 ME (i) 中最大和最小元素的值。ME是与ME 在同一维度上的标度能量图。

  为了更精确地定位,将能量图从 H ×W 上采样到更大的空间尺寸(通常为6 × 6或7 × 7)是有意义的,表示为
在这里插入图片描述
我们使用双线性插值作为上采样技术,在本文的所有CNN模型中,在这里插入图片描述 的大小都设置为25 × 25。

  (2)能量图结构:完成上述准备工作后,我们得到了 在这里插入图片描述,可以定量描述全局图像Patch的重要性程度。正如我们所知,在二维空间中实现搜索是很复杂的。因此,我们进一步将缩放后的能量图沿空间高度和宽度聚合为两个1-D结构化能量向量 Vw∈RE 和 Vh∈RH
在这里插入图片描述
  (3)贪婪边界搜索:基于 Vw 和 Vh,分别计算出 [x1,x2] 和 [y1,y2]。为了快速准确地定位一维能量向量中最重要的一维区域,提出了一种基于能量的类贪婪边界搜索方法。
  以 Vw 为例,我们提出了一些包含宽度矢量中不同元素能量的概念:
在这里插入图片描述
式中,E[0:W] 为宽度矢量中所有元素的能量之和,E[x1:x2] 为区域沿空间宽度从 x1 到 x2 的能量。

  在本文中,全局图像中的关键区域定义如下:该区域占据最小的面积,但包含不小于总能量的阈值(ETr),即 E[x1:x2] /E[0:W] > ETr。ETr 是能量比例的超参数。在这个定义的基础上,我们可以使用一种类似贪婪的算法来搜索最关键的区域,该算法如下:
在这里插入图片描述

算法可细分为以下三个步骤:

  步骤 A(初始化边界):首先,第1行到第8行,[x1, x2] 以能量最大的那一半 Vw 的边界初始化。

  步骤 B(调整边界):迭代调整 [x1, x2] 的边界,使其能量收敛到 ETr 的一个小邻居。步骤A 之后有两种状态:一种是从第9行到第16行,初始化区域 [x1, x2] 的能量高于 ETr;另一种是从17行到24行,[x1, x2] 的能量低于ETr。当能量高于 ETr 时,[x1, x2] 区域需要沿着能量下降最慢的方向收缩,直到能量不高于ETr为止。然而,当能量低于ETr时,需要沿着能量上升最快的方向扩大区域,直到不低于ETr。我们的贪婪边界搜索可以快速找到最小但信息量最大的区域。

  步骤 C(缩放边界):如第26和27行所示,由于上述边界在 [0,W] 的范围内,因此需要根据能量向量的维度将其缩放到[0,1]。

  通过以上三步可以得到全局图像中最关键区域的宽度边界[x1, x2]。[y1, y2]的高度边界可以用同样的算法得到。用[x1, x2, y1, y2]的整个边界来指导关键局部采样过程。

  C. Global–Local Two-Stream Architecture:
  根据缩放后的 [x1,x2,y1,y2] 边界,我们可以用双线性插值技术对放大后的全局图像 I'g∈R3×448×448 中的关键局部区域Il∈R3×224×224 进行采样,记作:
在这里插入图片描述
  全局分类分数和局部分类分数分别从全局图像 Ig∈R3×448×448 和关键局部区域Il∈R3×224×224中计算出来,分别表示为:
在这里插入图片描述
CNNg 和 CNNl 都是式 (1)-(4) 的级联。为了提取不同尺度的特征,这两种流具有相同的结构,但不共享参数。融合后的分类分数为全局分类分数和局部分类分数的平均值:
在这里插入图片描述

4. Experiments

  在本节中,首先介绍了本文使用的遥感图像场景数据集和评价指标。其次,详细介绍了实验设置和超参数的训练。接下来,给出了提出的 SKAL 的可视化示例,用于辅助解释。最后,我们报告了在每个数据集上的实验结果,并与一些最先进的方法进行了比较,并分析了基于 SKAL 的全局-局部双流架构的性能。

  A. 数据集和评估指标:

  (1)UC Merced Land-Use (UCM)数据集:UCM数据集[18]由2100幅图像组成,分为21个典型的土地利用场景类:农业、飞机、棒球场、海滩、建筑物、灌木丛、密集住宅、森林、高速公路、高尔夫球场、港口、十字路口、中型住宅、移动住宅公园、立交桥、停车场、河流、跑道、稀疏住宅、储罐和网球场。每个类包含100张光学图像,尺寸为256 × 256像素,每个像素在RGB色彩空间中具有 30 cm 的空间分辨率。

  (2)RSSCN7 数据集:RSSCN7 数据集包含2800幅图像,由七大典型场景类组成:草地、森林、农田、工业区、湖泊、停车场、居民区和河流。每个班级都有从谷歌地球上的全球卫星地图上收集的400张图像,这些图像分别以四种不同的比例尺进行采样。RSSCN7数据集中的图像为400 × 400像素。由于季节变化、天气变化和尺度多样性,RSSCN7是一个具有挑战性的数据集。

  (3)Aerial Image 数据集:Aerial Image 数据集(AID)[20]有1万幅图像,分为30类:机场、裸地、棒球场、海滩、桥梁、中心、教堂、商业、密集住宅、沙漠、农田、森林、工业、草甸、中等住宅、山区、公园、停车场、操场、池塘、港口、火车站、度假村、河流、学校、稀疏住宅、广场、体育场、储罐、高架桥。每个类都有数百个在RGB空间中测量600 × 600像素的大规模图像。每个像素的空间分辨率范围从 800厘米/像素 到 50厘米/像素。

  (4)NWPU-RESISC45 数据集:NWPU-RESISC45 数据集包含31500张图像,分为45类:飞机、机场、棒球场、篮球场、海滩、桥梁、灌木丛、教堂、圆形农田、云、商业区、密集住宅、沙漠、森林、高速公路、高尔夫球场、地面田径、港口、工业区、十字路口、岛屿、湖泊、草地、中型住宅、移动住宅公园、山区、立交桥、宫殿、停车场、铁路、火车站、矩形农田、河流、回旋处、跑道、海冰、船舶、雪山、稀疏住宅、体育场、储罐、网球场、露台、热电站、湿地。每个类别有700张256 × 256像素的图像,RGB色彩空间的空间分辨率范围约为 300厘米/像素 至 20厘米/像素。就图像数量和类别而言,它是迄今为止最大的遥感场景分类数据集,由于类间相似度较高,因此更具挑战性。

  B. 评估指标

  以下两个典型指标用于定量评价实验结果。

  (1)总体精度:总体精度(Overall Accuracy,OA)定义为正确分类的图像数量除以数据集中的图像总数。OA得分反映的是分类模型的整体性能,而不是每个类别。

  (2)混淆矩阵:混淆矩阵(Confusion Matrix,CM)是一个二维信息表,用于分析类间分类误差和混淆程度。矩阵的每一行代表一个预测类的所有图像样本,而每一列代表一个真实类的样本。

  为了获得可靠的实验结果,在UCM、RSSCN7和AID数据集上,我们使用相同的训练比率重复实验5次,对数据集进行随机分割,并给出这些结果的平均值和标准差。在NWPU-RESISC45数据集上,由于样本数量较多,重复次数为3次。

  C. 实验设置

  (1)CNN基线:为了评估基于 SKAL 的全局-局部双流架构在遥感图像场景分类中的有效性和鲁棒性,我们在上述四组数据集上进行了对比实验,实验基于三种广泛使用的CNN模型:AlexNet[15]、GoogleNet[17]和ResNet18[16]在ImageNet[50]上进行预训练。AlexNet主要由卷积层组成,GoogleNet连接不同大小的过滤器,ResNets有残余连接。当它们用于关键区域计算时,去除它们的分类器(FC层),并将最终的多层特征图上采样到25 × 25 × C (25 × 25为空间大小,C为特征图的维度),以获得更准确的定位。在这里,替换在ImageNet上预训练的模型的最后一层的技术是一种广泛使用的策略[38],[51],[52]。

  (2)训练参数:选择Adam算法[53]作为优化器,使用交叉熵损失作为所有模型的损失函数。所有模型都训练了50个epoch,批大小设置为64。初始化Adam的学习率为1e-4,每20个epoch除以10。对于全局-局部双流架构下的输入图像大小,全局流的输入是将全局图像调整为224×224,而局部流的输入是将放大后的全局图像448×448裁剪后再调整为224×224的局部区域。为了公平的比较,与我们的双流架构相比,SOTA的CNN模型都是基于224 × 224的输入大小。

  (3)数据增强:由于全局和局部图像的尺度差异,这两种流的数据增强方法不同。对于全局流,我们使用随机水平翻转来增强全局图像。对于局部流,除了随机水平翻转外,我们还从全局图像中随机裁剪一个正方形区域进行局部图像增强。为了在训练和测试过程中保持局部图像的尺度一致,将正方形面积的边长比例设置为50%。

  (4)训练和测试流程:我们采用两阶段训练策略,分别训练全局流和局部流。首先对全局流进行训练和测试,并根据全局图像给出一个初步的场景分类分数。然后,通过增强的局部样本对局部流进行训练,并根据关键区域,局部流给出另一个分数。最后,对这两个分数进行平均运算融合。

  此外,所有实验均在64-GB内存的CPU和1 × 12-GB的 NVIDIA GeForce GTX 1080Ti GPU的计算环境下使用Pytorch[54] 1.1.0版本实现。

  D. SKAL可视化

  为了更直观地理解我们在算法1中的SKAL,基于GoogleNet在UCM数据集中的遥感图像上进行SKAL的完整过程如图4所示。为了更好的解释,所有的宽度和高度坐标都从[0,1]放大到[0,25]的范围。这里,ETr设置为60%。
在这里插入图片描述

  在图4中,通过(1)和(5)-(7)从原始全局图像中提取能量图。得到初始化的宽度和高度区域,即[x1, x2, y1, y2] =[3,15,7,19]。初始能量比分别为69%和52%。由于空间宽度和高度的区域搜索过程的独立性,宽度[x1, x2]和高度[y1, y2]分别进行调整。首先,将高度面积[y1, y2]从[7, 19]迭代扩大到[5, 19],能量比从52%增加到62%。其次,将宽度区域[x1, x2]从[3, 15]迭代缩小到[4, 14],能量比从69%下降到59%。因此,[xinit, xinit, yinit, yinit]的最终关键区域为[4, 14, 5, 19]。该边界经过缩放后,用于引导关键区域采样。

  图4的结果表明,局部图像覆盖了全局图像中信息量最大的区域,结构向量的能量比快速收敛到60%的小区域。更多的局部图像的样本如图5和图6所示。图5给出了一些反映关键区域定位合理效果的判别类样本。图6提供了“sparse_residential”、“medium_residential”和“dense_residential”这三个模糊类别的一些样本。在图6中,“sparse_residential”关注的是单体建筑,“medium_residential”关注的是建筑与树木的界面,“dense_residential”强调的是相邻的一片房屋,可以通过房屋的连接来判断。

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

  E.与其他方法的比较

  (1)UC Merced Land-Use (UCM)数据集:UCM数据集是最早、应用最广泛的遥感场景分类数据集。因此,我们首先将基于SKAL的全局-局部双流架构应用于UCM,探索分类性能的提升,并找到一个合理的ETr值。我们基于AlexNet、ResNet18和GoogleNet进行了超参数调优研究,ETr分别设置为60%、70%和80%。训练样本的比例分别为50%和80%,遵循UCM数据集的分割方式。我们的结果如表1所示。在该表中,global表示全局流、local表示局部流和global + local表示全局流+局部流。
在这里插入图片描述
  结果表明,基于SKAL的全局-局部双流架构在两种分割比例下的UCM数据集上的分类表现有明显的改进。可以发现我们的方法不受CNN模型的限制。在三种CNN模型中,AlexNet的性能明显落后于GoogleNet和ResNet18。与ResNet18相比,GoogleNet中存在一些多尺度卷积块,更适合我们基于SKAL的双流方法。因此,当我们的双流架构被应用时,GoogleNet稍微领先于ResNet18。

  我们还对全局流和局部流进行了比较,如表1所示。虽然局部流的性能远不如全局流,但它们的融合结果比只有全局流要好。这一现象说明了全局流和局部流特征提取的独立性,即前者提取全局场景特征,后者挖掘局部关键特征。此外,很可能是全局流在决策过程中起主要作用,而局部流对其进行了补偿:如果全局流有明确的分类判断,局部流可以增强全局流的结果;相反,如果全局流在一些模糊的分类之间犹豫不决,则聚焦于扩大关键区域的局部流可以纠正全局流可能出现的错误分类结果。从表1可以看出,随着局部面积的增加,促进效果会降低,因为局部面积扩大时,局部特征与全局特征趋于同质化。因此,有必要将局部区域限制在一个合理的范围内,这是由ETr决定的。阈值过小会导致有用的局部特征丢失,而阈值过大则会降低对局部特征的识别。可以发现,60%、70%和80%都很好,但大多数情况下70%是最好的。在接下来的实验中,ETr默认设置为70%。
在这里插入图片描述

  通过全局和全局+局部的对比实验,对所提出的基于SKAL的全局-局部双流架构在UCM数据集上的训练和测试次数进行了探索,结果如表2所示。由于局部流需要关键区域的边界框,而关键区域的边界框依赖于全局流,所以只有局部流没有相应的时间开销。在50 epoch的64个小批图像训练阶段,全局的训练时间主要包括全局的特征提取和梯度反向传播,而全局+局部的训练时间包括全局、SKAL的特征提取和局部流的特征提取和梯度反向传播。由表2可知,global + local的培训时间只比local多一点点。如果考虑局部特征提取的时间,则全局+局部特征提取的时间开销可以相对忽略。在对图像逐一测试的测试阶段,全局的测试时间主要包括图像加载的全局特征提取,而提出的SKAL和局部特征提取存在额外的时间开销。虽然全局+局部的模型尺寸是全局的两倍,但前者的测试时间不到后者的两倍时间成本。这可能是由图像加载引起的,并且它还表明所建议的SKAL不会占用明显的运行时间。结果表明,该算法具有较低的计算复杂度。
在这里插入图片描述

  为了客观地评估所提出的基于SKAL的全局-局部双流方法的性能,如表3所示,我们将OA(%)与UCM数据集上的一些最先进的方法进行了比较,包括手工制作的基于特征的方法、无监督的基于特征的方法和基于深度特征的方法。从表3中可以看出,我们的方法明显优于所有其他最先进的场景分类方法。当使用50%的图像进行训练时,我们的双流方法获得了第一名,比ARCNet-VGG16的第二种方法明显提高了1.38%[8]。当训练率增加到80%时,与ARCNet-VGG16[8]的第三种方法相比,考虑到接近100%的OA,我们基于googlenet的双流架构有0.58%的显著增益。虽然Resnet101-FSL[6]比GoogleNet更深更大,但我们基于表3的全局-局部双流架构的OA(%)与GoogleNet上UCM数据集上一些最先进的结果的比较仍然优于Resnet101-FSL。我们还可以发现,我们的方法在分类准确率方面比手工特征方法[55]、[56]和无监督方法[30]有巨大的优势。

在这里插入图片描述

  此外,如图7所示,我们制作了GoogleNet的双流架构以及相应的两个CMs,进一步分析UCM数据集的每一类的改进。从图7中可以看出,在medium_residential、dense_residential、buildings、storage_tanks、intersection等场景中,存在一些误分类的样本。当采用本文提出的双流结构时,错误分类样本和场景的数量迅速减少,证明了本文方法的有效性。

  (2)RSSCN7 数据集

  RSSCN7数据集图像受季节变化、各种天气和尺度多样性的影响,是一个困难的遥感场景数据集。这些问题对所提出的全局局部双流结构在关键局部区域定位中的稳定性和鲁棒性提出了挑战。为了研究我们的方法在处理这些问题时的性能,我们在AlexNet、ResNet18和GoogleNet上进行了对比实验(ETr设置为默认值70%),OA(%)的实验结果如表4所示。在该表中,为了更清晰的对比,我们去掉了global的下标,不提供局部流的结果。
在这里插入图片描述

  表4的结果表明,在这三条CNN基线上应用所提出的双流架构时,获得了约2%的增长。广泛而有意义的改进有力地支持了我们方法的稳定性和有效性。特别是当CNN基线受到训练数据不足的限制时,从数据增强的角度来看,局部图像也可以看作是额外的训练样本。与现有的方法相比,本文提出的方法在OA结果上具有巨大的优势。我们基于ResNet18的global-local双流架构获得第一名,在训练率为20%的情况下,与基于resnet50的TEX-Net-LF方法相比,准确率提高了1.44%,在训练率为50%的情况下,准确率提高了2.04%[12]。

  为了研究基于所提出的双流架构的RSSCN7数据集中各类的性能,制作CM,如图8所示。根据该CM可以发现,错误分类样本主要分布在工业、停车场、草地和田野场景中。这些场景具有较大的类内多样性和较高的类间相似性,需要在今后的工作中加以解决。
在这里插入图片描述
  (3)Aerial Image 数据集:AID是覆盖大量背景噪声的高分辨率大尺度遥感场景数据集。在AID数据集上基于上述三条CNN基线在默认训练设置和ETr下进行对比实验。表5提供了我们的OA双流架构(%)所获得的结果,并与一些最先进的结果进行了比较。值得注意的是,本文报道的基于cnn的方法在AID数据集上的最新结果是基于224×224的图像大小,方差很小,因为图像大小对分类精度有重要影响。
在这里插入图片描述
  表5的结果表明,本文提出的全局-局部双流架构为CNN基线提供了两种优势。一方面,我们的二束架构明显和普遍提高了性能的所有三个CNN基线。我们基于ResNet18的全局-局部双流架构优于目前所有最先进的方法,在50%的训练率下OA的优势至少为0.78%,在20%的训练率下OA的优势至少为0.57%。另一方面,我们的双流架构减少了实验结果的标准差,尽管训练图像是从数据集中随机选择多次,但仍然提供了更稳定的分类精度。

在这里插入图片描述

  为了评估所提出的方法在AID数据集上的性能,我们在图9中做了一个CM,这是基于ResNet18的全局-局部双流架构实现的。如图所示,最容易被错误分类的场景是公园、度假村、火车站、广场、学校和中心。所有这些都与大量的建筑物和植被覆盖密切相关。这些高度相似的特征和对象限制了对AID数据集的进一步改进,这一问题可以通过更深入、更复杂的特征表示来改善。

  (4)NWPU-RESISC45 数据集:NWPU-RESISC45是最大的场景分类遥感数据集,包含45个具有挑战性的场景。得益于大量的训练数据,NWPU-RESISC45的分类结果更加稳定和有说服力。我们在NWPU-RESISC45上进行消融实验,实验条件与前三组数据集相同,包括CNN基线、训练设置和ETr。
在这里插入图片描述

  我们在使用/不使用所提出的双流架构的情况下进行了对比实验,并将OA(%)与一些最先进的方法进行了比较,如表6所示。根据结果,我们基于googlenet的global-local双流架构在10%的训练率下获得第二名,在20%的训练率下获得第一名。目前最好的方法是ResNet18 + AM + CL[9],当训练比例从10%增加到20%时,OA增益为0.29%。然而,我们基于googlenet的双流架构的OA增益为2.54%,这表明随着训练样本的增加,我们的方法具有更好的OA潜力。

在这里插入图片描述

  我们基于resnet18的双流架构的CM如图10所示。最容易被误分类的样本多属于高速公路、教堂、火车站、工业区、宫殿、商业区、湿地、河流和中等住宅等类别。在这些场景类中有许多令人困惑的对象和特征,这限制了OA。

5. Conclusion

  在本文中,我们提出了一种SKAL策略来定位遥感场景图像中最重要的区域。在此基础上,进一步提出了一种能够分别提取全局和局部特征的全局-局部双流架构,用于遥感图像的场景分类。为了验证所提出的基于SKAL的全局-局部双流架构的有效性和鲁棒性,我们在四种常用的遥感场景数据集上,基于AlexNet、ResNet18和GoogleNet三种广泛使用的CNN模型进行了大量的对比实验,并获得了这些数据集的所有SOTA结果。实验结果表明,该方法具有较强的全局和局部特征联合表示能力,在一定程度上解决了遥感场景图像的大尺度变化问题。

> Task :app:packageDebug FAILED Execution failed for task ':app:packageDebug'. > A failure occurred while executing com.android.build.gradle.internal.tasks.Workers$ActionFacade > java.lang.OutOfMemoryError (no error message) * Try: Run with --info or --debug option to get more log output. Run with --scan to get full insights. * Exception is: org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:packageDebug'. at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$3.accept(ExecuteActionsTaskExecuter.java:166) at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$3.accept(ExecuteActionsTaskExecuter.java:163) at org.gradle.internal.Try$Failure.ifSuccessfulOrElse(Try.java:191) at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:156) at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:62) at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:108) at org.gradle.api.internal.tasks.execution.ResolveBeforeExecutionOutputsTaskExecuter.execute(ResolveBeforeExecutionOutputsTaskExecuter.java:67) at org.gradle.api.internal.tasks.execution.ResolveAfterPreviousExecutionStateTaskExecuter.execute(ResolveAfterPreviousExecutionStateTaskExecuter.java:46) at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:94) at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46) at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:95) at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57) at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:56) at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36) at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77) at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55) at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52) at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:416) at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:406) at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165) at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250) at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158) at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:102) at org.gradle.internal.operations.DelegatingBuildOperationExecutor.call(DelegatingBuildOperationExecutor.java:36) at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52) at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:43) at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:355) at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:343) at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:336) at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:322) at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker$1.execute(DefaultPlanExecutor.java:134) at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker$1.execute(DefaultPlanExecutor.java:129) at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:202) at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.executeNextNode(DefaultPlanExecutor.java:193) at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:129) at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64) at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48) at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56) Caused by: org.gradle.tooling.BuildException: 1 exception was raised by workers: A failure occurred while executing com.android.build.gradle.internal.tasks.Workers$ActionFacade at com.android.build.gradle.internal.scope.BuildElements$WorkersBasedScheduler$transform$2.call(BuildElements.kt:171) at com.android.build.gradle.internal.scope.BuildElements$WorkersBasedScheduler$transform$2.call(BuildElements.kt:135) at com.android.build.gradle.internal.scope.BuildElements$WorkersBasedScheduler.into(BuildElements.kt:144) at com.android.build.gradle.internal.scope.BuildElementActionScheduler.into(BuildElementActionScheduler.kt:38) at com.android.build.gradle.tasks.PackageAndroidArtifact.doTaskAction(PackageAndroidArtifact.java:420) at com.android.build.gradle.internal.tasks.NewIncrementalTask$taskAction$$inlined$recordTaskAction$1.invoke(AndroidVariantTask.kt:73) at com.android.build.gradle.internal.tasks.NewIncrementalTask$taskAction$$inlined$recordTaskAction$1.invoke(AndroidVariantTask.kt:34) at com.android.build.gradle.internal.tasks.Blocks.recordSpan(Blocks.java:91) at com.android.build.gradle.internal.tasks.NewIncrementalTask.taskAction(NewIncrementalTask.kt:34) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:103) at org.gradle.api.internal.project.taskfactory.IncrementalInputsTaskAction.doExecute(IncrementalInputsTaskAction.java:32) at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:42) at org.gradle.api.internal.project.taskfactory.AbstractIncrementalTaskAction.execute(AbstractIncrementalTaskAction.java:25) at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:28) at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$5.run(ExecuteActionsTaskExecuter.java:476) at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:402) at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:394) at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165) at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250) at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158) at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:92) at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31) at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:461) at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:444) at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.access$200(ExecuteActionsTaskExecuter.java:93) at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$TaskExecution.execute(ExecuteActionsTaskExecuter.java:237) at org.gradle.internal.execution.steps.ExecuteStep.lambda$execute$0(ExecuteStep.java:32) at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:32) at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:26) at org.gradle.internal.execution.steps.CleanupOutputsStep.execute(CleanupOutputsStep.java:58) at org.gradle.internal.execution.steps.CleanupOutputsStep.execute(CleanupOutputsStep.java:35) at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:48) at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:33) at org.gradle.internal.execution.steps.CancelExecutionStep.execute(CancelExecutionStep.java:39) at org.gradle.internal.execution.steps.TimeoutStep.executeWithoutTimeout(TimeoutStep.java:73) at org.gradle.internal.execution.steps.TimeoutStep.execute(TimeoutStep.java:54) at org.gradle.internal.execution.steps.CatchExceptionStep.execute(CatchExceptionStep.java:35) at org.gradle.internal.execution.steps.CreateOutputsStep.execute(CreateOutputsStep.java:51) at org.gradle.internal.execution.steps.SnapshotOutputsStep.execute(SnapshotOutputsStep.java:45) at org.gradle.internal.execution.steps.SnapshotOutputsStep.execute(SnapshotOutputsStep.java:31) at org.gradle.internal.execution.steps.CacheStep.executeWithoutCache(CacheStep.java:208) at org.gradle.internal.execution.steps.CacheStep.execute(CacheStep.java:70) at org.gradle.internal.execution.steps.CacheStep.execute(CacheStep.java:45) at org.gradle.internal.execution.steps.BroadcastChangingOutputsStep.execute(BroadcastChangingOutputsStep.java:49) at org.gradle.internal.execution.steps.StoreSnapshotsStep.execute(StoreSnapshotsStep.java:43) at org.gradle.internal.execution.steps.StoreSnapshotsStep.execute(StoreSnapshotsStep.java:32) at org.gradle.internal.execution.steps.RecordOutputsStep.execute(RecordOutputsStep.java:38) at org.gradle.internal.execution.steps.RecordOutputsStep.execute(RecordOutputsStep.java:24) at org.gradle.internal.execution.steps.SkipUpToDateStep.executeBecause(SkipUpToDateStep.java:96) at org.gradle.internal.execution.steps.SkipUpToDateStep.lambda$execute$0(SkipUpToDateStep.java:89) at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:54) at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:38) at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:76) at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:37) at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:36) at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:26) at org.gradle.internal.execution.steps.ResolveCachingStateStep.execute(ResolveCachingStateStep.java:90) at org.gradle.internal.execution.steps.ResolveCachingStateStep.execute(ResolveCachingStateStep.java:48) at org.gradle.internal.execution.steps.CaptureStateBeforeExecutionStep.execute(CaptureStateBeforeExecutionStep.java:69) at org.gradle.internal.execution.steps.CaptureStateBeforeExecutionStep.execute(CaptureStateBeforeExecutionStep.java:47) at org.gradle.internal.execution.impl.DefaultWorkExecutor.execute(DefaultWorkExecutor.java:33) at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:140) ... 34 more Caused by: com.android.ide.common.workers.WorkerExecutorException: 1 exception was raised by workers: A failure occurred while executing com.android.build.gradle.internal.tasks.Workers$ActionFacade at com.android.build.gradle.internal.tasks.Workers$WorkerExecutorAdapter.await(Workers.kt:297) at com.android.build.gradle.internal.scope.BuildElements$WorkersBasedScheduler$transform$2.call(BuildElements.kt:169) ... 98 more Caused by: org.gradle.workers.internal.DefaultWorkerExecutor$WorkExecutionException: A failure occurred while executing com.android.build.gradle.internal.tasks.Workers$ActionFacade at org.gradle.workers.internal.DefaultWorkerExecutor$WorkItemExecution.waitForCompletion(DefaultWorkerExecutor.java:375) at org.gradle.internal.work.DefaultAsyncWorkTracker.waitForItemsAndGatherFailures(DefaultAsyncWorkTracker.java:142) at org.gradle.internal.work.DefaultAsyncWorkTracker.waitForItemsAndGatherFailures(DefaultAsyncWorkTracker.java:90) at org.gradle.internal.work.DefaultAsyncWorkTracker.waitForAll(DefaultAsyncWorkTracker.java:80) at org.gradle.internal.work.DefaultAsyncWorkTracker.waitForCompletion(DefaultAsyncWorkTracker.java:68) at org.gradle.workers.internal.DefaultWorkerExecutor.await(DefaultWorkerExecutor.java:274) at com.android.build.gradle.internal.tasks.Workers$WorkerExecutorAdapter.await(Workers.kt:295) ... 99 more Caused by: java.lang.OutOfMemoryError at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at com.android.builder.internal.packaging.ApkFlinger.close(ApkFlinger.kt:215) at com.google.common.io.Closer.close(Closer.java:214) at com.android.builder.internal.packaging.IncrementalPackager.close(IncrementalPackager.java:406) at com.android.build.gradle.tasks.PackageAndroidArtifact.doTask(PackageAndroidArtifact.java:683) at com.android.build.gradle.tasks.PackageAndroidArtifact.access$300(PackageAndroidArtifact.java:136) at com.android.build.gradle.tasks.PackageAndroidArtifact$IncrementalSplitterRunnable.run(PackageAndroidArtifact.java:828) at com.android.build.gradle.internal.tasks.Workers$ActionFacade.run(Workers.kt:348) at org.gradle.workers.internal.AdapterWorkAction.execute(AdapterWorkAction.java:50) at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:47) at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1$1.create(NoIsolationWorkerFactory.java:65) at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1$1.create(NoIsolationWorkerFactory.java:61) at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:98) at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.execute(NoIsolationWorkerFactory.java:61) at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44) at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41) at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:416) at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:406) at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165) at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250) at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158) at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:102) at org.gradle.internal.operations.DelegatingBuildOperationExecutor.call(DelegatingBuildOperationExecutor.java:36) at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41) at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:56) at org.gradle.workers.internal.DefaultWorkerExecutor$3.call(DefaultWorkerExecutor.java:215) at org.gradle.workers.internal.DefaultWorkerExecutor$3.call(DefaultWorkerExecutor.java:210) at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:215) at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164) at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:131) ... 3 more Caused by: java.lang.OutOfMemoryError: Java heap space at com.android.zipflinger.NoCopyByteArrayOutputStream.<init>(NoCopyByteArrayOutputStream.java:27) at com.android.zipflinger.Compressor.deflate(Compressor.java:32) at com.android.zipflinger.BytesSource.build(BytesSource.java:83) at com.android.zipflinger.BytesSource.<init>(BytesSource.java:43) at com.android.builder.internal.packaging.ApkFlinger$writeFile$1.call(ApkFlinger.kt:173) at com.android.builder.internal.packaging.ApkFlinger$writeFile$1.call(ApkFlinger.kt:45) Execution failed for task ':app:packageDebug'. > A failure occurred while executing com.android.build.gradle.internal.tasks.Workers$ActionFacade > java.lang.OutOfMemoryError (no error message) * Try: Run with --info or --debug option to get more log output. Run with --scan to get full insights. * Exception is: org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:packageDebug'. at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$3.accept(ExecuteActionsTaskExecuter.java:166) at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$3.accept(ExecuteActionsTaskExecuter.java:163) at org.gradle.internal.Try$Failure.ifSuccessfulOrElse(Try.java:191) at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:156) at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:62) at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:108) at org.gradle.api.internal.tasks.execution.ResolveBeforeExecutionOutputsTaskExecuter.execute(ResolveBeforeExecutionOutputsTaskExecuter.java:67) at org.gradle.api.internal.tasks.execution.ResolveAfterPreviousExecutionStateTaskExecuter.execute(ResolveAfterPreviousExecutionStateTaskExecuter.java:46) at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:94) at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46) at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:95) at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57) at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:56) at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36) at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77) at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55) at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52) at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:416) at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:406) at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165) at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250) at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158) at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:102) at org.gradle.internal.operations.DelegatingBuildOperationExecutor.call(DelegatingBuildOperationExecutor.java:36) at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52) at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:43) at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:355) at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:343) at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:336) at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:322) at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker$1.execute(DefaultPlanExecutor.java:134) at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker$1.execute(DefaultPlanExecutor.java:129) at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:202) at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.executeNextNode(DefaultPlanExecutor.java:193) at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:129) at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64) at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48) at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56) Caused by: org.gradle.tooling.BuildException: 1 exception was raised by workers: A failure occurred while executing com.android.build.gradle.internal.tasks.Workers$ActionFacade at com.android.build.gradle.internal.scope.BuildElements$WorkersBasedScheduler$transform$2.call(BuildElements.kt:171) at com.android.build.gradle.internal.scope.BuildElements$WorkersBasedScheduler$transform$2.call(BuildElements.kt:135) at com.android.build.gradle.internal.scope.BuildElements$WorkersBasedScheduler.into(BuildElements.kt:144) at com.android.build.gradle.internal.scope.BuildElementActionScheduler.into(BuildElementActionScheduler.kt:38) at com.android.build.gradle.tasks.PackageAndroidArtifact.doTaskAction(PackageAndroidArtifact.java:420) at com.android.build.gradle.internal.tasks.NewIncrementalTask$taskAction$$inlined$recordTaskAction$1.invoke(AndroidVariantTask.kt:73) at com.android.build.gradle.internal.tasks.NewIncrementalTask$taskAction$$inlined$recordTaskAction$1.invoke(AndroidVariantTask.kt:34) at com.android.build.gradle.internal.tasks.Blocks.recordSpan(Blocks.java:91) at com.android.build.gradle.internal.tasks.NewIncrementalTask.taskAction(NewIncrementalTask.kt:34) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:103) at org.gradle.api.internal.project.taskfactory.IncrementalInputsTaskAction.doExecute(IncrementalInputsTaskAction.java:32) at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:42) at org.gradle.api.internal.project.taskfactory.AbstractIncrementalTaskAction.execute(AbstractIncrementalTaskAction.java:25) at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:28) at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$5.run(ExecuteActionsTaskExecuter.java:476) at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:402) at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:394) at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165) at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250) at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158) at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:92) at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31) at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:461) at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:444) at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.access$200(ExecuteActionsTaskExecuter.java:93) at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$TaskExecution.execute(ExecuteActionsTaskExecuter.java:237) at org.gradle.internal.execution.steps.ExecuteStep.lambda$execute$0(ExecuteStep.java:32) at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:32) at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:26) at org.gradle.internal.execution.steps.CleanupOutputsStep.execute(CleanupOutputsStep.java:58) at org.gradle.internal.execution.steps.CleanupOutputsStep.execute(CleanupOutputsStep.java:35) at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:48) at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:33) at org.gradle.internal.execution.steps.CancelExecutionStep.execute(CancelExecutionStep.java:39) at org.gradle.internal.execution.steps.TimeoutStep.executeWithoutTimeout(TimeoutStep.java:73) at org.gradle.internal.execution.steps.TimeoutStep.execute(TimeoutStep.java:54) at org.gradle.internal.execution.steps.CatchExceptionStep.execute(CatchExceptionStep.java:35) at org.gradle.internal.execution.steps.CreateOutputsStep.execute(CreateOutputsStep.java:51) at org.gradle.internal.execution.steps.SnapshotOutputsStep.execute(SnapshotOutputsStep.java:45) at org.gradle.internal.execution.steps.SnapshotOutputsStep.execute(SnapshotOutputsStep.java:31) at org.gradle.internal.execution.steps.CacheStep.executeWithoutCache(CacheStep.java:208) at org.gradle.internal.execution.steps.CacheStep.execute(CacheStep.java:70) at org.gradle.internal.execution.steps.CacheStep.execute(CacheStep.java:45) at org.gradle.internal.execution.steps.BroadcastChangingOutputsStep.execute(BroadcastChangingOutputsStep.java:49) at org.gradle.internal.execution.steps.StoreSnapshotsStep.execute(StoreSnapshotsStep.java:43) at org.gradle.internal.execution.steps.StoreSnapshotsStep.execute(StoreSnapshotsStep.java:32) at org.gradle.internal.execution.steps.RecordOutputsStep.execute(RecordOutputsStep.java:38) at org.gradle.internal.execution.steps.RecordOutputsStep.execute(RecordOutputsStep.java:24) at org.gradle.internal.execution.steps.SkipUpToDateStep.executeBecause(SkipUpToDateStep.java:96) at org.gradle.internal.execution.steps.SkipUpToDateStep.lambda$execute$0(SkipUpToDateStep.java:89) at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:54) at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:38) at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:76) at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:37) at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:36) at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:26) at org.gradle.internal.execution.steps.ResolveCachingStateStep.execute(ResolveCachingStateStep.java:90) at org.gradle.internal.execution.steps.ResolveCachingStateStep.execute(ResolveCachingStateStep.java:48) at org.gradle.internal.execution.steps.CaptureStateBeforeExecutionStep.execute(CaptureStateBeforeExecutionStep.java:69) at org.gradle.internal.execution.steps.CaptureStateBeforeExecutionStep.execute(CaptureStateBeforeExecutionStep.java:47) at org.gradle.internal.execution.impl.DefaultWorkExecutor.execute(DefaultWorkExecutor.java:33) at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:140) ... 34 more Caused by: com.android.ide.common.workers.WorkerExecutorException: 1 exception was raised by workers: A failure occurred while executing com.android.build.gradle.internal.tasks.Workers$ActionFacade at com.android.build.gradle.internal.tasks.Workers$WorkerExecutorAdapter.await(Workers.kt:297) at com.android.build.gradle.internal.scope.BuildElements$WorkersBasedScheduler$transform$2.call(BuildElements.kt:169) ... 98 more Caused by: org.gradle.workers.internal.DefaultWorkerExecutor$WorkExecutionException: A failure occurred while executing com.android.build.gradle.internal.tasks.Workers$ActionFacade at org.gradle.workers.internal.DefaultWorkerExecutor$WorkItemExecution.waitForCompletion(DefaultWorkerExecutor.java:375) at org.gradle.internal.work.DefaultAsyncWorkTracker.waitForItemsAndGatherFailures(DefaultAsyncWorkTracker.java:142) at org.gradle.internal.work.DefaultAsyncWorkTracker.waitForItemsAndGatherFailures(DefaultAsyncWorkTracker.java:90) at org.gradle.internal.work.DefaultAsyncWorkTracker.waitForAll(DefaultAsyncWorkTracker.java:80) at org.gradle.internal.work.DefaultAsyncWorkTracker.waitForCompletion(DefaultAsyncWorkTracker.java:68) at org.gradle.workers.internal.DefaultWorkerExecutor.await(DefaultWorkerExecutor.java:274) at com.android.build.gradle.internal.tasks.Workers$WorkerExecutorAdapter.await(Workers.kt:295) ... 99 more Caused by: java.lang.OutOfMemoryError at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at com.android.builder.internal.packaging.ApkFlinger.close(ApkFlinger.kt:215) at com.google.common.io.Closer.close(Closer.java:214) at com.android.builder.internal.packaging.IncrementalPackager.close(IncrementalPackager.java:406) at com.android.build.gradle.tasks.PackageAndroidArtifact.doTask(PackageAndroidArtifact.java:683) at com.android.build.gradle.tasks.PackageAndroidArtifact.access$300(PackageAndroidArtifact.java:136) at com.android.build.gradle.tasks.PackageAndroidArtifact$IncrementalSplitterRunnable.run(PackageAndroidArtifact.java:828) at com.android.build.gradle.internal.tasks.Workers$ActionFacade.run(Workers.kt:348) at org.gradle.workers.internal.AdapterWorkAction.execute(AdapterWorkAction.java:50) at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:47) at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1$1.create(NoIsolationWorkerFactory.java:65) at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1$1.create(NoIsolationWorkerFactory.java:61) at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:98) at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.execute(NoIsolationWorkerFactory.java:61) at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44) at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41) at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:416) at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:406) at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165) at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250) at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158) at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:102) at org.gradle.internal.operations.DelegatingBuildOperationExecutor.call(DelegatingBuildOperationExecutor.java:36) at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41) at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:56) at org.gradle.workers.internal.DefaultWorkerExecutor$3.call(DefaultWorkerExecutor.java:215) at org.gradle.workers.internal.DefaultWorkerExecutor$3.call(DefaultWorkerExecutor.java:210) at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:215) at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164) at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:131) ... 3 more Caused by: java.lang.OutOfMemoryError: Java heap space at com.android.zipflinger.NoCopyByteArrayOutputStream.<init>(NoCopyByteArrayOutputStream.java:27) at com.android.zipflinger.Compressor.deflate(Compressor.java:32) at com.android.zipflinger.BytesSource.build(BytesSource.java:83) at com.android.zipflinger.BytesSource.<init>(BytesSource.java:43) at com.android.builder.internal.packaging.ApkFlinger$writeFile$1.call(ApkFlinger.kt:173) at com.android.builder.internal.packaging.ApkFlinger$writeFile$1.call(ApkFlinger.kt:45) 翻译上述真机调试中Runapp的报错
最新发布
06-24
<think>我们遇到了一个Gradle构建失败的问题,具体是在`:app:packageDebug`任务中发生了内存溢出(OutOfMemoryError)。错误信息指出在尝试执行`com.android.build.gradle.internal.tasks.Workers$ActionFacade`时发生了OOM(Java堆空间不足)。###错误原因分析1.**错误类型**:`java.lang.OutOfMemoryError:Javaheapspace`2.**发生位置**:在构建APK的打包过程中,具体是在写入文件到APK时(`ApkFlinger`)发生。3.**根本原因**:Gradle守护进程(Daemon)分配的内存不足,无法完成APK打包任务。###解决方案####方案1:增加Gradle的堆内存在`gradle.properties`文件中增加内存设置:```properties#设置更大的堆内存(至少2GB)org.gradle.jvmargs=-Xmx2048m-XX:MaxPermSize=512m-XX:+HeapDumpOnOutOfMemoryError-Dfile.encoding=UTF-8```####方案2:启用Gradle的构建缓存在`gradle.properties`中启用构建缓存:```properties#启用构建缓存org.gradle.caching=true```####方案3:禁用并行执行和守护进程(临时)如果增加内存不起作用,可以尝试禁用并行执行:```properties#禁用并行执行org.gradle.parallel=false#禁用守护进程(如果问题持续)org.gradle.daemon=false```####方案4:清理构建缓存在AndroidStudio中执行:-`File`>`InvalidateCaches/Restart`>`InvalidateandRestart`或者在命令行执行:```bash./gradlewclean```####方案5:优化应用以减少方法数(如果方法数过多)如果应用的方法数超过65536,考虑启用multidex:在`app/build.gradle`中:```groovyandroid{defaultConfig{...multiDexEnabledtrue}}dependencies{implementation'androidx.multidex:multidex:2.0.1'}```####方案6:减少资源文件大小-移除未使用的资源:在`build.gradle`中启用资源清理:```groovyandroid{...buildTypes{release{shrinkResourcestrueminifyEnabledtrueproguardFilesgetDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro'}}}```###详细步骤(以增加内存为例)1.打开项目根目录下的`gradle.properties`文件(如果没有则创建)。2.添加或修改以下行:```properties#设置JVM内存参数org.gradle.jvmargs=-Xmx2048m-XX:MaxPermSize=512m-XX:+HeapDumpOnOutOfMemoryError-Dfile.encoding=UTF-8```3.保存文件并重新同步项目(点击AndroidStudio的Sync按钮)。4.重新运行构建。###其他建议-**更新Gradle版本**:确保使用最新版本的Gradle和AndroidGradle插件。-**检查大文件**:查看项目中是否有非常大的资源文件(如图片、视频等),考虑压缩或移除。-**使用分析工具**:使用AndroidStudio的内存分析工具监控构建过程中的内存使用情况。###错误日志关键部分翻译-`Executionfailedfortask':app:packageDebug'`:任务`:app:packageDebug`执行失败。-`Afailureoccurredwhileexecutingcom.android.build.gradle.internal.tasks.Workers$ActionFacade`:在执行`Workers$ActionFacade`时发生失败。-`java.lang.OutOfMemoryError(noerrormessage)`:发生了内存溢出错误(没有错误消息)。-`Causedby:java.lang.OutOfMemoryError:Javaheapspace`:原因:Java堆空间内存溢出。§§相关问题§§:1.如何优化Gradle构建性能?2.Android应用构建过程中常见的OOM问题有哪些解决方案?3.如何启用和配置Android的多dex处理?4.如何分析Android构建过程中的内存使用情况?</think>### 错误翻译与解析 #### 核心错误信息翻译 ``` Execution failed for task ':app:packageDebug' > A failure occurred while executing com.android.build.gradle.internal.tasks.Workers$ActionFacade > java.lang.OutOfMemoryError (no error message) 执行任务 ':app:packageDebug' 失败 > 在执行 com.android.build.gradle.internal.tasks.Workers$ActionFacade 时发生错误 > java.lang.OutOfMemoryError (内存溢出错误) ``` #### 详细错误原因翻译 ``` Caused by: java.lang.OutOfMemoryError: Java heap space at com.android.zipflinger.NoCopyByteArrayOutputStream.<init>(...) at com.android.zipflinger.Compressor.deflate(...) at com.android.zipflinger.BytesSource.build(...) at com.android.builder.internal.packaging.ApkFlinger$writeFile$1.call(...) 原因:Java 堆空间内存溢出 发生在:com.android.zipflinger.NoCopyByteArrayOutputStream 的构造函数 发生在:com.android.zipflinger.Compressor 的压缩操作 发生在:com.android.zipflinger.BytesSource 的构建过程 发生在:APK打包工具写入文件时 ``` ### 错误本质分析 这是一个典型的 **内存溢出(OutOfMemoryError)** 问题,具体发生在 Android 应用打包阶段(APK打包)。根本原因是 Gradle 构建过程中分配的内存不足,导致打包任务失败。 #### 关键点分析: 1. **错误类型**:`java.lang.OutOfMemoryError: Java heap space` - 表明 JVM 堆内存不足 2. **发生位置**:APK 打包过程 (`:app:packageDebug`) - 特别是使用 zipflinger 库进行文件压缩时 3. **常见触发场景**: - 项目包含大量资源文件(图片、音频等) - 启用了复杂的代码混淆(ProGuard/R8) - 使用了多APK分包(multi-APK) - 项目模块多且复杂 ### 解决方案 #### 方案1:增加Gradle内存分配(推荐) 在项目根目录的 `gradle.properties` 文件中添加以下配置: ```properties # 增加最大堆内存到 4GB org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=1024m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 # 启用Gradle守护进程 org.gradle.daemon=true # 启用并行构建 org.gradle.parallel=true # 启用构建缓存 org.gradle.caching=true ``` #### 方案2:优化构建配置 在 `app/build.gradle` 中添加以下配置: ```groovy android { // 启用资源缩减 buildTypes { debug { shrinkResources true minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } // 配置打包选项 packagingOptions { exclude 'META-INF/*' doNotStrip '**/*.so' } } // 禁用某些耗内存的任务 tasks.whenTaskAdded { task -> if (task.name.contains("Merge") && task.name.contains("Dex")) { task.enabled = false } } ``` #### 方案3:命令行构建增加内存参数 使用命令行构建时添加内存参数: ```bash ./gradlew clean assembleDebug -Dorg.gradle.jvmargs="-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError" ``` #### 方案4:调整Android Studio设置 1. 打开 `Help > Edit Custom VM Options` 2. 增加Studio内存设置: ```ini -Xms1024m -Xmx4096m -XX:ReservedCodeCacheSize=1024m -XX:+UseCompressedOops ``` #### 方案5:清理构建缓存 ```bash # 清理Gradle缓存 ./gradlew clean ./gradlew --stop # 删除缓存目录 rm -rf $HOME/.gradle/caches/ ``` ### 错误预防策略 1. **资源优化**: ```groovy android { aaptOptions { cruncherEnabled = false // 禁用PNG预处理 } } ``` 2. **模块化构建**: ```groovy // 在settings.gradle中启用按需配置 enableFeaturePreview('STABLE_PUBLISHING') enableFeaturePreview('IMPROVED_POM_SUPPORT') ``` 3. **分析内存使用**: ```bash # 生成内存分析报告 ./gradlew assembleDebug --profile --scan ``` 4. **使用最新构建工具**: ```gradle dependencies { classpath 'com.android.tools.build:gradle:7.0.0' // 使用最新版 } ``` ### 关键错误位置说明 | 错误组件 | 作用 | 优化建议 | |---------|------|----------| | `zipflinger` | Android APK打包工具 | 减少资源文件大小 | | `ApkFlinger` | APK文件生成器 | 启用资源缩减 | | `IncrementalPackager` | 增量打包器 | 清理构建缓存 | | `NoCopyByteArrayOutputStream` | 内存流处理 | 增加JVM堆内存 |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值