大数据计算优化:Java 反射为什么慢?
来源:互联网 发布:解西游知乎 编辑:程序博客网 时间:2024/05/18 03:10
起因
测试
public class Test { public static void main(String[] args) throws Exception { doGet(); doReflectGet(); } public static void doGet() throws Exception { long start = System.currentTimeMillis(); A a = new A(); for (int i = 0; i < 100000000; i++) { a.getI(); } System.out.println(System.currentTimeMillis() - start); } public static void doReflectGet() throws Exception { long start = System.currentTimeMillis(); A a = new A(); Class<A> cls = A.class; Field field = cls.getField("i"); for (int i = 0; i < 100000000; i++) { field.get(a); } System.out.println(System.currentTimeMillis() - start); }}
结果:5ms 923ms
难道反射和普通get相差200倍的性能吗?!!
其实并不是,反射是慢,但慢多少这个例子并不能测试出来,这个例子第二个方法由于反射的存在,导致JVM无法优化。
第一个例子不管循环多少次,结果都是5ms左右,因为JVM直接把这段代码优化了,JVM直接判定循环中的代码没有对外界造成影响,所以直接忽略调用了。
第二个例子:
public class Test { public static void main(String[] args) throws Exception { doGet(); doReflectGet(); } public static void doGet() throws Exception { long start = System.currentTimeMillis(); Integer[] arr = new Integer[100000000]; A a = new A(); for (int i = 0; i < 100000000; i++) { arr[i] = a.getI(); } System.out.println(System.currentTimeMillis() - start); } public static void doReflectGet() throws Exception { long start = System.currentTimeMillis(); Integer[] arr = new Integer[100000000]; A a = new A(); Class<A> cls = A.class; Field field = cls.getField("i"); for (int i = 0; i < 100000000; i++) { arr[i] = (Integer) field.get(a); } System.out.println(System.currentTimeMillis() - start); }}
结果:513ms 2805ms
这才是真正的差距(修改循环次数性能差距变化不大),前面那个例子证明反射干扰了JVM的优化,这个例子说明在去除干扰优化后(只能说去除了干扰的循环优化)还差了5倍的性能!!为什么呢?
反射Field/get
jlongsun::misc::Unsafe::getLong (jobject obj, jlong offset){ jlong *addr = (jlong *) ((char *) obj + offset); spinlock lock; return *addr;}C++中其实就是简单的通过基地址和偏移来指针运算拿到内存值,感觉上没有什么劣势,只有JNI,但Unsafe可是JVM自带的JNI(Intrinsic function?),性能应该不会差。
总结
反射缺点:
1.由于是本地方法调用,让JVM无法优化(还有JIT?)。
2.反射方法调用还有验证过程和参数问题,参数需要装箱拆箱、需要组装成Object[]形式、异常的包装等等问题,篇幅问题这里不加以叙述。
解决方法就是前面提到的CodeGen(这边特指Run time code generation),既然没有getXXX方法,那就通过动态生成代码来生成getXXX方法(Javassist等技术)。
类似的反射其它用法:方法调用、newInstance类都要慢很多,这在业务系统中可以忽略不计(和Http网络延时、数据库访问等等比起来反射消耗的太小了),但是在大数据计算的场景中,往往性能瓶颈就在这些地方。
CodeGen在Spark中已经运用起来,主要用于消除方法的多态调用(这里主要是因为多态导致分支预测失败带来的性能影响)。
Flink和Spark在其它方面还克服了JVM的其它一些“缺点”(与其说是缺点,其实是妥协):内存管理来避免Full GC、对象序列化到字节内存中减小对象的内存开销、直接在字节内存上计算以减少不必要序列化同时缓存友好 等等。
- 大数据计算优化:Java 反射为什么慢?
- 大数据 java优化
- easyui datagrid 大数据加载效率慢,优化解决方法
- easyui datagrid 大数据加载效率慢,优化解决方法
- easyui datagrid 大数据加载效率慢,优化解决方法
- java计算大数据阶乘
- MySQL数据慢优化
- 为什么 .NET 的反射这么慢?
- 学习大数据为什么要先学Java?
- 学大数据为什么要学Java
- 为什么分次批量删除一个带索引的大表里的数据会慢
- java计算金额大数据用BigDecimal
- 【大数据技巧】MaxCompute优化去重计算的性能
- 为什么Java比C++慢?
- Java为什么比C慢?
- mysql笔记02:source命令导入大数据速度慢优化
- 计算机变慢10大原因
- java反射大汇总
- LoadRunner下设置监控Windows系统资源
- Tricks(二十九)—— 2^10000 的位数
- 项目Makefile书写示例(二)
- Qt Creator 插件开发(2):第一个插件
- landoj--1603--Minimum Sum(规律&&暴力)
- 大数据计算优化:Java 反射为什么慢?
- WSAIoctl Function
- javascript文档对象模型DOM入门详解
- numpy--prod和pad运算
- hadoop入门篇—简介(2)
- Socket心跳包机制
- sql语句备忘
- 用Spotlight实时监控Windows Server 08
- ArrayList和数组的相互转换