让你不再惧怕内存优化

news2024/11/19 11:30:25

原文链接 让你不再惧怕内存优化

之前曾经写过一篇关于如何做性能优化的文章,现在针对内存这一专项再做精细化的讨论。对于安卓应用开发来说,内存究竟会遇到什么样的问题,有什么方法可以用来测试和分析,以及有什么样的策略可以去实践优化,今天就来好好聊聊这个话题。

缘起

现代计算机是基于冯*诺依曼架构的,计算机的软件是运行在内存之中的,进程(也即运行中的程序)会耗费一定的内存,才能够正常执行。
在软件开发的中世纪,C和C++盛行的时代,是由软件开发人员(下称猿)自己管理内存,也就是说猿自己申请内存,并处理申请不到内存的情况,并在使用完成后自己负责释放内存,这无疑会加大程序开发难度,产生一些难以调试的问题,如内存越界或者内存踩踏以及野指针。到了近现代,自动内存管理成为主流,研发人员不再用自己去手动管理内存了,尽管用,可劲儿造,一切由GC(也即是Garbage Collector内存回收器)来善后。

这极大的解放了研发人员的双手,可以让他们把更多的精力放在接收产品经理的需求上面了,三天一小需求,一周一大需求,产品迭代速度相当快,业务发展迅速,老板相当高兴啊,这干掉BAT指日可待,赶英超美就在明天,IPO触手可及。然而,现实是极其骨感的。

内存问题会引发什么问题

对于安卓 应用程序来说,内存优化很重要,因为Java VM本身就是比较耗资源的,当应用复杂到一定程度的时候,就会出现由内存使用不当造成的问题。如,测试同学反馈说应用越用越卡,经常crash,用户也反馈说应用越来越不好用了。老板把老猿叫进办公室一顿骂,然后老板让老猿尽快来解决一下问题。

老猿只得把需求放一边,花时间看一看这些问题,然后说凭我多年经验来看,这怕是内存出了问题。

前面提过,现代编程语言一般都有GC,帮助研发人员管理内存。但由于各种原因,还是会出现内存相关的问题。

特别是对于安卓猿来说,实现应用的编程语言是Java(准确说是JVM,Java和Kotlin 以及像Scala都是基于JVM的编程语言),天生支持GC,导致很多人对内存管理知之甚少。当应用程序复杂到一定程度,当源码庞大到一定的量级时,性能问题,特别是内存性能问题便随之而来。

具体可能是内存出现问题的场景有:

  1. OOM导致的crash。OOM,也即OutOfMemoryError,可能发生在任何地方,当Heap中可用内存不足时,便可能会遇到此类crash
  2. 应用程序越用越慢,出现黑屏或者白屏。
  3. UI操作出现卡顿,不流畅。造成UI卡,不流畅的原因很多,当排除了其他原因时,就是内存问题了
  4. 应用程序莫名闪退

内存问题的具体类型及其原因

要想做好内存优化,则必须先弄懂内存问题的根本原因,然后再对内存问题进行归类,最后是通过技术手段来解决。

内存问题的根本原因

安卓应用程序是由Java构建的,而Java是支持GC的编程语言,所以安卓猿是不需要自己手动的去做内存管理的,只管不停的创建对象即可,Java虚拟机(JVM)会帮助我们管理内存,当有不用的对象时会自动被GC。

但是Java应用程序(当然也包括安卓)还是会遇到内存问题,主要是两类,一类是内存不合理使用,如内存使用过多,频繁创建大量对象,内存碎片等等;二是内存泄漏。很多人会把二者混为一谈,网络上绝大多数文章一谈性能优化,一谈内存优化,必然说到内存泄漏,但其实并不严谨。内存泄漏确实是最常见的内存优化内容,也确实是内存使用不合理的最常见问题,但内存问题并不局限于内存泄漏。

内存使用不合理

