Android 面试题 避免OOM(内存优化)三
🔥 OOM介绍(out of memory 内存溢出)🔥
Android和java中都会出现由于不良代码引起的内存泄露,为了使Android应用程序能够快速高效的运行,Android每个应用程序都会有专门Dalvik虚拟机实例来运行,也就是每个程序都在属于自己的进程中运行。
某个应用程序内存泄露仅仅只会使自己进程被kill掉不会影响其他进程(如果是system_process等系统进程出现问题,就会造成系统重启),另一方面,系统为每一个应用程序分配了不同的内存上限,如果超过这个上限被视为内存泄露,从而被kill掉。
Dalvik Heap size因不同设备的RAM不同而有所差异,应用占用内存接近这个阀值,在尝试分配内存就会引起outofmemoryError的错误。
🔥 出现OOM的几种情况 🔥
1、加载对象过大
2、相应资源过多,来不及加载。
解决方案 :
1、内存引用上做一些处理,常用的有软引用。
2、内存中加载图片直接在内存中做处理(如边界压缩)
3、这个GlideFresco 图片框架可能封装好了
4、动态回收内存
5、优化Delivk虚拟机的堆内存分配
6、自定义堆内存大小
🔥 共享内存 🔥
Android应用程序的进程都是从Zygote的进程fork出来的。Zygote进程在系统启动并载入通用的framework代码和资源后启动。一个新的应用程序启动,系统就会从Zygote中fork出来一个新的进程,在新的进程中加载并允许应用程序的代码。这使得大多数RAM pages被分配给framework的代码,并且RAM资源能够在应用的所有进程之间共享。
大多数static 数据被mmapped到一个进程中,这样使得同样的数据在进程之间能够共享,而且在需要的时候能paged out.常见static 数据包括Dalvik code ,app resourecs,so 文件等。
大多数情况下,Android通过显示的方式分配共享内存区域(例如ashmem或gralloc)来实现动态RAM区域能够在不同进程之间进行共享的机制。比如,Window Surface在APP和Screen Composition之间使用共享的内存,
Cursor Buffers在Content Provider与Clients之间共享内存。
🔥 分配与回收内存 🔥
每个进程的Dalvik heap都反应了使用内存的占用范围,(Dalvik Heap Size),他可以根据需要进行增长,但是系统有一个上限。
HeapSize跟实际的物理内存大小是不对等的,PSS(proportional Set Size)记录了应用程序自身占用以及和其他进程共享的内容。
Android不会对heap空闲区域进行做碎片整理。系统仅仅在新的内存分配之前判断Heap的尾端剩余空间是否足够,不够就会触发gc操作,从而腾出更多空闲的内存空间。
gc操作(garbage collection)也就是所谓的垃圾回收,Android在适当时候触发gc操作,将一些不再使用的对象回收,在Android高级系统针对Heap空间有一个Generational Heap Memory的模型,最近分配的对象在放在young generation区域,当停留一段时间,这个对象会被移动到old generation中,最后在移动到permanent generation区域中。
系统会根据内存中不同的内存数据类型进行gc操作,young generation区域的对象更容易被销毁,而且gc操作的速度比old generation的速度要快,时间更短。
每个generation的内存区域都有固定的大小,随着新的对象陆续被分配到此区域,当这些对象的大小快达到阀门值时,就会触发gc操作。通常情况下,gc操作发生时,所有线程都是暂停的。
如何查看本机heap size :
ActivityManager manager=(Activity)getSystemService(Context.ACTIVITY_SERVICE); int heapsize=manager.getMemoryClass();
🔥 应用切换操作 🔥
Android系统不会再用户切换应用的时候进行交换内存的操作,而是把不包含Foreground组件的应用进程放到LRUCache中,比如用户启动一个应用,系统会为它创建一个进程,但是当用户离开这个应用,此进程不会被立即销毁而是会放到一个Cache中,当用户切换回来够快速的恢复。
🔥 发生OOM的条件 🔥
通过不同的内存分配方式对不同的对象(bitmap,etc)进行操作因Android版本差异发生变化。
4.0以上,废除了external的计数器,类似bitmap的分配改到dalvik的Java heap(堆)中申请,只要allocated+新分配的内存>=getMemoryClass()就会发生OOM。(在AS memory monitor查看内存中Dalvik Heap的实时变化)
🔥 如何避免OOM 🔥
减少OOM的第一步就是要尽量减少新分配出来的对象占用内存的大小,尽量使用更加轻量的对象。
使用更加轻量的数据结构
考虑使用ArrayMap/SpareseArray而不是传统的HashMap等数据结构,Android系统为移动系统设计的容器ArrayMap更加高效,占用内存更少,因为HashMap需要一个额外的实例对象来记录Mapping的操作。而SparesArray高效的避免了key和value的自动装箱,而且避免了装箱后的解箱。
避免在Android中使用Enum
减少Bitmap对象的内存占用
Bitmap是一个消耗内存的大胖子,减少创建出来的Bitmap的内存占用很重要。一般有两种措施 :1、inSampleSize:缩放比例,在把图片载入内存之前,我们需要计算一个合适的缩放比例,避免不必要的大图载入。
2、decode format:解码格式,选择ARGB_8888/RBG_565/ARGB_4444/ALPHA_8,存在很大差异。
使用更小的图片
在设计图片资源的时候,我们要考虑图片是否存在可以压缩的空间,是否能使用更小的图片,使用小图在xml加载资源时就不会在初始化视图因为内存不足而发生InflationException,其根本原因就是发生了OOM。
🔥 内存对象的重复利用 🔥
Android最常用的缓存算法LRU(Least Recently Use)
复用系统自带的资源,比如字符串、图片、动画、样式、颜色、简单布局,在应用中直接引用,减少自身负重、apk大小、减少内存的开销、复用性更好。但需要考虑版本差异。
Listview和GirdView出现大量重复子组件的视图里面对ConvertView的复用。
Bitmap对象的复用
在ListView和GridView等显示大量图片的控件里面需要使用LRU机制缓存Bitmap.
利用inBitmap的高级特性提高Android系统在Bitmap分配和释放执行效率,inBitmap属性可以告知Bitmap解码器使用已经存在的内存区域而不是重新申请一块内存区域存放Bitmap,也就是新解码的Bitmap会使用之前那张bitmap在heap占用的内存区域,即使是上千张图片,也只占用屏幕能放下图片的内存
SDK19以后:新申请的BItmap大小必须小于或等于前面赋值过的bitmap的大小
新的Bitmap和原来的解码格式要相同,我们可以创建包含多种类型可以重用的bitmap对象池,这样后序的bitmap创建就可以找到合适的模板去重用。
避免在onDraw方法里面执行对象的创建
在onDraw这种频繁调用的方法要避免对象的创建操作,因为他会迅速增加内存的使用,引起频繁的gc,甚至内存抖动
StringBuilder
如果代码中有大量字符串拼接操作,使用StringBuilder代替"+"
🔥 避免对象的内存泄露 🔥
内存对象的泄露会导致不再使用的对象无法及时释放,不仅浪费了宝贵的内存空间,后续要分配内存的时候,空间不足造成OOM。这样,每级的generation会变小,gc更加容易触发,引起内存抖动,带来性能问题。
LeakCanary开源控件可以帮助我们发现内存泄露的问题。
Activity泄露是内存泄露最为严重的问题,涉及内存多,影响面广 .
- 内部类引用导致Activity的泄露
典型的是Handler导致的Activity泄露,如果Handler中有延迟的任务或者等待执行的任务队列过长,很可能因为Handler继续执行造成Activity的泄露。
引用链是Looper->MessageQueue->Message->handler->Activity,解决办法是在退出UI之前执行 remove Handler消息队列中的消息与runnable对象。或者使用Static+WeakReference的方式来判断Handler和Activity之间存在引用关系。- Activity Context被传递到其他实例中,可能导致自身被引用而发生泄露 .
- 考虑使用Application Context而不是Activity Context 除必须使用Activity Context的情况(Dialog的context必须是Activity),我们可以使用Application Context来避免Activity泄露
注意临时Bitmap的及时回收
大多数情况下,我们对Bitmap对象增加缓存机制,但是有时候部分bitmap需要及时回收。比如我们临时创建的摸个相对大的bitmap对象,变换得到新的bitmap对象后,尽快回收原始的bitmap,及时释放原来的空间。
注意监听器的注销
android程序里面register后要及时释放unregister那些监听器,自己手动add的listener,要记得remove这个listener.
注意缓存容器的对象泄露
有时候我们为了提高对象的复用性,把某些对象放到缓存容器中,如果这些对象没有及时从容器中清楚,也可能导致内存泄露.
注意webview的泄露
Android不同版本对webview产生有很大差异,较为严重的问题是webview的泄露,解决办法:为webview新开一个进程,通过AIDL与主进程通信,根据业务的需要在合适的时机进行销毁,从而达到内存的释放。
🔥 内存使用策略优化 🔥
谨慎使用large heap
android设备由于软硬件的差异,heap阀值不同,特殊情况下可以在manifest中使用largeheap=true声明一个更大的heap空间,使用getLargeMemoryClass()来获取到这个更大的空间。但是要谨慎使用,因为额外的空间会影响到系统整体的用户体验,并且会使每次gc的运行时间更长。切换任务时性能大打折扣,large heap并不一定能获取到更大的heap.
综合考虑设备内存阈值与其他因素设计合适的缓存大小
例如,在设计ListView或者GridView的Bitmap LRU缓存的时候,需要考虑的点有:1、应用程序剩下了多少可用的内存空间?
2、有多少图片会被一次呈现到屏幕上?有多少图片需要事先缓存好以便快速滑动时能够立即显示到屏幕?
3、设备的屏幕大小与密度是多少? 一个xhdpi的设备会比hdpi需要一个更大的Cache来hold住同样数量的图片。
4、不同的页面针对Bitmap的设计的尺寸与配置是什么,大概会花费多少内存?
5、页面图片被访问的频率?是否存在其中的一部分比其他的图片具有更高的访问频繁?如果是,也许你想要保存那些最常访问的到内存中,或者为不同组别的位图(按访问频率分组)设置多个LruCache容器。
6、onLowMemory() 与onTrimMemory() :
Android可以在不同的应用当中随意切换。为了让background转到foreground, 每一个background都会占用一定的内存。系统会根据内存的使用情况决定回收部分background的应用内存。background的应用从暂停状态恢复到foreground,比较快,如果从kill状态恢复比较慢。
资源文件需要选择合适的文件夹进行存放
我们知道hdpi/xhdpi/xxhdpi等等不同dpi的文件夹下的图片在不同的设备上会经过scale的处理。例如我们只在hdpi的目录下放置了一张100100的图片,那么根据换算关系,xxhdpi
的手机去引用那张图片就会被拉伸到200200。需要注意到在这种情况下,内存占用是会显著提高的。对于不希望被拉伸的图片,需要放到assets或者nodpi的目录下。
Try catch某些大内存分配的操作
在某些情况下,我们需要事先评估那些可能发生OOM的代码,对于这些可能发生OOM的代码,加入catch机制,可以考虑在catch里面尝试一次降级的内存分配操作。例如decode bitmap的时候,catch到OOM,可以尝试把采样比例再增加一倍之后,再次尝试decode。
谨慎使用static对象
因为static的生命周期过长,和应用的进程保持一致,使用不当很可能导致对象泄漏,在Android中应该谨慎使用static对象。
特别留意单例对象中不合理的持有
虽然单例模式简单实用,提供了很多便利性,但是因为单例的生命周期和应用保持一致,使用不合理很容易出现持有对象的泄漏。
珍惜Services资源
如果你的应用需要在后台使用service,除非它被触发并执行一个任务,否则其他时候Service都应该是停止状态。
另外需要注意当这个service完成任务之后因为停止service失败而引起的内存泄漏。
当你启动一个Service,系统会倾向为了保留这个Service而一直保留Service所在的进程。
这使得进程的运行代价很高,因为系统没有办法把Service所占用的RAM空间腾出来让给其他组件,另外Service还不能被Paged out。
这减少了系统能够存放到LRU缓存当中的进程数量,它会影响应用之间的切换效率,甚至会导致系统内存使用不稳定,从而无法继续保持住所有目前正在运行的service。
建议使用IntentService,它会在处理完交代给它的任务之后尽快结束自己。
优化布局层次,减少内存消耗
越扁平化的视图布局,占用的内存就越少,效率越高。我们需要尽量保证布局足够扁平化,当使用系统提供的View无法实现足够扁平的时候考虑使用自定义View来达到目的。
谨慎使用“抽象”编程
很多时候,开发者会使用抽象类作为”好的编程实践”,因为抽象能够提升代码的灵活性与可维护性。然而,抽象会导致一个显著的额外内存开销:他们需要同等量的代码用于可执行,那些代码会被mapping到内存中,因此如果你的抽象没有显著的提升效率,应该尽量避免他们。
使用nano protobufs序列化数据
Protocol buffers是由Google为序列化结构数据而设计的,一种语言无关,平台无关,具有良好的扩展性。类似XML,却比XML更加轻量,快速,简单。如果你需要为你的数据实现序列化与协议化,建议使用nano protobufs。
谨慎使用依赖注入框架
使用类似Guice或者RoboGuice等框架注入代码,在某种程度上可以简化你的代码。
🔥 谨慎使用多进程 🔥
使用多进程可以把应用中的部分组件运行在单独的进程当中,这样可以扩大应用的内存占用范围,但是这个技术必须谨慎使用,绝大多数应用都不应该贸然使用多进程,一方面是因为使用多进程会使得代码逻辑更加复杂,另外如果使用不当,它可能反而会导致显著增加内存。当你的应用需要运行一个常驻后台的任务,而且这个任务并不轻量,可以考虑使用这个技术。
一个典型的例子是创建一个可以长时间后台播放的Music Player。如果整个应用都运行在一个进程中,当后台播放的时候,前台的那些UI资源也没有办法得到释放。类似这样的应用可以切分成2个进程:一个用来操作UI,另外一个给后台的Service。
🔥 剔除不需要的代码 🔥
使用ProGuard来剔除不需要的代码
ProGuard能够通过移除不需要的代码,重命名类,域与方法等等对代码进行压缩,优化与混淆。使用ProGuard可以使得你的代码更加紧凑,这样能够减少mapping代码所需要的内存空间。
🔥 谨慎使用第三方libraries 🔥
很多开源的library代码都不是为移动网络环境而编写的,如果运用在移动设备上,并不一定适合。即使是针对Android而设计的library,也需要特别谨慎,特别是在你不知道引入的library具体做了什么事情的时候。例如,其中一个library使用的是nano protobufs, 而另外一个使用的是micro protobufs。这样一来,在你的应用里面就有2种protobuf的实现方式。这样类似的冲突还可能发生在输出日志,加载图片,缓存等等模块里面。另外不要为了1个或者2个功能而导入整个library,如果没有一个合适的库与你的需求相吻合,你应该考虑自己去实现,而不是导入一个大而全的解决方案。
🔥 设计风格 优化 🔥
设计风格很大程度上会影响到程序的内存与性能,相对来说,如果大量使用类似Material Design的风格,不仅安装包可以变小,还可以减少内存的占用,渲染性能与加载性能都会有一定的提升。
🔥 总结 🔥
内存优化并不就是说程序占用的内存越少就越好,如果因为想要保持更低的内存占用,而频繁触发执行gc操作,在某种程度上反而会导致应用性能整体有所下降,这里需要综合考虑做一定的权衡。
Android的内存优化涉及的知识面还有很多:内存管理的细节,垃圾回收的工作原理,如何查找内存泄漏等等都可以展开讲很多。OOM是内存优化当中比较突出的一点,尽量减少OOM的概率对内存优化有着很大的意义。
相关文章:

