Android学习总结之MMKV(代替SharedPreferences)
Q1:SharedPreferences 为什么会导致 ANR?MMKV 如何从根本上解决?
高频考察点:Android 主线程阻塞原理、SP 同步 / 异步机制缺陷、MMKV 内存映射技术
- SP 导致 ANR 的三大元凶:
- 同步提交(commit ()):直接在主线程执行磁盘 I/O,写入 XML 文件耗时可达 100ms+(尤其首次写入或文件较大时)。
- 异步陷阱(apply ()):虽异步写入,但依赖
QueuedWork
机制,在Activity.onStop()
/onDestroy()
时会强制等待所有未完成的 apply 任务,若任务堆积则阻塞主线程。 - 首次加载阻塞:首次调用
getXXX()
时,若 SP 尚未加载完数据(异步加载未完成),主线程会陷入wait()
无限等待,典型场景:启动页读取配置。
- MMKV 的解决方案:
- 内存映射(mmap):将文件直接映射到内存地址空间,写入时修改内存即可,由操作系统异步刷盘(非阻塞),省去传统 I/O 的用户态 / 内核态数据拷贝(4 次→0 次)。
- 无队列化异步:取消
QueuedWork
依赖,写入任务提交到独立线程池(默认线程数与 CPU 核心数相关),不阻塞 Activity 生命周期。 - 预加载机制:初始化时直接映射文件,首次读取无需等待加载,数据立即可见。
- 面试应答模板:
“SP 的 ANR 根源在于同步 I/O 和生命周期阻塞,而 MMKV 通过 mmap 实现内存直接操作,异步刷盘且不依赖 Activity 队列,从底层消除了主线程阻塞风险。某大厂曾统计,迁移 MMKV 后 ANR 率从 18% 降至 0.3%,核心就是解决了这两个痛点。”
Q2:MMKV 如何实现多进程数据一致性?相比 SP 的 ContentProvider 方案有何优势?
高频考察点:跨进程通信、文件锁机制、Android 多进程坑点
- SP 多进程的致命缺陷:
- 文件锁(
FileLock
)非原子操作,跨进程读写时可能出现 “写一半” 的情况(如进程 A 写入时被进程 B 打断,导致 XML 文件损坏),某电商 APP 曾因此出现 37% 的数据错乱率。 - 依赖
ContentProvider
通知变更时,需手动处理序列化和回调,实现复杂且易漏更新。
- 文件锁(
- MMKV 的多进程方案:
- 原子锁(flock):基于 Linux 系统级
flock()
锁,保证跨进程读写时的原子性(写时加独占锁,读时加共享锁),避免数据竞争。 - Ashmem 匿名内存:跨进程传输数据时,通过 Android 提供的
Ashmem
(匿名共享内存)传递内存地址,避免敏感数据落地到文件,提升安全性和传输效率。 - 变更广播:内置
ContentProvider
实现跨进程变更通知,自动触发其他进程的缓存刷新,无需开发者手动处理。
- 原子锁(flock):基于 Linux 系统级
- 对比优势:
方案 数据一致性 性能 实现复杂度 安全性 SP+ContentProvider 弱(需手动) 低(序列化开销) 高 数据落盘传输 MMKV 原生多进程 强(原子锁) 高(内存映射) 低(一行代码) Ashmem 防泄漏 - 面试应答模板:
“MMKV 通过系统级 flock 锁保证原子操作,结合 Ashmem 避免数据落盘传输,相比 SP 的文件锁和 ContentProvider 方案,既解决了数据错乱问题,又简化了多进程开发(只需传入MULTI_PROCESS_MODE
参数)。微信支付场景中,多进程并发读写的成功率从 SP 的 63% 提升至 MMKV 的 99.99%。”
Q3:Protobuf 序列化相比 XML 有哪些技术优势?为什么 MMKV 选择它而非 JSON?
高频考察点:数据格式对比、性能优化原理、二进制协议特性
- Protobuf 核心优势(对比 XML/JSON):
- 体积更小:二进制编码(非文本),去除冗余格式(如标签名),数据体积比 XML 缩小 45%(例:100KB XML → 55KB Protobuf),减少磁盘 I/O 和内存占用。
- 解析更快:无需解析复杂标签结构,通过字段编号(如
int32 age = 1
中的1
)直接定位数据,序列化 / 反序列化速度比 XML 快 5 倍(万次操作:Protobuf 20ms vs XML 100ms)。 - 类型安全:基于 IDL(接口定义语言)生成代码,编译期检查数据类型,避免运行时解析错误(如 XML 中字符串误读为数字)。
- 为何不选 JSON?
- JSON 是文本格式,需频繁进行字符解析(如
{}
、""
),性能低于二进制协议。 - 动态类型导致反序列化时需额外类型判断,内存占用更高(需解析成 Map/List 结构)。
- JSON 是文本格式,需频繁进行字符解析(如
- 面试应答模板:
“Protobuf 的二进制编码和字段编号机制,使其在性能和体积上碾压 XML/JSON。MMKV 作为高频读写的存储方案,选择 Protobuf 能大幅减少 I/O 耗时和内存占用,这也是为什么在 1000 次写入测试中,MMKV 仅需 7ms,而 SP 需 1200ms—— 其中 30% 的性能提升来自 Protobuf 编码优化。”
Q4:MMKV 如何避免 mmap 导致的内存泄漏?LRU 缓存机制如何实现?
高频考察点:内存管理、Android 资源回收、WeakReference 应用
- mmap 潜在风险:
- 若长期保持文件映射,且实例未被释放,可能导致内存占用过高(尤其多实例场景)。
- MMKV 的解决方案:
- LRU 缓存淘汰:内部维护一个
LRUCache
,根据最近使用时间自动释放长时间未访问的 MMKV 实例的 mmap 内存,默认最大缓存数量为 64(可通过MMKVConfig
配置)。 - 弱引用关联 Context:实例持有对 Context 的
WeakReference
,避免因 Context 被 MMKV 强引用导致的 Activity 泄漏(如非静态内部类持有 Activity 引用)。 - 进程退出自动释放:mmap 映射的内存由操作系统管理,进程结束后自动回收,无需手动释放。
- LRU 缓存淘汰:内部维护一个
- 面试应答模板:
“MMKV 通过 LRU 缓存和弱引用机制双重保障内存安全。LRU 会淘汰最少使用的实例的 mmap 映射,而弱引用避免 Context 泄漏。实际开发中,即使创建大量 MMKV 实例,内存占用也会稳定在合理范围 —— 某金融 APP 实测,同时维护 200 个实例时,内存波动不超过 5MB。”
Q5:迁移 MMKV 时需要注意哪些兼容性问题?如何验证迁移是否成功?
高频考察点:数据迁移实战、异常处理、灰度测试
- 迁移关键细节:
- 数据格式转换:
- SP 支持的
Set<String>
类型,MMKV 需通过encodeStringSet()
/decodeStringSet()
处理(底层用 Protobuf 重复字段存储)。 - 布尔 / 数值类型可无缝迁移,但需注意 SP 的
getXXX(key, default)
中默认值的处理(MMKV 无默认值概念,需显式判断containsKey()
)。
- SP 支持的
- 多进程模式适配:
- 若原 SP 依赖
Context.MODE_MULTI_PROCESS
(已废弃),需手动切换为 MMKV 的MULTI_PROCESS_MODE
,否则可能出现数据不同步。
- 若原 SP 依赖
- 迁移验证步骤:
- 灰度阶段对比新旧存储的读写耗时、内存占用。
- 编写自动化测试:随机写入 10 万 + 条数据,验证迁移前后
key-value
一致性(推荐用 MD5 校验整体数据)。 - 监控 ANR 率和 Crash 率,重点关注首次初始化和多进程场景。
- 数据格式转换:
- 面试应答模板:
“迁移时需注意数据格式(如 Set 类型)和多进程模式的适配,建议通过MMKV.importFromSharedPreferences(oldSp)
一键迁移,同时保留旧 SP 一段时间(如 7 天)用于数据回滚。某社交 APP 迁移时,通过灰度测试发现getBoolean(key, true)
在 MMKV 中需先判断containsKey(key)
,避免了默认值差异导致的逻辑错误。”
MMKV 实战操作代码详解(带核心注释)
1. 基础初始化与全局配置(必考点:初始化原理)
// MMKV 初始化(建议在 Application.onCreate() 中执行)
class MyApplication : Application() {override fun onCreate() {super.onCreate()// 基础初始化(默认存储路径:/data/data/包名/files/mmkv/)MMKV.initialize(this) // 高级配置(面试常问:如何优化内存/性能)val mmkvConfig = MMKVConfig.defaultMMKVConfig(this).apply {// 配置 LRU 缓存大小(默认 64 个实例,防止内存泄漏)maxCacheSize = 100 // 启用文件加密(AES-CFB-128 模式,需提供 16 字节密钥)cryptoKey = "1234567890123456".toByteArray() // 多进程模式(核心:flock 原子锁)multiProcessEnable = true }// 使用自定义配置初始化(适用于复杂场景)MMKV.initialize(mmkvConfig, this) }
}// 获取默认实例(全局唯一,共享同一存储文件)
val defaultKV = MMKV.defaultMMKV()
// 获取指定 ID 的实例(支持多文件隔离,如不同业务模块独立存储)
val userKV = MMKV.mmkvWithID("user_config")
注释解析:
- 初始化支持 默认配置 和 自定义配置,
mmkvWithID
实现多文件隔离,避免不同模块数据混杂(如用户配置 vs 业务缓存)。 cryptoKey
配置加密时,必须是 16/24/32 字节长度(对应 AES-128/192/256),面试可追问:“为何选择 CFB 模式?”(支持流式加密,适配 MMKV 的增量写入)。
2. 数据读写实战(全类型覆盖 + 性能优化点)
// 写入基础数据类型(原子操作,无锁设计)
fun writeBaseData() {val kv = MMKV.defaultMMKV()// 字符串(自动处理空值,SP 需手动判空)kv.encode("user_name", "John") // 数值类型(支持 Long/Double 等大字段,SP 性能随字段大小下降明显)kv.encode("user_age", 25) kv.encode("user_weight", 65.5f) // 布尔值(底层 Protobuf 用 varint 编码,比 XML 的 <bool> 标签更紧凑)kv.encode("is_vip", true) // 数组/集合(SP 的 Set<String> 需手动处理,MMKV 原生支持)kv.encode("favorite_fruits", setOf("Apple", "Banana"))
}// 读取数据(无首次加载阻塞,直接返回内存映射数据)
fun readBaseData(): String {val kv = MMKV.defaultMMKV()// 安全读取(提供默认值,与 SP 行为一致)val name = kv.decodeString("user_name", "Guest") // 集合读取(直接返回 Set,无需手动解析 XML)val fruits = kv.decodeStringSet("favorite_fruits", emptySet()) // 存在性检查(SP 需通过 getAll() 间接判断,MMKV 直接支持)val hasAge = kv.containsKey("user_age") return name
}// 批量读写(核心优化:避免多次 I/O,SP 需多次 commit/apply)
fun batchWrite() {val kv = MMKV.defaultMMKV()// 开启事务(保证批量操作原子性,失败自动回滚)kv.withTransaction {it.encode("key1", "value1")it.encode("key2", "value2")}
}
注释解析:
withTransaction
实现批量操作原子性,避免部分写入失败(SP 无此机制,需手动保证一致性)。- 数值类型写入时,MMKV 通过 Protobuf 的 Varint 编码(小数值用 1-2 字节存储),比 XML 的文本存储(如
25
需 2 字节字符)更高效。
3. 多进程场景实战(面试重灾区:跨进程一致性)
// 主进程初始化多进程实例(关键参数:MULTI_PROCESS_MODE)
val crossProcessKV = MMKV.mmkvWithID("cross_process_config", MMKV.MULTI_PROCESS_MODE // 启用系统级 flock 锁
)// 子进程读取数据(自动感知主进程变更)
fun childProcessRead() {val kv = MMKV.mmkvWithID("cross_process_config", MMKV.MULTI_PROCESS_MODE)// 注册变更监听(跨进程时自动触发,替代 SP 的 ContentObserver)kv.addOnContentChangedListener { key, oldValue, newValue ->Log.d("MMKV", "Key $key changed from $oldValue to $newValue")}// 读取数据(底层通过 Ashmem 共享内存,避免数据落盘传输)val value = kv.decodeString("cross_key")
}// 主进程写入(自动通知子进程)
fun mainProcessWrite() {crossProcessKV.encode("cross_key", "main_process_value")// 强制刷盘(可选,默认异步刷盘,如需立即持久化调用)crossProcessKV.flush()
}
注释解析:
MULTI_PROCESS_MODE
底层使用flock()
锁,保证跨进程读写原子性(SP 的MODE_MULTI_PROCESS
已废弃,且实现不可靠)。addOnContentChangedListener
内置跨进程广播(基于 ContentProvider),无需手动实现 IPC 通信(SP 需自定义 BroadcastReceiver)。
4. 数据加密实战(金融 / 支付场景必备)
// 初始化加密实例(关键:设置 cryptoKey,面试问:如何保护密钥?)
val encryptedKV = MMKV.mmkvWithID("encrypted_config",MMKV.MULTI_PROCESS_MODE,"16字节长度的密钥".toByteArray() // AES-CFB-128 密钥(必选)
)// 写入加密数据(底层自动加密,不影响上层 API)
encryptedKV.encode("secret_key", "敏感信息如支付密码")// 读取加密数据(自动解密,性能损耗仅 15%,远优于 SP 手动加密)
val secretValue = encryptedKV.decodeString("secret_key")// 密钥保护方案(面试延伸:MMKV 如何防反编译?):
// 1. 密钥存储在 Native 层(通过 JNI 从 SO 库获取,非 Java 层硬编码)
// 2. 使用白盒加密(White-Box Cryptography),防止密钥被逆向工程提取
注释解析:
- 加密对上层 API 透明,写入即加密、读取即解密,无需开发者处理加解密逻辑(SP 需手动实现,易出错)。
- 密钥建议通过 动态生成 + 安全通道传输(如从服务器下发),避免硬编码在 APK 中。
5. 数据迁移实战(从 SP 无缝切换,面试必问迁移步骤)
Step 1:初始化 MMKV(关键配置)
// 在 Application 中初始化 MMKV(建议与 SP 并行运行一段时间)
class MyApplication : Application() {override fun onCreate() {super.onCreate()// 初始化 MMKV(默认配置,如需加密/多进程在此配置)MMKV.initialize(this) // 保留旧 SP 实例(用于对比验证和回滚)spMigrationHelper = getSharedPreferences("old_sp", Context.MODE_PRIVATE)}companion object {lateinit var spMigrationHelper: SharedPreferences // 全局旧 SP 引用}
}
核心注意:
- 初始化 MMKV 时 不建议立即删除 SP,需先并行运行,确保迁移后功能正常
- 若旧 SP 使用多进程模式(
MODE_MULTI_PROCESS
),MMKV 需显式配置MULTI_PROCESS_MODE
Step 2:一键导入 SP 数据(核心迁移接口)
// 核心迁移方法(一行代码完成基础数据迁移)
fun migrateFromSP() {val oldSP = MyApplication.spMigrationHelper // 旧 SP 实例val mmkv = MMKV.defaultMMKV() // MMKV 目标实例// 1. 调用官方迁移工具(内部处理 XML 解析与 Protobuf 转换)MMKV.importFromSharedPreferences(oldSP) // 2. 特殊类型处理(SP 的 Set<String> 需手动迁移,MMKV 不支持自动转换)oldSP.getStringSet("old_set_key", emptySet())?.let {mmkv.encodeStringSet("new_set_key", it) // 显式调用 StringSet 接口}// 3. 处理默认值差异(SP 的 getXxx(key, default) 在 MMKV 中需先判断 key 是否存在)if (!mmkv.containsKey("old_key_with_default")) {mmkv.encode("old_key_with_default", "default_value") // 手动写入默认值}
}
源码级解析:
importFromSharedPreferences
内部逻辑:- 读取 SP 的 XML 文件字节流
- 解析 XML 节点,提取
key-value
对 - 按 MMKV 的 Protobuf 格式重新编码并写入
- Set<String> 迁移陷阱:SP 的
Set<String>
在 XML 中存储为多个<string-array>
节点,MMKV 需通过encodeStringSet()
显式处理,否则会丢失
Step 3:数据一致性验证(生产环境必做)
// 验证迁移后所有数据与旧 SP 完全一致
fun verifyMigrationIntegrity() {val oldSP = MyApplication.spMigrationHelperval mmkv = MMKV.defaultMMKV()// 1. 对比 key 集合(排除 MMKV 自动生成的元数据 key)val mmkvKeys = mmkv.allKeys().toSet()val spKeys = oldSP.all.keys.toSet()check(mmkvKeys.containsAll(spKeys)) { "Key missing: ${spKeys - mmkvKeys}" }// 2. 逐类型对比 value(覆盖所有数据类型)oldSP.all.forEach { (key, spValue) ->when (spValue) {is String -> {val mmkvValue = mmkv.decodeString(key)check(mmkvValue == spValue) { "String mismatch: $key" }}is Int -> {val mmkvValue = mmkv.decodeInt(key, -1) // 提供默认值避免 NPEcheck(mmkvValue == spValue) { "Int mismatch: $key" }}is Boolean -> {val mmkvValue = mmkv.decodeBool(key, false)check(mmkvValue == spValue) { "Boolean mismatch: $key" }}// 处理 Set<String>(SP 独有,需单独验证)is Set<*> -> {@Suppress("UNCHECKED_CAST")val spStringSet = spValue as Set<String>val mmkvStringSet = mmkv.decodeStringSet(key, emptySet())check(mmkvStringSet == spStringSet) { "Set mismatch: $key" }}}}// 3. 性能对比验证(可选,记录迁移前后读写耗时变化)val spReadTime = measureTimeMillis { oldSP.all }val mmkvReadTime = measureTimeMillis { mmkv.allKeys() }Log.d("Migration", "SP Read Time: $spReadTime ms, MMKV Read Time: $mmkvReadTime ms")
}
验证关键点:
- 默认值处理:MMKV 的
decodeXxx(key)
若 key 不存在返回null
(除基础类型可指定默认值),而 SP 的getXxx(key, default)
自动返回默认值,需在迁移后补全默认值逻辑 - 大文件迁移:若 SP 文件超过 1MB,
importFromSharedPreferences
可能耗时较长,建议在子线程执行,避免 ANR
Step 4:逐步废弃旧 SP(灰度与回滚策略)
// 生产环境推荐的灰度迁移流程
fun migrateInGrayMode() {// 阶段 1:双写阶段(同时写入 SP 和 MMKV,持续 1-2 个版本)fun writeData(key: String, value: String) {// 旧逻辑:写入 SPoldSP.edit().putString(key, value).apply() // 新逻辑:写入 MMKVMMKV.defaultMMKV().encode(key, value) }// 阶段 2:单读 MMKV + 对比验证(确认无误后关闭 SP 写入)fun readData(key: String): String {val mmkvValue = MMKV.defaultMMKV().decodeString(key)val spValue = oldSP.getString(key, "")// 对比双写数据一致性(用于监控报警)if (mmkvValue != spValue) {上报迁移不一致异常(key, mmkvValue, spValue)}return mmkvValue ?: spValue // 过渡期回退到 SP(防止 MMKV 数据缺失)}// 阶段 3:完全废弃 SP(确认 MMKV 稳定后清理旧数据)if (迁移验证通过 && 灰度周期结束) {oldSP.edit().clear().commit() // 清空旧 SP 数据删除旧 SP 文件(context, "old_sp.xml") // 物理删除文件(需适配 Android 10+ 存储策略)}
}// 辅助函数:删除旧 SP 文件(注意文件路径)
fun 删除旧 SP 文件(context: Context, spFileName: String) {val spDir = context.filesDir.resolve("shared_prefs")val spFile = spDir.resolve("$spFileName.xml")spFile.delete()
}
生产级最佳实践:
- 双写阶段:避免因迁移工具不完善导致的数据丢失,确保新老存储数据实时一致
- 灰度监控:通过 APM 工具(如 Firebase、Bugly)监控
mmkvValue != spValue
的异常比例,设定阈值(如 >0.1% 触发回滚) - 文件清理:Android 10+ 需通过
Context.deleteFile()
或直接操作文件路径,避免残留旧 XML 文件占用存储
6. 性能压测实战(面试加分项:如何复现 300 倍性能差)
// 写入性能测试(对比 SP/MMKV)
fun testWritePerformance() {val sp = getSharedPreferences("sp_test", Context.MODE_PRIVATE)val mmkv = MMKV.mmkvWithID("mmkv_test")val iterations = 1000// SP 同步写入val spStart = System.currentTimeMillis()repeat(iterations) {sp.edit().putString("key_$it", "value_$it").commit() // 同步提交,阻塞主线程}val spTime = System.currentTimeMillis() - spStart// MMKV 异步写入(默认异步刷盘,非阻塞)val mmkvStart = System.currentTimeMillis()repeat(iterations) {mmkv.encode("key_$it", "value_$it") // 直接写入内存映射,无需等待磁盘}val mmkvTime = System.currentTimeMillis() - mmkvStartLog.d("Performance", "SP Time: $spTime ms, MMKV Time: $mmkvTime ms")// 典型输出:SP 1200ms vs MMKV 7ms(300 倍差距,取决于设备性能)
}
注释解析:
- SP 的
commit()
同步写入在多次调用时累积阻塞时间,而 MMKV 的encode()
基于 mmap 直接操作内存,耗时几乎固定(仅内存操作 + 极短的异步刷盘调度)。 - 压测时建议在子线程执行,避免影响主线程,结果更接近真实场景。
代码级面试扩展
Q:MMKV 写入时为什么不需要显式调用 commit/apply?
答:
MMKV 使用 内存映射(mmap) 技术,encode()
直接修改内存中的映射区域,数据会由操作系统通过 msync()
异步刷盘(默认策略:延迟 500ms 或内存不足时触发)。
对比 SP:commit()
同步刷盘、apply()
异步但依赖队列阻塞,而 MMKV 的异步是真正的非阻塞,写入性能不受磁盘 I/O 影响(见 testWritePerformance
代码)。
Q:多进程模式下,如何保证多个进程同时写入不冲突?
答:
MMKV 在创建实例时通过 MMKV.MULTI_PROCESS_MODE
启用 flock 系统级文件锁:
- 写操作时加 独占锁(LOCK_EX),确保同一时间只有一个进程写入;
- 读操作时加 共享锁(LOCK_SH),允许多个进程同时读取。
该机制比 SP 的FileLock
更可靠(原子性由内核保证),代码示例见crossProcessKV
初始化。
Q:加密功能是否会影响性能?如何平衡安全与效率?
答:
MMKV 采用 AES-CFB-128 流式加密,加密过程与数据写入同时进行(无需等待完整数据块),性能损耗约 15%(实测 1000 次加密写入耗时约 10ms,非加密 7ms)。
对比手动加密 SP:需先序列化数据、加密、写入,耗时可能增加 50% 以上,且易因加密逻辑错误导致数据损坏(见 encryptedKV
代码示例)。
Q:MMKV.importFromSharedPreferences 内部是如何实现的?会有性能问题吗?
答:
- 内部实现:
- 读取 SP 的 XML 文件(位于
data/data/包名/shared_prefs/
目录) - 使用 Android 内置的
XmlPullParser
解析 XML 节点,提取key-value
对 - 按 MMKV 的 Protobuf 格式重新编码,通过 mmap 写入内存映射区域
- 读取 SP 的 XML 文件(位于
- 性能影响:
- 单文件迁移耗时与 SP 文件大小正相关(1MB 文件约 5ms,10MB 约 50ms)
- 建议在子线程执行迁移(避免阻塞主线程),可通过
runOnBackgroundThread { MMKV.importFromSharedPreferences(oldSP) }
实现
Q:迁移后发现部分数据丢失,可能的原因有哪些?
答:
- Set<String> 未手动迁移:MMKV 的
importFromSharedPreferences
不处理Set<String>
类型,需额外调用encodeStringSet()
- SP 文件损坏:若 SP 的 XML 文件因磁盘错误损坏(如突然断电),迁移工具会跳过损坏节点
- 数据类型不匹配:SP 中存储的非标量类型(如通过
putString()
存储 JSON)需手动解析后再写入 MMKV
Q:生产环境如何保证迁移过程的高可用性?
答:
- 双写双读:在迁移期间同时读写 SP 和 MMKV,通过一致性校验确保数据实时同步
- 灰度发布:分批次上线迁移功能(如先开放 1% 用户,逐步扩大到 100%)
- 回滚策略:保留旧 SP 数据一段时间(如 7 天),若出现大面积异常,可通过
MMKV.exportToSharedPreferences()
(需自定义工具)回退数据
相关文章:
Android学习总结之MMKV(代替SharedPreferences)
Q1:SharedPreferences 为什么会导致 ANR?MMKV 如何从根本上解决? 高频考察点:Android 主线程阻塞原理、SP 同步 / 异步机制缺陷、MMKV 内存映射技术 SP 导致 ANR 的三大元凶: 同步提交(commit ()…...
SWiRL:数据合成、多步推理与工具使用
SWiRL:数据合成、多步推理与工具使用 在大语言模型(LLMs)蓬勃发展的今天,其在复杂推理和工具使用任务上却常遇瓶颈。本文提出的Step-Wise Reinforcement Learning(SWiRL)技术,为解决这些难题带…...
【PostgreSQL数据分析实战:从数据清洗到可视化全流程】7.2 PostgreSQL与Python数据交互(psycopg2库使用)
👉 点击关注不迷路 👉 点击关注不迷路 👉 点击关注不迷路 文章大纲 PostgreSQL与Python数据交互:psycopg2库实战指南一、引言:数据交互的桥梁1.1 psycopg2核心优势 二、环境准备与基础连接2.1 安装配置2.1.1 安装psyco…...
【Prompt工程—文生图】案例大全
目录 一、人物绘图 二、卡通头像 三、风景图 四、logo设计图 五、动物形象图 六、室内设计图 七、动漫风格 八、二次元图 九、日常场景图 十、古风神化图 十一、游戏场景图 十二、电影大片质感 本文主要介绍了12种不同类型的文生图技巧,通过加入不同的图像…...
NVM完全指南:安装、配置与最佳实践
发布于 2025年5月7日 • 阅读时间:10分钟 💡 TL;DR: 本文详细介绍了如何完整卸载旧版Node.js,安装NVM,配置阿里云镜像源,以及设置node_global与node_cache目录,打造高效Node.js开发环境。 📋 目…...
成都养老机器人“上岗”,机器人养老未来已至还是前路漫漫?
近日,成都养老机器人“上岗”引发关注,赛博养老这一概念再次成为人们讨论的焦点,究竟赛博养老未来已来,还是仍需漫长等待,引发诸多思考。 成都研发的养老机器人“上岗”确实标志着智慧养老领域的又一进步,…...
数据中心 第十五次CCF-CSP计算机软件能力认证
总结一下图树算法比如krusal 迪杰斯特拉 prim算法喜欢改变距离定义 或者求别的东西 而拓扑排序喜欢大模拟 本题使用kerusal算法求出最后一条边就可以。 ac代码: #include <iostream> #include <vector> #include <algorithm>using namespac…...
【面试 · 一】vue大集合
目录 vue2 基础属性 组件通信 全局状态管理 vueX 路由 路由守卫 vue3 基础属性 组件通信 全局状态管理 Pinia 路由 路由守卫 vue2、vue3生命周期 setup vue2 基础属性 data:用于定义组件的初始数据,必须是一个函数,返回一个对…...
Java 常用的 ORM框架(对象关系映射)
Java 常用的 ORM(对象关系映射)框架有以下几种,每种都有其特点和使用场景: Hibernate ● 特点: ○ 完整的 ORM 框架,功能强大。 ○ 支持缓存机制(一级缓存、二级缓存)。 ○ 支持多种…...
自动化创业机器人:现状、挑战与Y Combinator的启示
自动化创业机器人:现状、挑战与Y Combinator的启示 前言 AI驱动的自动化创业机器人,正逐步从科幻走向现实。我们设想的未来是:商业分析、PRD、系统设计、代码实现、测试、运营,全部可以在monorepo中由AI和人类Co-founder协作完成…...
支持向量机
支持向量机(Support Vector Machine,SVM)是一种有监督的机器学习算法,可用于分类和回归任务,尤其在分类问题上表现出色。下面将从原理、数学模型、核函数、优缺点和应用场景等方面详细介绍。 原理 支持向量机的基本思…...
华为昇腾910B通过vllm部署InternVL3-8B教程
前言 本文主要借鉴:VLLM部署deepseek,结合自身进行整理 下载模型 from modelscope import snapshot_download model_dir snapshot_download(OpenGVLab/InternVL3-8B, local_dir"xxx/OpenGVLab/InternVL2_5-1B")环境配置 auto-dl上选择单卡…...
ZArchiver解压缩工具:高效解压,功能全面
在使用智能手机的过程中,文件管理和压缩文件的处理是许多用户常见的需求。无论是解压下载的文件、管理手机存储中的文件,还是进行日常的文件操作,一款功能强大且操作简便的文件管理工具都能极大地提升用户体验。今天,我们要介绍的…...
ETL介绍
(一)ETL介绍 “ETL,是英文Extract-Transform-Load的缩写,用来描述将数据从来源端经过抽取(Extract)、转换(Transform)、加载(Load)至目的端的过程。ETL一词较…...
2025.05.07-华为机考第三题300分
📌 点击直达笔试专栏 👉《大厂笔试突围》 💻 春秋招笔试突围在线OJ 👉 笔试突围OJ 03. 城市紧急救援队伍协同规划 问题描述 智慧城市建设中,卢小姐负责设计一套紧急救援队伍协同系统。城市被规划为一个 n n n \times n...
缓存菜品-04.功能测试
一.功能测试 redis中的数据已缓存 查询数据时并没有发sql 修改鸡蛋汤价格为5元。 缓存数据没有了 价格修改成功 停售启售是一样的。修改后清理,再次查询又被缓存到redis中。...
跨境电商生死局:动态IP如何重塑数据生态与运营效率
凌晨三点的深圳跨境电商产业园,某品牌独立站运营总监李明(化名)正盯着突然中断的广告投放系统。后台日志显示,过去24小时内遭遇了17次IP封禁,直接导致黑五促销期间损失23%的预期流量。这并非个案——2023年跨境电商行业…...
day 14 SHAP可视化
一、原理——合作博弈论 SHAP(SHapley Additive exPlanations)是一种用于解释机器学习模型预测结果的方法,它基于合作博弈论中的 Shapley 值概念。Shapley 值最初用于解决合作博弈中的利益分配问题。假设有 n 个参与者共同合作完成一项任务并…...
处理PostgreSQL数据库事务死锁过程
查询pg_locks表,获取未得到满足的锁信息: select * from pg_locks where granted is false ; --查询得不到锁的,那就是两个互相等待对方持有的锁查询活动的事务会话进程,和上一步的锁的事务对应起来: select * from …...
大数据、物联网(IoT)、平台架构与设计重构大模型应用
结合大数据、物联网(IoT)、平台架构与设计重构大模型应用,需构建一个数据驱动、实时响应、弹性扩展的智能系统。以下从技术架构、数据流、核心模块设计三个维度展开: 一、整体架构设计 分层架构(基于云-边-端协同): [物联网设备层] → [边缘计算层] → [大数据平台层]…...
开发 Chrome 扩展中的侧边栏图标设置实录(Manifest V3)
在开发自己的 Chrome 扩展 Pocket Bookmarks(口袋书签) 的过程中,我遇到了一个看似简单却颇具挑战的问题:如何在扩展的侧边栏显示自定义图标? 这篇文章记录一下我踩过的坑,以及最终的解决方案。 这里说的侧…...
Baumer工业相机堡盟工业相机如何通过BGAPI SDK在Linux系统下设置多个USB相机(C++)
Baumer工业相机堡盟工业相机如何通过BGAPI SDK在Linux系统下设置多个USB相机(C) Baumer工业相机Baumer工业相机BGAPI SDK在Linux系统下设置USB相机的技术背景Linux系统内核 USB 模块内存的修改内存限制的确定使用 GRUB 引导加载程序修改内存限制使用 U-B…...
zst-2001 历年真题 知识产权
知识产权 - 第1题 发表权有时间限制 其他下面3个没有 c 知识产权 - 第2题 bd是财产权 c 知识产权 - 第3题 b 知识产权 - 第4题 d 知识产权 - 第5题 d 知识产权 - 第6题 d 知识产权 - 第7题 d 知识产权 - 第8题 b是国务院发布的 d没有复制权…...
设备与驱动:UART设备
大部分的嵌入式系统都包括一些I/O设备,例如仪器上的数据显示屏、工业设备上的串口通信、数据采集设备上模拟数据采样、用于保存数据的Flash/SD卡以及网络设备上的以太网接口等,都是嵌入式系统中容易找到的I/O设备例子。 本专栏主要是分享RT-Thread是如何…...
Linux 服务器静态 IP 配置初始化指南
✅ 第一步:确认网络管理方式 运行以下命令判断系统使用的网络管理服务: # 检查 NetworkManager 是否活跃 systemctl is-active NetworkManager# 检查 network(旧服务)是否活跃 systemctl is-active network或者检查配置路径&…...
【ROS2】Nav2源码之行为树定义、创建、加载
1、简述 在 Navigation2 里,机器人的导航是一项复杂的任务,包含路径规划、避障、恢复机制等多个子任务。行为树能把这些子任务组织成清晰的层次结构,让机器人可以依据不同的情况做出合理的决策。例如,当机器人在导航途中碰到障碍物时,行为树可以决定是重新规划路径、尝试…...
Redis持久化存储介质评估:NFS与Ceph的适用性分析
#作者:朱雷 文章目录 一、背景二、Redis持久化的必要性与影响1. 持久化的必要性2. 性能与稳定性问题 三、NFS作为持久化存储介质的问题1. 性能瓶颈2. 数据一致性问题3. 存储服务单点故障4. 高延迟影响持久化效率.5. 吞吐量瓶颈 四、Ceph作为持久化存储介质的问题1.…...
如何统一修改word中所有英文字母的字体格式
1.需求分析 我想让整篇论文中的所有英文字母格式都修改为Time New Roman格式。 2.直观操作流程 点击左上角开始 --> 点击替换 --> 点击更多 --> 点击特殊格式 --> 选择查找内容为任意字母(Y) --> 将光标点到替换内容 --> 点击格式 --> 点击字体 --> …...
服务器上机用到的设备
服务器上机通常需要以下硬件设备: 服务器主机: CPU:选择高性能的多核处理器,如英特尔至强(Xeon)系列或AMD EPYC系列,以满足高并发和多任务处理需求。 内存(RAM)…...
【Java ee 初阶】多线程(8)
Synchronized优化: 一、锁升级 锁升级时一个自适应的过程,自适应的过程如下: 在Java编程中,有一部分的人不一定能正确地使用锁,因此,Java的设计者为了让大家使用锁的门槛更低,就在synchronize…...
数字孪生大屏UI设计
近年来,5G、大数据、云计算等新一代信息技术的蓬勃发展,计算机仿真技术与拟真软件的成熟运用,让数字孪生技术开始蔓延渗透到“互联网”相关的产业中。数字孪生大屏给予了可视化的数据直观窗口,其中展现的动态映射与实时数据让业务流转效率得到了有效提升,管理、运营和决策都能高…...
【Java ee 初阶】多线程(9)上
一、信号量Semaphore 本质上就是一个计数器,描述了一种“可用资源”的个数 申请资源(P操作):使得计数器-1 释放资源(V操作):使得计数器1 如果计数器为0了,继续申请资源ÿ…...
eclipse开发环境中缺少JavaEE组件如何安装
新版本eclipse去掉server了吗?在最近新版本的eclipse里面,确实找不到server模块了,无法配置tomcat等web服务器插件了。我们需要自己手工安装一下javaEE组件才行。 1 1:找到自己当前eclipse版本号码 2:去这个地址&…...
stm32之ADC
目录 1.简介2.逐次逼近型ADC3.基本结构4.输入通道5.转换模式6.触发控制7.数据对齐8.转换时间7.校准10.ADC外围电路11.api和结构体11.1 结构体11.2 api1. ADC_DeInit2. ADC_Init3. ADC_StructInit4. ADC_Cmd5. ADC_DMACmd6. ADC_ITConfig7. ADC_ResetCalibration8. ADC_GetReset…...
从电话到V信语音:一款App实现全场景社交脱身
作为一名资深社恐人士,我深知那些无法脱身的社交场合有多煎熬。上周参加一个行业聚会,面对滔滔不绝的陌生人,我如坐针毡却又找不到合适的离场理由。这时我突然想起之前朋友推荐的一款神器应用,它让我得以优雅脱身。今天就来分享这…...
conda init before conda activate
先conda init 然后退出命令窗口,再重新打开命令窗口再conda activate...
MySQL数据库高可用(MHA)详细方案与部署教程
一:MHA简介 核心功能 二:MHA工作原理 三:MHA组件 四:MHA 架构与工具 MHA架构 Manager关键工具 Node工具 五:工作原理与流程 1: 故障检测 2: 故障切换(Failover) 3 : 切换模式 六&a…...
《Python星球日记》 第44天: 线性回归与逻辑回归
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏:《Python星球日记》,限时特价订阅中ing 目录 一、引言:回归方法的重要性二、线性回归原理与损失函数1. 线性回归的数学模型2. 损失函数:衡量…...
Flutter TabBar / TabBarView 详解
目录 一、引言 二、基本用法 代码解析 三、主要属性 3.1 TabBar 3.2 TabBarView 四、进阶定制:突破默认样式 4.1 视觉样式深度定制 4.2 自定义指示器与标签 4.3 动态标签管理 五、工程实践关键技巧 5.1 性能优化方案 5.2 复杂手势处理 5.3 响应式布局…...
001 环境搭建
🦄 个人主页: 小米里的大麦-CSDN博客 🎏 所属专栏: Linux_小米里的大麦的博客-CSDN博客 🎁 GitHub主页: 小米里的大麦的 GitHub ⚙️ 操作环境: Visual Studio 2022 文章目录 Linux 环境搭建全解析:从历史到实践一、Linux 的起源与…...
Spark-core-RDD入门
RDD基本概念 Resilient Distributed Dataset 叫做弹性分布式数据集,是Spark中最基本的数据抽象,是分布式计算的实现载体,代表一个不可变,可分区,里面的元素并行计算的集合。 - Dataset: 一个数据集合&…...
在scala中,转换算子和行动算子有什么区别
在Scala结合Spark编程中,转换算子(Transformation)和行动算子(Action)有以下区别: 执行机制 **转换算子**: 具有惰性求值(延迟计算)特性 。它对RDD(弹性分布…...
六级阅读---2024.12 卷一 仔细阅读1
文章 Imagine youre an alien sent to Earth to document the behaviour of the mammals inhabiting the planet. You stumble into a movie theatre thats showing the latest Hollywood horror film. Several dozen humans are gathered together in a dark, undercoated r…...
驱动开发硬核特训 · 专题篇:Vivante GPU 与 DRM 图形显示体系全解析(i.MX8MP 平台实战)
视频教程请关注 B 站:“嵌入式Jerry”。 一、背景导读:GPU 与 DRM 到底谁负责“显示”? 在嵌入式 Linux 图形系统中,“画面怎么显示出来”的问题,表面看似简单,实则涉及多个内核子系统与用户态组件的协同&…...
智慧医疗时代下的医疗设备智能控费系统解决方案
—以科技赋能医疗控费,构建精细化管理新生态 一、行业背景与现存痛点 (一)政策驱动与行业挑战 随着DRG/DIP支付改革全面落地、医保基金监管趋严,医疗机构面临“提质增效”与“成本管控”的双重压力。国家卫健委数据显示&#x…...
软件设计师2025
笔记链接 第5章:软件工程基础知识 第6章:结构化开发方法 第10章:网络和信息安全基础知识...
Umi+React+Xrender+Hsf项目开发总结
一、菜单路由配置 1.umirc.ts 中的路由配置 .umirc.ts 文件是 UmiJS 框架中的一个配置文件,用于配置应用的全局设置,包括但不限于路由、插件、样式等。 import { defineConfig } from umi; import config from ./def/config;export default defineCon…...
【软件设计师:数据结构】1.数据结构基础(一)
一 线性表 1.线性表定义 线性表是n个元素的有限序列,通常记为(a1,a2,…,an)。 特点: 存在惟一的表头和表尾。除了表头外,表中的每一个元素均只有惟一的直接前驱。除了表尾外,表中的每一个元素均只有惟一的直接后继。2.线性表的存储结构 (1)顺序存储 是用一组地址连续…...
linux_进程地址空间(虚拟地址空间)
一、进程地址空间是什么? 先看这样一个具体的例子 #include<stdlib.h> #include <stdio.h> #include<unistd.h> int main() {int a1;pid_t idfork();while(1){if(id0){printf("i am child,pid:%d,ppid:%d,a:%d ,&a:%p\n",getpid(…...
计操第四章存储管理
地址再定位...