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
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团队
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解析共用基础设施,性能有保证