Low Memory Killer in Android

目录

低内存管理(Linux vs Android)

Linux内存回收

shrink_slab原理

shrink_zone原理

oom killer

oom killer设计原则

OOM killer具体实现

android的lmk(Low Memory Killer)

Android系统特点

oom killer在android中的不足

​​​​​​​LMK概述

LMK提供的接口

接口说明

​​​​​​​minfree与adj初始化

LMK实现

​​​​​​​LMK驱动实现

​​​​​​​android进程管理

​​​​​​​Android组件

​​​​​​​Android进程生命周期

进程adj调整

adj值

​​​​​​​adj调整原则

​​​​​​​adj调整时机

​​​​​​​adj调整算法

如何降低被kill概率


低内存管理(Linux vs Android)

Linux内存回收

低内存情况下,Linux内存回收有二种模式:一种是直接内存回收,通过伙伴系统分配一大块内存或者需要创建一个很大的缓冲区时,如果没有足够多的free pages,那么系统会尽快进行页面回收;另一种是定期内存回收,定期唤醒kswapd内核线程,当系统空闲内存小于阈值时则进行页面回收。具体的系统调用过程,请参考图1所示。

由图1可知,不管是直接页面回收,还是周期页面回收,最终都调用shrink_slab()和shrink_zone()。直接页面回收,在一定的约束下,如果最终没能释放所需page,则调用OOM(out of memory) killer。

shrink_slab原理

先向操作系统内核注册shrinker函数,会在内存较少时主动释放一些内存。shrink_slab()会遍历shrinker链表,回调所有注册了shrinker函数的处理内存的操作。当前,linux操作系统中主要的shrinker函数有:

  1. shrink_dcache_memory():负责dentry缓存。
  2. shrink_icache_memory():负责inode缓存。
  3. mb_cache_shrink_fn():负责用于文件系统元数据的缓存。

shrink_zone原理

shrink_zone()的主要工作可以分为三步:

  1. 通过shrink_active_list()将页面从active移到inactive list;
  2. 通过shrink_inactive_list()将inactive list的页放入临时链表;
  3. 最终调用shrink_page_list()回收页。回收算法如图2所示。

           图1 内存回收系统调用

            图2 回收页算法

oom killer

Oom killer是kernel在内存耗尽时的最后手段,使用oom_badness(),为每个进程计算得分,移除得分高的进程。

系统调用如下:

out_of_memory()->select_bad_process->oom_badness()

OOM killer计算进程得分的策略是:损失最少的工作,释放最大的内存同时不伤及无辜,并且杀掉的进程数尽量少。

oom killer设计原则

OOM killer简单粗暴地作风并不符合linux的风格,并且也不符合linux机制与策略分离的原则。然而当系统内存不足时,已经是将死之态,也无需讲究,暴力往往是最有效的解决方案。虽然粗暴,但是手段上也需要尽可能地“精致”,oom killer从提出到现在,也已经进行了几次优化,目前的设计遵循下面几方面:

  • 以进程所占物理内存作为判断依据

  进程实际所需的是物理内存,而早期以进程的虚拟地址空间大小为基准,显然不准确。

  • 可配置的用户建议策略

有些进程占用物理内存很大,但是也在做很重要的事情,如KDE桌面主进程。因此必须将一部分控制权交出给用户层,用户可以根据具体情况,对进程重要度加权。

  • 简单并且合理的默认策略

  将特权进程,免于oom killer机制;如果按现有选择策略,无法选择出需要被结束的进程,那么就直接panic,因为已经无能为力;另外,杀子不杀父,因为如果kill父进程,还需要为子进程托孤,需要做很多事情,而这会加重内存不足的情况,况且父进程往往是关键服务,而子进程往往是工作都线程,kill父进程对用户的影响更大。

OOM killer具体实现

  • 提供给用户层的接口:  /proc/<pid>/oom_adj & /proc/<pid>/oom_score_adj

    oom_adj是以前的接口,为了兼容低版本,现在还保留此接口,但是最好不要再使用了。

    oom_score_adj的值会影响各个进程的最终得分,范围是-1000(OOM_SCORE_ADJ_MIN)~1000(OOM_SCORE_ADJ_MAX)。用户空间可以调整进程的oom_score_adj值,来影响oom killer的行为,值越大,进程越容易被kill,-1000可以关闭oom killer对此进程的作用。

  • 算法

oom_badness是oom killer选择“bad”进程的核心算法,流程图如图3所示。

         图3 oom_badness流程图

