BoxLang Core Performance 是 ortus-boxlang/skills 下的一个技能,用于在编写或审查性能敏感的 BoxLang 核心 Java 代码(BIF、组件、运行时服务、热点路径)时提供优化模式。该技能面向在负载下、常处于紧循环中运行的 BIF、组件和运行时代码,指出看似只读取数据的代码也可能产生大量分配,并列出曾影响核心字符串 BIF 的模式及规避方法。
使用原则:规则零是先测量。这些模式应只应用于热点路径和 BIF,而不是一次性初始化代码;除非性能剖析或基准测试表明有必要,否则可读性优先。
字符串部分:String.substring() 会分配新字符串并复制字符,在大输入循环中代价高昂。替代方案包括:用 s.startsWith(p, n) 替代 substring(n).startsWith(p);用 regionMatches 替代 substring 后的 equals/equalsIgnoreCase;用 indexOf(x, n) 替代 substring(n).indexOf(x)(结果为绝对位置);用 charAt(i) 替代 substring(i, i+1)。若需多次使用子串,应提取一次复用。大小写不敏感搜索中,toLowerCase().indexOf(b.toLowerCase()) 每次调用会创建两个新字符串,应在循环外小写化一次或改用 regionMatches(true, ...)。循环中禁止用 + 或 += 拼接字符串,应使用 StringBuilder,已知大致长度时预设容量,直接追加字符区间,避免热点路径中的 String.format(),优先 String.join() 或 StringJoiner。
正则部分:热点路径中不要用 String.split()、replaceAll()、matches() 处理非平凡正则,因为每次调用都会编译 Pattern;应将 Pattern 预编译为 private static final;单字符分隔符可用 indexOf 循环或用非正则元字符字面量的 split();不需要模式时优先 String.replace(CharSequence, CharSequence) 而非 replaceAll()。
BoxLang 特有部分:Key.of(String) 每次调用都会分配新 Key,应在循环外提升为 static final 字段,优先复用 Key 上已有常量(如 Key.string、Key.value、Key.array),动态运行时字符串构建的 Key 应按操作构建一次而非每次迭代。全 final 字段的类可在构造函数中预计算 hashCode 并缓存返回值,参考 NameValuePair 和 Key。Attempt 与 Optional 包装器适合 API 边界,避免在内层循环逐元素创建,改用普通 null 检查。在 _invoke() 开头将每个参数取到局部变量,避免循环内重复 arguments.get(key),使用类型化 getter。热点路径日志使用参数化消息,昂贵参数构造需用 logger.isDebugEnabled() 保护。
集合与分配部分:已知或可低成本估算大小时预设 ArrayList、HashMap、StringBuilder 容量;避免循环内装箱,优先基本类型局部变量;热点 BIF 中用普通 for 循环替代 stream 和 lambda;只读检查时直接迭代而非复制;将 size()、length()、小写化后的查找串、编译后的 Pattern 等不变量提出循环;空或 null 输入提前返回。
并发部分:共享热点路径优先无锁结构(ConcurrentHashMap、LongAdder、AtomicReference)而非 synchronized;可能在虚拟线程上运行的代码中,避免在 synchronized 块内阻塞,可用 ReentrantLock;避免 ThreadLocal 缓存大对象;调用用户提供的 BoxLang 代码(闭包、回调)时不要持锁。
测量部分:用真实输入规模复现慢速场景,大字符串才能暴露 substring() 成本;微观问题用 JMH 基准测试,手写 System.nanoTime() 循环会因 JIT 预热和死代码消除而失真;系统级问题用性能剖析,Java Flight Recorder 和 async-profiler 可显示分配热点,应关注分配剖析而非仅 CPU 时间;修复热点后保留基准测试或添加回归测试。
技能还提供热点路径代码审查清单,包括:没有仅用于比较或搜索的 substring();循环内不对同一值调用 toLowerCase()/toUpperCase();循环或每次调用中不对固定名称使用 Key.of();没有每次调用编译正则;循环中没有 += 字符串拼接;StringBuilder 在已知大小时预设容量;内层循环中无逐元素 stream、Attempt 或装箱;不变量已提出循环;null/空输入提前返回;虚拟线程路径的 synchronized 内无阻塞;并通过基准测试或剖析验证而非假设。参考资料包括 BoxLang 文档、JMH、Java Flight Recorder、JEP 444 虚拟线程,以及相关技能 bif-development、runtime-architecture、logging。