主要分为三个方面:

  1. 浪费内存,简单来理解就是用一个人住着一千平米的大平层
  2. 大量创建小对象,产生碎片,内存碎片会造成JVM中的内存管理效率变低,当后面申请大块内存的时候效率就变差,它需要把小对象(碎片)进行转移压缩,以腾出更大的空间给大的对象使用。简单理解,这个时候JVM的效率就会变差,你的应用程序性能变差,甚至可能引起卡顿。
  3. 频繁创建对象,特别是较大的对象,造成内存抖动,也即应用程序使用的内存忽多忽少,会频繁的触发GC,从而影响JVM的运行效率。

内存泄漏

JVM是支持自动GC的,也就是说JVM帮助你管理内存,当有不再使用的对象时,会被JVM自动回收,此称之为GC(Garbage Collection)。但如果对象长期处于『使用』状态,并且超出了它本应该存的周期,无法被及时GC,这就会造成泄漏。一般来说,这也没啥影响,但是如果泄漏的对象太多,或者泄漏的时间够长,就会把系统配额Java Heap空间耗尽,应用程序便会因没有内存创建对象而OOM,就会crash。即使没有crash,因为剩余空间较少,会频繁触发GC,从而导致应用程序卡顿严重。

内存泄漏的根本原因是对象的生命周期错乱,对象存活了超过了其本该的生命周期,或者简言之,一个本该是较短的生命周期的对象被一个更长生命周期的对象所引用着,就会导致它本该生命周期结束时无法被GC,便产生了泄漏。

这是要重点关注对象的生命周期,只有管理好了对象的生命周期,才能彻底的解决内存泄漏问题。

安卓应用中的生命周期

固定生命周期的对象

安卓应用程序里面,有一些是有固定生命周期的,或者说有明显生命周期,且不是由研发人猿自己控制的,如框架层控制的那一坨东西。

  • Activity
  • Fragment
  • View

特别是Activity,它也是内存泄漏的头号对象,90%的内存泄漏都是Activity对象。这货完全由系统框架控制,并且有明显的生命周期,而且还有重建实例的情况(涉及状态恢复时),所以它的生命周期其实相当短暂,并且它跟进程和主线程没有任何关系,Activity退出 了(走了onDestroy)进程仍还在,主线程也仍还在。而,又因为它是应用程序的第1级入口,应用程序所有的对象,以及GUI所有的东西,全部都由Activity直接或者间接持有,换句话说,Activity泄漏了,你整个应用程序的对象也基本上全泄漏了。

较长生命周期对象

这里所谓的长生命周期,是指它们的生命周期是与进程绑定的,除非进程退出,或者明显的执行一些退出,否则一直随进程而存在:

  • Looper,或者说消息队列,这玩意儿除非主动quit,否则一直存在。主线程的Looper与进程同在,自己创建的Looper要手动退出才算终结。
  • 被static修饰的成员变量,这东西的生命周期是跟进程一样的
  • 单例,单例必须由static来修饰,所以与进程生命周期是一样的,进程在,则单例在
  • 线程池,或者一个长时间运行的thread,除非主动去shutdown
  • RxJava的Schedulers,这玩意跟looper一样,都是长时间运行的消息队列,且与进程绑定的
  • 系统框架,手机还在开机系统框架就在运行,所以它的生命周期远远长于某一个应用程序
  • Application和ApplicationContext,这东西与进程生命周期是一样的,相当于单例了

业务逻辑中的生命周期

业务逻辑就纯属于应用程序的本身逻辑了,无法一概而论,但一般来说,主页面的生命周期肯定是长于某个子页面的。那么子页面在其退出后,理论上它的绝大多数对象应该要被回收。

如何发现内存问题

生活中不是缺少美,而是缺少发现。

对于内存优化,第一步就是要通过各种测试手段发现问题。最理想的情况是建立一种监控手段,这样最能保住革命果实,以及非常及时的发现问题。

这里指的是一般性的粗略手段来发现你的应用有内存问题了,可能需要优化了。并且这些测试方法最好能做成定期监控,这样一旦内存性能有回撤时,能尽快发现。

『队长,我们暴露了』

很多时候都是问题主动找上门来了。

前方有雷区

