美文网首页
使用反射的性能开销体现在哪,如何优化它?

使用反射的性能开销体现在哪,如何优化它?

作者: BlueSocks | 来源:发表于2023-05-22 14:33 被阅读0次

在JVM中反射是怎样实现

先看示例代码:

public class Solution {
    public static void show(int i) {
        new Exception("#" + i).printStackTrace();
    }

    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("Solution");
        Method method = clazz.getMethod("show", int.class);
        method.invoke(null, 0);
    }
}

从以上代码看 Method.invoke来执行反射方法调用,为方便查看调用哪些类,打印了show方法的栈信息:

java.lang.Exception: #0
    at Solution.show(Solution.java:15)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at Solution.main(Solution.java:21)

Method的invoke方法,看一下实现:

public final class Method extends Executable {
  ...
  public Object invoke(Object obj, Object... args) throws ... {
    ... // 权限检查
    MethodAccessor ma = methodAccessor;
    if (ma == null) {
      ma = acquireMethodAccessor();
    }
    return ma.invoke(obj, args);
  }
}


可以看到,实际上是委派给了MethodAccessor来处理,MethodAccessor是一个接口,有2个已有的具体实现: 一个通过本地方法(NativeMethodAccessorImpl)来实现反射,即本地实现;一个则使用了委派模式(DelegatingMethodAccessorImpl),即委派实现

在默认情况下,方法的反射调用为委派实现,委派给本地实现来进行方法调用.在调用超过15次之后,委派实现便会将委派对象切换至动态实现,这个动态实现的字节码是自动生成的,将直接使用invoke指令来调用目标方法.

带来的性能开销

上面示例代码中,先后使用了Class.forName,Class.getMethodMethod.invoke三个调用。其中Class.forName会调用本地方法,Class.getMethod会遍历该类的公有方法.如果没有匹配到,它还将遍历父类的公有方法,所以这两个操作都是非常耗时的。

以getMethod为代表的查找方法操作,会返回查找得到结果的一份拷贝.因此,应当避免在热点代码中使用返回Method数组的getMethods或者getDeclaredMethods方法,以减少不必要的堆空间消耗。

除反射调用外,还额外做了两个操作:

  • Method.invoke是一个变长参数方法,在字节码层面它的最后一个参数是Object数组.编译器会在方法调用处生成一个长度为传入参数数量的Object数组,并将传入参数一一存储进该数组中.
  • 由于Object数组不能存储基本类型,编译器会将传入的基本数据类型进行自动装箱.

影响反射调用耗时有以下原因:

  1. 方法表查找
  2. 构建Object数组以及可能存在的自动装拆箱操作
  3. 运行时权限检查
  4. 可能没有方法内联/逃逸分析

优化反射性能开销

从引起性能开销的原因入手:

  1. 尽量避免反射调用虚方法
  2. 关闭运行时权限检查 : setAccessible(true)
  3. 可能需要增大基本数据类型对应的包装类缓存
  4. 关闭Inflation机制(直接动态生成字节码)
  5. 提高JVM关于每个调用能够记录的类型数目

相关文章

  • 反射的性能开销都在哪

    1.反射调用过程中会产生大量的临时对象,这些对象会占用内存,可能会导致频繁 gc,从而影响性能。2.反射调用方法时...

  • Java反射

    什么是反射? 反射的作用? 反射性能优化?

  • 【广撒网】ConcurrentHashMap提供的compute

    在实际项目中,会使用反射来编写代码,为了优化反射的性能,我们需要缓存Field对象。而缓存Field对象时,我们需...

  • RecyclerView性能优化实战

    在Android中RecyclerView的使用随处可见,它的性能优化程度跟用户体验息息相关。 性能优化实战的例子...

  • 架构解读

    高性能架构 关注点 性能指标,性能测试,性能优化 具体优化内容如概述所示 如何合理使用缓存 分布式缓存架构 采用...

  • 性能优化 optimize performance

    React 性能优化 React内部使用了多种巧妙的算法来减少由于UI更新导致的开销很大的DOM操作。对于很多应用...

  • 性能优化随想

    大家老说性能,老说性能优化。那什么是性能?如何做性能优化?如何做好性能优化? 性能是主观的,一般通过一些客观指标来...

  • 前端面试必问及加分点---性能优化篇

    如何进行网站性能优化 你有用过哪些前端性能优化的方法? 谈谈性能优化问题 代码层面的优化 前端性能优化最佳实践

  • 服务端问题排查与系统优化方法

    在面试时,必不可少的一项就是考察候选人的问题解决能力,如何排查XX问题?如何优化XX系统性能。会使用工具并不能体现...

  • MySQL Explain命令用法

    引用自 MySQL 性能优化神器 Explain 使用分析 一、介绍 explain显示了mysql如何使用索引...

网友评论

      本文标题:使用反射的性能开销体现在哪,如何优化它?

      本文链接:https://www.haomeiwen.com/subject/gdctedtx.html