android的lmk(Low Memory Killer)

 Linux 已经有oom killer了,那android为何又要引入LMK呢?

Android系统特点

Android是嵌入式系统,通常运行在内存很有限的设备上(如手机,平板)。这类设备,有一个特点就是“屏幕独占性”,即要调出一个任务,意味着必须退出或隐藏当前的任务。另外,此类设备同时运行的任务不会太多,并且影响用户使用体验的任务比较好识别。除此之外,“高交互”的系统特征,决定必须提高系统响应速度,因为这直接影响用户的使用体验。而在嵌入式系统中,资源相当有限,如何在低配置设备中,提高响应速度,是android需要重点考虑的。

Android使用的特点,决定设计的方案:

  1. Android进程并不主动退出,而是作为“空进程”保留在内存中,以便用户再次进入该应用时,可以提高响应速度;
  2. 因为进程不主动退出,必须有一套机制能实时按一定的策略回收内存;
  3. Android进程的重要度,随着用户操作的改变而改变,即实时改变;

oom killer在android中的不足

  • 启动时机不合适

OOM killer是在内存被耗尽时,启动的极端内存回收机制。Android中用户对系统反应快慢非常敏感。别说在内存被耗尽时,就是在内存不足时,系统出现,反应延迟,也会严重影响用户体验。再加上android进程的“不主动退出”机制,所以需要周期检查,进行内存管理。

  • 选择进程策略不合适

OOM killer根据进程所占物理内存为主要判断依据,加上oom_score_adj和其它一些因素调整。在android中,物理内存占用多少,不能成为其主要判断依据,而应该根据进程的重要程度,在相同重要度的情况下,才考虑内存因素。进程的重要度是相对于用户而言的。

  • 选择进程范围不合适

OOM killer候选的进程是除init进程,内核线程以及一些特权进程外的所有进程。而android只需要管理zygote启动之后的进程,之前的native进程都不需要考虑。

  • 用户层控制权的不合适

OOM killer提供了oom_score_adj的接口,程序可以自己设置值,降低OOM时的得分。一般而言,这个值不会频繁去变更。这对于android系统特点3)并不适用。在android系统中,用户层需要大部分的控制权限,并且能够根据用户操作实时变更进程的重要度。

​​​​​​​LMK概述

由于android有自己的应用场景,而OOM killer并不能满足其要求,因此引入了LMK机制。主要解决android中“进程不主动退出”机制所引起的内存回收问题。

LMK复用了linux低内存管理中的shrink_slab机制,将lmk注册到shrinker键表中,那么linux中无论是直接回收还是定期回收内存,都可以调到lmk的处理。

LMK还复用了用户层控制接口(/proc/<pid>/oom_score_adj),并将其作为选择进程的主要依据。FW层的AMS作为类似于“任务管理器”,根据用户行为管理进程。

LMK的整体结构如图4所示。

              图4 LMK整体结构

LMK提供的接口

接口说明

LMK将剩余内存分为几个等级,最多支持6个等级,分级的策略则交与用户层制定,其值写入以下文件,作为LMK驱动的参数。minfree值以页为单位(一页为4K)。

LMK按重要度(adj)为进程分组,最多支持6组,组数一般都设成与minfree对应。与minfree一样,分组的策略也由用户层制定,并将值写入以下文件,作为LMK驱动的参数。

这里需要注意,前面OOM killer中已经介绍过,adj是低版本的接口,现在已经换成oom_score_adj。但是由于android上层一直使用旧接口,修改上层太麻烦,因此android用户层仍然继续使用adj接口,只是在kernel层将adj自动转换成oom_score_adj。

上面二个接口,就是用户层制定的,即在什么的内存情况下,LMK开始工作,并且以什么标准工作。表1配置,当内存剩余6656*4K=26M时,LMK将kill所有 adj为9及以上的进程。

Minfree

3072

4096

4608

6656

8704

10752

adj

0

1

3

9

11

15

          表1 mk配置​​​​​​​

​​​​​​​minfree与adj初始化

Android原生对minfree和adj的初始化,放在ProcessList:updateOomLevels()中。系统默认adj配置如下所示:

  系统给minfree给了二个标准配置,mOomMinFreeLow与mOomMinFreeHigh​​​​​​​

