当前位置: 首页 > news >正文

鸿蒙系列-如何更好地使用 ArkUI 的 Image 组件?

如何使用好 ArkUI 的 Image 组件?

开发者经常需要在应用中显示一些图片,例如:按钮中的logo、网络图片、本地图片等。在应用中显示图片需要使用 Image 组件实现,Image支持多种图片格式,包括png、jpg、bmp、svg和gif,具体用法请参考Image组件。

ArkUI 的 Image组件类比SwiftUI中的Image,也就是UIKit中的UIImageView。

本文主要对 Image 如何展示图像做一些解读,然后对 Image组件一些特殊属性做分析,进而帮助开发者理解设置前后的效果,以及我们会尝试寻找何时使用这个属性最佳,进而给开发者提出参考方案。

一、整理潜在的优化点:

Image组件将图片显示到屏幕上分为三步:加载、解码、渲染:

在这里插入图片描述

一般情况下,我们直接调用组件操作的是加载这个步骤。因此,从渲染过程分析,考虑以下几个方面可能可以提升性能。

· 异步下载图片。

一般情况下,图片加载流程会异步进行,以避免阻塞主线程,影响UI交互。但是特定情况下,图片刷新时会出现闪烁,这时可以使用syncLoad属性,使图片同步加载,从而避免出现闪烁。

· 将image解码放到子线程。

以Image组件为例。当其显示在屏幕上时,需要Image作为数据源。 Image持有的数据是没有解码的压缩数据,能节省较多的内存和加快存储。 当image被赋值给Image时,图像数据会被解码,变成RGB的颜色数据。 解码是一个计算量较大的任务,且需要CPU来执行。

解码出来的图片体积与图片的宽高有关,与图片原来的体积无关。

图片解码是耗时操作,如果图片非常大,建议放到子线程解码。

在这里插入图片描述

图片解码可能会产生什么问题?

在上下滑动展示图片的过程中,我们会在lazyforeach的方法加载Image图片,相当于在主线程同时进行IO操作、解码等操作。这会造成内存迅速增长和CPU负载瞬间提升。 并且内存的迅速增加会触发系统的内存回收机制,尝试回收其他后台进程的内存,增加CPU的工作量。

如果系统无法提供足够的内存,则会先结束后台app进程,同时造成UI卡顿。

· 使用合适尺寸的图片,减少为了适配屏幕产生的bitmap计算。

使用图片资源管理工具,存储不同分辨率的图片,在不同分辨率的设备使用最适合的尺寸。如果图片是网络获取,可以通过传参的类型告诉服务端,服务端根据设备类型返回最合适尺寸的图片。

· 使用缓存 (内存缓存和磁盘缓存)

详细分析见下文《图片缓存》章节。

· 直接存储压缩后的图片,避免下次从使用缓存的时候再次解压缩。

· 减少内存级别的拷贝。(采用字节对齐)

在图像渲染时是通过一块一块渲染,因此数据是一块块地取,如果一块连续的内存数据里结尾的数据不是图像的内容,是内存里其他的数据,会影响读取效率。
块的大小和CPU cache有关,64位系统按64byte作为一块数据去读取和渲染,让图像数据对齐64byte就可以避免图形管理器再拷贝一份数据进行修补。

· 图片预下载。

提前将需要的图片下载到本地,并在CPU空闲的时候解压缩。

二、对 syncLoad 加载原理分析

我们发现聊天列表头像图片很小,加载很快,根据官方文档指示:在加载图片的耗时比较短的时候,通过异步加载的效果会大打折扣,建议配置 Image.syncLoad属性。

Image($r('app.media.icon')).syncLoad(true)

这个修改虽然很简单,按字面意思就是设置 同步/异步 加载,但我们想弄清楚的是什么时候使用这个特性才是合理的?

为了彻底搞明白,我们尝试阅读实现代码,从代码上看设置了这个值对组件有什么影响。

分析结果

结论先行,通过下面的分析我们可以得知,设置.syncLoad(true)这个值,产生的影响是:创建图片时是否创建一个异步任务,是否使用互斥锁。

而我们知道,创建异步任务和使用互斥锁也是有开销的,进而会影响内存和性能。

在这里插入图片描述

分析过程

