SlideShare a Scribd company logo
1 of 39
FASTJSON那些事
阿里中间件 温绍锦
2017.07.26
为了无法计算的价值
FASTJSON发展历程
json-lib太慢
Jackson-1.x API不友好
Jackson-1.x LGPG协议
项目创建
2010.12
开源&发展
2011.1开源到alibabatech.org
2011.7开源到github.com/alibaba
github.com/eishay/jvm-serializers 评测json/databind分类最快
2012年最受欢迎中国开源软件
2013年开源中国10大热门开源项目
2016年最受欢迎中国开源软件
2016年发布1.1.52.android针对android做性能优化
2017年maven中央仓库月下载超过15万
被广泛采用
2012~2017
安全漏洞
2017.1.27除夕确认漏洞开始修复
2017.3.15内部修复完毕对外公告
漏洞披露规范未造成严重影响
2011 2017.1
为了无法计算的价值
协议类型 序列化 反序列化
json/dsl-platform 文本 526 806
json-array/fastjson/databind 文本 650 696
msgpack/databind 二进制 796 1052
protobuf 二进制 1173 719
json/fastjson/databind 文本 1058 1241
thrift 二进制 1455 731
json/jackson/databind 文本 1164 1866
hessian 文本 2842 4622
json/gson/databind 文本 4667 4403
bson/jackson/databind 二进制 4105 5449
java-built-in 二进制 5046 23279
json/json-lib/databind 文本 19853 71969
Eishay性能对比
数据来源 https://github.com/eishay/jvm-serializers/wiki
json/databind分类最快
为了无法计算的价值
基于数组性能极致
比流行的二进制协议快!
fastjson fastjson-android jackson gson moshi
最新版本 1.2.35 1.1.60.android 2.8.9 2.8.1 1.5.0
最新版大小 481K 215K 1580K 232K 124K + 81K
性能 非常好 Android下非常好 好 一般 一般
github star 9932 3084 9523 2911
github fork 3377 781 2163 220
支持JSONPath 支持 不支持 不支持 不支持 不支持
支持泛型 支持 支持 支持
支持循环引用 支持 不支持 不支持 不支持
为了无法计算的价值
Java JSON库同类产品对比
526
650
796
1173
1058
1455
1164
2842
4667
4105
5046
19853
806
696
1052
719
1241
731
1866
4622
4403
5449
23279
71969
0 10000 20000 30000 40000 50000 60000 70000 80000
json/dsl-platform
json-array/fastjson/databind
msgpack/databind
protobuf
json/fastjson/databind
thrift
json/jackson/databind
hessian
json/gson/databind
bson/jackson/databind
java-built-in
json/json-lib/databind
反序列化 序列化
481
214
1580
232 205
0
200
400
600
800
1000
1200
1400
1600
1800
fastjson fastjson-android jackson gson moshi
大小(K)
最新版大小(K)
9916
3070
9506
2911
0
2000
4000
6000
8000
10000
12000
fastjson jackson gson moshi
github star github fork
fastjson-1.1.60.android gson-2.8.1 内置org.json moshi-1.5.0 jackson-2.8.9
首次序列化10000次 1204 2925 14253
首次反序列化10000次 2436 3284 20475
序列化100000次 1245 4822 2551 1864
反序列化100000次 1672 3179 3441 2765
TradeObjectParse 26k1000次 956 985 1982 1161
CartObjectParse-70k1000次 2034 2375 5039 2449
为了无法计算的价值
Android环境性能对比
• 测试环境-华为P10(低端手机环境fastjson会更有优势)
• 数值为耗时,单位毫秒,越小越好
• 内置org.json在某些机型下性能会较好,但都不如fastjson
• 首次序列化/反序列化在fastjson每次new ParserConfig/SerializeConfig,jackson每次new ObjectMapper,gson每次new Gson
在Android下绝对优势碾压其他JSON库,针对首次优化对首屏快速展示效果非常好
0
2000
4000
6000
8000
10000
12000
14000
16000
fastjson-1.1.60.android gson-2.8.1 jackson-2.8.9
首次序列化10000次
0
5000
10000
15000
20000
25000
fastjson-1.1.60.android gson-2.8.1 jackson-2.8.9
首次反序列化10000次
0
1000
2000
3000
4000
5000
6000
fastjson-1.1.60.android gson-2.8.1 moshi-1.5.0 jackson-2.8.9
序列化100000次
0
500
1000
1500
2000
2500
3000
3500
4000
fastjson-1.1.60.android gson-2.8.1 moshi-1.5.0 jackson-2.8.9
反序列化100000次
0
500
1000
1500
2000
2500
fastjson-1.1.60.android gson-2.8.1 moshi-1.5.0 jackson-2.8.9
TradeObjectParse 26k1000次
0
1000
2000
3000
4000
5000
6000
fastjson-1.1.60.android gson-2.8.1 内置org.json jackson-2.8.9
CartObjectParse-70k1000次
FASTJSON的优点
性能最好
第三方评测性能最好
Android 4/5/6/7下性能最好
活跃受欢迎
Github上同类产品Star最多
多年开源中国最受欢迎开源软件评比在前10
稳定
回归测试3869个
测试覆盖率86%
功能完备
支持泛型、JDK8
支持JSONPath
要想写什
么就写什
么吧!
为了无法计算的价值
开源在github上 https://github.com/alibaba/fastjson
Maven仓库下载地址 http://repo1.maven.org/maven2/com/alibaba/fastjson/
性能优化技术
1/3
使用ThreadLocal
1
2
3
SymbolTable
IdentityHashMap
在SerializeWriter/JSONScanner中使用ThreadLocal保存一
个byte[]/char[],减少内存分配
一个特别优化过的HashMap,将常用的Name保存起来,不
用每次分配
能避免并发导致的死锁和正确性问题的IdentityHashMap
缓存Method/Field/Constructor
4
5
6
针对int/long序列化的优化
针对int/long反序列化的优化
在ParserConfig/SerializeConfig中保存
Class/Method/Field/Constructor,降低反射开销
针对int/long类型序列化做特别优化,
避免toString+WriteString减少对象分配
参考自Integer.parseInt
Integer.parseInt不支持输入为char[],支持String类型参数
针对10进制优化
为了无法计算的价值
性能优化技术
2/3
针对float/double反序列化的优化
7
8
9
encodeUTF8/decodeUTF8优化
使用asm动态生成serializer/deserializer
Float/Doubble.parse方法很慢
VR场景有大量的float/double
参考sun.nio.cs.ArrayEncoder/ sun.nio.cs.ArrayDecoder
内置asm,基于objectweb asm 3.3改造,只保留必要部分,
不到2000行代码
小方法手动内联
10
11
12
快速匹配算法
fnv_hash匹配优化
Android方法调用开销大
Oracle HotSpot的内联效果也不够好
直接访问Field比访问getter/setter更好
Fastjson独创的提升json反序列化性能的算法
基于字节码动态生成实现
性能超越jackson的关键
来自json/dsl-platform的优化算法
用于android版本性能优化
为了无法计算的价值
性能优化技术
3/3
BeanToArray超强性能模式
Enum Parse优化
字符串遍历优化
超越大多数二进制协议
兼顾性能和可维护性
避免Name对象创建
兼顾HotSpot和Android Dalvik的最优解法
BitFlags
16
17
18
JVM Instrinstic
日期类型Parse优化
一个Int表示32个选项
结合Enum的ordinal
利用JVM Intrinsic方法优化
SimpleDateFormat线程并不安全
SimpleDateFormat一次只能匹配一种格式
为了无法计算的价值
13
14
15
为了无法计算的价值
public class SerializeWriter {
static ThreadLocal<char[]> bufLocal = new ThreadLocal<char[]>();
char[] buf;
public SerializeWriter() {
buf = bufLocal.get();
if (buf != null)
bufLocal.set(null);
else
buf = new char[2048];
}
void close() {
if (buf.length <= 1024 * 64)
bufLocal.set(buf);
}
void expandCapacity(int minimumCapacity) {
int newCapacity = buf.length + (buf.length >> 1) + 1;
if (newCapacity < minimumCapacity) newCapacity = minimumCapacity;
buf = Arrays.copyOf(buf, newCapacity);
}
}
// com.alibaba.fastjson.serializer.SerializeWriter
1.通过bufLocal重用char[]
2. 处理一个线程同时多个SerializeWriter的问题
1. close时返还到bufLocal
2. 控制大小避免bufLocal过大
1
ThreadLocal
public class SymbolTable {
final String[] symbols;
final int indexMask;
public SymbolTable(int tableSize) {
this.indexMask = tableSize - 1;
this.symbols = new String[tableSize];
this.addSymbol("$ref", 0, 4, "$ref".hashCode());
this.addSymbol("@type", 0, 5, ”@type".hashCode());
}
public String addSymbol(char[] buffer, int offset, int len, int hash) {
String symbol = symbols[hash & indexMask];
if (symbol != null) {
if (hash == symbol.hashCode() && len == symbol.length()) {
for (int i = 0; i < len; i++)
if (buffer[offset + i] != symbol.charAt(i))
return new String(buffer, offset, len);
}
return symbol;
}
return symbols[hash & indexMask]
= new String(buffer, offset, len).intern();
}
} // com.alibaba.fastjson.parser.SymbolTable
2
1. 用于保存常用的Name,减少对象创建
2. 相同hash分桶的symbol只保留一份
3. 在char[]直接和已存的symbol比较
4. 预设必须存在的symbol
SymbolTable
为了无法计算的价值
public class IdentityHashMap<K, V> {
final Entry<K, V>[] buckets
final int indexMask;
public IdentityHashMap (int tableSize){
this.indexMask = tableSize - 1;
this.symbols = new Entry[tableSize];
}
public boolean put(K key, V value) {
int hash = System.identityHashCode(key);
int bucket = hash & indexMask;
for (Entry e = buckets[bucket]; e != null; e = entry.next) {
if (key == e.key) {
e.value = value;
return true;
}
}
Entry<K, V> e = new Entry<K, V>(key, value, hash, buckets[bucket]);
buckets[bucket] = e; // 并发是处理时会可能导致缓存丢失,但不影响正确性
return false;
}
} // com.alibaba.fastjson.util.IdentityHashMap
3
1. 基于System.identityHashCode
2. 不支持resize避免并发导致死循环
3. 特别处理避免锁缓存丢失不影响正确性
为了无法计算的价值
Identity
HashMap
public class JavaBeanInfo {
final Class clazz;
final Constructor defaultConstructor;
final FieldInfo[] fields;
}
public class FieldInfo {
public final String name;
public final Method method;
public final Field field;
public final Class declaringClass
public final JSONField fieldAnnotation;
public final JSONField methodAnnotation;
public Object get(Object object) {
return method != null
? method.invoke(object)
: field.get(object);
}
}
// com.alibaba.fastjson.util.JavaBeanInfo
// com.alibaba.fastjson.util.FieldInfo
1. getField/getMethod的开销远大于反射调用
2. 缓存Class避免ClassLoader.findClass开销
3. 缓存Constructor/Method/Field减低反射开销
4. 缓存Annotation等信息减少访问元数据API
为了无法计算的价值
4
降低
Reflect开销
// com.alibaba.fastjson.util.IOUtils#stringSize(long)
static int stringSize(long x) {
long p = 10;
for (int i = 1; i < 19; i++) {
if (x < p) return i;
p = 10 * p;
}
return 19;
}
// com.alibaba.fastjson.util.IOUtils#getChars(long, int, char[])
// com.alibaba.fastjson.util.IOUtils#getChars(int, int, char[])
static void getChars(long i, int index, char[] buf) {
... ...
}
// 在序列化这样使用
int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
char[] chars = new char[size];
getChars(i, size, chars);
为了无法计算的价值
5
int/long
序列化优化
1. int/long的序列化都采用相同的算法
2. 比toString + writeString相比减少对象创建
3. float/double是基于toString+WriteString实现较慢
// com.alibaba.fastjson.parser.JSONScanner#scanInt
// com.alibaba.fastjson.parser.JSONScanner#scanLong
// com.alibaba.fastjson.parser.JSONScanner#scanFieldInt
// com.alibaba.fastjson.parser.JSONScanner#scanFieldLong
int value;
if (ch >= ‘0’ && ch <= ‘9’) {
value = ch - ‘0’;
for (;;) {
ch = charAt(offset++);
if (ch >= ‘0’ && ch<= ‘9’) {
value = value * 10 + (ch - ‘0’);
} else if (ch == ‘.’) {
// error handle
} else {
break;
}
}
}
为了无法计算的价值
int->char[]
Parse优化
1. 参考自Integer.parseInt
2. Integer.parseInt不支持输入为char[]
3. 针对10进制优化
6
为了无法计算的价值
int intVal = ch - ‘0’, power = 1;
for (;;) {
ch = charAt(bp + (offset++));
if (ch >= ‘0’ && ch <= ‘9’) {
intVal = intVal * 10 + (ch - ‘0’);
continue;
} else break;
}
if (ch == ‘.’) {
ch = charAt(bp + (offset++));
if (ch >= ‘0’ && ch <= ‘9’) {
intVal = intVal * 10 + (chLocal - ‘0’);
for (power = 10; ;power *= 10) {
ch = charAt(bp + (offset++));
if (ch >= ‘0’ && ch <= ‘9’) {
intVal = intVal * 10 + (ch - ‘0’);
continue;
} else break;
}
}
}
value = ((float) intVal) / power;
if (negative)
value = -value;
1. Float.parseFloat输入需要String
2. Float.parseFloat和Double.parseDouble很慢
3. 先当int处理,最后再除小数位得到float
4. 在android某些机型上能得到数十倍的性能提升
5. VR相关的场景有大量的float
7
float/double
Parse优化
package sun.nio;
public class CharsetEncoder {
ByteBuffer encode(CharBuffer in);
}
package com.alibaba.fastjson;
public class IOUtils {
static int encodeUTF8(char[] chars, int offset, int len, byte[] bytes);
}
8
1. 参考自sun.nio.cs.ArrayEncoder
2. 英文场景能快25%
3. 中文场景能快100%
4. 数组直接操作比CharBuffer速度快
5. sun.nio.cs.ArrayDecoder在Java8英文场景性能不如new String(byte, charset)
UTF8Encode
优化
为了无法计算的价值
// 反序列化实现
public class ASMDeserializerFactory {
public ObjectDeserializer createJavaBeanDeserializer() {
ClassWriter cw = new ClassWriter();
MethodVisitor mw = new MethodWriter(cw, ACC_PUBLIC, "deserialze”);
… …
mw.visitVarInsn(ALOAD, context.var(“lexer”));
mw.visitVarInsn(BIPUSH, seperator);
mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase
, "scanString", "(C)Ljava/lang/String;");
... ...
byte[] code = cw.toByteArray();
Class deserClass = classLoader.defineClassPublic(code);
return deserClass.newInstance();
}
} // com.alibaba.fastjson.parser.deserializer.ASMDeserializerFactory
// com.alibaba.fastjson.serializer.ASMSerializerFactory
// 序列化实现
public class ASMSerializerFactory {
}
9
1. 内置一个asm实现
2. 基于ObjectWeb ASM 3.3.1裁剪
3. 不到2000行
4. 无依赖,体积小
5. 无反射开销
6. 产生式编程能减少分支判断 为了无法计算的价值
ASM动态字
节码优化
public class Model {
public final int value;
public Model(int value) {
this.value = value;
}
}
public class Model {
private int value;
public Model(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
1. Android下方法调用的开销较大
2. Android下有方法数量65535的限制
3. 无论Android Dalvik或者HotSpot方法调用都有开销
4. 小方法手动内联是一个优化技巧,提升性能,减少体积
5. public final Field代替只读getter
6. public Field代替可读写的getter & setter
为了无法计算的价值
10
小方法内联
{”id”:1001,”name”:”wenshao”}
public class Person {
public int id;
public String name;
}
Person p = new Persson();
// 先挨个Name做匹配
if (matchField(“id”)) p.id = readInt();
if (matchField(“name”)) p.name = readString();
// 剩下不匹配的走常规模式
String name = readName();
FieldDeserializer fieldDeser = fndFieldDeserializer(name);
if (fieldDeser != null) fieldDeser.readValue();
// charArrayCompare是matchField的关键实现
static boolean charArrayCompare(String src, int offset, char[] dest) {
for (int i = 0; i < destLen; ++i) {
if (dest[i] != src.charAt(offset + i)) return false;
return true;
}
} 为了无法计算的价值
11
快速匹配算法1. Name占据JSON字符串相当大的一部分
2. 通过匹配而不是把Name读取出来性能大幅提升
3. 读写按照相同的顺序匹配成功率会更高
4. 缺失部分字段不会影响匹配
5. 不符合顺序走普通模式
6. 快速匹配算法合适使用ASM动态字节码实现
7. Name匹配可以用SIMD指令优化
8. 这个算法是性能超越Jackson的关键
9. 快速匹配算法不适合用于Android
{”id”:1001,”name”:”wenshao”}
public class Person {
public int id;
public String name;
}
long readNameHash() {
long hash = 0x811c9dc5;
for (; i < text.length; ++p) {
char ch = text.charAt(p);
if (ch == '"') break;
hash ^= ch;
hash *= 0x1000193;
}
return hash;
}
long nameHash = readNameHash();
FieldDeserializer fieldDeser = fndFieldDeserializer(nameHash);
if (fieldDeser != null) {
fieldDeser.readValue();
}
// 以上为伪码,真实实现会复杂很多 为了无法计算的价值
FNVHash
匹配算法1. 快速排序需要动态字节码生成不能用于Android
2. 算法来自dsl-platform和jsoniter
3. 假设一个对象内不会存在相同hash值的Name
4. 避免了Name对象的创建,性能非常好
5. Android版本使用fnv_hash匹配算法和标准不同
6. 采用fnv_hash_64而不是fnv_hash_32不同于jsoniter
12
为了无法计算的价值
// 文档 https://github.com/alibaba/fastjson/wiki/BeanToArray_cn
class Company {
public int code;
public List<Department> departments = new ArrayList<Department>();
}
@JSONType(serialzeFeatures = SerializerFeature.BeanToArray
, parseFeatures = Feature.SupportArrayToBean)
class Department {
public int id;
public Stirng name;
public Department() {}
public Department(int id, String name) {
this.id = id;
this.name = name;
}
}
Company cmpy = new Company();
cmpy.code = 100;
cmpy.departments.add(new Department(1001, "Sales"));
cmpy.departments.add(new Department(1002, "Financial"));
// {"code":10,"departments":[[1001,"Sales"],[1002,"Financial"]]}
String text = JSON.toJSONString(commpany);
13
BeanToArray
性能超强模式
1. 开启BeanToArray之后,输出是JSONArray结构
2. 局部开启BeanToArray能兼顾性能和可维护性的平衡
3. BeanToArray模式性能超越大多数二进制协议
public class EnumDeserializer {
protected final Enum[] enums;
protected final long[] hashCodes;
public EnumDeserializer (Class enumClass) {
this.enums = (Enum[]) enumClass.getEnumConstants();
this.hashCodes = new long[enums.length];
for (int i = 0; i < enums.length; ++i)
hashCodes[i] = fnv_hash_64(enums[i].name);
Arrays.sort(hashCodes);
Arrays.sort(enums, (a, b) -> {
long x = fnv_hash_64(a.name()), y = fnv_hash_64(b.name());
return (x < y) ? -1 : ((x == y) ? 0 : 1)
});
}
public Enum getEnumByHashCode(long hashCode) {
int index = Arrays.binarySearch(this.hashCodes, hashCode);
if (index < 0) return null;
return enums[index];
}
Enum scanEnum() {
long hashCoce = lexer.scanHashCode();
return getEnumByHashCode(hashCode);
}
} 为了无法计算的价值
14
Enum Parse
优化
1. 读取name的HashCode
2. 通过HashCode查找Enum
1. 构建一个排好序的hashcode数组
2. 构建根据hashCode排序的enums
// 方法一
String text = …;
char[] chars = text.toCharArray(); // 这里会导致一次内存分配和拷贝,速度较慢
for (int i = 0; i < chars.length; ++i) {
char ch = chars[i];
}
// 方法二
for (int i = 0; i < text.length(); ++i) {
char ch = text.charAt(i); // Android下每次调用length()方法会有开销
}
// 方法三
for (int i = 0, len = text.length(); i < len; ++i) {
char ch = text.charAt(i);
}
为了无法计算的价值
字符串
遍历优化
15
1. Android下Dalvik小方法调用不会内联有开销
2. 方法三是最优解,兼顾HotSpot和Dalvik
为了无法计算的价值
public enum Feature {
UseBigDecimal, SortFeidFastMatch, IgnoreNotMatch;
public final int mask;
private Feature() {
mask = (1 << ordinal());
}
}
int features = 0;
features |= Feature.UseBigDecimal.mask;
features |= Feature.SortFeidFastMatch.mask;
features |= Feature.IgnoreNotMatch.mask;
public class JSONLexer {
private int features;
public boolean isEnabled(Feature feature) {
return (features & feature.mask) != 0;
}
public void config(Feature feature, boolean state) {
if (state) features |= feature.mask;
else features &= ~feature.mask;
}
}
16
BitFlags
1. 使用int的每一个bit标识一个选项
2. int只有32个bit所以只有32个选项
3. 通过 & mask != 0查看是否已设置选项
4. 通过 |= 和 &=配置选项
5. ASM动态代码生成时会根据常用组合做优化
查看JVM Intrinsic的代码路径 src/share/vm/classfile/vmSymbols.hpp
class String {
int compareTo(String);
int indexOf(String);
boolean equals(String);
}
class System {
int identityHashCode(Object);
long currentTimeMillis();
long nanoTime();
void arrayCopy(Object, int, Object, int, int);
}
class Arrays {
void copyOf(...);
void copyOfRange(...);
boolean equals(...);
}
http://hg.openjdk.java.net/jdk8/jdk8/hotspot/file/87ee5ee27509/src/share/v
m/classfile/vmSymbols.hpp 为了无法计算的价值
17
JVM Intrinsic
1. JVM提供的Instrinsic很少
2. 通常只是System.arrayCopy有用
3. 查看是否Instrinsic注意参数类型匹配
4. 在阿里关键方法可以提需求给JVM团队
class JSONScanner {
public Calendar calendar;
public boolean scanISO8601DateIfMatch() {
char y0 = charAt(bp);
char y1 = charAt(bp + 1);
char y2 = charAt(bp + 2);
char y3 = charAt(bp + 3);
char M0 = charAt(bp + 4);
char M1 = charAt(bp + 5);
char d0 = charAt(bp + 6);
char d1 = charAt(bp + 7);
if (!checkDate(y0, y1, y2, y3, M0, M1, d0, d1))
return false;
char h0 = charAt(bp + 8);
char h1 = charAt(bp + 9);
char m0 = charAt(bp + 10);
char m1 = charAt(bp + 11);
char s0 = charAt(bp + 12);
char s1 = charAt(bp + 13);
setCalendar(y0, y1, y2, y3, M0, M1, d0, d1);
... ...
}
} // com.alibaba.fastjson.parser.JSONScanner 为了无法计算的价值
DateParse
优化
18
1. SimpleDateFormat线程不安全
2. SimpleDateFormat一次只能识别一种格式
3. 一次遍历支持大多数常用DateFormat的识别
FASTJSON优化技术总结
No. 2
GC友好
减少内存分配
用好简单类型
CPU友好
连续操作,CacheLine对齐
批量操作,SIMD优化
用好数组
算法
用好Hash算法
为特定场景优化的算法
避免锁
锁的开销会较大
为了无法计算的价值
FASTJSON相关技术和功能点
1/3
TypeReference
1
2
3
JSONPath
自定序列化Filter
用以支持泛型,避免编译擦除
支持求值、修改、统计
可以当做OQL使用
和JSON解析公用基础设施,保证性能
各种SerializerFilter
自定义反序列化
4
5
6
避免SubString引用问题
WriteClassName
用于支持MapBean的ExtraProcessable
避免Android 2/34/5下subString引用问题
自带类型信息
曾有安全漏洞
SeeAlso
为了无法计算的价值
为了无法计算的价值
class Model {}
// 例一
List<Model> models = JSON.parseObject(“[{},{},{}]”
,
new TypeReference<List<Model>>(){});
// 例二
Type type = new TypeRefrence<List<Model>>(){}.getType();
List<Model> models = JSON.parseObject(“[{},{},{}]”, type);
// 例三 框架支持
class Response<T> {
public T data;
}
public static <T> Response<T> parseRepsonse(String json, Type type) {
return JSON.parseObject(json, new TypeReference<Response<T>>() {});
}
Type
Reference
1. 通过内嵌声明一个TypeReference派生类获得类型
2. 避免了编译擦除泛型信息问题
3. 例二中的type单例化处理性能更好
1
public class Entity {
public Integer id;
public String name;
public Object value;
public Entity() {}
public Entity(Integer id, Object value) {
this.id = id; this.value = value;
}
public Entity(String name) { this.name = name; }
}
Entity entity = new Entity(123, new Object());
assertSame(entity.value, JSONPath.eval(entity, "$.value"));
assertTrue(JSONPath.contains(entity, "$.value"));
assertTrue(JSONPath.containsValue(entity, "$.id", 123));
assertTrue(JSONPath.containsValue(entity, "$.value", entity.value));
assertEquals(2, JSONPath.size(entity, "$"));
assertEquals(0, JSONPath.size(new Object[0], "$"));
为了无法计算的价值
JSONPath
https://github.com/alibaba/fastjson/wiki/JSONPath
2
1. 可以通过JSONPath求值、修改、统计、判断是否存在
2. 可将JSONPath当做OQL使用
3. 和JSON解析共用基础设施,性能有保证
// 根据PropertyName和PropertyValue来判断是否序列化
public interface PropertyPreFilter extends SerializeFilter {
boolean apply(JSONSerializer serializer, Object obj, String name);
}
// 和PropertyFilter不同只根据object和name进行判断,在调用getter之前
// 这样避免了getter调用可能存在的异常
public interface PropertyPreFilter extends SerializeFilter {
boolean apply(JSONSerializer serializer, Object obj, String name);
}
// 序列化时修改Key
public interface NameFilter extends SerializeFilter {
String process(Object obj, String propertyName, Object propertyValue);
}
// 序列化是修改Value
public interface ValueFilter extends SerializeFilter {
Object process(Object obj, String propertyName, Object propertyValue);
}
// 和ValueFilter类似,只是多了BeanContext参数可用。
public interface ContextValueFilter extends SerializeFilter {
Object process(BeanContext ctx, Object obj, String name, Object val);
}
// 还有其他的SerializeFilter LabelFilter SimplePropertyPreFilter
// 注册在Class级别
https://github.com/alibaba/fastjson/wiki/Class_Level_SerializeFilter 为了无法计算的价值
自定义
序列化Filter
3
https://github.com/alibaba/fastjson/wiki/SerializeFilter
为了无法计算的价值
public class Model implements JSONSerializable, ExtraProcessable {
protected Map<String, Object> attributes
= new HashMap<String, Object>();
public Map<String, Object> getAttributes() { return attributes;}
public Object getAttribute(String name) { return
attributes.get(name); }
public void write(JSONSerializer serializer
, Object fieldName
, Type fieldType
, int features) throws IOException {
serializer.write(attributes); // 定制序列化
}
public void processExtra(String key, Object value) {
attributes.put(key, value); // 定制反序列化
}
}
自定义
反序列化
1. ExtraProcessable用于不匹配类型的序列化,可用于使用MapBean的框架
2. 类似功能的还有PropertyProcessable
4
https://github.com/alibaba/fastjson/wiki/PropertyProcessable_cn
https://github.com/alibaba/fastjson/wiki/ExtraProcessable
JDK 1.4/5/6 & Android 2/3/4/5 subString返回的字符串会持有原字符串char[]引用
static boolean V6; // android 6
static {
int version = -1;
try {
Class<?> clazz = Class.forName("android.os.Build$VERSION");
Field field = clazz.getField("SDK_INT");
version = field.getInt(null);
} catch (Exception e) {
// skip
}
V6 = version >= 23;
}
// subString处理
if (V6) {
strVal = text.substring(startIndex, endIndex);
} else {
int chars_len = endIndex - startIndex;
char[] chars = sub_chars(bp + offset, chars_len);
strVal = new String(chars, 0, chars_len);
}
为了无法计算的价值
避免
SubString
引用问题
5
为了无法计算的价值
public class Model {
public int id;
public String name;
public Entity() {}
public Entity(int id, String name) {
this.id = id;
this.name = name;
}
}
Model model = new Model(3, "wenshao");
String text = JSON.toJSONString(model, SerializerFeature.WriteClassName);
// 如下是toJSONString返回的text,@type是类型信息
// {"@type":"com.alibaba.json.demo.Model","id":3,"name":”wenshao"}
Model model2 = (Model) JSON.parse(text);
assertEquals(model.id, model2.id);
assertEquals(model.name, model2.name);
Write
ClassName
1. 这个功能在1.2.24之前有安全漏洞
2. 1.2.25之后会有安全保护
3. 自带类型信息使得json相当于Java的序列化
6
安全升级公告 https://github.com/alibaba/fastjson/wiki/security_update_20170315
支持类似JAXB的XmlSeeAlso功能 https://github.com/alibaba/fastjson/wiki/JSONType_seeAlso_cn
Fastjson那些事

More Related Content

Similar to Fastjson那些事

数据处理算法设计要点
数据处理算法设计要点数据处理算法设计要点
数据处理算法设计要点thinkinlamp
 
Flex 4.5 action data communication
Flex 4.5 action data communicationFlex 4.5 action data communication
Flex 4.5 action data communicationjexchan
 
Open Api&Sip
Open Api&SipOpen Api&Sip
Open Api&Sipcenwenchu
 
HITCON CTF 2014 BambooFox 解題心得分享
HITCON CTF 2014 BambooFox 解題心得分享HITCON CTF 2014 BambooFox 解題心得分享
HITCON CTF 2014 BambooFox 解題心得分享Chong-Kuan Chen
 
Fast Esp搜索系统
Fast Esp搜索系统Fast Esp搜索系统
Fast Esp搜索系统xiaochawan
 
twMVC#46_SQL Server 資料分析大躍進 Machine Learning Services
twMVC#46_SQL Server 資料分析大躍進 Machine Learning ServicestwMVC#46_SQL Server 資料分析大躍進 Machine Learning Services
twMVC#46_SQL Server 資料分析大躍進 Machine Learning ServicestwMVC
 
Lucene2 4学习笔记1
Lucene2 4学习笔记1Lucene2 4学习笔记1
Lucene2 4学习笔记1yiditushe
 
Python学习笔记
Python学习笔记Python学习笔记
Python学习笔记Lingfei Kong
 
用JAX-RS和Jersey完成RESTful Web Services
用JAX-RS和Jersey完成RESTful Web Services用JAX-RS和Jersey完成RESTful Web Services
用JAX-RS和Jersey完成RESTful Web Servicesjavatwo2011
 
自然语言处理 中文分词程序实验报告%28含源代码%29
自然语言处理 中文分词程序实验报告%28含源代码%29自然语言处理 中文分词程序实验报告%28含源代码%29
自然语言处理 中文分词程序实验报告%28含源代码%29aemoe
 
Android resource-management
Android resource-managementAndroid resource-management
Android resource-managementLucas Xu
 
Coreseek/Sphinx 全文检索实践指南
Coreseek/Sphinx 全文检索实践指南Coreseek/Sphinx 全文检索实践指南
Coreseek/Sphinx 全文检索实践指南HonestQiao
 
Hadoop与数据分析
Hadoop与数据分析Hadoop与数据分析
Hadoop与数据分析George Ang
 

Similar to Fastjson那些事 (20)

Ooredis
OoredisOoredis
Ooredis
 
Ooredis
OoredisOoredis
Ooredis
 
数据处理算法设计要点
数据处理算法设计要点数据处理算法设计要点
数据处理算法设计要点
 
第5章数组
第5章数组第5章数组
第5章数组
 
Flex 4.5 action data communication
Flex 4.5 action data communicationFlex 4.5 action data communication
Flex 4.5 action data communication
 
Open Api&Sip
Open Api&SipOpen Api&Sip
Open Api&Sip
 
HITCON CTF 2014 BambooFox 解題心得分享
HITCON CTF 2014 BambooFox 解題心得分享HITCON CTF 2014 BambooFox 解題心得分享
HITCON CTF 2014 BambooFox 解題心得分享
 
Php
PhpPhp
Php
 
Fast Esp搜索系统
Fast Esp搜索系统Fast Esp搜索系统
Fast Esp搜索系统
 
twMVC#46_SQL Server 資料分析大躍進 Machine Learning Services
twMVC#46_SQL Server 資料分析大躍進 Machine Learning ServicestwMVC#46_SQL Server 資料分析大躍進 Machine Learning Services
twMVC#46_SQL Server 資料分析大躍進 Machine Learning Services
 
Lucene2 4学习笔记1
Lucene2 4学习笔记1Lucene2 4学习笔记1
Lucene2 4学习笔记1
 
Python学习笔记
Python学习笔记Python学习笔记
Python学习笔记
 
用JAX-RS和Jersey完成RESTful Web Services
用JAX-RS和Jersey完成RESTful Web Services用JAX-RS和Jersey完成RESTful Web Services
用JAX-RS和Jersey完成RESTful Web Services
 
自然语言处理 中文分词程序实验报告%28含源代码%29
自然语言处理 中文分词程序实验报告%28含源代码%29自然语言处理 中文分词程序实验报告%28含源代码%29
自然语言处理 中文分词程序实验报告%28含源代码%29
 
Scala+spark 2nd
Scala+spark 2ndScala+spark 2nd
Scala+spark 2nd
 
Android resource-management
Android resource-managementAndroid resource-management
Android resource-management
 
Coreseek/Sphinx 全文检索实践指南
Coreseek/Sphinx 全文检索实践指南Coreseek/Sphinx 全文检索实践指南
Coreseek/Sphinx 全文检索实践指南
 
Hadoop与数据分析
Hadoop与数据分析Hadoop与数据分析
Hadoop与数据分析
 
Xapian介绍
Xapian介绍Xapian介绍
Xapian介绍
 
Glider
GliderGlider
Glider
 

Fastjson那些事

  • 2. FASTJSON发展历程 json-lib太慢 Jackson-1.x API不友好 Jackson-1.x LGPG协议 项目创建 2010.12 开源&发展 2011.1开源到alibabatech.org 2011.7开源到github.com/alibaba github.com/eishay/jvm-serializers 评测json/databind分类最快 2012年最受欢迎中国开源软件 2013年开源中国10大热门开源项目 2016年最受欢迎中国开源软件 2016年发布1.1.52.android针对android做性能优化 2017年maven中央仓库月下载超过15万 被广泛采用 2012~2017 安全漏洞 2017.1.27除夕确认漏洞开始修复 2017.3.15内部修复完毕对外公告 漏洞披露规范未造成严重影响 2011 2017.1 为了无法计算的价值
  • 3. 协议类型 序列化 反序列化 json/dsl-platform 文本 526 806 json-array/fastjson/databind 文本 650 696 msgpack/databind 二进制 796 1052 protobuf 二进制 1173 719 json/fastjson/databind 文本 1058 1241 thrift 二进制 1455 731 json/jackson/databind 文本 1164 1866 hessian 文本 2842 4622 json/gson/databind 文本 4667 4403 bson/jackson/databind 二进制 4105 5449 java-built-in 二进制 5046 23279 json/json-lib/databind 文本 19853 71969 Eishay性能对比 数据来源 https://github.com/eishay/jvm-serializers/wiki json/databind分类最快 为了无法计算的价值 基于数组性能极致 比流行的二进制协议快!
  • 4. fastjson fastjson-android jackson gson moshi 最新版本 1.2.35 1.1.60.android 2.8.9 2.8.1 1.5.0 最新版大小 481K 215K 1580K 232K 124K + 81K 性能 非常好 Android下非常好 好 一般 一般 github star 9932 3084 9523 2911 github fork 3377 781 2163 220 支持JSONPath 支持 不支持 不支持 不支持 不支持 支持泛型 支持 支持 支持 支持循环引用 支持 不支持 不支持 不支持 为了无法计算的价值 Java JSON库同类产品对比
  • 5. 526 650 796 1173 1058 1455 1164 2842 4667 4105 5046 19853 806 696 1052 719 1241 731 1866 4622 4403 5449 23279 71969 0 10000 20000 30000 40000 50000 60000 70000 80000 json/dsl-platform json-array/fastjson/databind msgpack/databind protobuf json/fastjson/databind thrift json/jackson/databind hessian json/gson/databind bson/jackson/databind java-built-in json/json-lib/databind 反序列化 序列化
  • 6. 481 214 1580 232 205 0 200 400 600 800 1000 1200 1400 1600 1800 fastjson fastjson-android jackson gson moshi 大小(K) 最新版大小(K) 9916 3070 9506 2911 0 2000 4000 6000 8000 10000 12000 fastjson jackson gson moshi github star github fork
  • 7. fastjson-1.1.60.android gson-2.8.1 内置org.json moshi-1.5.0 jackson-2.8.9 首次序列化10000次 1204 2925 14253 首次反序列化10000次 2436 3284 20475 序列化100000次 1245 4822 2551 1864 反序列化100000次 1672 3179 3441 2765 TradeObjectParse 26k1000次 956 985 1982 1161 CartObjectParse-70k1000次 2034 2375 5039 2449 为了无法计算的价值 Android环境性能对比 • 测试环境-华为P10(低端手机环境fastjson会更有优势) • 数值为耗时,单位毫秒,越小越好 • 内置org.json在某些机型下性能会较好,但都不如fastjson • 首次序列化/反序列化在fastjson每次new ParserConfig/SerializeConfig,jackson每次new ObjectMapper,gson每次new Gson 在Android下绝对优势碾压其他JSON库,针对首次优化对首屏快速展示效果非常好
  • 8. 0 2000 4000 6000 8000 10000 12000 14000 16000 fastjson-1.1.60.android gson-2.8.1 jackson-2.8.9 首次序列化10000次 0 5000 10000 15000 20000 25000 fastjson-1.1.60.android gson-2.8.1 jackson-2.8.9 首次反序列化10000次 0 1000 2000 3000 4000 5000 6000 fastjson-1.1.60.android gson-2.8.1 moshi-1.5.0 jackson-2.8.9 序列化100000次 0 500 1000 1500 2000 2500 3000 3500 4000 fastjson-1.1.60.android gson-2.8.1 moshi-1.5.0 jackson-2.8.9 反序列化100000次 0 500 1000 1500 2000 2500 fastjson-1.1.60.android gson-2.8.1 moshi-1.5.0 jackson-2.8.9 TradeObjectParse 26k1000次 0 1000 2000 3000 4000 5000 6000 fastjson-1.1.60.android gson-2.8.1 内置org.json jackson-2.8.9 CartObjectParse-70k1000次
  • 11. 性能优化技术 2/3 针对float/double反序列化的优化 7 8 9 encodeUTF8/decodeUTF8优化 使用asm动态生成serializer/deserializer Float/Doubble.parse方法很慢 VR场景有大量的float/double 参考sun.nio.cs.ArrayEncoder/ sun.nio.cs.ArrayDecoder 内置asm,基于objectweb asm 3.3改造,只保留必要部分, 不到2000行代码 小方法手动内联 10 11 12 快速匹配算法 fnv_hash匹配优化 Android方法调用开销大 Oracle HotSpot的内联效果也不够好 直接访问Field比访问getter/setter更好 Fastjson独创的提升json反序列化性能的算法 基于字节码动态生成实现 性能超越jackson的关键 来自json/dsl-platform的优化算法 用于android版本性能优化 为了无法计算的价值
  • 12. 性能优化技术 3/3 BeanToArray超强性能模式 Enum Parse优化 字符串遍历优化 超越大多数二进制协议 兼顾性能和可维护性 避免Name对象创建 兼顾HotSpot和Android Dalvik的最优解法 BitFlags 16 17 18 JVM Instrinstic 日期类型Parse优化 一个Int表示32个选项 结合Enum的ordinal 利用JVM Intrinsic方法优化 SimpleDateFormat线程并不安全 SimpleDateFormat一次只能匹配一种格式 为了无法计算的价值 13 14 15
  • 13. 为了无法计算的价值 public class SerializeWriter { static ThreadLocal<char[]> bufLocal = new ThreadLocal<char[]>(); char[] buf; public SerializeWriter() { buf = bufLocal.get(); if (buf != null) bufLocal.set(null); else buf = new char[2048]; } void close() { if (buf.length <= 1024 * 64) bufLocal.set(buf); } void expandCapacity(int minimumCapacity) { int newCapacity = buf.length + (buf.length >> 1) + 1; if (newCapacity < minimumCapacity) newCapacity = minimumCapacity; buf = Arrays.copyOf(buf, newCapacity); } } // com.alibaba.fastjson.serializer.SerializeWriter 1.通过bufLocal重用char[] 2. 处理一个线程同时多个SerializeWriter的问题 1. close时返还到bufLocal 2. 控制大小避免bufLocal过大 1 ThreadLocal
  • 14. public class SymbolTable { final String[] symbols; final int indexMask; public SymbolTable(int tableSize) { this.indexMask = tableSize - 1; this.symbols = new String[tableSize]; this.addSymbol("$ref", 0, 4, "$ref".hashCode()); this.addSymbol("@type", 0, 5, ”@type".hashCode()); } public String addSymbol(char[] buffer, int offset, int len, int hash) { String symbol = symbols[hash & indexMask]; if (symbol != null) { if (hash == symbol.hashCode() && len == symbol.length()) { for (int i = 0; i < len; i++) if (buffer[offset + i] != symbol.charAt(i)) return new String(buffer, offset, len); } return symbol; } return symbols[hash & indexMask] = new String(buffer, offset, len).intern(); } } // com.alibaba.fastjson.parser.SymbolTable 2 1. 用于保存常用的Name,减少对象创建 2. 相同hash分桶的symbol只保留一份 3. 在char[]直接和已存的symbol比较 4. 预设必须存在的symbol SymbolTable 为了无法计算的价值
  • 15. public class IdentityHashMap<K, V> { final Entry<K, V>[] buckets final int indexMask; public IdentityHashMap (int tableSize){ this.indexMask = tableSize - 1; this.symbols = new Entry[tableSize]; } public boolean put(K key, V value) { int hash = System.identityHashCode(key); int bucket = hash & indexMask; for (Entry e = buckets[bucket]; e != null; e = entry.next) { if (key == e.key) { e.value = value; return true; } } Entry<K, V> e = new Entry<K, V>(key, value, hash, buckets[bucket]); buckets[bucket] = e; // 并发是处理时会可能导致缓存丢失,但不影响正确性 return false; } } // com.alibaba.fastjson.util.IdentityHashMap 3 1. 基于System.identityHashCode 2. 不支持resize避免并发导致死循环 3. 特别处理避免锁缓存丢失不影响正确性 为了无法计算的价值 Identity HashMap
  • 16. public class JavaBeanInfo { final Class clazz; final Constructor defaultConstructor; final FieldInfo[] fields; } public class FieldInfo { public final String name; public final Method method; public final Field field; public final Class declaringClass public final JSONField fieldAnnotation; public final JSONField methodAnnotation; public Object get(Object object) { return method != null ? method.invoke(object) : field.get(object); } } // com.alibaba.fastjson.util.JavaBeanInfo // com.alibaba.fastjson.util.FieldInfo 1. getField/getMethod的开销远大于反射调用 2. 缓存Class避免ClassLoader.findClass开销 3. 缓存Constructor/Method/Field减低反射开销 4. 缓存Annotation等信息减少访问元数据API 为了无法计算的价值 4 降低 Reflect开销
  • 17. // com.alibaba.fastjson.util.IOUtils#stringSize(long) static int stringSize(long x) { long p = 10; for (int i = 1; i < 19; i++) { if (x < p) return i; p = 10 * p; } return 19; } // com.alibaba.fastjson.util.IOUtils#getChars(long, int, char[]) // com.alibaba.fastjson.util.IOUtils#getChars(int, int, char[]) static void getChars(long i, int index, char[] buf) { ... ... } // 在序列化这样使用 int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i); char[] chars = new char[size]; getChars(i, size, chars); 为了无法计算的价值 5 int/long 序列化优化 1. int/long的序列化都采用相同的算法 2. 比toString + writeString相比减少对象创建 3. float/double是基于toString+WriteString实现较慢
  • 18. // com.alibaba.fastjson.parser.JSONScanner#scanInt // com.alibaba.fastjson.parser.JSONScanner#scanLong // com.alibaba.fastjson.parser.JSONScanner#scanFieldInt // com.alibaba.fastjson.parser.JSONScanner#scanFieldLong int value; if (ch >= ‘0’ && ch <= ‘9’) { value = ch - ‘0’; for (;;) { ch = charAt(offset++); if (ch >= ‘0’ && ch<= ‘9’) { value = value * 10 + (ch - ‘0’); } else if (ch == ‘.’) { // error handle } else { break; } } } 为了无法计算的价值 int->char[] Parse优化 1. 参考自Integer.parseInt 2. Integer.parseInt不支持输入为char[] 3. 针对10进制优化 6
  • 19. 为了无法计算的价值 int intVal = ch - ‘0’, power = 1; for (;;) { ch = charAt(bp + (offset++)); if (ch >= ‘0’ && ch <= ‘9’) { intVal = intVal * 10 + (ch - ‘0’); continue; } else break; } if (ch == ‘.’) { ch = charAt(bp + (offset++)); if (ch >= ‘0’ && ch <= ‘9’) { intVal = intVal * 10 + (chLocal - ‘0’); for (power = 10; ;power *= 10) { ch = charAt(bp + (offset++)); if (ch >= ‘0’ && ch <= ‘9’) { intVal = intVal * 10 + (ch - ‘0’); continue; } else break; } } } value = ((float) intVal) / power; if (negative) value = -value; 1. Float.parseFloat输入需要String 2. Float.parseFloat和Double.parseDouble很慢 3. 先当int处理,最后再除小数位得到float 4. 在android某些机型上能得到数十倍的性能提升 5. VR相关的场景有大量的float 7 float/double Parse优化
  • 20. package sun.nio; public class CharsetEncoder { ByteBuffer encode(CharBuffer in); } package com.alibaba.fastjson; public class IOUtils { static int encodeUTF8(char[] chars, int offset, int len, byte[] bytes); } 8 1. 参考自sun.nio.cs.ArrayEncoder 2. 英文场景能快25% 3. 中文场景能快100% 4. 数组直接操作比CharBuffer速度快 5. sun.nio.cs.ArrayDecoder在Java8英文场景性能不如new String(byte, charset) UTF8Encode 优化 为了无法计算的价值
  • 21. // 反序列化实现 public class ASMDeserializerFactory { public ObjectDeserializer createJavaBeanDeserializer() { ClassWriter cw = new ClassWriter(); MethodVisitor mw = new MethodWriter(cw, ACC_PUBLIC, "deserialze”); … … mw.visitVarInsn(ALOAD, context.var(“lexer”)); mw.visitVarInsn(BIPUSH, seperator); mw.visitMethodInsn(INVOKEVIRTUAL, JSONLexerBase , "scanString", "(C)Ljava/lang/String;"); ... ... byte[] code = cw.toByteArray(); Class deserClass = classLoader.defineClassPublic(code); return deserClass.newInstance(); } } // com.alibaba.fastjson.parser.deserializer.ASMDeserializerFactory // com.alibaba.fastjson.serializer.ASMSerializerFactory // 序列化实现 public class ASMSerializerFactory { } 9 1. 内置一个asm实现 2. 基于ObjectWeb ASM 3.3.1裁剪 3. 不到2000行 4. 无依赖,体积小 5. 无反射开销 6. 产生式编程能减少分支判断 为了无法计算的价值 ASM动态字 节码优化
  • 22. public class Model { public final int value; public Model(int value) { this.value = value; } } public class Model { private int value; public Model(int value) { this.value = value; } public int getValue() { return value; } } 1. Android下方法调用的开销较大 2. Android下有方法数量65535的限制 3. 无论Android Dalvik或者HotSpot方法调用都有开销 4. 小方法手动内联是一个优化技巧,提升性能,减少体积 5. public final Field代替只读getter 6. public Field代替可读写的getter & setter 为了无法计算的价值 10 小方法内联
  • 23. {”id”:1001,”name”:”wenshao”} public class Person { public int id; public String name; } Person p = new Persson(); // 先挨个Name做匹配 if (matchField(“id”)) p.id = readInt(); if (matchField(“name”)) p.name = readString(); // 剩下不匹配的走常规模式 String name = readName(); FieldDeserializer fieldDeser = fndFieldDeserializer(name); if (fieldDeser != null) fieldDeser.readValue(); // charArrayCompare是matchField的关键实现 static boolean charArrayCompare(String src, int offset, char[] dest) { for (int i = 0; i < destLen; ++i) { if (dest[i] != src.charAt(offset + i)) return false; return true; } } 为了无法计算的价值 11 快速匹配算法1. Name占据JSON字符串相当大的一部分 2. 通过匹配而不是把Name读取出来性能大幅提升 3. 读写按照相同的顺序匹配成功率会更高 4. 缺失部分字段不会影响匹配 5. 不符合顺序走普通模式 6. 快速匹配算法合适使用ASM动态字节码实现 7. Name匹配可以用SIMD指令优化 8. 这个算法是性能超越Jackson的关键 9. 快速匹配算法不适合用于Android
  • 24. {”id”:1001,”name”:”wenshao”} public class Person { public int id; public String name; } long readNameHash() { long hash = 0x811c9dc5; for (; i < text.length; ++p) { char ch = text.charAt(p); if (ch == '"') break; hash ^= ch; hash *= 0x1000193; } return hash; } long nameHash = readNameHash(); FieldDeserializer fieldDeser = fndFieldDeserializer(nameHash); if (fieldDeser != null) { fieldDeser.readValue(); } // 以上为伪码,真实实现会复杂很多 为了无法计算的价值 FNVHash 匹配算法1. 快速排序需要动态字节码生成不能用于Android 2. 算法来自dsl-platform和jsoniter 3. 假设一个对象内不会存在相同hash值的Name 4. 避免了Name对象的创建,性能非常好 5. Android版本使用fnv_hash匹配算法和标准不同 6. 采用fnv_hash_64而不是fnv_hash_32不同于jsoniter 12
  • 25. 为了无法计算的价值 // 文档 https://github.com/alibaba/fastjson/wiki/BeanToArray_cn class Company { public int code; public List<Department> departments = new ArrayList<Department>(); } @JSONType(serialzeFeatures = SerializerFeature.BeanToArray , parseFeatures = Feature.SupportArrayToBean) class Department { public int id; public Stirng name; public Department() {} public Department(int id, String name) { this.id = id; this.name = name; } } Company cmpy = new Company(); cmpy.code = 100; cmpy.departments.add(new Department(1001, "Sales")); cmpy.departments.add(new Department(1002, "Financial")); // {"code":10,"departments":[[1001,"Sales"],[1002,"Financial"]]} String text = JSON.toJSONString(commpany); 13 BeanToArray 性能超强模式 1. 开启BeanToArray之后,输出是JSONArray结构 2. 局部开启BeanToArray能兼顾性能和可维护性的平衡 3. BeanToArray模式性能超越大多数二进制协议
  • 26. public class EnumDeserializer { protected final Enum[] enums; protected final long[] hashCodes; public EnumDeserializer (Class enumClass) { this.enums = (Enum[]) enumClass.getEnumConstants(); this.hashCodes = new long[enums.length]; for (int i = 0; i < enums.length; ++i) hashCodes[i] = fnv_hash_64(enums[i].name); Arrays.sort(hashCodes); Arrays.sort(enums, (a, b) -> { long x = fnv_hash_64(a.name()), y = fnv_hash_64(b.name()); return (x < y) ? -1 : ((x == y) ? 0 : 1) }); } public Enum getEnumByHashCode(long hashCode) { int index = Arrays.binarySearch(this.hashCodes, hashCode); if (index < 0) return null; return enums[index]; } Enum scanEnum() { long hashCoce = lexer.scanHashCode(); return getEnumByHashCode(hashCode); } } 为了无法计算的价值 14 Enum Parse 优化 1. 读取name的HashCode 2. 通过HashCode查找Enum 1. 构建一个排好序的hashcode数组 2. 构建根据hashCode排序的enums
  • 27. // 方法一 String text = …; char[] chars = text.toCharArray(); // 这里会导致一次内存分配和拷贝,速度较慢 for (int i = 0; i < chars.length; ++i) { char ch = chars[i]; } // 方法二 for (int i = 0; i < text.length(); ++i) { char ch = text.charAt(i); // Android下每次调用length()方法会有开销 } // 方法三 for (int i = 0, len = text.length(); i < len; ++i) { char ch = text.charAt(i); } 为了无法计算的价值 字符串 遍历优化 15 1. Android下Dalvik小方法调用不会内联有开销 2. 方法三是最优解,兼顾HotSpot和Dalvik
  • 28. 为了无法计算的价值 public enum Feature { UseBigDecimal, SortFeidFastMatch, IgnoreNotMatch; public final int mask; private Feature() { mask = (1 << ordinal()); } } int features = 0; features |= Feature.UseBigDecimal.mask; features |= Feature.SortFeidFastMatch.mask; features |= Feature.IgnoreNotMatch.mask; public class JSONLexer { private int features; public boolean isEnabled(Feature feature) { return (features & feature.mask) != 0; } public void config(Feature feature, boolean state) { if (state) features |= feature.mask; else features &= ~feature.mask; } } 16 BitFlags 1. 使用int的每一个bit标识一个选项 2. int只有32个bit所以只有32个选项 3. 通过 & mask != 0查看是否已设置选项 4. 通过 |= 和 &=配置选项 5. ASM动态代码生成时会根据常用组合做优化
  • 29. 查看JVM Intrinsic的代码路径 src/share/vm/classfile/vmSymbols.hpp class String { int compareTo(String); int indexOf(String); boolean equals(String); } class System { int identityHashCode(Object); long currentTimeMillis(); long nanoTime(); void arrayCopy(Object, int, Object, int, int); } class Arrays { void copyOf(...); void copyOfRange(...); boolean equals(...); } http://hg.openjdk.java.net/jdk8/jdk8/hotspot/file/87ee5ee27509/src/share/v m/classfile/vmSymbols.hpp 为了无法计算的价值 17 JVM Intrinsic 1. JVM提供的Instrinsic很少 2. 通常只是System.arrayCopy有用 3. 查看是否Instrinsic注意参数类型匹配 4. 在阿里关键方法可以提需求给JVM团队
  • 30. class JSONScanner { public Calendar calendar; public boolean scanISO8601DateIfMatch() { char y0 = charAt(bp); char y1 = charAt(bp + 1); char y2 = charAt(bp + 2); char y3 = charAt(bp + 3); char M0 = charAt(bp + 4); char M1 = charAt(bp + 5); char d0 = charAt(bp + 6); char d1 = charAt(bp + 7); if (!checkDate(y0, y1, y2, y3, M0, M1, d0, d1)) return false; char h0 = charAt(bp + 8); char h1 = charAt(bp + 9); char m0 = charAt(bp + 10); char m1 = charAt(bp + 11); char s0 = charAt(bp + 12); char s1 = charAt(bp + 13); setCalendar(y0, y1, y2, y3, M0, M1, d0, d1); ... ... } } // com.alibaba.fastjson.parser.JSONScanner 为了无法计算的价值 DateParse 优化 18 1. SimpleDateFormat线程不安全 2. SimpleDateFormat一次只能识别一种格式 3. 一次遍历支持大多数常用DateFormat的识别
  • 33. 为了无法计算的价值 class Model {} // 例一 List<Model> models = JSON.parseObject(“[{},{},{}]” , new TypeReference<List<Model>>(){}); // 例二 Type type = new TypeRefrence<List<Model>>(){}.getType(); List<Model> models = JSON.parseObject(“[{},{},{}]”, type); // 例三 框架支持 class Response<T> { public T data; } public static <T> Response<T> parseRepsonse(String json, Type type) { return JSON.parseObject(json, new TypeReference<Response<T>>() {}); } Type Reference 1. 通过内嵌声明一个TypeReference派生类获得类型 2. 避免了编译擦除泛型信息问题 3. 例二中的type单例化处理性能更好 1
  • 34. public class Entity { public Integer id; public String name; public Object value; public Entity() {} public Entity(Integer id, Object value) { this.id = id; this.value = value; } public Entity(String name) { this.name = name; } } Entity entity = new Entity(123, new Object()); assertSame(entity.value, JSONPath.eval(entity, "$.value")); assertTrue(JSONPath.contains(entity, "$.value")); assertTrue(JSONPath.containsValue(entity, "$.id", 123)); assertTrue(JSONPath.containsValue(entity, "$.value", entity.value)); assertEquals(2, JSONPath.size(entity, "$")); assertEquals(0, JSONPath.size(new Object[0], "$")); 为了无法计算的价值 JSONPath https://github.com/alibaba/fastjson/wiki/JSONPath 2 1. 可以通过JSONPath求值、修改、统计、判断是否存在 2. 可将JSONPath当做OQL使用 3. 和JSON解析共用基础设施,性能有保证
  • 35. // 根据PropertyName和PropertyValue来判断是否序列化 public interface PropertyPreFilter extends SerializeFilter { boolean apply(JSONSerializer serializer, Object obj, String name); } // 和PropertyFilter不同只根据object和name进行判断,在调用getter之前 // 这样避免了getter调用可能存在的异常 public interface PropertyPreFilter extends SerializeFilter { boolean apply(JSONSerializer serializer, Object obj, String name); } // 序列化时修改Key public interface NameFilter extends SerializeFilter { String process(Object obj, String propertyName, Object propertyValue); } // 序列化是修改Value public interface ValueFilter extends SerializeFilter { Object process(Object obj, String propertyName, Object propertyValue); } // 和ValueFilter类似,只是多了BeanContext参数可用。 public interface ContextValueFilter extends SerializeFilter { Object process(BeanContext ctx, Object obj, String name, Object val); } // 还有其他的SerializeFilter LabelFilter SimplePropertyPreFilter // 注册在Class级别 https://github.com/alibaba/fastjson/wiki/Class_Level_SerializeFilter 为了无法计算的价值 自定义 序列化Filter 3 https://github.com/alibaba/fastjson/wiki/SerializeFilter
  • 36. 为了无法计算的价值 public class Model implements JSONSerializable, ExtraProcessable { protected Map<String, Object> attributes = new HashMap<String, Object>(); public Map<String, Object> getAttributes() { return attributes;} public Object getAttribute(String name) { return attributes.get(name); } public void write(JSONSerializer serializer , Object fieldName , Type fieldType , int features) throws IOException { serializer.write(attributes); // 定制序列化 } public void processExtra(String key, Object value) { attributes.put(key, value); // 定制反序列化 } } 自定义 反序列化 1. ExtraProcessable用于不匹配类型的序列化,可用于使用MapBean的框架 2. 类似功能的还有PropertyProcessable 4 https://github.com/alibaba/fastjson/wiki/PropertyProcessable_cn https://github.com/alibaba/fastjson/wiki/ExtraProcessable
  • 37. JDK 1.4/5/6 & Android 2/3/4/5 subString返回的字符串会持有原字符串char[]引用 static boolean V6; // android 6 static { int version = -1; try { Class<?> clazz = Class.forName("android.os.Build$VERSION"); Field field = clazz.getField("SDK_INT"); version = field.getInt(null); } catch (Exception e) { // skip } V6 = version >= 23; } // subString处理 if (V6) { strVal = text.substring(startIndex, endIndex); } else { int chars_len = endIndex - startIndex; char[] chars = sub_chars(bp + offset, chars_len); strVal = new String(chars, 0, chars_len); } 为了无法计算的价值 避免 SubString 引用问题 5
  • 38. 为了无法计算的价值 public class Model { public int id; public String name; public Entity() {} public Entity(int id, String name) { this.id = id; this.name = name; } } Model model = new Model(3, "wenshao"); String text = JSON.toJSONString(model, SerializerFeature.WriteClassName); // 如下是toJSONString返回的text,@type是类型信息 // {"@type":"com.alibaba.json.demo.Model","id":3,"name":”wenshao"} Model model2 = (Model) JSON.parse(text); assertEquals(model.id, model2.id); assertEquals(model.name, model2.name); Write ClassName 1. 这个功能在1.2.24之前有安全漏洞 2. 1.2.25之后会有安全保护 3. 自带类型信息使得json相当于Java的序列化 6 安全升级公告 https://github.com/alibaba/fastjson/wiki/security_update_20170315 支持类似JAXB的XmlSeeAlso功能 https://github.com/alibaba/fastjson/wiki/JSONType_seeAlso_cn