01
—
事件起因
02
—
迹象表现

“http-nio-8080-exec-197” #7490 daemon prio=5 os_prio=0 tid=0x00007fdd5806b000 nid=0xed1 waiting for monitor entry [0x00007fdd1b7d5000]java.lang.Thread.State: BLOCKED (on object monitor)at org.apache.catalina.webresources.CachedResource.validateResources(CachedResource.java:125)- waiting to lock <0x000000008015c660> (a org.apache.catalina.webresources.CachedResource)at org.apache.catalina.webresources.Cache.getResources(Cache.java:129)at org.apache.catalina.webresources.StandardRoot.getResources(StandardRoot.java:315)at org.apache.catalina.webresources.StandardRoot.getClassLoaderResources(StandardRoot.java:231)at org.apache.catalina.loader.WebappClassLoaderBase.findResources(WebappClassLoaderBase.java:995)at java.lang.ClassLoader.getResources(ClassLoader.java:1142)at com.alibaba.fastjson.util.ServiceLoader.load(ServiceLoader.java:33)at com.alibaba.fastjson.parser.ParserConfig.getDeserializer(ParserConfig.java:459)at com.alibaba.fastjson.parser.ParserConfig.getDeserializer(ParserConfig.java:354)at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:639)at com.alibaba.fastjson.JSON.parseObject(JSON.java:350)at com.alibaba.fastjson.JSON.parseObject(JSON.java:318)at com.alibaba.fastjson.JSON.parseObject(JSON.java:281)
(熟悉java线程知识的同学都知道,这个线程状态为BLOCKED,说明线程在等待获取一个排它锁。)
通过上面的堆栈信息,暂且抱怀疑的态度,怀疑跟这个ParserConfig#getDeserializer方法关系。
03
—
根因分析
3.1. 定位出问题的代码
Type parameterizedTypeClass = ParameterizedTypeImpl.make(cl, types, null);if (!result.contains(CAN_NOT_SERIALIZABLE_0) && !result.contains(CAN_NOT_SERIALIZABLE_1) && !result.contains(CAN_NOT_SERIALIZABLE_2)) {MyParserConfig config = new MyParserConfig();// 禁用ASM影响性能config.setAsmEnable(false);object = JSON.parseObject(result, parameterizedTypeClass, config);}// 泛型内套泛型以上parseObject可能返回null值if (object == null) {// 此处未提供对多种序列化协议的支持object = PojoUtils.realize(JSONObject.parseObject(result, Map.class), cl, parameterizedTypeClass);}
--> com.alibaba.fastjson.JSON#parseObject(java.lang.String, java.lang.reflect.Type, com.alibaba.fastjson.parser.ParserConfig, com.alibaba.fastjson.parser.Feature...)--> com.alibaba.fastjson.JSON#parseObject(java.lang.String, java.lang.reflect.Type, com.alibaba.fastjson.parser.ParserConfig, com.alibaba.fastjson.parser.deserializer.ParseProcess, int, com.alibaba.fastjson.parser.Feature...)--> com.alibaba.fastjson.parser.DefaultJSONParser#parseObject(java.lang.reflect.Type, java.lang.Object)--> com.alibaba.fastjson.parser.ParserConfig#getDeserializer(java.lang.reflect.Type)--> com.alibaba.fastjson.parser.ParserConfig#get--> com.alibaba.fastjson.util.IdentityHashMap#get--> com.alibaba.fastjson.parser.ParserConfig#getDeserializer(java.lang.Class<?>, java.lang.reflect.Type)
3.2. 深入剖析代码问题
继续看com.alibaba.fastjson.util.IdentityHashMap#get这个方法的实现:
public final V get(K key) {final int hash = System.identityHashCode(key); // #1final int bucket = hash & indexMask;for (Entry<K, V> entry = buckets[bucket]; entry != null; entry = entry.next) {if (key == entry.key) {return (V) entry.value;}}return null;}
public static void main(String[] args) {System.out.println(System.identityHashCode(new String()));System.out.println(System.identityHashCode(new String()));System.out.println(new String("zhangsan").hashCode());System.out.println(new String("zhangsan").hashCode());}以上代码输出:6210098751265094477-1432604556-1432604556
public ObjectDeserializer getDeserializer(Class<?> clazz, Type type) {ObjectDeserializer deserializer = get(type);if (deserializer != null) {return deserializer;}if (type == null) {type = clazz;}deserializer = get(type);if (deserializer != null) {return deserializer;}{JSONType annotation = TypeUtils.getAnnotation(clazz,JSONType.class);if (annotation != null) {Class<?> mappingTo = annotation.mappingTo();if (mappingTo != Void.class) {return getDeserializer(mappingTo, mappingTo);}}}if (type instanceof WildcardType || type instanceof TypeVariable || type instanceof ParameterizedType) {deserializer = get(clazz);}if (deserializer != null) {return deserializer;}for (Module module : modules) {deserializer = module.createDeserializer(this, clazz);if (deserializer != null) {putDeserializer(type, deserializer);return deserializer;}}...此处省略1w行代码...putDeserializer(type, deserializer);return deserializer;}
public void putDeserializer(Type type, ObjectDeserializer deserializer) {Type mixin = JSON.getMixInAnnotations(type);if (mixin != null) {IdentityHashMap<Type, ObjectDeserializer> mixInClasses = this.mixInDeserializers.get(type);if (mixInClasses == null) {//多线程下可能会重复创建,但不影响正确性mixInClasses = new IdentityHashMap<Type, ObjectDeserializer>(4);this.mixInDeserializers.put(type, mixInClasses);}mixInClasses.put(mixin, deserializer);} else {this.deserializers.put(type, deserializer);}}


04
—
根因总结
05
—
解决方案
ConcurrentMap<Type, Type> classTypeCache= new ConcurrentHashMap<Type, Type>(16, 0.75f, 1);Type parameterizedTypeClass = ParameterizedTypeImpl.make(cl, types, null);Type cachedType = classTypeCache.get(parameterizedTypeClass);if (cachedType == null) {classTypeCache.putIfAbsent(parameterizedTypeClass, parameterizedTypeClass);cachedType = classTypeCache.get(parameterizedTypeClass);}if (!result.contains(CAN_NOT_SERIALIZABLE_0) && !result.contains(CAN_NOT_SERIALIZABLE_1) && !result.contains(CAN_NOT_SERIALIZABLE_2)) {MyParserConfig config = new MyParserConfig();// 禁用ASM影响性能// config.setAsmEnable(false);object = JSON.parseObject(result, cachedType, config);}// 泛型内套泛型以上parseObject返回null值,通过以下方式弥补if (object == null) {// 此处未提供对多种序列化协议的支持object = PojoUtils.realize(JSONObject.parseObject(result, Map.class), cl, cachedType);}
package test;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.util.ParameterizedTypeImpl;import java.lang.reflect.Type;import java.util.Objects;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.ConcurrentMap;import java.util.concurrent.atomic.AtomicLong;public class FastjsonLeakDemo {// static ConcurrentMap<Type, Type> classTypeCache// = new ConcurrentHashMap<Type, Type>(16, 0.75f, 1);public static void main(String[] args) {System.out.println(System.identityHashCode(new String()));System.out.println(System.identityHashCode(new String()));System.out.println(new String("zhangsan").hashCode());System.out.println(new String("zhangsan").hashCode());/*-Xms30m-Xmx30m-XX:+PrintGCDateStamps-XX:+PrintGCDetails-XX:+PrintHeapAtGC-XX:+PrintGCApplicationStoppedTime-Xloggc:/tmp/gc_%p_%t_.log-XX:+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath=/tmp/*/final long start = System.currentTimeMillis();final AtomicLong counter = new AtomicLong(0);Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {@Overridepublic void run() {System.out.println("count: " + counter.get());System.out.println("took " + (System.currentTimeMillis() - start) + " ms");}}));UserInfo someInfo = new UserInfo();someInfo.setName("Tom");Response<UserInfo> result = new Response<>();result.setData(someInfo);result.setRetCode(0);result.setMessage("Success");String json = JSON.toJSONString(result);// 模拟请求while (true) {ParameterizedTypeImpl type = new ParameterizedTypeImpl(new Type[]{UserInfo.class}, null, Response.class);// Type cachedType = classTypeCache.get(type);// if (cachedType == null) {// classTypeCache.putIfAbsent(type, type);// cachedType = classTypeCache.get(type);// }Response<UserInfo> tmpResult = (Response<UserInfo>) JSON.parseObject(json, type);Objects.requireNonNull(tmpResult);counter.incrementAndGet();}}public static class Response<T> {private int retCode;private String message;private T data;public int getRetCode() {return retCode;}public void setRetCode(int retCode) {this.retCode = retCode;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}public T getData() {return data;}public void setData(T data) {this.data = data;}}public static class UserInfo {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}}}
文章转载自编程阁楼,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




