概览:本文讲解了内存优化的核心知识,内存泄漏、LRUCache缓存、资源加载与释放、内存动态管理,并拓展了其他常见性能优化点。
什么是内存优化?
内存优化:降低应用运行时内存占用,避免内存泄漏、溢出等问题。
内存:RAM(Random Access Memory)随机存取存储器,通常被称为运行内存或主存。
为什么要进行内存优化?
内存占用过高会导致应用卡顿(丢帧)、后台存活率低、设备发烫、耗电增加、启动速度慢等表现,也会间接加剧CPU的负载,如果内存溢出,还会造成应用立即崩溃。
内存优化的方向
1、ArkTS内存优化
typescript
1. 避免内存泄漏
ArkTs有一个垃圾回收机制,认为你不用的变量会帮你回收掉。
比如getData()里,如果只写了let str = ''一行代码,在执行完成后str里面存的内容最后就被回收掉了。
但是现在我又return了一个函数,这个函数访问了外面的str,最终这个函数又被fn()进行一个接收,这就会产生了一个内存泄漏。
内存泄露原因:str始终处于一个可访达的状态,这个变量就不会被垃圾回收。
import { promptAction } from '@kit.ArkUI'
function getData() {
let str = ''
return () => {
promptAction.openToast({
message: `str的内容为:${str}`
})
str += 'a'
}
}
const fn = getData()
@Entry
@Component
struct Index {
build() {
Column() {
Button('闭包')
.onClick(() => {
fn()
})
}
}
}
2. 使用LRUCache缓存
LRUCache类似AppStorage和LocalStorage,但是不具备数据产生变化UI更新的能力,就是一个全局缓存的工具。
优点:AppStorage和LocalStorage可以一直存内容,但是LRUCache有存储上线(默认64),根据最少使用的规则去覆盖之前存过的东西,更轻量级一些。
import { util } from '@kit.ArkTS'
const lruCache: util.LRUCache<string, Object> = new util.LRUCache(64) // 初始化(默认64)
lruCache.put('key', 0) // 添加
lruCache.get('key') // 获取
lruCache.remove('key') // 删除
lruCache.clear() // 清空
3. 使用合理尺寸的图片
8k分辨率的图片:占用内存127MB
4k分辨率的图片:占用内存32MB
2k分辨率的图片:占用内存14MB
2、 资源内存优化
arduino
1.预置图片加载优化
预制图片 > 纹理转换 > 超压缩编码 > 超压缩解码 > 渲染 > 显示
在build-profile.json5里开启
"resOptions": {
"copyCodeResource": {
"enable": false
},
"compression": {
"media": {"enable": true}, // enable 是否启用纹理压缩
"filters": [
{
"method": {"type": "sut","blocks": "4x4"}, // 转换类型
"files": {"path":["./**/*"],"size": [[0,'1000k']]}, // 匹配条件:哪些目录或者哪些尺寸的图片可以开启超压缩
"exclude": {"path": ["./**/*.webp"],"size": [[0,"10k"]]} // 移除匹配
}
]
}
}
文件 耗时 内存占用
原图(.png) 62.103ms 598965KB
纹理超压缩(.sut) 15.309ms 167723KB
自适应可变纹理压缩(.astc) 38.239ms 167723KB
2.资源释放优化
分帧渲染完成后释放:this.displayAbi?.stop() this.displayAbi?.off('frame')
文件读写完成后释放:fileIo.close()
录音完成后释放:stop、release、finish、shutdown
使用完后要停止引擎工作,释放引擎资源。
案例:内存动态管理
1、内存动态管理
scss
EntryAbility.ets
// 内存发生变化时会触发的回调函数,level内存级别
onMemoryLevel(level: AbilityConstant.MemoryLevel): void {
// 判断设备的内存情况,是否进行自己的内存释放
if (level === AbilityConstant.MemoryLevel.MEMORY_LEVEL_MODERATE) {
// 不重要的资源进行内存释放
} else if (level === AbilityConstant.MemoryLevel.MEMORY_LEVEL_LOW) {
// 停止一些非核心任务
} else if (level === AbilityConstant.MemoryLevel.MEMORY_LEVEL_CRITICAL) {
// 释放所有不影响程序运行的内存
}
}
2、缓存工具 ets > utils > MemoryManager
typescript
import { util } from '@kit.ArkTS'
class MemoryManger<T> {
lruCache: util.LRUCache<string, T>
constructor(length = 50) {
this.lruCache = new util.LRUCache(length)
}
put(key: string, value: T) {
this.lruCache.put(key, value)
}
get(key: string) {
return this.lruCache.get(key)
}
clear() {
this.lruCache.clear()
}
}
// 画布数据
export const canvasMemory = new MemoryManger<ImageData>()
3、画布
Canvas画布:developer.huawei.com/consumer/cn...
CanvasRenderingContext2D:developer.huawei.com/consumer/cn...
kotlin
import { canvasMemory } from '../utils/MemoryManager'
@Entry
@Component
struct Index {
ctx: CanvasRenderingContext2D = new CanvasRenderingContext2D()
@State canvasWidth: number = 0
@State canvasHeight: number = 0
@Watch('setColor') @State colorVal: number = 0
@Watch('setWidth') @State widthVal: number = 6
@State step: number = 0 // 画布展示的是哪一步
@State maxStep: number = 0 // 绘制的总步数
build() {
Column() {
this.CanvasBuilder() // 画布
Column({ space: 12 }) {
this.canvasCacheBuilder() // 画布缓存
this.canvasModificationBuilder() // 画布修改
}.width('100%')
.padding(12)
}.width('100%')
.height('100%')
.backgroundColor('#f5f7f8')
}
// 画布
@Builder
CanvasBuilder() {
Canvas(this.ctx)
.width('100%')
.layoutWeight(1)
.backgroundColor('#fff')
.onReady(() => {
this.ctx.lineWidth = this.widthVal
this.ctx.strokeStyle = 'rgb(0,0,0)'
})
.onAreaChange((_, _new) => {
this.canvasWidth = _new.width as number
this.canvasHeight = _new.height as number
})
.gesture(PanGesture()
.onActionStart((e) => {
this.ctx.beginPath() // 开始
this.ctx.moveTo(e.fingerList[0].localX, e.fingerList[0].localY)
})
.onActionUpdate((e) => {
this.ctx.lineTo(e.fingerList[0].localX, e.fingerList[0].localY)
this.ctx.stroke()
})
.onActionEnd(() => {
this.ctx.closePath() // 结束
this.step++ // 画完一步之后+1
this.maxStep++
const canvasData = this.ctx.getImageData(0, 0, this.canvasWidth, this.canvasHeight)
canvasMemory.put(this.step.toString(), canvasData) // 存储画布数据
})
.onActionCancel(() => {
this.ctx.closePath() // 结束
this.step++
this.maxStep++
const canvasData = this.ctx.getImageData(0, 0, this.canvasWidth, this.canvasHeight)
canvasMemory.put(this.step.toString(), canvasData) // 获取画布数据
}))
}
// 画布缓存
@Builder
canvasCacheBuilder() {
Row() {
Button('上一步')
.fontSize(24)
.enabled(this.step > 0)
.onClick(() => {
this.step--
const canvasData = canvasMemory.get(this.step.toString())
if (!canvasData) {
this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
} else {
this.ctx.putImageData(canvasData, 0, 0)
}
})
Button('下一步')
.fontSize(24)
.enabled(this.step < this.maxStep)
.onClick(() => {
// TODO
this.step++
const canvasData = canvasMemory.get(this.step.toString())
this.ctx.putImageData(canvasData, 0, 0)
})
Button('清空画布')
.fontSize(24)
.onClick(() => {
this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
})
}.width('100%')
.justifyContent(FlexAlign.SpaceAround)
}
// 画布修改
@Builder
canvasModificationBuilder() {
Column({ space: 12 }) {
Row({ space: 12 }) {
Text('画笔颜色')
Slider({
value: $$this.colorVal,
min: 0,
max: 255 * 7,
})
.blockColor(this.setColor())
.selectedColor(Color.Transparent)
.trackColor(new LinearGradient([{ color: 'rgb(0,0,0)', offset: 0 },
{ color: 'rgb(0,0,255)', offset: 1 / 7 }, { color: "rgb(0,255,0)", offset: 2 / 7 },
{ color: "rgb(0,255,255)", offset: 3 / 7 }, { color: "rgb(255,0,0)", offset: 4 / 7 },
{ color: "rgb(255, 0, 255)", offset: 5 / 7 }, { color: "rgb(255, 255, 0)", offset: 6 / 7 },
{ color: "rgb(255, 255, 255)", offset: 1 }]))
.layoutWeight(1)
}
.width('100%')
Row({ space: 12 }) {
Text('画笔粗细')
Slider({
value: $$this.widthVal,
min: 2,
max: 10,
style: SliderStyle.InSet
})
.selectedColor(Color.Transparent)
.trackColor(Color.Black)
.blockSize({
width: this.widthVal,
height: this.widthVal
})
.layoutWeight(1)
}
.width('100%')
}
.justifyContent(FlexAlign.SpaceAround)
}
setColor() {
const color = Math.floor(this.colorVal / 255)
.toString(2)
.padStart(3, '0')
.split('')
.reverse()
.join(',')
.replace('0', (this.colorVal % 255).toString())
.split(',')
.reverse()
.map((item: string) => {
return item === '1' ? '255' : item
})
.join(',')
this.ctx.strokeStyle = `rgb(${color})`
return `rgb(${color})`
}
setWidth() {
this.ctx.lineWidth = this.widthVal
}
}
其他优化
arduino
1.尽量使用const声明常量
2.减少使用嵌套 export*/import* 的方式
3.减少全局变量存储,使用局部变量提升属性访问速度
4.防止传感器滥用
5.减少不必要的后台任务,防止抢占资源
6.防止算法时间复杂度失控
7.清理冗余文件和代码
8.使用HSP代理HAR(HAR包跟随打包)
9.删除无用依赖