新闻详情

新闻详情

首页 / 资讯中心 / 详情

内存泄漏排查实战:Android、Native与JVM工具链

发布时间:2026/9/29 4:00:31来源:尧图网络
内存泄漏排查实战:Android、Native与JVM工具链
内存泄漏这四个字在客户端开发里几乎就是一句我知道它在那儿但一时半会儿抓不到它。真到了线上用户反馈用久了就卡、切几个页面就闪退你手上能用的检查工具其实就那么几类看内存曲线的、抓堆转储的、自动找引用链的、还有直接盯 native 分配的。工具本身不难用难的是搞清楚什么时候该用哪一个以及拿到数据之后怎么读。这篇就把我这些年反复用过的几套工具串起来讲一遍从 Android 端的 LeakCanary、Memory Profiler 一直到 MAT、ASan、Perfetto再到服务端 JVM 的那套老办法中间穿插大量实操步骤和踩坑记录。适合已经能写业务代码、但对内存问题还是一头雾水的同学也适合想把手里的排查流程再规范一遍的老手。1. 先把涨得快和回不去分开判断是不是真泄漏很多人一看到 Profiler 里那条内存曲线往上走第一反应就是泄漏了。然后花两天时间抓堆、看引用链最后发现是图片缓存没做上限。这种时间浪费得非常可惜因为排查方向从一开始就定错了。真正的内存泄漏有一个非常明确的定义对象已经不再被业务需要但仍然被 GC Roots 可达导致无法回收。注意这里的关键词是不再被业务需要而不是内存占用高。1.1 三种内存曲线的形态差异先把三种常见情况的表现说清楚你对号入座一下能省掉大量无谓的排查。内存抖动的表现是曲线呈锯齿状频繁上下大幅波动。典型的触发场景是在onDraw、onScroll或者循环里做字符串拼接、创建临时对象、装箱拆箱。这类问题的危害是频繁触发 GC表现为滑动掉帧而不是最终 OOM。它跟泄漏是两回事处理方式是减少临时对象分配比如把String拼接换成StringBuilder复用、把循环里的对象创建提到循环外。缓存膨胀的表现是曲线一路缓涨但在内存压力下能被系统回收一部分。图片库、列表数据、HashMap 缓存都属于这一类。它最后也会 OOM但根因是没有上限而不是该释放的没释放。处理方式很直接给 LruCache 设maxSize用onTrimMemory分级清理。真正的泄漏表现最典型进一个页面内存涨一截退出这个页面内存不降反复进出十次内存阶梯式上升每次的增量基本一致。这种规律的阶梯曲线基本可以直接判定为页面级对象泄漏最常见的就是 Activity 或 Fragment 没被回收。这三种曲线的区分不需要什么高级工具Android Studio 的 Profiler、甚至adb shell dumpsys meminfo连续采样几次就能看出来。我一般的习惯是先手动触发一次 GCProfiler 里那个垃圾桶形状的按钮再进出一个可疑页面再触发 GC看内存能不能回到基线。回不去才值得往下深挖。注意判断泄漏一定要在 GC 之后看。没触发 GC 的内存上涨可能只是对象还在新生代里待着这不叫泄漏。1.2 先定层再选工具Java 堆、Native 堆、系统侧确定了是真泄漏之后第二件事是定层。Android 应用的内存分好几块Profiler 里能看到 Java、Native、Graphics、Code、Stack、Others 这几类。Java 堆的泄漏用 LeakCanary 和 MAT 就能解决Native 堆的泄漏这两样工具基本无能为力得换 ASan、LSan 或者 Perfetto 的 heapprofd。怎么快速分清是哪一层看 Profiler 里各条曲线的增速。如果 Java 那条线明显阶梯上升就是 Java 层。如果 Native 那条线一直涨而 Java 很平稳大概率是 native 内存问题比如图片解码后的 native bitmap、OpenGL 纹理没释放、音视频解码器没关。如果是 Graphics 涨多半跟 Surface、Canvas、动画有关。这个定型动作看起来简单但它是整个排查流程里性价比最高的一步。我见过太多次一群人对着 Java 堆转储文件看到眼睛发酸结果问题出在 native 侧的一个解码器上。选错工具后面所有努力都是白费。2. Android 现场的轻量取证adb 与 Memory Profiler 的分工不是所有排查都需要一上来就抓几百 MB 的堆转储。在真机上做初步定位时adb命令加上 Profiler 这套组合已经能覆盖八成场景而且成本极低不需要改动业务代码也不需要调试包之外的特殊配置。这一节我把这两类手段的具体用法和看数据的重点拆开讲。2.1 dumpsys meminfo 里最该看的那几行数字dumpsys meminfo是我最常用的第一把刀因为它输出快、信息密度高而且在线上灰度包里也能通过日志系统采集。基本用法adb shell dumpsys meminfo com.example.app输出的前半部分是内存分类统计Pss Total、Private Dirty、Heap Size、Heap Alloc、Heap Free 这些数字。但对排查泄漏来说真正关键的是后半部分那几个对象计数adb shell dumpsys meminfo com.example.app -d加-d参数会打印更详细的对象统计重点看这几项字段含义泄漏时的表现Activities当前存活的 Activity 实例数退出页面后不减少持续增长ViewRootImpl当前存活的视图树根节点数与 Activities 同步异常增长AppContextsApplication Context 实例数正常恒为 1大于 1 说明有异常Views当前存活的 View 实例数单页面反复进出后不回落Cursors未关闭的游标数长期大于 0 说明有未关闭的查询WebViews存活的 WebView 实例数与页面生命周期不匹配我自己最常用的是Activities和ViewRootImpl这两个数。操作方法很简单进页面之前记一次数退出页面、触发 GC、再记一次数如果 Activities 没有回到进页面之前的值那基本可以坐实这个页面泄漏了。这个方法的好处是不需要任何工具链一条命令就能定位到哪个页面泄漏然后再用后面讲的工具去查为什么泄漏排查范围一下子缩小了。提示不同厂商 ROM 对dumpsys meminfo的输出格式有细微差异字段名可能略有不同但 Activities、ViewRootImpl 这类关键计数基本一致。2.2 Memory Profiler 抓堆的三个动作与时机Profiler 的 Memory 面板比纯粹的堆转储更直观因为它是实时曲线加上可交互的堆快照。但要用好它得搞清楚三个动作的顺序顺序错了数据就没意义。第一个动作是等一下再抓。不要刚进页面就 dump那时候对象还在创建过程中。等到页面完全展示稳定之后再操作。第二个动作是手动触发 GC。按下那个垃圾桶图标等内存曲线掉到基线。这一步的目的是把可以回收的对象清掉剩下的才是真正被持有的。第三个动作是 Dump Java heap。抓完快照后在 Profiler 的类列表里按 Retained Size 排序看哪个类的实例数和内存占用异常。比如一个本该只有一个实例的 Presenter 类出现了几十个实例或者某个页面的 View 类数量远大于当前打开的页面数这些都是很明确的信号。Profiler 里还有个常被忽略的功能Record Java/Kotlin allocations。它会记录一段时间内所有对象的分配堆栈适合排查短生命周期的对象滥用比如在循环里反复创建 Bitmap。这个跟泄漏是两个场景别搞混。实测下来Profiler 的优点是可视化好、上手快缺点是它抓的快照分析能力弱于 MAT没法方便地看引用链。所以我的流程通常是Profiler 定位到可疑的类然后决定是继续用 MAT 深挖还是直接用 LeakCanary 在复现场景里自动抓引用链。3. LeakCanary把找引用链这件事自动化如果说前两节讲的是人工排查那 LeakCanary 就是把整个流程自动化了。它的价值不在于发现泄漏——前面说过曲线和 Activities 计数已经能发现——而在于它直接把从 GC Roots 到泄漏对象的那条最短强引用路径打出来。这条路径是人工用 MAT 一步步点出来的东西LeakCanary 几秒钟就能给你。3.1 弱引用加 ReferenceQueue 判断对象死活LeakCanary 判断一个对象是否泄漏的机制说白了就是等 GC 回收它等不到就说明有人抱着不放。具体实现上它用ObjectWatcher来登记需要观察的对象登记时创建的是一个KeyedWeakReference带 key 的弱引用同时把这个弱引用注册到一个ReferenceQueue上。流程是这样的调用watch登记对象后LeakCanary 会主动触发一次 GC。GC 之后如果这个对象真的被回收了那么对应的弱引用会被放入ReferenceQueue程序从队列里 poll 到这个引用就知道对象已经正常释放了。如果等了一段时间默认是 5 秒队列里还是没出现它说明对象仍然被强引用持有LeakCanary 就会 dump 一次堆交给内置的 Shark 分析引擎去算引用链。注意LeakCanary 触发 GC 这个动作本身是有成本的所以它默认只在 debug 包里工作线上包不要带。理解了这个机制你就能明白为什么 LeakCanary 有时会报误报——如果对象只是暂时被某个短命的对象持有5 秒窗口过后就释放了它其实不是泄漏。LeakCanary 对这种情况的处理是标记为UNKNOWN而不是LEAKING这在后面 3.3 里展开讲。3.2 接入姿势与自定义关注对象LeakCanary 2.x 的接入简单到有点反直觉只需要在模块的 gradle 文件里加一行依赖不需要在 Application 里写任何初始化代码dependencies { debugImplementation com.squareup.leakcanary:leakcanary-android:2.14 }它会通过一个 ContentProvider 在应用启动时自动完成初始化。自动监控的范围包括 Activity、Fragment、Fragment 的 View、ViewModel 和 Service。这里有个细节值得注意Service 的监控在旧版本里是默认关闭的因为 Service 的生命周期判断需要额外调用如果你的项目里 Service 泄漏高发需要手动在 Service 的onDestroy里调用AppWatcher.objectWatcher.watch(this)。另外如果你有自定义的对象需要监控比如自己写的单例管理器、编译期生成的组件持有者可以手动登记AppWatcher.objectWatcher.watch( watchedObject myObject, description MyManager instance )这套机制我一般在两种场景下用一是怀疑某个全局注册的监听器没注销二是排查自定义 View 被谁持有时。手动watch加上业务复现路径比漫无目的地抓堆高效得多。3.3 leak trace 里 Leaking: YES 与 UNKNOWN 的区别LeakCanary 输出的 leak trace 是从 GC Roots 一路画到泄漏对象的引用链每一层都会标注对象的存活状态。这里最关键的一点是要看链上每一个节点找到那个不该存在的引用。典型的输出会长这样一个 Activity 被一个匿名内部类持有这个内部类又被一个 MessageQueue 持有的 Message 持有Message 最后被主线程持有。看到这条链结论就很清楚了——某个 Handler 发了延时消息页面销毁时没有removeCallbacksAndMessages(null)。链上标注Leaking: YES的对象是确定泄漏的Leaking: NO说明它本身是应该长期存活的比如主线程、ApplicationLeaking: UNKNOWN表示 LeakCanary 无法判断它的生命周期。排查的重点是那些 Leaking: YES 但本不该长期存活的对象通常泄漏的断点就在它附近。还有一个提高信噪比的技巧LeakCanary 支持配置referenceMatchers把已知的系统框架持有行为比如某些 ROM 上InputMethodManager短暂持有 View标记为可忽略。不配置的话你会被大量系统层面的噪音淹没。这部分配置需要在自定义的AppWatcher.Config或者LeakCanary.Config里做具体做法是往LeakCanary.config里追加自己的引用匹配规则。4. 堆转储到手之后MAT 里的分析路径不是所有环境都能跑 LeakCanary。线上包、用户设备、没有调试权限的场景你手里只有一份从崩溃平台或者用户那里捞回来的堆转储文件。这时候 MAT 就是主力。MAT 的分析思路和 LeakCanary 自动生成引用链不太一样它是给你一堆视图让你自己找线索所以更需要方法。4.1 hprof-conv 什么情况下还需要Android 早期导出的 hprof 文件带的是 Android 特有的格式包含了 zygote 堆的一些信息标准 MAT 打不开需要用 SDK 里的hprof-conv转换hprof-conv input.hprof output.hprof这个工具在platform-tools目录下Android Studio 用户的 SDK 路径里都有。需要注意从 Android Studio 3.0 开始Profiler 导出的 hprof 已经是标准格式可以直接拖进 MAT不再需要转换。如果你拿到的 hprof 打开时报格式错误先检查一下来源——自己用am dumpheap抓的、或者从某些内存监控 SDK 导出的通常还是要转。adb shell am dumpheap com.example.app /data/local/tmp/heap.hprof adb pull /data/local/tmp/heap.hprof用am dumpheap抓的文件默认是 Android 格式记得转。4.2 Histogram 和 Dominator Tree 各自解决什么问题MAT 的两个核心视图经常让人搞混我把它们的分工说清楚。Histogram是按类聚合的实例统计显示每个类的实例数、Shallow Size对象自身占用、Retained Size回收它之后能释放的总内存。它的强项是发现异常数量。比如Activity类下有 5 个实例但当前只有一个页面那就有问题又比如某个自定义Callback类有 200 个实例明显超出了预期。用正则过滤类名能很快缩小范围。Dominator Tree是按支配关系组织的显示的是如果回收某个对象能连带释放多少内存。它的强项是发现内存大户。一个巨大的 Bitmap 或者一个超大的数组在 Dominator Tree 里会非常显眼你能看到它下面支配了一大片内存。有些情况下泄漏的对象本身不大但它持有了几十 MB 的数据这种就必须用 Dominator Tree 才看得出来。我的一般顺序是先用 Histogram 按实例数找异常类确定可疑类后再在 Dominator Tree 里看它实际占了多少内存、持有了什么。两个视图配合着看比单看一个效率高得多。表格对比一下更容易记视图排序依据主要用途典型场景Histogram类名分组看实例数找数量异常的类Activity 多实例、Listener 堆积Dominator Tree支配关系看 Retained Size找内存大户大图未释放、超大缓存Top Consumers综合占用快速定位占用前几名初步摸底Duplicate Classes类重复加载排查多 ClassLoader插件化、热修复场景4.3 Path to GC Roots 一定要排除的引用类型找到可疑对象之后右键它选择Path to GC Roots然后务必选择exclude weak/soft references。这一步是新手最容易出错的地方。原因很简单弱引用和软引用在 GC 发生时会被回收它们本身不构成泄漏。如果不排除这两类引用你看到的引用链上会出现大量来自 WeakHashMap、缓存容器、监听器注册表的链路这些都不是真正的持有者。排除之后剩下的强引用路径才是问题所在。还有一个需要排除的是 Finalizer 相关的引用。某些对象在被回收前会进入 FinalizerQueue如果看到链路上有FinalizerReference那说明对象其实正在被回收流程处理不是泄漏。这两个排除项做完引用链往往会变得干净很多一眼就能看出是谁在持有。顺带提一个很多人不知道的点MAT 里可以看对象的被支配者和支配者两种关系。右键选Immediate Dominators能看到谁支配了它反过来看Retained Set能看到它支配了哪些对象。排查一个复杂对象图的时候这两个视角能帮你快速理清谁持有谁。5. Native 与跨语言场景ASan、LSan、Perfetto 补位前面整条链路都是围绕 Java 堆展开的。但现实中的 OOM 有很大一部分发生在 native 侧尤其是涉及音视频、图像处理、自研引擎的应用。这时候 LeakCanary 和 MAT 全部失效因为它们看不到 native 内存的分配和释放。这一节讲 native 侧该怎么查。5.1 Native 泄漏的证据链和 Java 完全不同Native 内存泄漏的排查逻辑跟 Java 是不同的Java 侧你有完整的对象图和引用关系能顺着引用链找持有者Native 侧你面对的是裸的内存地址没有对象概念你只能看到某处分配了 4MB从来没有释放。Android NDK 提供了AddressSanitizerASan和LeakSanitizerLSan。ASan 的定位是检测越界访问和释放后使用LSan 则是专门检测内存泄漏的——它在程序退出时扫描还有哪些分配没有释放并打印出分配时的调用栈。这个调用栈就是 native 泄漏排查里最有价值的信息。接入方式是在build.gradle或者 CMake 配置里开启 sanitizer重新编译一个 debug 包android { defaultConfig { externalNativeBuild { cmake { arguments -DANDROID_STLc_shared } } } buildTypes { debug { // ASan 需要配合 android:debuggable 和特定配置 } } }实际配置比这复杂一些需要在 Application 里加载 ASan 的运行库或者通过wrap.sh脚本启动应用。官方文档里有完整的步骤。关键点在于ASan 会显著拖慢应用运行速度并且内存占用会翻好几倍所以只能在小规模的复现场景里用不能拿它做长时间压测。LSan 的局限也需要知道它只能检测程序退出时仍未释放的内存。如果泄漏的指针被某个全局变量持有LSan 就认为它还是可达的不会报出来。这种情况下需要一个能持续观察 native 内存增长的工具。5.2 Perfetto/heapprofd 在系统级归因里的位置当 LSan 报不出来但 native 内存确实在涨时Perfetto 的heapprofd就是下一个选择。它做的是 native 堆采样分析通过周期性采样和拦截 malloc/free 调用记录每个分配点的调用栈和累计分配量。它的使用方式是配置 trace config 文件指定要采集的进程和采样参数然后通过命令行启动adb shell perfetto -c /data/misc/perfetto-configs/heap.cfg --txt -o /data/misc/perfetto-traces/heap.pftrace抓完之后把 trace 文件拉到本地在 Perfetto 的网页界面里打开就能看到按调用栈聚合的 native 分配视图。heapprofd 的优势是采样开销低可以在接近真实的运行环境中用这是 ASan 做不到的。另外Perfetto 也能做系统级的内存归因比如查看各个进程的 Pss 变化、分析内存回收行为、观察 lmkd 的杀进程时机。当你的应用被系统杀掉但你怀疑不是自己的问题而是系统内存压力导致的Perfetto 的系统级 trace 能给出证据。6. 服务端 JVM 与工具选型对照前面的内容偏 Android但内存泄漏这个问题在服务端同样普遍尤其是长时间运行的应用。服务端排查的工具体系跟 Android 有重叠也有差异这里单独拎出来说因为很多人从客户端转到服务端后会发现原来那套东西不全适用。6.1 jstat、jmap 与 MAT 的配合流程服务端 JVM 的第一步永远是看 GC 情况用jstatjstat -gcutil pid 1000 10它会每秒打印一次各个分代的占用百分比和 GC 次数、耗时。如果老年代O的占用率一直上升Full GC 之后也降不下来那就是典型的服务端内存泄漏。确认之后用jmap抓堆jmap -dump:live,formatb,fileheap.hprof pid注意live这个参数它表示只 dump 存活对象会先触发一次 Full GC。加上它能让 dump 出来的文件小很多也更能反映真实泄漏情况。代价是这次 Full GC 会让服务停顿几秒到几十秒具体取决于堆大小。抓到的 hprof 直接用 MAT 打开分析思路跟前面讲的一样Histogram 找异常实例数Dominator Tree 找内存大户Path to GC Roots 排除弱引用和软引用之后看持有者。还有一个服务端特有的工具JProfiler 和 YourKit这类商业分析器。它们相比 MAT 的优势是能实时连接正在运行的 JVM、能看 CPU 和内存的关联、能追踪线程状态适合排查那些内存涨的同时 CPU 也在飙的复合问题。如果预算允许这两个工具在服务端排查效率上确实比 MAT 高一个档次。6.2 各工具适用场景对照表把这些工具按场景整理一下方便你遇到问题时快速决策工具运行环境核心能力开销适用阶段dumpsys meminfoAndroid 真机对象计数、内存分类极低初步定位页面Memory ProfilerAndroid Studio实时曲线、堆快照中复现与初步分析LeakCanaryAndroid debug 包自动引用链分析中高触发 GC开发期日常排查MAT桌面深度堆分析无离线拿到 hprof 后ASan / LSanAndroid debug 包native 泄漏检测极高小规模复现Perfetto (heapprofd)Android 真机native 采样分析低接近真实环境jstat / jmap服务端 JVMGC 观察、堆 dump低 / 高停顿服务端排查JProfiler / YourKit服务端 JVM实时综合分析中复杂复合问题这张表我建议存下来。很多人的问题不是工具不会用而是手里有一堆工具但不知道该抽哪一把。7. 几个反复踩到的坑工具用熟了之后剩下的问题基本都集中在数据怎么读和环境怎么配上。这一节讲几个我自己反复踩过、也见过很多人踩的坑。7.1 混淆后的 hprof 与类名还原Release 包抓出来的 hprof类名全是a.a.a这种看引用链跟看天书一样。解决办法是用抓包时对应版本的混淆映射文件mapping.txt还原。MAT 本身不带这个功能需要用工具把 hprof 里的类名替换回去。常见的做法有两个一是用官方提供的retrace工具先把 hprof 里的类名提取出来做映射二是用社区里的一些 hprof 混淆还原脚本。我自己更常用的是简单粗暴的方式——在 MAT 里打开文件后把 mapping.txt 里的映射关系手动对照着看关键的那几个类因为真正需要看清楚的类通常就那么三五个没必要全量还原。这里有个必须注意的点mapping.txt 必须和抓包时的包版本严格对应。混淆映射在不同版本之间是会变化的用错版本还原出来的类名同样是错的而且错得很隐蔽容易误导排查方向。线上抓包时一定要把版本号和 mapping 文件对应存档。7.2 弱引用链、Finalizer 造成的误报这个坑在 4.3 里提过但值得单独强调一遍因为它太常见了。用 MAT 查引用链时如果忘了排除弱引用和软引用你会看到很多看起来泄漏的链路比如某个对象被WeakHashMap持有、被ThreadLocal的内部结构持有。这些实际上都不会阻止 GC 回收对象。我有一个同事曾经花了整整一天追一条ThreadLocalMap的链路最后发现是排查方法错了。另一个容易误判的是 Finalizer。如果引用链上出现FinalizerReference或者FinalizerWatchdogDaemon说明这个对象正在等待执行finalize方法它最终还是会被回收的。这种链路也不代表泄漏。一个简单的验证方法找到可疑对象后手动触发几次 GCLeakCanary 会自动触发MAT 里没这个功能需要在应用侧触发然后再抓一次堆对比。如果两次抓的堆里这个对象的实例数明显减少了那之前的链路就是误报。这个方法虽然笨但在关键判断上非常可靠。7.3 dump 带来的卡顿与线上风险最后说一个运维层面的坑堆转储本身会暂停应用。在 Android 上早期的 dump 机制会挂起所有线程一个几百 MB 的堆转储能让应用卡死好几秒甚至更久用户很可能以为应用崩了。Android 11 之后引入了 fork 子进程来做 dump暂停时间大幅缩短但依然有明显开销。服务端这边更严重。前面提过jmap加上live参数会触发 Full GC大堆的情况下停顿几十秒完全有可能对于在线服务来说这是一次实打实的可用性风险。所以线上环境抓堆一定要走审批和限流流程最好在流量低峰期做并且提前准备好降级方案。如果不想承担这种风险替代方案是用采样式的监控定期采集dumpsys meminfo的关键计数、或者用轻量的内存监控组件持续观察增长趋势只在确认必要时才做完整 dump。牺牲一部分精确度换取线上稳定性这个权衡在实际生产环境里通常是值得的。我个人这些年的体会是内存泄漏排查真正的门槛从来不是工具本身而是判断力——判断是不是真泄漏、判断在哪一层、判断哪条引用链是噪音哪条是线索。工具只负责把数据摆到你面前读数据这件事没人能替你做。所以我现在的习惯是每排查完一个泄漏问题都会把当时的曲线截图、引用链、修复方式记到一个自己的案例库里攒到几十条之后你会发现大部分泄漏其实就那么几个套路看一眼曲线形态心里就有数了。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Claude Code 入门:安装配置、对接 DeepSeek 与实战改代码 2026/9/29 10:41:28

Claude Code 入门:安装配置、对接 DeepSeek 与实战改代码

1. 认识 Claude Code:它到底是个什么工具1.1 一个能直接帮你改代码的AI命令行助手Claude Code 是 Anthropic 推出的命令行编程助手,它不是简单的聊天机器人,而是在你的终端里直接读取项目代码、分析逻辑、修改文件、运行命令的AI工具。第一次…

阅读更多 →
Avalonia × Modbus TCP:工业监控面板开发实战与避坑解析 2026/9/29 10:41:28

Avalonia × Modbus TCP:工业监控面板开发实战与避坑解析

做工业上位机这些年,Windows Forms 和 WPF 用了不少,但每次一提到跨平台就头疼。直到我在一个设备监控项目里被要求"客户可能用 Linux 工控机,界面也得跑起来",才真正开始折腾 Avalonia。配合 Modbus TCP 协议去读 PLC …

阅读更多 →
软件测试面试通关指南:从基础理论到AI测试趋势 2026/9/29 10:41:28

软件测试面试通关指南:从基础理论到AI测试趋势

1. 软件测试基础理论:别只背八股文,面试官想听的是你的测试思维先聊一个我在面试中反复遇到的场面:候选人简历上写着“熟悉软件测试流程,掌握测试用例设计方法”,结果我抛出一个最简单的登录功能,让他现场设…

阅读更多 →
用DeepSeek-Harness搭建Agent开发环境:以Android登录模块为例 2026/9/29 10:41:21

用DeepSeek-Harness搭建Agent开发环境:以Android登录模块为例

最近在折腾 Agent 项目,发现圈子里讨论最多的几个词就是 DeepSeek-Harness、harness anything、skill 编排。踩了一圈坑之后我最大的体会是:搭 Agent 开发环境,难的不是装框架,而是怎么让环境"可靠"——可复现、可调试、…

阅读更多 →
能用AI和懂AI之间,隔着一个数据标注员的距离:用TaoToken统一Key打通标注流水线 2026/9/29 10:41:08

能用AI和懂AI之间,隔着一个数据标注员的距离:用TaoToken统一Key打通标注流水线

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
边缘计算多算法并发实战:4路视频20+算法架构与调优 2026/9/29 10:41:08

边缘计算多算法并发实战:4路视频20+算法架构与调优

1. 从“一算法一盒子”到“一盒子多算法”的架构演进做过视频智能分析项目的人,大概都经历过那种“盒子堆成山”的场面。一个园区项目,人脸识别一台边缘盒子、车牌识别一台、安全帽检测再来一台、区域入侵再补一台,机柜里塞得满满当当&#x…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