Android 面试题 避免OOM(内存优化)三
🔥 OOM介绍(out of memory 内存溢出)🔥 Android和java中都会出现由于不良代码引起的内存泄露,为了使Android应用程序能够快速高效的运行,Android每个应用程序都会有专门Dalvik虚拟机实例来运行,…...

SpringBoot集成Lock4j 底层使用Redission 实现分布锁
Lock4j 在分布式系统中,实现锁的功能对于保证数据一致性和避免并发冲突是非常重要的。Lock4j是一个简单易用的分布式锁框架,而Redisson是一个功能强大的分布式解决方案,可以与Lock4j进行集成。 操作步骤 第一步:添加依赖 首先&…...

TortoiseSVN操作使用
说明 SVN常用于程序代码版本控制,由于业务需求需将生产资料通过SVN进行管控,涉及人员众多,权限分支管理需要细化,特此记录SVN的学习操作. 前言 版本控制是管理信息修改的艺术,它一直是程序员最重要的工具,程序员经常会花时间作出小的修改, 然…...

第五篇-ChatGLM2-6B模型下载
下载chatglm2-6b模型文件 https://huggingface.co/THUDM/chatglm2-6b方法一:huggingface页面直接点击下载 一个一个下载,都要下载方法二:snapshot_download下载文件 可以使用如下代码下载 创建下载环境 conda create --name hfhub pytho…...