· 由于我们使用的是Image组件,Image组件属于ArkUI,所以我们找到ArkUI的代码仓下载代码:https://gitee.com/openharmony/arkui_ace_engine

· 打开下载后的工程,找到对应Image的目录:

在这里插入图片描述

设置初始值

· 查看 image_pattern.h中属性的定义,其中省略了无关的代码:

namespace OHOS::Ace::NG {class ACE_EXPORT ImagePattern : public Pattern, public SelectionHost {DECLARE_ACE_TYPE(ImagePattern, Pattern, SelectionHost);public:...private:...bool syncLoad_ = false;bool isShow_ = true;ACE_DISALLOW_COPY_AND_MOVE(ImagePattern);
};} // namespace OHOS::Ace::NG#endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_IMAGE_IMAGE_PATTERN_H

通过 bool syncLoad_ = false; 我们知道了syncLoad的默认属性是false,如果不设置,图片加载就是异步的。

LoadImageDataIfNeed()

· 查看 image_pattern.cpp中的实现,其中省略了无关的代码:

void ImagePattern::ToJsonValue(std::unique_ptr<JsonValue>& json) const
{...json->Put("syncLoad", syncLoad_ ? "true" : "false");...
}

ToJsonValue这个方法通过将sync属性转换成json值,和我们目的无关,不需要细看。

找到另一个实现方法 LoadImageDataIfNeed 和图片加载强相关,我们粗略看一下整段代码:

void ImagePattern::LoadImageDataIfNeed()
{// 获得图片布局属性auto imageLayoutProperty = GetLayoutProperty<ImageLayoutProperty>();CHECK_NULL_VOID(imageLayoutProperty);// 获得图片绘制属性auto imageRenderProperty = GetPaintProperty<ImageRenderProperty>();CHECK_NULL_VOID(imageRenderProperty);auto src = imageLayoutProperty->GetImageSourceInfo().value_or(ImageSourceInfo(""));UpdateInternalResource(src);if (!loadingCtx_ || loadingCtx_->GetSourceInfo() != src) {LoadNotifier loadNotifier(CreateDataReadyCallback(), CreateLoadSuccessCallback(), CreateLoadFailCallback());loadingCtx_ = AceType::MakeRefPtr<ImageLoadingContext>(src, std::move(loadNotifier), syncLoad_);LOGI("start loading image %{public}s", src.ToString().c_str());loadingCtx_->LoadImageData();}if (loadingCtx_->NeedAlt() && imageLayoutProperty->GetAlt()) {auto altImageSourceInfo = imageLayoutProperty->GetAlt().value_or(ImageSourceInfo(""));LoadNotifier altLoadNotifier(CreateDataReadyCallbackForAlt(), CreateLoadSuccessCallbackForAlt(), nullptr);if (!altLoadingCtx_ || altLoadingCtx_->GetSourceInfo() != altImageSourceInfo ||(altLoadingCtx_ && altImageSourceInfo.IsSvg())) {altLoadingCtx_ = AceType::MakeRefPtr<ImageLoadingContext>(altImageSourceInfo, std::move(altLoadNotifier));altLoadingCtx_->LoadImageData();}}
}

其中重点部分:如果 loadingCtx_ 不存在 或者 loadingCtx_ 的图片地址和当前不一致时就会创建一个 RefPtr

	// 判断条件:如果 loadingCtx_ 不存在 或者 loadingCtx_ 的图片地址和当前不一致时if (!loadingCtx_ || loadingCtx_->GetSourceInfo() != src) {//  创建一个 loadingCtx_, syncLoad_ 是其中一个属性loadingCtx_ = AceType::MakeRefPtr<ImageLoadingContext>(src, std::move(loadNotifier), syncLoad_);loadingCtx_->LoadImageData();}

RefPtr

那么 loadingCtx_ 是一个什么东西呢?通过查看定义文件 image_pattern.h发现:

    RefPtr<ImageLoadingContext> loadingCtx_;

loadingCtx_ 是一个 RefPtr 类型的指针。