很不幸,你的应用程序中弹身亡(crash了),还是OOM。这是Java语言中的一个运行时的错误,可能在创建任何对象时发生,但一般来说创建比较大的对象时,这里的大是指对内存需求大,如图片,或者大块数组时,更容易发生。

当你的应用程序出现了OOM的时候,就是一个特别明显的信号,告诉你要重视内存优化了。

遇到终结者了,是lowmemorykiller

有时候,没有明显的错误,但是应用却闪退了,特别是在后台,或者跳到其他应用页面时。

这个会比较隐蔽,通常会引发其他表象的问题。最明显的问题就是,当跳转到其他页面,再返回时,发现原来的页面状态不存在了,比如你的应用要访问一个URL,跳转到了网页浏览器,但从浏览器返回时,要么你的应用不在了,要么你的应用的原先状态不在了。这其中的原因就是当你的应用不在前台了,就被系统回收了,其中一个占大头的原因就是占用内存太多,被系统的lmkd(lowmemorykiller)干掉了。

因为系统要保证整个设备的正常运转,所以会把占用内存太多的先杀掉,以释放内存。

当你的应用频繁的遇到被lowmemory killer干掉时,也是一个明显的信号,要重视内存优化了。

读懂系统GC日志

有些时候不像前面那样严重,但是查看logcat日志时,能发现大量的GC日志,就像这样的

259857:01-08 20:00:17.836 10083 26337 26347 I test.test: NativeAlloc concurrent copying GC freed 141174(6852KB) AllocSpace objects, 29(12MB) LOS objects, 49% free, 24MB/48MB, paused 180us total 308.126ms
279178:01-08 20:00:19.618 10083 26337 26347 I test.test: Background young concurrent copying GC freed 469755(20MB) AllocSpace objects, 40(3608KB) LOS objects, 41% free, 28MB/48MB, paused 396us total 124.817ms

这是系统在进行GC,通常来说这没有什么问题。但如果在短时间内,比如某个页面,点了某个按扭后大量出现此类日志,也是一个明显的信号,告诉你要重视内存优化了。

主动出击,以攻为守

作为一个优秀的猿,不能坐着等问题上来,要能主动的去创造问题。每当完成一个需求后,或者写了一大坨代码以后,就需要主动的去查看一下内存方面是否有需要优化的地方。我们可以通过如下测试方法,来看内存是否有问题,是否需要做优化。重点就是看应用程序在一定时间内,使用的内存是否一直在增长, 有没有抖动,并且在GC后,或者退出 后是否仍不回落。

meminfo

具命令是adb shell dumpsys meminfo ,这个命令还是比较常见的,网上有很多资料可以用,可以看后面罗列的参考文章中来详细了解它的具体用法以及各个字段的意义,这里就重复了。

  • dumpsys
  • dumpsys meminfo 的原理和应用
  • adb shell dumpsys meminfo 详解
  • dumpsys meminfo 含义

需要关注一下重点,就是,可以重点看Java Heap一栏的数据变化,这是Java层的占用内存情况。另外就是每次运行meminfo其实会对进程产生影响。所以,这个命令可以用来粗维度的监控,查看一些信息,做一些定性的分析。

它最大的优点是方便,且只要是进程都可以查看,不用有源码。

Android Studio的Memory Profiler

在远古时代安卓SDK中会有DDMS,里面是一套调试工具,但现在都集成到Android Studio的Profiler里面了,通常会在下方的工具栏里面,如果 没有就到菜单View->Tools Window->Profiler把它调出来。然后选择要调试的进程,默认它会把CPU,Network,Memory和功耗都显示,这里可以双击Memory那一坨,就会进入专门的内存页面。

它会以时间轴的方式来图形化的展示内存使用情况,非常的直观和方便。通过这个可以直观的看到两个问题,就是嫌疑内存泄漏以及内存抖动。

嫌疑内存泄漏就是看到曲线一直在增长,且通过显示GC,或者退出后,或者停止某项目操作后,仍不回落的,这就非常有可能有泄漏的存在,泄漏是超出了它本该的生命周期,比如某一操作结束了,退出 了某一页面,甚至退出应用了,内存仍没有回落,就可能有问题。