然后根据内存屏幕大小,决定用lowminfree还是highminfree。内存在300~700M之间用lowminfree;内存在700M及以上,用highminfree;屏幕在480*800~1280*800之间用lowminfree;屏幕1280*800及以下,用highminfree。内存与屏幕,只要有一个需要用highminfree ,最终就用highminfree。

除此之外,如果是64bit设备,minfree*1.5。还可以根据设备配置config:

对minfree微调,adj与minfree的值,最终调用lmkd写入module/lowmemorykiller/parameters/,

作为参数传入lowmemorykiller驱动。

LMK实现

    LMK整套机制,可以分为二部分,一个是LMK驱动,一个是AM对进程的管理。

​​​​​​​LMK驱动实现

前面已经提到,LMK是通过shrinker机制,将自己整合进kernel。在内存不足或者定期检查内存时,都会通过shrink_slab,回调lmk操作。Lowmem_shrink()就是lmk驱动的核心。具体操作流程参考图5。

          图5 lowmem_shrink流程图

​​​​​​​android进程管理

Lmk驱动只是机制,实现非常简单。Android运行时的进程管理才是LMK的复杂点,因为需要在运行时实时为每个进程打分(设置adj)。

​​​​​​​Android组件

Android中,进程是由组件组成,每个组件都扮演不同的角色。组件的不同状态决定进程的adj。四大组件分别是activity,services,Broadcast,Content Providers。

1.Activity

   Activity是屏幕上单独的虚拟UI。通常,activity是应用与用户交互的主要组件。在activity的生命周期中,有create,running,pause,stop,destroy之些状态,当在running状态时,表示用户正在操作这个activity。

          图6 Activity生命周期

2.Services

Services通常是在后台运行的组件,但是也可以在前台启动。应用一旦在后台启动service,即使用户切换到其它应用,service还会在后台运行。服务没有用户交互界面。

         图7 service生命周期

3.Content Providers

Content Providers为不同的应用提供内容(数据),支持在文件或数据库中存储结构化数据。其它应用可以通过content resolver访问数据。

4.Broadcast Receiver

可以接收系统范围的广播。应用可以发起广播信息给另外一个应用,如文件下载已经完成等。它没有任何用户界面,但是会在status bar上形成注意信息。

​​​​​​​Android进程生命周期

Android系统试图尽可能地保持应用进程,但是最终需要为新的或者更重要的进程回收内存,而移除老的进程。为了决定kill哪个进程,系统根据进程中运行的组件和这些组件的状态,给每个进程一个重要度。系统总是先kill最不重要的进程,来回收系统资源。

重要度分为5个level:(the first is most important and is killed last)

1.Foreground process(前台进程)

用户正在操作的进程。下面任何一个条件成立,都可以认为是前台进程。

  1. 包含用户正在交互的Activity(Activity的onResume()已经被执行);
  2. 包含Service,并且这个Service绑定于用户正在交互的activity;
  3. 包含Service,并且这个Service运行在前台-Service已经执行startForeground();
  4. 包含Service,并且这个Service正在执行其生命周期的callbacks(onCreate(),onStart(),or onDestroy());
  5. 包启BroadcastReceiver,并且其正在执行onReceive()方法。

在给定的任一时间,通常只有小部分Foreground process(前台进程)。

2.Visible process(可见进程)

没有任何前台组件,但是用户在屏幕上仍然可以看到。下面任何一个条件成立,都可以认为是可见进程。

  1. 包含一个不在前台Activity,但是用户仍可以看到(其onPause()方法已经被执行);这可能发生,例如,前台activity起了一个dialog,此时后台的activity不在前台,但是用户仍能看到。
  2. 包含一个Service,并且此Service绑定到visible/foreground activity。

3.Service process(服务进程)

运行着Service但不属于前台和可见的进程,且此service已经执行过startService()。尽管服务进程与用户可见的无关,但是却正在做用户关心的事(如正在后台播放音乐或者从网上下载数据),因此系统会尽可能地保持他们运行。

4.Background process(后台进程)

包含一个activity,当前对用户不可见(activity的onStop()已经被执行)。这些进程不会直接影响用户体验,系统会为前台/可见/服务进程回收内存,随时会kill他们。通常会有很多后台进程在运行,他们被保存在LRU(least recently used)列表中,确保用户最后使用的最后被kill。如果一个activity正确地实现了其生命周期方法,并且保存了其正确地状态,那么它被kill,就不会影响用户体验,因为当用户回到这个activity时,由于其已经保存了其状态,用户看到地还是原来的。

5.Empty process(空进程)

