这又是一个性能优化 Bug 修复的版本更新版本,大家按需升级。
1. 性能优化
这个版本的性能优化包括:
1.1 使用 SWAR (SIMD Within A Register) 技巧来优化序列化字符串的性能
序列化时,写字符串检测是否存在特别字符是一个性能关键点,这个版本使用 SWAR (SIMD Within A Register) 的技巧来做快速检测。如下
package com.alibaba.fastjson2;
class JSONWriterUTF8 {
protected final long byteVectorQuote;
JSONWriterUTF8(Context ctx) {
// " -> 0x22, ' -> 0x27
this.byteVectorQuote = this.useSingleQuote ? 0x2727_2727_2727_2727L : 0x2222_2222_2222_2222L;
}
public void writeStringLatin1(byte[] value) {
final long vecQuote = this.byteVectorQuote;
int i = 0;
final int upperBound = (value.length - i) & ~7;
// 这里一次检测8个byte是否存在特别字符
for (; i < upperBound; i += 8) {
if (containsEscaped(IOUtils.getLongLittleEndian(value, i), vecQuote)) {
break;
}
}
// ...
}
static boolean containsEscaped(long v, long quote) {
/*
for (int i = 0; i < 8; ++i) {
byte c = (byte) data;
if (c == quote || c == '\\' || c < ' ') {
return true;
}
data >>>= 8;
}
return false;
*/
long x22 = v ^ quote; // " -> 0x22, ' -> 0x27
long x5c = v ^ 0x5c5c5c5c5c5c5c5cL;
x22 = (x22 - 0x0101010101010101L) & ~x22;
x5c = (x5c - 0x0101010101010101L) & ~x5c;
return ((x22 | x5c | (0x7F7F_7F7F_7F7F_7F7FL - v + 0x1010_1010_1010_1010L) | v) & 0x8080808080808080L) != 0;
}
}
https://github.com/alibaba/fastjson2/blob/2.0.54/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF8.java#L484
1.2 优化在 JDK 16 + 的 readString 性能
在 JDK 16 + 的版本下,使用 StringLatin1.indexOfChar 方法加速扫描特殊字符,优化 readString 的性能。这个算法来自 wycst 的贡献。
class JSONReaderASCII {
static final int ESCAPE_INDEX_NOT_SET = -2;
protected int nextEscapeIndex = ESCAPE_INDEX_NOT_SET;
public String readString() {
int slashIndex = nextEscapeIndex;
if (slashIndex == ESCAPE_INDEX_NOT_SET || (slashIndex != -1 && slashIndex < offset)) {
nextEscapeIndex = slashIndex = IOUtils.indexOfChar(bytes, '\\', offset, end);
}
if (slashIndex == -1 || slashIndex > index) {
valueLength = index - offset;
offset = index;
// ...
}
}
https://github.com/alibaba/fastjson2/blob/2.0.54/core/src/main/java/com/alibaba/fastjson2/JSONReaderASCII.java#L1445
获取 StringLatin1.IndexOfChar MethodHandle 的代码 https://github.com/alibaba/fastjson2/blob/2.0.54/core/src/main/java/com/alibaba/fastjson2/util/JDKUtils.java#L342
1.3 int/long/float/double 的读取写性能
优化的技巧是一次性读取两个数字,如下:
package com.alibaba.fastjson2;
class JSONReaderUTF8 {
public final int readInt32Value() {
// ...
while (inRange
&& offset + 1 < end
&& (digit = IOUtils.digit2(bytes, offset)) != -1
) {
// max digits is 19, no need to check inRange (result == MULT_MIN_100 && digit <= (MULT_MIN_100 * 100 - limit))
if (inRange = (result > INT_32_MULT_MIN_100)) {
result = result * 100 - digit;
offset += 2;
}
}
}
}
package com.alibaba.fastjson2.util;
class IOUtils {
public static int digit2(byte[] bytes, int off) {
short x = UNSAFE.getShort(bytes, ARRAY_BYTE_BASE_OFFSET + off);
if (BIG_ENDIAN) {
x = Short.reverseBytes(x);
}
int d;
if ((((x & 0xF0F0) - 0x3030) | (((d = x & 0x0F0F) + 0x0606) & 0xF0F0)) != 0
) {
return -1;
}
return ((d & 0xF) << 3) + ((d & 0xF) << 1) // (d & 0xF) * 10
+ (d >> 8);
}
}
https://github.com/alibaba/fastjson2/blob/2.0.54/core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF8.java#L3506
这个优化最初灵感源泉来自 https://github.com/wycst/wast 的 io.github.wycst.wast.json.JSONTypeDeserializer.NumberImpl#deserializeInteger 所采用的算法,然后做了进一步的改进。
2. Issues
修复 toJSONString 实际可用空间小于预设的问题 #255
格式化输出支持使用空格代替 Tab,新增两个 JSONWriter.Feature 为 PrettyFormatWith2Space 和 PrettyFormatWith4Space
修复非法输入缺少第一个花括号 {不报错的问题 #2592
增强识别枚举,原来不能识别 toString 的结果,新增识别 toString 的结果。 #2820
JSONObject.to 支持 Void.class 和 void.class 返回 null #2879
修复 CSVWriter 写入 CSV 数据超过 65536 个字节时报错 #2988
修复 SeeAlso 类型父类是 Abstract 类型时 JVM Crash 的问题 #2987
修复初始化 JSONFactory 某些场景会导致循环依赖导致死锁的问题 #2994
修复 JSON.toJSON 方法行为和 JSON.toJSONString 不一致的问题 #2981
增强对注释的支持 #2983
修复某些场景 JSONWriter.Feature.WriteNulls 导致 Long/Double 序列化结果为 Null 的问题 #3049
修复 toJavaObject 方法不能识别早期时间毫秒到 LocalDateTime 的转换问题 #3091
修复反序列化 private Class 结果和 toJavaObject 方法结果不一致的问题 #3134
增强 fastjson 1.x 的兼容性 #3208 #2739 # 3144 #3157
增强对 Record 的支持 #3090
JSONPath 支持使用 path 设置 List 中的对象 #3125
修复 private Boolean 类型字段使用 valueFilter 报错的问题 #3076
修复 JDK 9+ writeStringEscaped 某些场景数组越界报错 #3209
增加对 Key 为 Map 类型的反序列化支持 #3214
JSON.parseObject 方法在某些情况下反序列化编码出错的问题 #3223 #3219
支持修改 useGsonAnnotation 配置 #3258
fastjson2 缺省是能识别 Gson 的 Annotation 的,这个可以通过接口或者 JVM 启动参数关闭
import com.alibaba.fastjson2.JSONFactory;
// 手工关闭
JSONFactory.setUseGsonAnnotation(false);
也支持通过 JVM 启动参数关闭
-Dfastjson2.useGsonAnnotation=false
修复错误格式没有及时报错的问题 #3260
MAVEN 依赖配置
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.54</version>
</dependency>
android5 针对优化版本
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.54.android5</version>
</dependency>
android8 针对优化版本
这个版本支持 java.time 和 Optional
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.54.android8</version>
</dependency>
1.x 兼容版本
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.54</version>
</dependency>
Spring 5 extension 配置
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2-extension-spring5</artifactId>
<version>2.0.54</version>
</dependency>
Spring 6 extension 配置
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2-extension-spring6</artifactId>
<version>2.0.54</version>
</dependency>
3. 相关链接
FASTJSON 1.x 用户升级指南 https://github.com/alibaba/fastjson2/wiki/fastjson_1_upgrade_cn
相关 issues https://github.com/alibaba/fastjson2/milestone/54
代码 tag https://github.com/alibaba/fastjson2/tree/2.0.54
Maven 下载 https://repo1.maven.org/maven2/com/alibaba/fastjson2/fastjson2/2.0.54/
Android5 特别优化版本 https://repo1.maven.org/maven2/com/alibaba/fastjson2/fastjson2/2.0.54.android5/
Android8 特别优化版本 https://repo1.maven.org/maven2/com/alibaba/fastjson2/fastjson2/2.0.54.android8/
1.x 兼容版本 https://repo1.maven.org/maven2/com/alibaba/fastjson/2.0.54/
性能测试报告 https://github.com/alibaba/fastjson2/blob/main/docs/benchmark/benchmark_2.0.54.md