一、Java Flight Recorder
参考:https://blog.csdn.net/qq_37552993/article/details/102545780
配置之后让测试程序运行五分钟,得到下面这张图
ps:所copy的对象只有俩字段
二、分析耗时原因
2.1 isReadable isWriteable
每次copy时会对每个字段的可读性和可写性进行重复判断,而由于在复制过程中类的结构一般不会改变,所以这俩状态应该被缓存下来。
结论来源:https://blog.csdn.net/u010209217/article/details/84837821
通过反射方式获取可读性和可写性
isPackageAccessible:195, ReflectUtil (sun.reflect.misc)
get:71, MethodRef (java.beans)
getReadMethod:207, PropertyDescriptor (java.beans)
getReadMethod:1256, PropertyUtilsBean (org.apache.commons.beanutils)
isReadable:1435, PropertyUtilsBean (org.apache.commons.beanutils)
copyProperties:281, BeanUtilsBean (org.apache.commons.beanutils)
copyProperties:137, BeanUtils (org.apache.commons.beanutils)
copyProperties:8, CommonsBeanUtilsPropertiesCopier (com.test.BeanutilsTest.copier.Impl)
CommonsBeanUtilsPropertiesCopierTest:25, CopierShowError (com.test.BeanutilsTest)
main:38, CopierShowError (com.test.BeanutilsTest)
isPackageAccessible:195, ReflectUtil (sun.reflect.misc)
getBeanInfo:164, Introspector (java.beans)
getPropertyDescriptors:980, PropertyUtilsBean (org.apache.commons.beanutils)
getPropertyDescriptors:1084, PropertyUtilsBean (org.apache.commons.beanutils)
getPropertyDescriptor:912, PropertyUtilsBean (org.apache.commons.beanutils)
isWriteable:1523, PropertyUtilsBean (org.apache.commons.beanutils)
copyProperties:281, BeanUtilsBean (org.apache.commons.beanutils)
copyProperties:137, BeanUtils (org.apache.commons.beanutils)
copyProperties:8, CommonsBeanUtilsPropertiesCopier (com.test.BeanutilsTest.copier.Impl)
CommonsBeanUtilsPropertiesCopierTest:25, CopierShowError (com.test.BeanutilsTest)
main:38, CopierShowError (com.test.BeanutilsTest)
2.2 StringBuffer.append
输出了大量的日志调试信息
结论来源:https://segmentfault.com/a/1190000019356477
append:262, StringBuffer (java.lang)
convert:1077, BeanUtilsBean (org.apache.commons.beanutils)
copyProperty:437, BeanUtilsBean (org.apache.commons.beanutils)
copyProperties:286, BeanUtilsBean (org.apache.commons.beanutils)
copyProperties:137, BeanUtils (org.apache.commons.beanutils)
copyProperties:8, CommonsBeanUtilsPropertiesCopier (com.test.BeanutilsTest.copier.Impl)
CommonsBeanUtilsPropertiesCopierTest:25, CopierShowError (com.test.BeanutilsTest)
main:38, CopierShowError (com.test.BeanutilsTest)
2.3、getSimpleProperty
与isReadable类似,此函数内调用了getReadMethod以获取读方法,在getReadMethod又进一步调用反射方法,所以性能低。
isPackageAccessible:195, ReflectUtil (sun.reflect.misc)
get:71, MethodRef (java.beans)
getReadMethod:207, PropertyDescriptor (java.beans)
getReadMethod:1256, PropertyUtilsBean (org.apache.commons.beanutils)
getSimpleProperty:1325, PropertyUtilsBean (org.apache.commons.beanutils)
copyProperties:284, BeanUtilsBean (org.apache.commons.beanutils)
copyProperties:137, BeanUtils (org.apache.commons.beanutils)
copyProperties:8, CommonsBeanUtilsPropertiesCopier (com.test.BeanutilsTest.copier.Impl)
CommonsBeanUtilsPropertiesCopierTest:25, CopierShowError (com.test.BeanutilsTest)
main:38, CopierShowError (com.test.BeanutilsTest)
这个函数中获取的读方法实际上已存在于descriptorsCache这个缓存中,但是需要通过反射进一步校验访问权限
校验访问权限。
Method get() {
if (this.methodRef == null) {
return null;
}
Method method = this.methodRef.get();
if (method == null) {
method = find(this.typeRef.get(), this.signature);
if (method == null) {
this.signature = null;
this.methodRef = null;
this.typeRef = null;
return null;
}
this.methodRef = new SoftReference<>(method);
}
return isPackageAccessible(method.getDeclaringClass()) ? method : null;// 若有权限,返回方法,反之返回null
}
三、总结
通过火焰图发现StringBuffer.append isReadable isWriteable getSimpleProperty调用时间长,进一步分析发现,耗时问题可以被分为两类,一类是字符串拼接,一类是反射方法isPackageAccessible的调用。
除了上述方法之外,还有很多其他方法也调用了isPackageAccessible
end