另外就是内存抖动,就是能看到内存曲线 有毛刺,短时间内忽上忽下的,这就是内存抖动。

leakcanary

这货也是非常流行的,专门用于检测内存泄漏的工具,它的功能较为强大,除了可以监控以外,还可以给出详细的trace。具体使用可以参考官方的文档,并不难。

但它最大的问题在于,必须参与项目构建。假如你想研究一下竞品的情况,就没有办法了。

如何调试内存问题

通过前面提到的手段,我们可以发现内存有一些问题了,需要进行内存方面的优化了,但这还不够,还需要一些精细化的调试方法来具体定位问题,这样才能更好的去进行优化。

那么有哪些具体的调试方法呢?

Allocation tracer

这个是前面提到的Android Studio Profier里面的工具。用Profiler可以发现问题,但还需要进一步的深入的分析问题。这就需要Allocation tracer了。

具体做法就是,当你发现某一系列操作后内存一直增长,或者看到有抖动现象时,就可以抓取这段时间的Heap dump,然后详细分析,现在Android Studio都集成好了,只需要点几下,就能抓到,并把结果列出来,可以看到具体创建哪些对象,以及它们的引用关系是怎样的。

可以参考 以下资源来详细了解如何使用此工具:

  • The Android Profiler
  • Inspect your app’s memory usage with Memory Profiler
  • Android 内存优化篇 - 使用profile 和 MAT 工具进行内存泄漏检测

MAT

这是专门用于Java heap内存分析的工具,相当强大。但不能直接使用。

需要先想办法抓取进程的heap dump,然后转换为Java标准的格式(因为安卓的Heap与Java SE的并不一样,安卓 SDK中有转换工具),然后再用MAT打开即可,它的功能要远强大于前面的提到的Allocation tracer。所以,如果要深度的分析和优化,还是要用MAT。

关于MAT的具体使用方法,可以参考以下资源:

  • 官方文档
  • 【Android 内存优化】使用 Memory Analyzer ( MAT ) 工具
  • Android 内存优化(1) - MAT 使用入门

leakcanary

除了能监控以外,它还能分析具体的内存泄漏,并给出trace,所以当发现问题后,具体定位问题的时候,也可以使用此工具,还是相当强大的。

它的使用相当简单,直接把它加入到dependencies,然后构建 就好了。

至于它的分析结果也是相当直观的,会以Notification的方式通知你,点开后有一个页面展示出引用关系链,然后判断是否是泄漏,即可。

详细可以参阅它的官方文档就可以了。

如何优化内存

内存优化,一大半在于测试,监控和调试分析,约占70%,这部分是重头,因为只有找到具体的代码位置,才好去修复问题,并且修复后还要验证问题是否真的修复了。不能光在那里看代码,想当然的认为把几个内部类改为static,或者传递引用了ApplicationContext,就能优化了内存。

对于性能优化,当然也包括内存优化,必须用测试手段进行量化,以此来验证是否真有有改善。

本节内容,假设已通过前面提到的测试方法发现了内存问题,并通过调试手段定位到了具体位置。优化的手段也要针对 具体的问题来进行:

避免内存泄漏

内存优化的大头是要避免泄漏,所以重点来谈谈如何避免内存泄漏。

前面提到了,内存泄漏的根本原因是生命周期混乱,较长生命周期的对象,甚至是超长生命周期的对象,持有了较短生命周期的对象,这一定会导致泄漏。所以,要想真的解决内存泄漏问题,必须设计好对象的生命周期,这是根本解决之法。

要尽可能的,缩小对象的生命周期

对象的生周期不应该超出它本该存在的范围,并且应该尽可能的减少对象的生命周期,这个可能在设计阶段考虑到。但一般较难执行,代码复杂了,很难控得住。

对于超过Activity生命周期的对象要及时清理