不包含任何存活应用组件的进程。保存这些进程,纯粹就是为了缓存,方便下次组件运行在此进程时,加速启动时间。系统为了在进程缓存和kernel缓存间平衡系统资源,总是会kill这些进程。

进程adj调整

adj值

  adj的有效范围从-17~15,各值所代表的意思,如表2所示:

adj

意思

UNKNOWN_ADJ

16

实现需要,一般不会给进程设置,只做为临时值过渡

CACHED_APP_MAX_ADJ

15

进程不可见,并且只包启activity组件

CACHED_APP_MIN_ADJ

9

进程不可见,并且只包含activity组件

SERVICE_B_ADJ

8

包含service组件的进程分成二组,比起A,B组中的service相对比较陈旧,对用户影响小。

PREVIOUS_APP_ADJ

7

用户使用的前一个应用的进程。提高此进程优先级,是因为按back键返回到前一个应用的操作非常普通。

HOME_APP_ADJ

6

Home进程

SERVICE_ADJ

5

含serivce组件的进程

HEAVY_WEIGHT_APP_ADJ

4

重量级应用的进程

BACKUP_APP_ADJ

3

正在备份操作的进程

PERCEPTIBLE_APP_ADJ

2

进程含有用户可感知的组件,如后台音乐播放器

VISIBLE_APP_ADJ

1

进程含有用户可见的activity

FOREGROUND_APP_ADJ

0

前台进程

PERSISTENT_SERVICE_ADJ

-11

系统进程或长驻进程绑定的进程

PERSISTENT_PROC_ADJ

-12

系统长驻进程,如电话。

SYSTEM_ADJ

-16

系统进程,默认设为-16

NATIVE_ADJ

-17

系统不管的native进程,统一设为-17

​​​​​​​adj调整原则

Android基于进程中存活着组件的重要度,尽可能地提高进程的重要水平。例如,一个进程既有一个service,又有一个visible activity,那么进程会被标为可见进程,而非服务进程。

另外,如果其它进程依赖一个进程,那么这个进程的重要度也可能提高-提供服务的进程,其重要度不能低于其服务的进程。例如:进程A为进程B提供content provider,或者进程A的Service绑定于进程B的组件,那么进程A的重要度至少等同于进程B。

因为运行service的进程,其重要度比运行后台activity的进程高,所以如果activity需要长时间的操作(尤其这个操作比activity存活时间长),那么启动service比简单的创建工作线程,效果好。例如,网页上上传图片的activity,应该启动service来执行上传操作,这样即使用户leave这个activity,上传操作仍旧会在后台继续。使用service,其操作的优先级至少是“服务进程”,而不需要关心activity。同理,broadcast receivers也推荐使用service,而非将耗时操作放在线程里。

​​​​​​​adj调整时机

  只要组件状态发生变化,就会进行adj调整,具体看下面列出的各个函数。

  • Serivce状态变化

        bindServiceLocked

        unbindServiceLocked

        realStartServiceLocked

        sendServiceArgsLocked

        bringDownServiceLocked

        removeConnectionLocked

        serviceDoneExecutingLocked

  • Contentprovider处理

        getContentProviderImpl

        removeContentProvider

        removeContentProviderExternalUnchecked

        publishContentProviders

  • Broadcast处理

        processCurBroadcastLocked

        deliverToRegisteredReceiverLocked

        processNextBroadcast

  • Activity状态变更

        realStartActivityLocked

        resumeTopActivityInnerLocked

        finishSubActivityLocked

        finishVoiceTask

        finishCurrentActivityLocked

        destroyActivityLocked

  • Application调整

        addAppLocked

        attachApplicationLocked

        appDiedLocked

        setSystemProcess

        setProcessForeground

        updateProcessForegroundLocked

        killAllBackgroundProcesses

        killPackageProcessesLocked

        foregroundTokenDied

        trimApplications

        bindBackupAgent

        unbindBackupAgent

​​​​​​​adj调整算法

Adj值调整,主要在AMS:computeOomAdjLocked()方法中实现。算法实现如图8所示。

                     图8 调adj算法

如果进程中有service和contentprovider组件,那么进程的adj还需要随client进程adj而调整。

以bindService启动service时,会有bind flag,某些flag值会影响client进程与service宿主进程。如表2所示:

Bind flag

影响

BIND_WAIVE_PRIORITY

此次binding服务,不影响进程(包含service的进程)调度与内存管理优先级。