【Matlab】基于长短期记忆网络的数据分类预测(Excel可直接替换数据)
【Matlab】基于长短期记忆网络的数据分类预测(Excel可直接替换数据) 1.模型原理2.数学公式3.文件结构4.Excel数据5.分块代码6.完整代码7.运行结果1.模型原理 “基于长短期记忆网络的数据分类预测”是一种利用长短期记忆网络(Long Short-Term Memory, LSTM)进行数据分类任务…...

C++网络编程 TCP套接字基础知识,利用TCP套接字实现客户端-服务端通信
1. TCP 套接字编程流程 1.1 概念 流式套接字编程针对TCP协议通信,即是面向对象的通信,分为服务端和客户端两部分。 1.2 服务端编程流程: 1)加载套接字库(使用函数WSAStartup()),创建套接字&…...

苍穹外卖-day07
苍穹外卖-day07 本项目学自黑马程序员的《苍穹外卖》项目,是瑞吉外卖的Plus版本 功能更多,更加丰富。 结合资料,和自己对学习过程中的一些看法和问题解决情况上传课件笔记 视频:https://www.bilibili.com/video/BV1TP411v7v6/?sp…...

简化Java单元测试数据
用EasyModeling简化Java单元测试 EasyModeling 是我在2021年圣诞假期期间开发的一个 Java 注解处理器,采用 Apache-2.0 开源协议。它可以帮助 Java 单元测试的编写者快速构造用于测试的数据模型实例,简化 Java 项目在单元测试中准备测试数据的工作&…...