前面提到过的超长生命周期的东西,如Looper,如Frameworks,如单例,如RxJava的Schedulers,如线程池,这些东西的生命周期远长于Activity,所以,一定要在对应的地方,及时清除对Activity的引用持有。

后面的参考 资料里面也有大量的实用建议可以参考,这里就不重复了。避免内存泄漏应该要被总结成为编程规范,然后在团队内部推行,当然也可以设计一些源码静态检测工具,来强制执行。当然,再好的工具和规范也需要人来遵守,任何事情能够在编码阶段防止发生,成本是最小的,收益 是最大的。

WeakReference和SoftReference不是救命稻草

千万不要用WeakReference和SoftReference这东西来修复内存泄漏问题,它们根本就不是用来修复内存泄漏问题的。

再说一遍,内存泄漏是由生命周期混乱造成的。

如果强行使用WeakReference来代替原来的强引用,就会造成想使用对象的时候它却被回收了,这时你的正常逻辑就没法走了,而且如何正确的处理这种异常case,也是很难恰当 的处理的。

WeakReference这东西最最合理,最为适合的场景就是缓存里面,也就是说它本身是用于一种可有可无的引用关系,这样一旦被GC了,也不会影响原有逻辑,因为对象本来就可能在(Cache Hit),也可能不在缓存里面(Cache Miss),使用者必须处理在或者不在两种case。因为缓存的清理可能不够及时(必须由编码人员手动设置条件去清理,比如在退出的时候),当JVM需要GC时,因为都是WeakReference,GC就可以快速的回收对象释放内存。

不要到处给对象引用置为null

很多有过C++经验的同学,可能会习惯在对象使用完成后,手动把对象置为null。但其实这是完全没有必要的,只会造成不必要的混乱,JVM会自己去追踪每个对象,它到底还有没有被引用持有着。我们要把精力重点放在对象生命周期的把控上面,简单的置为null,不会缩减对象的生命周期,所以它对解决和防止泄漏方面没有任何帮助。

内存使用优化方式

除了避免内存泄漏,其他一些方式也是有很多技术可以用于优化的。

减少内存浪费

内存浪费,就是使用了没必要的内存,虽然可能不会引发问题,但是还是会增加风险,比如同样都是后台进程,你的应用占用内存稍大了一些,被杀的风险就高了一些。

减少内存浪费,核心的方法就是按需申请,特别像图片这种内存占用大户,一定要按需要来加载,何为需要就是目标View的大小,具体可以看官方教程Loading Large Bitmaps Efficiently。以及尽可能的要复用bitmap。

再如资源图片,设置合理的分辨率,没有必要啥都上高清,且要为低精度设备提供单独的一套资源。

以及像不是要求那么清晰的场景就用RGB_565,而非RGBA_8888等等,这些都是在编码的时候就可以提高内存使用的方法。

使用缓存

缓存是计算机史上最伟大的发明,甚至是人类史上最伟大的发明,它无处不在从硬件到软件都会使用缓存,并且它在各种东西的设计之中都是很重要的一部分。

前面提到的内存抖动问题,就需要用缓存来解决,以避免频繁创建对象。特别是涉及图片的场景,比如流行的图片加载开源库里面都有专门的缓存的机制,有些是二级,有些是三级。当需要设计缓存时,可以重点参考图片加载库中的缓存设计。

另外,SDK中也有标准的缓存组件可以用,LruCache,这是针对内存层面的缓存,可以看这篇文章来详细了解使用方法。

合理复用对象

这里的意思是使用像享元这样的设计模式,来合理的复用对象。

需要注意的是享元(Flyweight Pattern)的适用场景,它适用于创建对象的成本较高,比如创建对象需要的一些资源较昂贵,不同的对象仅是有不同的属性,或者说对象本身在使用的时候的表现是不同的。

一个典型的例子就是绘图的形状,比如一个页面有大量的不同的形状需要绘制,有方的,有圆的,有白色的,有彩色的,有实边的有虚线的。常规的思路是一个基类叫Shape,里面有各种属性,还有一个draw方法,子类可以定义不同的属性,各自实现draw方法。然后根据需求创建一大坨具体的对象,遍历调用draw方法。这是面向对象编程(OOP)中的非常标准的多态(Polymophsim)。事实上,你只需要创建一个对象就够了,它会根据不同的属性画出不同的效果。这就是设计模式中的享元模式,具体可以参考这篇文章来详细了解。