BIND_ALLOW_OOM_MANAGEMENT

允许进程(包含service的进程)通过其正常的内存管理。

BIND_NOT_FOREGROUND

不允许此次连接提高service进程的优先级到“foreground调度优先级”,但是service进程的“内存优先级”还是会被提高到与client进程一样(即client没被kill之前,service绝不会被kill),但是为了cpu调度的目的,service进程会被留在后台。这个flag只会影响这种情况:client是前台进程,但是service是后台进程。

BIND_ADJUST_WITH_ACTIVITY

如果从activity发起binding服务,进程的优先级会随着activity的优先级调整而调整。

BIND_ABOVE_CLIENT

认为连接的service比client进程重要,如果oom,会先kill client进程。

BIND_IMPORTANT

对于client来说,这个服务非常重要,如果client是前台进程,那么服务所在进程也应该是前台进程。通常情况下,即使client是前台的,进程也只能提高到visibility的优先级。

BIND_NOT_VISIBLE

即使client是visible,也不考虑进程为visible

           表2 serivce绑定flag

如何降低被kill概率

    进程要做到完全不被kill,基本也不可能。除非进程是系统进程,由init启动,那么就可以继承init的adj(-17),这样即使system_server进程被kill了,也不会被kill,不过可以做到尽可能不被lmk选中。

  • 提供进程优先级

后台操作尽可能用service来实现,而不用线程实现,因为包含service的进程优先级比普通进程高。

重载系统back按键事件,使activity在后台运行,而不是被destory。

依赖于其他优先级高的进程。

  • 修改进程属性

    如phone进程,设置persistent属性。

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

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

相关文章

乡村振兴与农村环境整治:加强农村环境治理,改善农村人居环境,打造干净整洁、生态宜居的美丽乡村

目录 一、引言 二、农村环境整治的重要性 1、提升农民生活质量 2、促进农村经济发展 3、保护农村生态环境 三、当前农村环境面临的问题 1、垃圾处理不当 2、污水处理设施缺乏 3、农业面源污染严重 四、加强农村环境治理的措施 1、完善农村垃圾处理体系 2、加强农村…

2010-2015 年阿拉斯加北坡苔原植物功能类型连续覆盖图

ABoVE: Tundra Plant Functional Type Continuous-Cover, North Slope, Alaska, 2010-2015 2010-2015 年阿拉斯加北坡苔原植物功能类型连续覆盖图 简介 文件修订日期&#xff1a;2021-08-27 数据集版本: 1 摘要 该数据集以 30 米的分辨率提供了阿拉斯加北坡约 12.5 万平方…

CodeMirror 创建标签计算编辑器

在日常开发中对于一些数据计算场景可能会遇到标签计算的需求&#xff0c;下面关于如何使用CodeMirror实现标签计算编辑功能。 1&#xff0c;结果图 2&#xff0c;主体代码逻辑 大家只需要复制粘贴主要codeMirror使用逻辑即可 <template><el-dialogref"dialogRe…

7.2 Go 使用error类型

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

godot.bk

1.搜索godot国内镜像&#xff0c;直接安装&#xff0c;mono是csharp版本 2.直接解压&#xff0c;50m&#xff0c;无需安装&#xff0c;直接运行 3.godot里分为场景&#xff0c;节点 主场景用control场景&#xff0c;下面挂textureact放背景图片&#xff0c;右键实例化子场景把…

自然语言处理(NLP)—— 置信度(Confidence)

1. 置信度&#xff08;Confidence&#xff09;的概念 置信度&#xff08;Confidence&#xff09;在机器学习和统计中通常指一个模型对其做出的预测是正确的确信程度。在分类任务中&#xff0c;置信度通常由模型赋予特定类别的概率值来表示。例如&#xff0c;在文本分类或实体识…

外界访问docker服务失败

各位i大佬请问一下&#xff1a;我容器起了&#xff0c;但是外网访问不了目标机器的9090端口。 我检查了&#xff1a;1.本机的防火墙已关闭&#xff0c; 2.目标机器的9090端口显示正在被docker监听。 3.外网可以访问目标机器。 4.docker日志&#xff0c;未显示服务报错。 5…

07.与jenkins集成实现cicd

7.与jenkins集成实现ci/cd ip地址服务内存192.168.111.11kube-apiserver 80801G192.168.111.13k8s-node22G192.168.111.14jenkins(tomcat jdk) 8080 kubelet docker1G192.168.111.15gitlab 8080,80 docker2G 通过jenkins一键操作实现发布服务&#xff0c;jenkins对接k8s …