我们也可以在源码memery 下的 referenced.h 中找到 RefPtr 的定义,由于对我们分析图片加载影响不大,简单看一下,可以得知:

  1. RefPtr 使用引用计数管理实例
  2. 由于在一些场景需要隐式转换,所以在构造函数中移除了 explicit(explicit指定构造函数或转换函数 (C++11起)为显式, 即它不能用于隐式转换和复制初始化)
template<class T>
class RefPtr final {public:...private:...explicit RefPtr(T* rawPtr, bool forceIncRef = true) : rawPtr_(rawPtr){if (rawPtr_ != nullptr && forceIncRef) {// Increase strong reference count for holding instance.rawPtr_->IncRefCount();}}...
};

AceType::MakeRefPtr

再回到之前的调用代码 loadingCtx_ = AceType::MakeRefPtr<ImageLoadingContext>(src, std::move(loadNotifier), syncLoad_);,主要关注下使用到的 MakeRefPtr 函数,可以得知:

  1. Referenced::MakeRefPtr 是用于创建新实例的,而这个创建的新实例是继承于 Referenced的。
  2. 使用 RefPtr 管理指针。
template<class T, class... Args>
static RefPtr<T> MakeRefPtr(Args&&... args)
{return Claim(new T(std::forward<Args>(args)...));
}

我们发现 MakeRefPtr 这个函数核心是调用 Claim函数,所以我们需要找到 Claim函数:

template<class T>
static RefPtr<T> Claim(T* rawPtr)
{if (MemoryMonitor::IsEnable()) {MemoryMonitor::GetInstance().Update(rawPtr, static_cast<Referenced*>(rawPtr));}return RefPtr<T>(rawPtr);
}

通过代码可以得知,Claim通过内存监控管理器用 原始指针构建 RefPtr,而 syncLoad_ 是作为一个 std::forward<Args>(args)...)的一个参数被管理起来。

ImageLoadingContext()

· 知道了 syncLoad_ 是怎么被管理的之后,我们再看 syncLoad_ 怎么用就更容易理解了。
通过之前的指针类型定义 RefPtr<ImageLoadingContext> loadingCtx_;,我们可以找到 ImageLoadingContext 这个类,在cpp实现中找到了这个方法 OnDataLoading()

void ImageLoadingContext::OnDataLoading()
{if (auto obj = ImageProvider::QueryImageObjectFromCache(src_); obj) {DataReadyCallback(obj);return;}ImageProvider::CreateImageObject(src_, WeakClaim(this), syncLoad_);
}

可以发现是CreateImageObject() 这个方法创建了图片对象,并且使用了 syncLoad_ 这个参数作为创建时的初始值参数。所以我们再次在 image_provider.cpp这个文件中找到 CreateImageObject() 这个方法:

这个方法是重点,所以完整展示代码,并添加一些注释

CreateImageObject()

void ImageProvider::CreateImageObject(const ImageSourceInfo& src, const WeakPtr<ImageLoadingContext>& ctx, bool sync)
{if (!RegisterTask(src.GetKey(), ctx)) {// 如果任务已经在跑了,直接返回return;}if (sync) {// 如果是同步的,直接调用helper类创建CreateImageObjHelper(src, true);} else {// 如果是异步的,使用了一个互斥锁std::scoped_lock<std::mutex> lock(taskMtx_);// 创建一个可取消的任务CancelableCallback<void()> task;// 以src作为唯一键值绑定任务task.Reset([src] { ImageProvider::CreateImageObjHelper(src); });tasks_[src.GetKey()].bgTask_ = task;// 放到后台去执行任务ImageUtils::PostToBg(task);}
}

image_utils.cpp

void ImageUtils::PostToBg(std::function<void()>&& task)
{CHECK_NULL_VOID(task);ImageUtils::PostTask(std::move(task), TaskExecutor::TaskType::BACKGROUND, "BACKGROUND");
}
void ImageUtils::PostTask(std::function<void()>&& task, TaskExecutor::TaskType taskType, const char* taskTypeName)
{auto taskExecutor = Container::CurrentTaskExecutor();if (!taskExecutor) {LOGE("taskExecutor is null when try post task to %{public}s", taskTypeName);return;}taskExecutor->PostTask([task, id = Container::CurrentId()] {ContainerScope scope(id);CHECK_NULL_VOID(task);task();},taskType);
}
/*** Post a task to the specified thread.** @param task Task which need execution.* @param type FrontendType of task, used to specify the thread.* @return Returns 'true' whether task has been post successfully.*/
bool PostTask(Task&& task, TaskType type) const
{return PostDelayedTask(std::move(task), type, 0);
}

mock_image_utils.cpp

void ImageUtils::PostToBg(std::function<void()>&& task)
{// mock bg thread poolif (g_threads.size() > MAX_THREADS) {return;}g_threads.emplace_back(std::thread(task));
}

emplace_back() 函数在原理上比 push_back() 有了一定的改进,包括在内存优化方面和运行效率方面。内存优化主要体现在使用了就地构造(直接在容器内构造对象,不用拷贝一个复制品再使用)+强制类型转换的方法来实现,在运行效率方面,由于省去了拷贝构造过程,因此也有一定的提升。

~ImageLoadingContext()

有创建就有销毁,同样我们在析构函数中也找到响应证据,如果是异步的,就会在析构函数中调用CancelTask 取消任务:

ImageLoadingContext::~ImageLoadingContext()
{// 取消后台任务if (!syncLoad_) {auto state = stateManager_->GetCurrentState();if (state == ImageLoadingState::DATA_LOADING) {// 取消 CreateImgObj 任务ImageProvider::CancelTask(src_.GetKey(), WeakClaim(this));} else if (state == ImageLoadingState::MAKE_CANVAS_IMAGE) {// 取消 MakeCanvasImage 任务if (InstanceOf<StaticImageObject>(imageObj_)) {ImageProvider::CancelTask(canvasKey_, WeakClaim(this));}}}
}

总结:综上分析,我们知道了设置了.syncLoad(true)这个值后,创建图片时就不会创建一个异步任务,而我们知道,创建异步任务和互斥锁也是有开销的,会影响内存和性能,所以是否使用这个属性取决于 空间和时间 的取舍,关键在于这个阈值是在哪里。为了找出多大的图片使用 syncLoad 更好,我们做了如下测试:

对比测试

todo。。。

三、objectFit、autoResize 属性对 Image 组件性能的影响

todo

四、图片缓存

ArkUI的图片缓存策略以及我们建议的图片缓存策略:
todo

相关文章:

鸿蒙系列-如何更好地使用 ArkUI 的 Image 组件?

如何使用好 ArkUI 的 Image 组件&#xff1f; 开发者经常需要在应用中显示一些图片&#xff0c;例如&#xff1a;按钮中的logo、网络图片、本地图片等。在应用中显示图片需要使用 Image 组件实现&#xff0c;Image支持多种图片格式&#xff0c;包括png、jpg、bmp、svg和gif&am…...

机器学习简介[01/2]:简单线性回归

Python 中的机器学习简介&#xff1a;简单线性回归 一、说明 简单线性回归为机器学习提供了优雅的介绍。它可用于标识自变量和因变量之间的关系。使用梯度下降&#xff0c;可以训练基本模型以拟合一组点以供未来预测。 二、技术背景 这是涵盖回归、梯度下降、分类和机器学习的其…...

Kubernetes技术--k8s核心技术yaml资源编排

(1).引入 我们可以使用kubectl实现单行指令的操作,但是这样做的坏处是不复用,所以为了更好的实现对一系列资源的编排工作。kuberntes中使用一种叫做资源清单文件(yaml)来实现对资源管理和资源对象编排部署。 (2).概述 yaml是一种标记语言。为了强调这种语言以数据做为中心,而…...

clickhouse-配置解释

详细内容看官网文档 一、全局服务配置 1.配置详解 名称含义默认值allow_use_jemalloc_memory允许使用 jemalloc 内存1&#xff08;布尔&#xff09;asynchronous_heavy_metrics_update_period_s更新异步指标的时间段&#xff08;以秒为单位&#xff09;120asynchronous_metr…...

基于亚马逊云科技无服务器服务快速搭建电商平台——性能篇

使用 Serverless 构建独立站的优势 在传统架构模式下&#xff0c;如果需要进行电商大促需要提前预置计算资源以支撑高并发访问&#xff0c;会造成计算资源浪费并且增加运维工作量。本文介绍一种新的部署方式&#xff0c;将 WordPress 和 WooCommerce 部署在 Amazon Lambda 中。…...

LINQ详解(查询表达式)

什么是LINQ&#xff1f; LINQ(语言集成查询)是将查询功能直接集成到C#中。数据查询表示简单的字符串&#xff0c;在编译时不会进行类型检查和IntelliSense(代码补全辅助工具)支持。 在开发中&#xff0c;通常需要对不同类型的数据源了解不同的查询语句&#xff0c;如SQL数据库…...

【DEVOPS】现状篇

0. 目录 1. 前言2. 现状2.1 需求管理2.2 开发流程2.3 测试流程2.4 部署流程2.5 维护阶段 3. 后记4. 相关 1. 前言 一直以来&#xff0c;深感内部工程化能力欠缺&#xff0c;急于将事情向前推进&#xff0c;总是希望能够向前走几步&#xff0c;再走几步。 可惜的是&#xff0c…...

Linux文件管理知识:查找文件(第二篇)

Linux文件管理知识:查找文件&#xff08;第二篇&#xff09; 上篇文章详细介绍了linux系统中查找文件的工具或者命令程序locate和find命令的基本操作。那么&#xff0c;今天这篇文章紧接着查找文件相关操作内容介绍。 Find命令所属操作列表中的条目&#xff0c;有助于我们想要…...

医疗小程序:让服务更高效,用户体验更优化

随着移动互联网的快速发展&#xff0c;小程序已经成为了一个热门的开发方向。医疗健康类小程序也不例外&#xff0c;拥有广泛的市场需求和前景。本文将为你提供一份完整的医疗健康类小程序开发攻略&#xff0c;帮助你快速开发上线一个专业成熟的小程序商城。 一、选择合适的小程…...

C++11 std::transform函数使用说明

std::transform是C标准库中的一个算法&#xff0c;它用于对输入范围内的元素进行操作&#xff0c;并将结果存储在输出范围内。这个算法特别适合于将一种数据类型转换为另一种数据类型。 函数定义在头文件algorithm中 std::transform的基本语法如下&#xff1a; std::transfor…...

JavaScript-DOM查询

获取元素节点 获取元素节点的子节点 元素节点的属性 节点的修改 JavaScript中的DOM&#xff08;文档对象模型&#xff09;是一种编程接口&#xff0c;它允许JavaScript与HTML文档交互。创建DOM查询&#xff0c;可以使用多种方法. 获取元素节点 1. getElementById() – 通…...

大数据-玩转数据-Flink 水印

一、Flink 中的水印 在Flink的流式操作中, 会涉及不同的时间概念&#xff1a; 1.1 处理时间 是指的执行操作的各个设备的时间&#xff0c;对于运行在处理时间上的流程序, 所有的基于时间的操作(比如时间窗口)都是使用的设备时钟。比如, 一个长度为1个小时的窗口将会包含设备…...

【Apollo】阿波罗自动驾驶系统:驶向未来的智能出行(含源码安装)

前言 Apollo (阿波罗)是一个开放的、完整的、安全的平台&#xff0c;将帮助汽车行业及自动驾驶领域的合作伙伴结合车辆和硬件系统&#xff0c;快速搭建一套属于自己的自动驾驶系统。 开放能力、共享资源、加速创新、持续共赢是 Apollo 开放平台的口号。百度把自己所拥有的强大、…...

网络-Netty

how pipeline.addLast(ChannelHandler)...

如何使用vue-smooth-dnd

Vue Smooth DnD是一个基于Vue的平滑易用的拖放库。它提供了简单易用的API和可自定义的样式。 要使用Vue Smooth DnD&#xff0c;可以按照以下步骤进行操作&#xff1a; 安装Vue Smooth DnD npm install vue-smooth-dnd --save 在组件中引入Vue Smooth DnD import VueSmoot…...

为AWS认证做好准备:一份全面的备考指南

随着云计算的快速发展&#xff0c;越来越多的专业人士选择获取AWS&#xff08;亚马逊网络服务&#xff09;认证。这个认证不仅可以证明你对AWS的理解和专业技能&#xff0c;还有助于你在云计算领域获得更好的工作机会。 以下是一份全面的备考指南&#xff0c;帮助你为AWS认证做…...

尚硅谷SpringMVC

九、HttpMessageConverter...

django的简易的图书管理系统jsp书店进销存源代码MySQL

本项目为前几天收费帮学妹做的一个项目&#xff0c;Java EE JSP项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、项目描述 django的简易的图书管理系统 系统有1权限&#xff1a…...

力扣125. 验证回文串

125. 验证回文串 如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后&#xff0c;短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。 字母和数字都属于字母数字字符。 给你一个字符串 s&#xff0c;如果它是 回文串 &#xff0c;返回 true &…...

用WebStorm创建Mock数据

WebStorm是一款强大的集成式开发环境&#xff0c;它集成了许多实用的功能&#xff0c;包括Mock数据的创建。 下面是用WebStorm创建Mock数据的步骤&#xff1a; 打开WebStorm&#xff0c;选择一个项目或新建一个项目&#xff1b;在项目中创建一个名为“mock”的文件夹&#xf…...

React Native在HarmonyOS 5.0阅读类应用开发中的实践

一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强&#xff0c;React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 &#xff08;1&#xff09;使用React Native…...

srs linux

下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935&#xff0c;SRS管理页面端口是8080&#xff0c;可…...

Keil 中设置 STM32 Flash 和 RAM 地址详解

文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程

STM32F1 本教程使用零知标准板&#xff08;STM32F103RBT6&#xff09;通过I2C驱动ICM20948九轴传感器&#xff0c;实现姿态解算&#xff0c;并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化&#xff0c;适合嵌入式及物联网开发者。在基础驱动上新增…...

comfyui 工作流中 图生视频 如何增加视频的长度到5秒

comfyUI 工作流怎么可以生成更长的视频。除了硬件显存要求之外还有别的方法吗&#xff1f; 在ComfyUI中实现图生视频并延长到5秒&#xff0c;需要结合多个扩展和技巧。以下是完整解决方案&#xff1a; 核心工作流配置&#xff08;24fps下5秒120帧&#xff09; #mermaid-svg-yP…...

Kubernetes 节点自动伸缩(Cluster Autoscaler)原理与实践

在 Kubernetes 集群中&#xff0c;如何在保障应用高可用的同时有效地管理资源&#xff0c;一直是运维人员和开发者关注的重点。随着微服务架构的普及&#xff0c;集群内各个服务的负载波动日趋明显&#xff0c;传统的手动扩缩容方式已无法满足实时性和弹性需求。 Cluster Auto…...

二叉树-144.二叉树的前序遍历-力扣(LeetCode)

一、题目解析 对于递归方法的前序遍历十分简单&#xff0c;但对于一位合格的程序猿而言&#xff0c;需要掌握将递归转化为非递归的能力&#xff0c;毕竟递归调用的时候会调用大量的栈帧&#xff0c;存在栈溢出风险。 二、算法原理 递归调用本质是系统建立栈帧&#xff0c;而非…...

Linux 内存管理调试分析:ftrace、perf、crash 的系统化使用

Linux 内存管理调试分析&#xff1a;ftrace、perf、crash 的系统化使用 Linux 内核内存管理是构成整个内核性能和系统稳定性的基础&#xff0c;但这一子系统结构复杂&#xff0c;常常有设置失败、性能展示不良、OOM 杀进程等问题。要分析这些问题&#xff0c;需要一套工具化、…...

ubuntu清理垃圾

windows和ubuntu 双系统&#xff0c;ubuntu 150GB&#xff0c;开发用&#xff0c;基本不装太多软件。但是磁盘基本用完。 1、查看home目录 sudo du -h -d 1 $HOME | grep -v K 上面的命令查看$HOME一级目录大小&#xff0c;发现 .cache 有26GB&#xff0c;.local 有几个GB&am…...

【笔记】结合 Conda任意创建和配置不同 Python 版本的双轨隔离的 Poetry 虚拟环境

如何结合 Conda 任意创建和配置不同 Python 版本的双轨隔离的Poetry 虚拟环境&#xff1f; 在 Python 开发中&#xff0c;为不同项目配置独立且适配的虚拟环境至关重要。结合 Conda 和 Poetry 工具&#xff0c;能高效创建不同 Python 版本的 Poetry 虚拟环境&#xff0c;接下来…...