认识几种不同的内存类型

通过各种工具查看的内存时,如通过meminfo以及像Memory profiler,但可以发现有不同种类,需要重点关注以几种:

Java Heap

也即通常意义上的heap内存(堆内存),名字可能会是Java,Java Heap,或者Java allocate,但都是一样就是指纯Java代码中通过new创建对象时使用的内存。

Native Heap

因为Java是支持JNI与C/C++接通,也即native方法,那么通过native方法创建的对象是计算在Native之中的,它与Java层是分开的,当然通过native方法(malloc或者new)创建的对象,要记得去释放,否则是一定会泄漏的。

因为Android的大部分是由C/C++实现的,Java层仅是封装,Frameworks层大部分功能都由JNI转到native层去实现的,因此native这部分的内存也是很多的,并且由于Frameworks本身会大量调用JNI native层,所以即使你的应用程序根本没有用到JNI,但是还是会看到Native内存使用。

Graphics

主要是涉及OpenGL ES的相关内存占用,如GL Surfaces,如Texture或者如Framebuffer等,它们所占用的内存。

这里需要特别注意的是,即使你的应用没有用到OpenGL相关的东西,但仍可能会有此部分内存占用,这是由于硬件加速本身也是通过OpenGL ES实现的。

ion内存

这个是为了效率,直接从kernel层开出shared buffer,以加速内存使用效率,这个是偏底层的,大部分普通app是用不到的。

可以参考一下这个The Android ION memory allocator。

共享内存

可以理解为Linux中的匿名共享内存,可以用来实现IPC通信,但它并不会被Profiler计算在Java或者Native里面。非死不可出品的Fresco当初牛逼的地方就在于把Bitmap放在匿名共享内存里面,从而不占用应用自己的Heap空间。

可以参考这两个文章:

  • Ashmem(Android共享内存)使用方法和原理
  • Android shared memory

学无止境

深入学习GC相关知识,如JVM的GC如何演进。

也可以学习一下其他编程语言的GC机制。

不要过早优化,更不能过度优化

性能优化这个事情是要在架构设计和产品设计阶段就需要考虑的事情,比如是否要加入缓存。

但如果前期想太多,会造成严重的扭曲,会让你陷入无限的复杂问题里面,难以自拔(本是问题1,但是变成了问题A,问题B,直到问题z,最初的问题1却被忽略了),反倒不是好事情。

最为想理的情况就是小步迭代,先提出能满足需求的最小版本,然后逐步迭代。比如说做一个新的feature的时候,先用最简单的架构和设计来实现,然后考虑补充细节,处理异常case,再考虑可能的扩展,然后考虑性能优化。

剩下的是态度

不是说一线开发的态度,而是老板们的态度。

性能问题是直接影响体验,所以只有重视体验的老板才会重视性能问题。而且这也不是研发猿的问题,需要测试,产品经理都要能重视性能问题,才能最终把性能做好。产品同学不能只顾着提需求,也要平衡性能,并且给研发同学一定的时间去注重性能问题,而测试同学更加重要,需要不断精进你的测试方法,帮助研发同学更好的解决问题,并且要有监控手段,比如说A版本做了性能优化专项,那么为了保留革命果实,需要有一种监控手段,以防性能出现重大回撤。

很多事情不能怪研发,就像有一位技术相当不错的同事说过的话,当时大家聊起性能优化的事情,他说:『道理大家都懂,但当左边是产品经理在那里催需求,右边是设计师在那说按扭还差几个象素,测试同学在那崔你赶紧发版本啊,我还等着测完回家呢!当你处在这种条件下,谁TMD的还管性能啊,先实现了再说吧,甚至代码格式都懒得改了。』