【前缀和】42. 接雨水

本文涉及知识点 C算法&#xff1a;前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 LeetCode42. 接雨水 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 示例 1&#xff1a; 输入&am…

【PPT】根据字体大小自动缩放文本框大小

【PPT】根据字体大小自动缩放文本框大小 一般我们新建文本框输入文字后&#xff0c;文本框的大小是不会自动缩放的&#xff0c;是根据你一开始拖动的尺寸固定的 你可以设置文本框的长度随着文字的变化而自动调整。这样&#xff0c;无论你输入多少文字&#xff0c;文本框都会自…

FFmpeg开发笔记(三十三)分析ZLMediaKit对H.264流的插帧操作

《FFmpeg开发实战&#xff1a;从零基础到短视频上线》一书的“3.4.3 把原始的H264文件封装为MP4格式”介绍了如何把H.264裸流封装为MP4文件。那么在网络上传输的H.264裸流是怎样被接收端获取视频格式的呢&#xff1f;前文指出H.264流必定以“SPS帧→PPS帧→IDR帧”开头&#x…

深入JVM:全面解析GC调优

文章目录 深入JVM&#xff1a;全面解析GC调优一、序言二、GC调优指标三、GC在线监控1、Jstat工具2、VisualVM工具 四、GC日志分析1、收集GC日志2、GCViewer工具3、GCeasy工具 五、GC问题调优1、调整JVM内存大小&#xff08;1&#xff09;调整堆内存大小及比例&#xff08;2&…

ChatGPT-4o 有何特别之处?

文章目录 多模态输入&#xff0c;多模态输出之前的模型和现在模型对比 大家已经知道&#xff0c;OpenAI 在 GPT-4 发布一年多后终于推出了一个新模型。它仍然是 GPT-4 的一个变体&#xff0c;但具有前所未见的多模态功能。 有趣的是&#xff0c;它包括实时视频处理等强大功能&…

疫情物资捐赠和分配系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;管理员管理&#xff0c;机构管理&#xff0c;用户管理&#xff0c;发放管理&#xff0c;物资管理 前台账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;物资论坛&#xff0c;公告信息…

7.1 Go 错误的概念

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

Python:由b站临时短链接获取到永久链接(去除分享中的杂项)

&#x1f4da;博客主页&#xff1a;knighthood2001 ✨公众号&#xff1a;认知up吧 &#xff08;目前正在带领大家一起提升认知&#xff0c;感兴趣可以来围观一下&#xff09; &#x1f383;知识星球&#xff1a;【认知up吧|成长|副业】介绍 ❤️如遇文章付费&#xff0c;可先看…

LabVIEW在高校电力电子实验中的应用

概述&#xff1a;本文介绍了如何利用LabVIEW优化高校电力电子实验&#xff0c;通过图形化编程实现参数调节、实时数据监控与存储&#xff0c;并与Simulink联动&#xff0c;提高实验效率和数据处理能力。 需求背景高校实验室在进行电机拖动和电力电子实验时&#xff0c;通常使用…

MongoDB CRUD操作:插入文档

MongoDB CRUD操作&#xff1a;插入文档 文章目录 MongoDB CRUD操作&#xff1a;插入文档使用MongoDB Atlas UI插入文档插入单个文档插入多个文档插入行为自动创建集合_id字段原子性写确认 在MongoDB中插入文档的集中方式&#xff1a; 使用编程语言提供的驱动程序&#xff0c;在…

Table表格组件不请求接口,实现表格里某条数据的本地编辑功能(Vue3+ArcoDesign)

【背景】 在 Vue3 ArcoDesign项目中&#xff0c;使用ArcoDesign-Table表格组件不请求接口&#xff0c;实现表格里某条数据的本地编辑功能。最后统一通过接口发送数据。 【步骤】 1. 在表格每条数据列后添加一个“编辑”按钮&#xff0c;点击该按钮弹出一个对话框&#xff0c…

flink 作业报日志类冲突的解决方案

文章目录 背景思考初步解决方案深入思考下终极解决方案总结 背景 实时作业在页面提交任务后&#xff0c;报NoSuchMethodException 方法&#xff0c;看了下是关于log4j的&#xff0c;首先是作业升级了很多依赖的版本&#xff0c;其次flink 也升级 到了1.19版本 思考 打的Jar有…