
Bazel 与 ProGuard 优化步骤完全指南从 -optimizations 过滤器到 Gson 代码注入【免费下载链接】bazela fast, scalable, multi-language and extensible build system项目地址: https://gitcode.com/GitHub_Trending/ba/bazel本篇技术指南以 Bazel 仓库内置的 ProGuard 6.2.2 文档third_party/java/proguard/proguard6.2.2/docs/manual/optimizations.md为核心系统讲解 ProGuard 优化步骤的开关控制、-optimizations过滤器的通配符语法、全部 34 类优化项的语义与隐含依赖关系、三个 JVM 级调优属性以及默认开启的 Gson 序列化代码注入优化的原理与已知限制。读完本文你将掌握在 Bazel Java 目标中按需裁剪 ProGuard 优化能力、规避激进优化风险、并为 Gson 序列化场景正确配置 keep 规则的方法。优化步骤在 ProGuard 流程中的位置ProGuard 是一个集**压缩shrinking、优化optimization、混淆obfuscation、预校验preverification**于一体的 Java 字节码处理器。在 Bazel 的 Java 构建链中它以proguard.ProGuard为主类被包装为proguard可执行目标见 tools/jdk/BUILD.java_tools用于对打包产物执行上述处理。优化步骤位于压缩与混淆之间默认情况下 ProGuard 会对全部输入类文件执行优化——内联并合并类与类成员并在字节码级别优化所有方法。该步骤由 usage.md 中定义的三个选项协同控制选项作用-dontoptimize完全不优化输入类文件-optimizations在更细粒度上启用/禁用单个优化项专家选项仅优化开启时生效-optimizationpasses n指定优化轮数默认 1 轮多轮可能带来进一步改进若一轮后无改进则提前终止与优化配套的还有-assumenosideeffects、-assumenoexternalsideeffects、-assumenoescapingparameters、-assumevalues等假设类选项它们告诉 ProGuard 哪些方法没有副作用从而允许删除未被使用的调用典型场景是移除日志代码见 examples.md。这些选项同样仅适用于优化开启的情况且官方文档明确警告做出假设可能是危险的容易破坏被处理代码只有在你完全清楚后果时才使用。-optimizations与过滤器语法-optimizations接受一个基于下方优化项名称的过滤器filter。过滤器的匹配语义与 ProGuard 其他所有过滤器类名、文件名、属性名等一致具体规则定义在 usage.md 的 Filters 小节过滤器是逗号分隔的名称列表仅名称与列表中某项匹配的优化会通过过滤通配符?匹配优化名称中的任意单个字符通配符*匹配优化名称中的任意片段在优化过滤器语境下*可以跨层级匹配与通用过滤器中的**语义类似可匹配包含斜杠分隔符在内的任意部分名称前加感叹号!表示排除且被排除的名称不再参与与后续优化名称的匹配。一个容易踩坑的细节是ProGuard 不会检查过滤器的拼写错误。如果你写错了某个优化项名称它会被静默当作一个不匹配任何东西的条目优化照常进行这可能导致你以为关闭了某项优化实际却并未关闭。因此在配置-optimizations时必须格外小心最好逐字对照官方名称列表。官方文档给出了三个典型的过滤器示例-optimizations code/simplification/variable,code/simplification/arithmetic只执行指定的两个 peephole窥孔优化。-optimizations !method/propagation/*执行所有优化但排除在方法之间传播值的那几类即method/propagation/parameter与method/propagation/returnvalue。-optimizations !code/simplification/advanced,code/simplification/*先排除code/simplification/advanced再放行所有简化类优化最终效果是只执行全部 peephole 简化优化但不包括基于控制流/数据流分析的 advanced 简化。注意这里!只影响其后匹配到的名称——由于code/simplification/advanced先被显式排除后续code/simplification/*不会再把它匹配进来。优化项全目录语义与隐含依赖下面按类别完整列出 ProGuard 6.2.2 支持的全部优化项。每项的⇒标注表示该优化必然隐含启用的后续优化*best used with*则表示推荐搭配非强制。注意随着版本演进优化项会被新增和重组因此该列表在未来版本中可能变化。库专项优化优化项说明library/gson尽可能优化 Gson 库的使用方式详见下文 Gson 优化专节类Class级优化优化项说明class/marking/final尽可能将类标记为finalclass/unboxing/enum尽可能将枚举类型简化为整型常量class/merging/vertical尽可能在类层次结构中垂直合并类父类与子类合并class/merging/horizontal尽可能在类层次结构中水平合并类兄弟类合并class/merging/wrapper尽可能将包装类与其包装的类合并字段Field级优化优化项说明field/removal/writeonly删除只写不读的字段⇒code/removal/advancedfield/marking/private尽可能将字段标记为privatefield/propagation/value跨方法传播字段的值⇒code/simplification/advanced方法Method级优化优化项说明method/marking/private尽可能将方法标记为private虚方法去虚化devirtualizationmethod/marking/static尽可能将方法标记为static虚方法去虚化⇒code/removal/advancedmethod/marking/final尽可能将方法标记为finalmethod/marking/synchronized尽可能去除方法的synchronized标记在可以证明线程安全的前提下method/removal/parameter删除未被使用的方法参数⇒code/removal/advancedmethod/propagation/parameter将方法调用处的参数值传播到被调用方法内部⇒code/simplification/advancedmethod/propagation/returnvalue将方法的返回值传播到其调用处⇒code/simplification/advancedmethod/inlining/short内联短方法method/inlining/unique内联只被调用一次的方法method/inlining/tailrecursion尽可能简化尾递归调用代码Code级优化优化项说明code/merging通过修改分支目标合并相同的代码块code/simplification/variable对变量加载/存储指令执行 peephole 优化code/simplification/arithmetic对算术指令执行 peephole 优化code/simplification/cast对类型转换指令执行 peephole 优化code/simplification/field对字段加载/存储指令执行 peephole 优化code/simplification/branch对分支指令执行 peephole 优化⇒code/removal/simplecode/simplification/object对对象实例化执行 peephole 优化code/simplification/string对常量字符串执行 peephole 优化code/simplification/math对java.lang.Math方法调用执行 peephole 优化code/simplification/advanced基于**控制流分析CFA与数据流分析DFA**简化代码推荐与code/removal/advanced搭配使用code/removal/advanced基于控制流分析CFA与数据流分析DFA删除死代码⇒code/removal/exceptioncode/removal/simple基于简单的控制流分析删除死代码⇒code/removal/exceptioncode/removal/variable从局部变量帧中删除未使用的变量code/removal/exception删除 try 块为空的异常处理code/allocation/variable优化局部变量帧上的变量分配寄存器分配优化从上面的清单可以看出 ProGuard 优化的整体策略先做结构性的标记与合并class/marking/*、class/merging/*再通过去虚化与参数传播打开更多死代码删除的空间最后用多轮 peephole 和基于 CFA/DFA 的 advanced 简化收尾。例如method/marking/static把虚方法转为静态方法后调用点不再依赖多态分派code/removal/advanced就能进一步删除因此变成不可达的分支。隐含依赖链一览部分优化之间存在强制的前置依赖开启上游优化会自动启用下游优化field/removal/writeonly ⇒ code/removal/advanced field/propagation/value ⇒ code/simplification/advanced method/marking/static ⇒ code/removal/advanced method/removal/parameter ⇒ code/removal/advanced method/propagation/parameter ⇒ code/simplification/advanced method/propagation/returnvalue ⇒ code/simplification/advanced code/simplification/branch ⇒ code/removal/simple code/removal/advanced ⇒ code/removal/exception code/removal/simple ⇒ code/removal/exception理解这条依赖链对编写过滤器很重要例如你若想仅排除code/removal/exception由于code/removal/advanced或code/removal/simple会强制开启它你必须同时排除上游项否则排除不生效。JVM 级调优属性非官方设置除-optimizations外ProGuard 还提供若干非官方的 JVM 系统属性来控制优化行为它们通过-D...方式作为 JVM 参数传入且可能在未来的版本中消失。这些属性对内联膨胀类问题非常实用系统属性默认值说明maximum.inlined.code.length8字节可被内联的短方法最大代码长度字节数。内联过长的方法会无谓地膨胀代码体积maximum.resulting.code.lengthJSE8000字节JME2000字节内联后目标方法允许达到的最大代码长度。许多 JVM 不会对过长的方法应用即时编译JIT因此不能让方法长得过大optimize.conservatively未设置允许输入代码中带有故意抛异常但无其他用途的普通指令NullPointerException、ArrayIndexOutOfBoundsException、ClassCastException。默认情况下 ProGuard 会直接丢弃这类看似无用的指令从而让大多数常见代码得到更好的优化开启该属性则保守处理保留这类指令在 Bazel 中通过java_binary等目标的jvm_flags或执行脚本的JAVA_OPTS传入这些属性即可例如设置-Dmaximum.inlined.code.length16允许内联更长的短方法或-Doptimize.conservatively开启保守优化模式以兼容用异常做控制流的旧代码。Gson 优化反射序列化的字节码级替换library/gson优化是 ProGuard 6.x 引入的一项颇具代表性的能力值得单独展开。原理与收益ProGuard 通过分析检测出哪些领域类domain class被 Gson 库用于序列化然后用注入的、经过优化的直读/直写代码替换 Gson 基于反射的字段读写实现——即在读写 JSON 时直接访问领域类的字段而不是走反射。该项优化带来的三个收益领域类可以放心混淆与 Gson 一起使用的领域类不再受反射约束类名与字段名可被自由重命名性能更好注入的序列化代码直接访问字段比依赖反射的 Gson 原始实现更快配置更少优化会自动保留序列化所需的类与字段开发者无需手工编写大量 keep 规则。配置方式Gson 优化默认启用只要应用代码没有使用下文列出的不支持的 Gson 特性就不需要任何额外配置见 examples.md 的佐证。已知限制无法优化的场景ProGuard无法优化以下 Gson 使用场景一旦检测到会自动为所有受影响的领域类保留原始的基于反射的 Gson 实现序列化的类中使用了以下 Gson 注解之一JsonAdapterSinceUntil序列化的类在签名中包含泛型类型变量generic type variables序列化使用的 Gson 实例由配置了以下选项的GsonBuilder构建excludeFieldsWithModifiersetFieldNamingPolicy回退场景下的 keep 配置当上述任一特性被使用时受影响领域类的序列化字段会重新依赖反射访问因此在 ProGuard/DexGuard 配置中必须显式 keep这些字段否则它们可能被压缩、优化或混淆掉导致运行时反射找不到字段。原文档给出的通用做法关闭优化时的等效配置是在配置文件中加入-keepclassmembers class com.example.SerializedClass { fields; init(); }或借助SerializedName注解批量保留字段同时允许字段名混淆-keepclasseswithmembers,allowobfuscation,includedescriptorclasses class * { com.google.gson.annotations.SerializedName fields; } -keepclassmembers enum * { com.google.gson.annotations.SerializedName fields; }调试阶段还可以配合-addconfigurationdebugging选项在运行时获得关于必要 keep 配置的反馈信息。在 Bazel 构建中的实践要点库级配置的合法性约束Bazel 对库library级别的 ProGuard 配置有严格限制。仓库中的 tools/jdk/proguard_allowlister.py 明确说明库的 ProGuard 配置只允许使用-keep、-assumenosideeffects、-assumevalues、以及带参数的-dontnote和-dontwarn。这样限制的原因正如该文件注释所述——防止某个库通过依赖传递把禁用混淆这类影响面巨大的全局效果意外施加到整个二进制产物上。因此像-optimizations这样的全局优化过滤选项通常应在最终二进制目标而非库目标的配置中设置并通过proguard_allowlister的校验。实战配置示例通用场景在最终应用配置中执行最多 3 轮优化让压缩、内联效果最大化源自 examples.md-injars in.jar -outjars out.jar -libraryjars java.home/jmods/java.base.jmod(!**.jar;!module-info.class) -optimizationpasses 3 -overloadaggressively -repackageclasses -allowaccessmodification -keep public class com.example.MyMainAndroid 场景Dalvik 1.0/1.5 无法处理某些算术简化结果官方示例见 examples.md通过过滤器精确关闭该项-android -dontpreverify -repackageclasses -allowaccessmodification -optimizations !code/simplification/arithmetic -keep public class com.example.MyActivity这个例子是-optimizations过滤器最典型的用途只排除已知在当前运行时环境下有问题的单项其余优化全部保留。Gson 项目保持默认配置即可享受library/gson优化若使用了不支持的 Gson 特性则按上文回退场景下的 keep 配置补上 keep 规则。注意若因兼容性问题整体关闭了优化-dontoptimizeGson 将退回反射实现此时必须用-keepclassmembers保留序列化字段与无参构造器否则运行时反射会失败。小结优化步骤由-dontoptimize总开关、-optimizations细粒度过滤器与-optimizationpasses轮数共同控制三者均定义于 usage.md 的 Optimization Options优化过滤器沿用 ProGuard 通用过滤器语义?匹配单字符、*匹配任意片段、!排除且不回溯且不校验拼写34 类优化项可按 library / class / field / method / code 五个层级组织多项优化之间存在⇒隐含依赖编写排除规则时要顺带排除上游项三个 JVM 系统属性maximum.inlined.code.length、maximum.resulting.code.length、optimize.conservatively用于控制内联膨胀与保守优化属非官方设置未来版本可能移除library/gson默认启用将 Gson 反射序列化替换为直接字段访问带来可混淆、高性能、少配置三重收益使用JsonAdapter/Since/Until、泛型签名或受限GsonBuilder配置时自动回退反射实现需手动补 keep 规则在 Bazel 中库级 ProGuard 配置受 proguard_allowlister.py 约束全局优化选项应放在最终二进制目标配置中。如需查阅更完整的配置选项、过滤器细节与更多实战示例可继续阅读仓库内的 usage.md 与 examples.md。【免费下载链接】bazela fast, scalable, multi-language and extensible build system项目地址: https://gitcode.com/GitHub_Trending/ba/bazel创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考