所以,这是整个工程体系的事情,只有整个研发体系都注重性能,性能才会好,体验才会好,而这就需要一个老板的支持了,否则,性能不可能好,产品汪们只顾着提需求,设计师只顾着画面精美,研发同学光实现需求都做不完,哪有精力去搞性能啊!测试同学也不能只用粗浅的测试方法,只说性能不好,具体哪不好,不应该都让研发自己去调试,去发现问题。另外,也需要做好性能监控机制,以保住革命果实。要不然,A版本辛辛苦苦搞了一轮性能优化,也有大幅改善,然后到了B版本,或者几个月后,再来一轮。

这就是很骨感的现实。所以,在现实生活中只有大厂头部应用 才真的重视性能和体验,并且才能把性能和体验做好。

参考资料

  • Overview of memory management
  • Profile your app performance
  • Android内存管理机制
  • 最全的Android内存优化技巧
  • Android性能优化之内存优化
  • 深入探索Android内存优化
  • Android性能优化之内存优化
  • 深入探索 Android 内存优化(炼狱级别-上)
  • 深入探索 Android 内存优化(炼狱级别-下)
  • 内存优化深入版
  • Dealing with Large Memory Requirements on Android

原创不易,打赏点赞在看收藏分享 总要有一个吧

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【LeetCode-中等题】904. 水果成篮

