GD32高效调试:SEGGER Embedded Studio与J-Link RTT实战

发布时间:2026/9/28 1:48:20
GD32高效调试:SEGGER Embedded Studio与J-Link RTT实战 有段时间我一直觉得GD32 这样的国产 MCU 玩不出什么花来毕竟生态和 STM32 比还是差了一截资料也散。但后来等我把开发环境从 Keil 切到 SEGGER Embedded Studio下面简称 SES再配合 J-Link 的 RTT 功能这个看法彻底变了。调试效率的提升是非常直观的尤其是以前做串口日志打印动不动就遇到 UART 引脚被业务占用、波特率不匹配、还要额外接一根 USB 转串口线属实烦人。用上 RTT 之后这些麻烦基本全没了。这篇文章我就把完整过程写下来从“为什么选 SES 而不是继续用 Keil”到 RTT 内部的原理再到如何把 RTT 移植进 GD32 工程、怎么把 printf 重定向到 RTT最后附上我踩过的几个坑和排查思路。内容偏向实战GD32F103 为例其他型号操作基本一致照着做就行。1. 调试方案选型为什么我把 GD32 开发切到了 SEGGER Embedded Studio1.1 面对 GD32 开发Keil、IAR 与 SES 怎么选先说个大家绕不开的问题GD32 的开发环境到底怎么选。大部分从 STM32 转过来的同学第一反应是 Keil MDK因为 GD32 官方确实提供了 Keil 的 Device Pack装上之后直接用无缝衔接。Keil 的优势是资料多、教程多、同事之间传工程方便。但用久了你会发现它有几个让人抓狂的地方代码编辑体验一般工程文件管理起来不够直观编译速度越来越慢尤其是工程大了之后每次编译都像是看进度条等下班。IAR 也不错代码体积优化确实有一手在资源受限的项目里优势明显。但 IAR 是商业软件License 成本摆在那儿个人用起来没那么方便。SES 则是个很特殊的存在它是 SEGGER 自家出的集成开发环境默认编译器是 SEGGER 基于 LLVM 的编译器也支持 GCC对 J-Link 的调试支持是“原生”级别的。最吸引我的地方是它对个人用户免费官方只对商业用途收费而且安装包非常小启动速度快界面干净。从 GD32 的支持度来说SES 在较新的版本里可以直接选择 GD32 系列芯片配合 J-Link 自动识别下载算法体验很顺。我整理了下面这个表方便你根据自己实际情况判断开发工具编译器调试器支持GD32 支持度适合场景Keil MDKArm Compiler 5/6CMSIS-DAP / J-Link / 其他官方 Pack支持完善爱用现成工程、团队协作、维护老项目IAR EWARMICCARMJ-Link / 各种官方支持追求代码体积、有商业 LicenseSEGGER Embedded StudioSEGGER CC / GCCJ-Link 最稳内置 GD32 设备支持个人开发者、重视调试效率、玩 J-LinkSES 的调试功能不是简单的“能下个程序能看个变量”它和 J-Link 之间的通信链路做了很深度的优化。断点管理、内存查看、实时变量监视都很顺手。我个人的结论很简单如果你手头有 J-Link又想提升日常开发体验SES 值得花一下午时间折腾这个成本很容易就能赚回来。1.2 J-Link RTT 到底解决了什么痛点J-Link 在嵌入式开发圈子里几乎是“标配”级别的调试器但很多人对它的用法停留在“下载程序”和“Keil 里点个 Debug”。其实 J-Link 自带了一套非常强的高效调试通信机制叫 RTT全称是 Real-Time Transfer实时传输。RTT 干了一件什么事呢它允许目标芯片在运行的时候通过 SWD/JTAG 接口直接向 PC 端发送数据也能从 PC 端接收数据全程不需要占用 UART 外设也不需要额外的调试引脚。目标芯片内部只需要在内存里留一小块缓冲区J-Link 会通过调试接口周期性地访问这块内存把数据搬运到电脑上。这解决了一个非常现实的痛点调试日志往哪打。传统方案是串口打印你得留一个 UART接一个 USB 转串口模块电脑上开个串口助手还要设置波特率、数据位、停止位。然后你会发现产品上电运行后业务逻辑已经占用了所有串口最后实在没办法只能临时从 PCB 上飞线出来或者为了调试专门留一个测试口。RTT 完全不需要这些它走的是调试通道。只要 J-Link 能连上芯片RTT 就能工作数据带宽还远高于普通串口。我把串口打印和 RTT 做一个直观对比对比项传统串口打印J-Link RTT硬件占用需要空闲 UART、电平转换、USB 转串口零额外硬件只靠 SWD/JTAG数据带宽一般 115200 bps约 11 KB/s实测可达数百 KB/s 甚至更高对 CPU 影响串口寄存器写入 中断占用资源几行内存写指令几乎无感双向交互需要额外设计通信协议原生支持下行输入可直接做命令终端实时性受波特率和中断影响写入即时生效无阻塞很多人用 RTT 只看中了“不占串口”但它的精髓是双向交互和低干扰。你在 PC 端可以直接往 MCU 发命令MCU 收到后执行并实时返回结果这不就是调试器版本的串口终端吗关键还不打断程序的运行实时任务该怎么跑还怎么跑。这套机制对 GD32 这类性能不错的 MCU 来说非常适合用来打印调试信息、观察状态变量、做命令交互。2. RTT 原理拆解先搞明白它为什么快再用它2.1 RTT 的快递柜模型控制块与环形缓冲区我第一次接触 RTT 的时候光顾着用没搞清楚它的机制结果一遇到“RTT Viewer 没输出”就两眼一抹黑。后来仔细读了一下 SEGGER 的源码和文档才明白它本质上就是在共用一块“快递柜”。RTT 在 RAM 中维护了一个控制块Control Block这个控制块里记录着每个通道的缓冲区信息比如缓冲区地址、读指针、写指针、缓冲区大小等等。控制块本身是一个结构体代码里通常叫做SEGGER_RTT_CB。MCU 侧如果要发送数据只要往当前通道的写指针位置写入数据再把写指针往后挪即可整个过程就是纯内存操作不涉及外设寄存器、不产生中断。J-Link 那边则像是一个尽职尽责的快递员不断通过 SWD/JTAG 接口读取这块内存区域发现你放了新包裹就取走然后通过 USB 发给电脑上的 RTT Viewer。因为这个模型本质上就是一块“内存 指针”所以它比串口快很多。串口每发一个字节都要经过移位寄存器要设置波特率速度上限很明显。RTT 则是内存写入CPU 执行几条指令就完事了数据真正传输到电脑的速度取决于 J-Link 的轮询频率和 USB 带宽。我实际在 GD32F103 上测过如果只是往 RTT 里打短字符串基本是“瞬时完成”。你把SEGGER_RTT_printf放在中断里调用只要控制好数据量和缓冲区大小也不会明显拖慢中断响应。这个特性在调试高频业务的时候特别有用比如 PWM 控制、电机电流采样这些场景你用串口打印很容易因为波特率不够导致“打不过来”RTT 则可以稳定地输出。2.2 为什么串口打印和半主机模式都不适合高频调试聊完了 RTT 的原理就不得不回头说一说传统调试方案的局限否则你不会知道 RTT 这个设计有多聪明。串口打印慢是很多人都能直观感受到的。115200 bps 的波特率换算下来理论每秒也就传输 11KB 左右再扣掉串口发包的起始位、停止位实际有效数据更少。打印一行 100 字节左右的日志可能要耗时近 10 毫秒。如果你的控制周期是 1 毫秒那问题就大了日志还没打完下一个控制周期已经到了。很多人不得不做“标志位 主循环统一打印”的套路但这会丢失实时性调试结果往往失真。另一个更隐蔽的坑是 ARM 半主机模式Semihosting。在 Keil 里用微库或者默认的 printf 重定向有时候编译器会走半主机方式它通过 BKPT 指令或 SVC 异常让 CPU 进入调试陷阱然后由调试器代为实现输出。这种方式原理上没啥问题但它在输出时会让 CPU 停滞尤其是调试器处理速度跟不上时程序像被按住了暂停键。在实时性要求比较高的项目里这几乎不可用。RTT 不一样它不会触发任何异常或陷阱就是普普通通的内存写操作CPU 不会停下也不会被调试器阻塞。这就是“实时传输”和“打断式调试”的本质区别。2.3 关于 RTT 时延与带宽很多人理解错了我经常看到有人问RTT 的时延到底是多少为什么有时候 RTT Viewer 里的数据会突然跳出一大段。这其实涉及两个完全不同的概念写入时延和读取时延。写入时延是指 MCU 调用SEGGER_RTT_Write把数据放进缓冲区的耗时。因为操作的是内存写入时延非常低基本可以忽略。读取时延是指数据已经写进缓冲区了但 J-Link 还没把它搬到电脑上直到下一次调试接口轮询时才被取走。这个读取时延和 J-Link 的轮询周期有关一般在微秒到毫秒级别。所以你看到的“突然跳出一大段”不是因为 RTT 慢而是因为数据早就进缓冲区了J-Link 在某一个瞬间把它们一起搬到了 PC 端。还有带宽的问题。RTT 的标称带宽很高但实际能跑多少取决于几个因素SWD/JTAG 接口速度、J-Link 型号、USB 传输方式、数据缓冲区大小。数据量大时如果缓冲区不够新数据会把没来得及取走的数据覆盖掉造成丢日志。因此讲究一点的工程会在SEGGER_RTT_Conf.h里把上行缓冲区调大比如 4096 字节甚至更高同时控制高频日志的打印频率。理解了这些你在排障时就不会一看到“输出延迟”就怀疑 RTT 本身了。3. 实操SES 下从零搭建 GD32 工程并移植 RTT3.1 准备工作软件包与源码都从哪来进入实战环节。先说准备工作你需要以下几样东西SEGGER Embedded Studio。去 SEGGER 官网注册个账号之后在下载页面选对应平台版本。它对个人用户免费下载后直接安装。J-Link Software and Documentation Pack。这是 J-Link 的驱动包里面包含了 J-Link 的驱动、命令行工具 J-Link Commander、RTT Viewer 等工具还有 RTT 的源码压缩包。安装路径默认在C:\Program Files\SEGGER\JLinkRTT 源码通常在安装目录下文件名类似SEGGER_RTT_V*.zip。GD32 标准外设库。以 GD32F103 为例去 GD32 官网或者 Gitee/GitHub 上找GD32F10x_Firmware_Library压缩包里包含标准外设库的源码、示例工程和文档。我建议直接下最新的版本尽量用官方源码网上有些人二次打包的库可能改过东西出了问题不好查。一块 GD32F103 的核心板或者开发板加上一个 J-Link。J-Link 建议用正版或者兼容性好的版本否则容易遇到各种玄学问题。这些工具准备好之后可以先给开发板上电用 J-Link 连接打开 J-Link Commander 输入connect指令看能不能正确识别到芯片。这一步能提前排除驱动或者接线问题免得后面在 SES 里排查半天。3.2 新建工程与 GD32 标准库文件组织打开 SES菜单选择Project - New Project模板选择Empty Project。新建之后第一件事是给工程指定芯片型号。SES 较新的版本在 Project Options 里可以直接搜索到 GD32 系列搜索GD32F103C8就能看到。如果确实找不到可以安装对应的 Device Support PackageSEGGER 官网上有各厂商的设备支持包下载安装后重启 SES 即可。芯片型号确定后接下来是把 GD32 标准库文件加进工程。这里我建议直接在文件系统层面把库文件复制到你的工程目录下然后通过Project - Add Existing Files把以下内容加进去启动文件GD32F103 是 Cortex-M3 内核对应的是startup_gd32f10x_md.s如果你的芯片是 HD 系列就选startup_gd32f10x_hd.s这个不能选错选错了启动之后时钟和外设配置都会有问题。系统时钟文件system_gd32f10x.c这里负责 SystemCoreClock 等全局变量和 SystemInit 的实现。标准外设库的源码文件比如gd32f10x_gpio.c、gd32f10x_usart.c、gd32f10x_rcu.c用到哪些模块就加哪些不需要全部加进来否则编译时间会变长。核心头文件gd32f10x.h、gd32f10x_libopt.h等。文件多了之后我习惯在 SES 里按照Firmware、Startup、User、RTT这样的分组建目录不是必须但工程整洁一点找代码不费劲。这里有个容易忽略的小细节gd32f10x_libopt.h里有一堆外设头文件的使能宏如果你在工程里用了某个外设但不小心把它注释掉了编译时会报“找不到定义”这类错误所以加库文件时建议顺手检查一遍这个文件。3.3 J-Link 调试与下载参数配置工程文件组织好编译能通过之后紧接着就是配置调试器。这一步非常关键很多人在 SES 里连不上 J-Link都是配置姿势不对。在 SES 里打开Project - Options - Debugger需要确认几个核心选项目标和调试器要选 J-Link接口选 SWD速度可以先设 4MHz如果目标板接线较长或者布线干扰大可以降到 1MHz。Device 选项里要确保选的是 GD32F103C8这样才能自动匹配下载算法和启动文件。如果 Device 列表里没有也可以选择Cortex-M3然后手动设置 Flash 下载算法但我更推荐直接把 Device Support Package 装好让 SES 自动处理。下载相关的设置在Project - Options - Debugger - Flash DownloadSES 通常会自动识别 J-Link 内置的 GD32 Flash Loader。如果你发现下载时提示No flash loader或者Failed to load flash loader基本可以确定是 Device 没匹配上。配置结束之后直接按 F5 下载并启动调试。如果这一步能顺利跑起来说明工程底座已经没问题了。我建议在 RTT 移植之前先写一个最简单的 LED 闪烁程序确认芯片能跑、调试器稳定。基础打牢了后面加 RTT 的时候就算出问题也知道不是最底层的事。3.4 集成 SEGGER_RTT 并跑通第一个测试程序接下来就是重头戏把 RTT 源码加进工程。解压之前提到的SEGGER_RTT_V*.zip你会看到典型的 RTT 文件结构。真正需要加入工程的只有这几个文件SEGGER_RTT.cRTT 的核心实现包括初始化、读写函数。SEGGER_RTT_printf.c可选提供类似 printf 的格式化输出函数。SEGGER_RTT.h、SEGGER_RTT_Conf.h、SEGGER_RTT_printf.h对应的头文件和配置头文件。把这三个 .c 文件加入工程把头文件所在目录加到Project - Options - Code - Preprocessor的 Include Paths 里。SEGGER_RTT_Conf.h是配置文件里面有几个宏值得关注比如BUFFER_SIZE_UP和BUFFER_SIZE_DOWN分别定义上行缓冲区和下行缓冲区的大小默认值是 1024 字节我先按住不表后面单独讲。加好文件之后写一个最简单的主程序来验证#include gd32f10x.h #include SEGGER_RTT.h int main(void) { int count 0; SEGGER_RTT_Init(); while (1) { SEGGER_RTT_printf(0, RTT run ok, count %d\r\n, count); delay_1ms(500); } }delay_1ms是简单的延时函数你可以用 SysTick 来实现也可以先用一个粗一点的循环延时把整个流程跑通。编译下载后打开 RTT Viewer菜单里选择File - Connect设备选 GD32F103C8接口 SWD速度可以保持和调试配置一致连接成功之后你就会看到不断滚动的打印信息。第一次跑通 RTT 的感觉说实话挺爽的完全没有串口那套波特率、COM 口的繁琐配置J-Link 一插屏幕上就开始刷数据了。到这一步RTT 的集成基本完成接下来就是怎么把它用出花来。4. RTT 实战技巧从 printf 到命令交互4.1 把 printf 重定向到 RTT释放串口引脚SEGGER_RTT_printf虽然好用但很多现有工程的代码都是直接调用printf如果你想让所有日志都跑到 RTT 里去最省事的方法是把printf重定向到 RTT。使用 GCC/SEGGER 编译器的时候可以通过重写_write函数实现#include stdio.h #include SEGGER_RTT.h int _write(int file, char *ptr, int len) { (void)file; SEGGER_RTT_Write(0, ptr, (unsigned int)len); return len; }然后在代码里直接调用printf即可所有格式化输出都会走 RTT。需要注意重定向的写法在不同编译器下不一样Keil 的 ARM Compiler 你可能需要重新实现fputc而 SES 默认用的 SEGGER 编译器或者 GCC 则是_write。这块是很多人移植时最容易卡住的地方建议先确认你的工程实际使用的编译器再动手改。重定向完成后UART 引脚就可以彻底释放了UART 外设留给真正的业务通信调试信息全部走 RTT。我在实际项目中就是把调试日志和业务数据彻底分开串口只给业务用调试看 RTT再也不用为“调试串口被占用”吵架了。4.2 用多通道和彩色日志让调试信息更有层次RTT 支持多通道默认有 0 通道和 1 通道你可以在配置里开启最多 16 个通道。每个通道之间是独立的环形缓冲区互不干扰。这意味着你可以把不同级别的日志分开打比如通道 0 打普通信息通道 1 打错误信息通道 2 打高频的实时数据。RTT Viewer 里可以为不同通道设置不同的颜色我常用的配置是通道 0 青色、通道 1 红色、通道 2 黄色。这样调试时一眼就能看到错误级别的信息不用在满屏白字里大海捞针。设置方法是在 RTT Viewer 的通道配置右键修改也可以直接在 RTT 的SEGGER_RTT_Conf.h里通过宏定义来预配置。使用通道也很简单SEGGER_RTT_printf的第一个参数就是通道号。甚至可以封装几个宏#define LOG_INFO(fmt, ...) SEGGER_RTT_printf(0, [INFO] fmt \r\n, ##__VA_ARGS__) #define LOG_ERROR(fmt, ...) SEGGER_RTT_printf(1, [ERROR] fmt \r\n, ##__VA_ARGS__)有了宏之后代码里想打日志就直接用LOG_INFO(temp %d, temp);简单清晰。调试完发布版本时如果需要把日志关掉只需要在宏定义里改成空即可不用动业务代码。4.3 RTT 回显法构建一个最简单的调试命令终端RTT 的双向通信能力是最容易被忽略的亮点。除了往电脑发数据RTT 还能接收 PC 端发送的字符这就为构建一个调试命令行提供了基础。所谓 RTT 回显法就是 PC 端通过 RTT Viewer 输入字符MCU 侧用函数读取并回显同时解析这些字符作为命令执行。MCU 侧读取下行数据非常简单char ch; if (SEGGER_RTT_Read(0, ch, 1) 1) { SEGGER_RTT_Write(0, ch, 1); // 回显 // 解析命令 }你可以在主循环里轮询这个函数。我实测下来用 RTT 做命令终端输入手感比串口终端还顺因为不需要接额外的串口线工作在调试接口上数据直接走 USB。再进一步可以写一个简单的命令行解析逻辑比如接收led on、led off来控制 LED接收ver来打印版本号。这在实际调设备时非常有用你可以在不打断程序运行的情况下修改参数、切换模式、检查状态寄存器。我之前调试一个温控项目PID 参数完全靠 RTT 命令行动态调整调完直接观察温度曲线整个过程程序一次都没重启过效率比“改代码-重新烧录-看现象”高太多了。4.4 配合 SES 调试器看变量比打印高一个维度RTT 适合打日志、做交互但如果想看某个变量在程序运行过程中的实时变化比打印更好的方式是直接利用 SES 的调试视图。程序停在断点时你在 Watch 窗口里输入变量名就能看到当前值。但断点会打断 CPU和“实时传输”的理念有点冲突。SES 里还有一种不打断运行的变量观察方式通过 Live Watch 窗口看变量的实时值。配合 RTT你可以做到高频数据用 RTT 打印趋势低频状态用 Live Watch 观察关键路径用断点单步分析。三种手段互相补充基本覆盖了调一般的逻辑问题所需的所有手段。如果你需要把运行数据可视化SEGGER 还有个工具叫 SystemView可以实时记录任务调度、中断、事件等行为配合 RTT 的底层机制能看到比普通打印更丰富的系统运行状态。不过那套工具需要额外的移植和配置属于进阶玩法先不展开。5. 常见问题与排查技巧实录5.1 问题速查表报错、现象与直接对策这里把我在 RTT 调试过程中遇到的典型问题整理成一张速查表方便你将来直接对号入座。现象可能原因直接对策No J-Link found驱动未安装/USB 线损坏/调试器未识别重装 J-Link 驱动、换线、用 J-Link Commander 验证下载时提示找不到 Flash LoaderSES 设备型号没选对安装 Device Support Package确认 Device 为 GD32 型号RTT Viewer 能连接但无输出主程序没有调用 SEGGER_RTT_Init在 main 函数开头执行初始化日志乱码编码不匹配/打印内容含中文确认文件编码尽量使用英文日志日志一卡一顿或莫名跳动上行缓冲区太小/J-Link 读取延迟调大 BUFFER_SIZE_UP程序能跑但无法连接调试器芯片读保护/选项字节被改用 J-Link Commander 解锁或全片擦除RTT 数据丢了一部分缓冲区覆盖/打印太频繁降低打印频率或增大缓冲区加了这张表基本能解决 80% 的常规问题。下面挑几个坑详细说因为这几个问题我踩过之后记忆深刻。5.2 诡异的“No J-Link found”到底怎么排查No J-Link found这个报错出现频率很高但它指的不是“没插 J-Link”而是电脑没有识别到 J-Link 设备。先排除最简单的驱动装没装。J-Link 驱动包安装后在设备管理器里应该能看到一个 J-Link 的 USB 设备如果显示黄色感叹号重装驱动。然后是 USB 线的问题这个非常隐蔽。有些 USB 线只能供电没有数据线芯插上去灯亮但电脑完全不识别。我手里就有好几根这种线都是从杂牌充电线里随手抓来的。换一根确认能传输数据的线问题立刻解决。再就是 J-Link 固件状态。打开 J-Link Commander输入connect如果能显示设备型号和固件版本说明 J-Link 本体没问题。如果提示Cannot connect to J-Link可以按住 J-Link 板上的复位键然后重新插 USB必要时用J-Link Configurator修复固件。实测下来九成“No J-Link found”问题逃不出这三步排查范围。5.3 GD32 锁死导致无法连接怎么办这个坑几乎每个玩 GD32 的人都会遇到一次。症状是程序写完烧进去之后突然在 SES 里连接不上调试器了提示Cannot connect to target或者连接成功后无法读写 Flash。这种情况大概率是代码里无意间使能了读保护或者调试口被复用成普通 GPIO 了。GD32 和 STM32 类似有一套选项字节机制读保护开启后调试器默认无法读取 Flash 内容。处理办法是用 J-Link Commander 执行解锁。先把 J-Link 连接到板子打开命令行工具输入connect选择设备型号比如 GD32F103C8然后输入unlock GD32F103C8或者直接使用全片擦除命令把选项字节和 Flash 一起恢复。执行完以后重新上电再回到 SES 里连接基本就能恢复。这里必须提醒一句解锁和全片擦除意味着芯片里的程序和数据全部没了所以这个方法只适合开发调试阶段千万别在已经量产或者装有重要数据的板子上尝试。养成一个好习惯每次烧录前先想清楚这板子里面有没有需要保留的东西。5.4 RTT 丢数据、打印乱码的排查思路如果你发现 RTT 打出来的数据频繁丢失或者突然缺了一部分第一个怀疑对象应该是BUFFER_SIZE_UP太小。某些日志量大、打印频繁的场景下J-Link 读取速度跟不上 MCU 写入速度新写入的数据就会覆盖未读取的数据造成丢失。解决方法是把SEGGER_RTT_Conf.h里的BUFFER_SIZE_UP从默认值改大比如 4096 字节代价只是多占一点 RAM对于 GD32F103 这种动辄几十 KB 内存的芯片来说完全能接受。打印乱码则大概率是编码问题。RTT Viewer 底层按字节传输如果你的日志混入了 UTF-8 格式的中文查看端没有按同样的编码解析就会乱码。我自己的习惯是调试日志全部用英文不跟编码较劲。如果确实需要中文日志先把整个工程的源文件统一成 UTF-8 编码然后在 RTT Viewer 的显示设置里把编码调成 UTF-8能解决大部分乱码。最后还有一个让人容易忽略的坑在 HardFault 中断里调用SEGGER_RTT_printf。如果程序死机了CPU 进入异常处理流程RTT 缓冲区里新添加的数据很可能没有被 J-Link 及时取走看起来像是“RTT 坏了”。遇到这种情况先不要怀疑 RTT 本身用调试器看当前 PC 指针停在了哪找到真正触发硬错误的原因才是关键。用 RTT 的时间越长我越觉得调试这件事对开发效率的影响比很多人想象的大得多。一个顺手、不打断思路的调试工具能让你把精力全部放在代码逻辑上而不是消耗在“折腾日志输出”这种杂事上。现在我自己新建 GD32 工程时默认就会把 RTT 集成进工程模板哪怕刚开始用不到也提前留着等真出问题的时候能少走不少弯路。最后再分享一个小经验调试阶段把上行缓冲区调到 4096 字节日志量再大也不慌发布版本时再改小能省一点 RAM。这套用法我用到现在稳定省心希望对你有用。