P1041 [NOIP2003 提高组] 传染病控制
题目 题目背景 本题是错题,后来被证明没有靠谱的多项式复杂度的做法。测试数据非常的水,各种玄学-做法都可以通过,不代表算法正确。因此本题题目和数据仅供参考。 近来,一种新的传染病肆虐全球。蓬莱国也发现了零星感染者&#…...

TypeScript -- 基础类型
文章目录 TypeScript -- 基础类型let 和 const基本类型写法布尔类型 -- boolean数字类型 -- number字符串类型 -- string数组类型元组类型枚举类型 -- enum任意类型 -- any空值 -- voidNull 和 Undefined不存在的类型 -- never对象 -- object类型断言 TypeScript – 基础类型 1…...

Cookie 与 Session 的作用及区别、结合使用
Cookie的作用 在网站中,http请求是无状态的。也就是说即使第一次和服务器连接后并且登录成功后,第二次请求服务器依然不能知道当前请求是哪个用户。 Cookie的出现就是为了解决这个问题,第一次登录后服务器返回一些数据(Cookie&a…...

【Redis】面试题
1. 为什么要用缓存 1. 提高系统的读写性能。 2. 减轻数据库的压力,防止大量的请求到达数据库,让数据库压力剧增,拖垮数据库。redis数据存储在内存中,高效的数据结构,读写数据比数据库快。 将热点数据存储在redis当中&…...
(学习笔记-硬件结构)CPU如何执行程序?
冯诺依曼模型 冯诺依曼模型主要由五部分组成:运算器、控制器、存储器、输入设备、输出设备。 控制器(Control Unit):从内存中取指令、翻译指令、分析指令,然后根据指令的内存向有关部件发送控制命令,控制相…...

curl: (26) Failed to open/read local data from file/application
Windows10、Windows环境用curl命令上传文件报错: curl: (26) Failed to open/read local data from file/application假设我要上传的文件目录是: F:\我的下载\test.xlsx 错误写法1:使用单引号 curl -X POST "https://xxx/upload&quo…...

2023年深圳杯数学建模 D题 基于机理的致伤工具推断
致伤工具的推断一直是法医工作中的热点和难点。由于作用位置、作用方式的不同,相同的致伤工具在人体组织上会形成不同的损伤形态,不同的致伤工具也可能形成相同的损伤形态。致伤工具品种繁多、形态各异,但大致可分为两类:锐器&…...

DMA传输原理与实现详解(超详细)
DMA(Direct Memory Access,直接内存访问)是一种计算机数据传输方式,允许外围设备直接访问系统内存,而无需CPU的干预。 文章目录 Part 1: DMA的工作原理配置阶段:数据传输阶段: Part 2: DMA数据…...
【《React Hooks实战》——指导你使用hook开发性能优秀可复用性高的React组件】
使用React Hooks后,你很快就会发现,代码变得更具有组织性且更易于维护。React Hooks是旨在为用户提供跨组件的重用功能和共享功能的JavaScript函数。利用React Hooks, 可以将组件分成多个函数、管理状态和副作用,并且不必声明类即…...

Ajax详细讲解
Ajax(Asynchronous JavaScript And XML)即异步 JavaScript 和 XML,是一组用于在网页上进行异步数据交换的 Web 开发技术,可以在不刷新整个页面的情况下向服务器发起请求并获取数据,然后将数据插入到网页中的某个位置。…...

黑苹果如何在macOS Sonoma中驱动博通网卡
准备资源(百度:黑果魏叔 下载) 资源包中包含:AirportBrcmFixup.kext/IOSkywalkFamily.kext/IO80211FamilyLegacy.kext/OpenCore-Patcher 使用方法: 1.将 csr-active-config 设置为 03080000 全选代码 复制 2.在 …...

JVM-Cpu飙升排查及解决
https://blog.csdn.net/m0_37542440/article/details/123679011 1. 问题情况 在服务器上执行某个任务时,系统突然运行缓慢,top 发现cpu飙升,一度接近100%,最终导致服务假死。 2. 问题排查 1. 执行 “top” 命令:查看所…...

exoplayer3 ffmpeg 扩展库编译 aar,导入集成
exoplayer3 ffmpeg 扩展库编译 aar,导入集成。 已经编译完成的aar:https://download.csdn.net/download/mhhyoucom/88086822 编译项目方法: github下载项目:https://github.com/google/ExoPlayer FFmpeg 模块提供 ,…...

Shell免交互
免交互 免交互就是:不需要人为控制就可以完成的自动化操作,自动化运维 Shell脚本和免交互是一个概念,是有两种写法。 Here Document 免交互 使用I/O(输入/输出)重定向的方式将命令的列表提供给交互式的程序或者命令cat read 是一种标准输入…...

设计模式之四:工厂模式
引言:除了使用new操作符之外,还有更多制造对象的方法。同时,实例化这个活动不应该总是公开地进行。 1.简单工厂模式 这里有一些相关的具体类,要在运行时有一些具体条件来决定究竟实例化哪个类。这样的代码(if..elseif…...

斩获CVPR 2023竞赛2项冠军|美团街景理解中视觉分割技术的探索与应用
总第569篇 2023年 第021篇 视觉分割技术在街景理解中具有重要地位,同时也面临诸多挑战。美团街景理解团队经过长期探索,构建了一套兼顾精度与效率的分割技术体系,在应用中取得了显著效果。同时,相关技术斩获了CVPR 2023竞赛2项冠军…...

UE4/5C++多线程插件制作(十五、将模板统一,修改统一后的其他类,修改继承,修改返回类型等)
目录 MTPManageBase.h MTPAbandonable.h MTPAbandonableManage.h MTPThreadInterface.h MTPThreadAgendyManage.h MTPThreadTaskManage.h MTPManage.cpp MTPThreadTaskManage.h...

K8S系统监控:使用Metrics Server和Prometheus
Kubernetes 也提供了类似的linux top的命令,就是 kubectl top,不过默认情况下这个命令不会生效,必须要安装一个插件 Metrics Server 才可以。 Metrics Server 是一个专门用来收集 Kubernetes 核心资源指标(metrics)的…...

数据结构基础之排序算法
在数据结构中,常见的排序算法有以下几种: 冒泡排序(Bubble Sort):通过比较相邻元素并交换它们的位置,每轮将最大(或最小)的元素冒泡到末尾,重复执行直到排序完成。 fun…...

Spark(37):Streaming DataFrame 和 Streaming DataSet 创建
目录 0. 相关文章链接 1. 概述 2. socket source 3. file source 3.1. 读取普通文件夹内的文件 3.2. 读取自动分区的文件夹内的文件 4. kafka source 4.1. 导入依赖 4.2. 以 Streaming 模式创建 Kafka 工作流 4.3. 通过 Batch 模式创建 Kafka 工作流 5. Rate Source…...

SpringBoot集成Thymeleaf
Spring Boot 集成 Thymeleaf 模板引擎 1、Thymeleaf 介绍 Thymeleaf 是适用于 Web 和独立环境的现代服务器端 Java 模板引擎。 Thymeleaf 的主要目标是为开发工作流程带来优雅的自然模板,既可以在浏览器中正确显示的 HTML,也可以用作静态原型…...

算法练习(2):牛客在线编程03 二叉树
package jz.bm;import jz.TreeNode;import java.util.*;public class bm3 {/*** BM23 二叉树的前序遍历*/public int[] preorderTraversal (TreeNode root) {ArrayList<Integer> list new ArrayList<>();preOrder(root, list);int[] res new int[list.size()];fo…...