文章目录 题目方法一:滑动窗口方法二: 题目 题目的意思就是:找至多包含两种元素的最长子串,返回其长度 方法一:滑动窗口 class Solution { // 滑动窗口 找至多包含两种元素的最长子串,返回其长度public …

红队打靶:ConnectTheDots打靶思路详解(vulnhub)

目录 写在开头 第一步:主机发现和端口扫描 第二步:FTP和NFS渗透(失败) 第三步:web渗透 第四步:jsfuck解码 第五步:再次FTP渗透与莫尔斯电码解码 第六步:vim读取断电swp文件…

数据科学家必备的20个Python库

公众号:尤而小屋作者:Peter编辑:Peter 大家好,我是Peter~ 小屋里面一直在输出关于数据科学领域的文章,绝大部分都是基于Python,少量的MySQL(MySQL存储数据用)。本文重点给大家介绍P…

保姆级教程 --redis启动命令

1、在redis目录 打开命令 windowr 输入cmd 2、输入 redis-server.exe redis.windows.conf 启动redis命令,看是否成功 3、可能会启动失败,报28 Nov 09:30:50.919 # Creating Server TCP listening socket 127.0.0.1:6379: bind: No error 4、报错后&am…

获取该虚拟机的所有权失败,主机上的某个应用程序正在使用该虚拟机

点击“openstack-controller”虚机 打开出现如下错误,点击“获取所有权” 点击“取消” 这时候不要删除虚拟机,这种错误一般是由于虚拟机没有正常关闭引起的。 找到openstack-controller的虚拟磁盘文件及配置文件存放的位置,删除openstack-…

【系统设计系列】缓存

系统设计系列初衷 System Design Primer: 英文文档 GitHub - donnemartin/system-design-primer: Learn how to design large-scale systems. Prep for the system design interview. Includes Anki flashcards. 中文版: https://github.com/donnemart…

电子信息工程专业课复习知识点总结:(三)数电

绪论 第一章 数字逻辑概论 1.数字集成电路相比模拟电路的优点? ①稳定i性高,抗干扰能力强 ②数字电路只用0和1进行逻辑运算,所以比较容易设计电路。 ③便于集成,体积小、成本低 ④可编程性强,可以使用加密技术提高保…

SQL6 查找学校是北大的学生信息

描述 题目:现在运营想要筛选出所有北京大学的学生进行用户调研,请你从用户信息表中取出满足条件的数据,结果返回设备id和学校。 示例:user_profile iddevice_idgenderageuniversityprovince12138male21北京大学Beijing23214male…

Spring Boot将声明日志步骤抽离出来做一个复用类

上文Spring Boot日志基础使用 设置日志级别中我们写了个比较基本的日志操作 但也随之产生了一个问题 我们这行代码 能不能不写? 具体说 我们不希望每个需要日志的类都声明一个在这 看着太不美观了 我们最简单方法当然是继承 我们找个目录创建一个类 叫 BaseClass…

星际争霸之小霸王之小蜜蜂(十二)--猫有九条命

系列文章目录 星际争霸之小霸王之小蜜蜂(十一)--杀杀杀 星际争霸之小霸王之小蜜蜂(十)--鼠道 星际争霸之小霸王之小蜜蜂(九)--狂鼠之灾 星际争霸之小霸王之小蜜蜂(八)--蓝皮鼠和大…

力扣刷题61-旋转链表

题目来源: 力扣61-旋转链表 题目描述: 思路:双指针 因为它倒得永远是倒数k个 class Solution {public ListNode rotateRight(ListNode head, int k) {ListNode slowhead,fasthead,lenhead;int l0;//1 求长度while(len!null){l;lenlen.next…

【JAVA-Day05】深入理解Java数据类型和取值范围

深入理解Java数据类型和取值范围 深入理解Java数据类型和取值范围摘要一、Java的数据类型1.1 存储单位1.2 Java基本数据类型 二、Java的取值范围2.1 变量定义2.2 取值范围验证 三、总结 博主 默语带您 Go to New World. ✍ 个人主页—— 默语 的博客👦🏻…

孙哥Spring源码第21集

第21集 refresh-invokeBeanFactoryPostProcessor-下半部分BeanFactoryPostPrcessor处理 【视频来源于:B站up主孙帅suns Spring源码视频】【微信号:suns45】 1、注解处理添加的的BeanDefinitionRegistryFactoryProcessor在哪个地方? 在第一…

【Flutter】引入网络图片时,提示:Failed host lookup: ‘[图片host]‘

在使用 NetworkImage 组件加载外部图片时,提示 Failed host lookup: [图片host] 错误。 排查方向 1、清理缓存 解决方案: 尝试flutter clean清空缓存后重新安装依赖flutter pub get重新构建项目flutter create . 走完上述三个步骤后,再次…

COMO-ViT论文阅读笔记

Low-Light Image Enhancement with Illumination-Aware Gamma Correction and Complete Image Modelling Network 这是一篇美团、旷视、深先院、华为诺亚方舟实验室、中国电子科技大学 五个单位合作的ICCV2023的暗图增强论文,不过没有开源代码。 文章的贡献点一个是…

工作游戏时mfc140u.dll丢失的解决方法,哪个方法可快速修复mfc140u.dll问题

在 Windows 操作系统中,mfc140u.dll 文件是非常重要的一个组件,许多基于 MFC(Microsoft Foundation Classes)的程序都需要依赖这个文件。然而,有些用户在运行这些程序时可能会遇到mfc140u.dll丢失的问题,导…

技术分析需谨慎,各位投资者应该这样做

技术市场分析中存在许多工具,其中之一便是烛台模式。然而对于这些模式和指标,FPmarkets澳福和各位投资者应持谨慎的态度,因为它们仅仅展示了一种可能的结果,而无法确保其绝对准确。 关于蜡烛图交易的提示,包括Maruboz…

RK3399平台开发系列讲解(内核调试篇)spidev_test工具使用

🚀返回专栏总目录 文章目录 一、环境二、执行测试三、回环测试四、字节发送测试五、32位数据发送测试沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 在 Linux 系统上,“spidev_test” 是一个用于测试和配置 SPI(Serial Peripheral Interface)设备的命令行工具。…

2023-2024年最值得选的微信小程序毕业设计选题大全:100个热门选题推荐✅

一、前言 💗博主介绍:✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌💗 毕业设计选题非常重要&a…

页面优化技术

页面优化技术 文章目录 页面优化技术1.概述2.页面缓存URL缓存对象缓存2.1 页面缓存2.2 对象缓存2.3 测试效果 3.页面静态化 1.概述 Aim:减少对数据库的访问 页面缓存URL缓存对象缓存’ Aim:实现前后端分离 页面静态化,前后端分离 Aim: 静态资…