Java 反射性能
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/435553/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java Reflection Performance
提问by dmanxiii
Does creating an object using reflection rather than calling the class constructor result in any significant performance differences?
使用反射而不是调用类构造函数创建对象会导致任何显着的性能差异吗?
采纳答案by Yuval Adam
Yes - absolutely.Looking up a class via reflection is, by magnitude, more expensive.
是的,一点没错。从数量上看,通过反射查找类的开销更大。
Quoting Java's documentation on reflection:
引用Java 的反射文档:
Because reflection involves types that are dynamically resolved, certain Java virtual machine optimizations can not be performed. Consequently, reflective operations have slower performance than their non-reflective counterparts, and should be avoided in sections of code which are called frequently in performance-sensitive applications.
由于反射涉及动态解析的类型,因此无法执行某些 Java 虚拟机优化。因此,反射操作的性能比非反射操作慢,应避免在对性能敏感的应用程序中频繁调用的代码段中使用。
Here's a simple test I hacked up in 5 minutes on my machine, running Sun JRE 6u10:
这是我在 5 分钟内在我的机器上完成的一个简单测试,运行 Sun JRE 6u10:
public class Main {
public static void main(String[] args) throws Exception
{
doRegular();
doReflection();
}
public static void doRegular() throws Exception
{
long start = System.currentTimeMillis();
for (int i=0; i<1000000; i++)
{
A a = new A();
a.doSomeThing();
}
System.out.println(System.currentTimeMillis() - start);
}
public static void doReflection() throws Exception
{
long start = System.currentTimeMillis();
for (int i=0; i<1000000; i++)
{
A a = (A) Class.forName("misc.A").newInstance();
a.doSomeThing();
}
System.out.println(System.currentTimeMillis() - start);
}
}
With these results:
有了这些结果:
35 // no reflection
465 // using reflection
Bear in mind the lookup and the instantiation are done together, and in some cases the lookup can be refactored away, but this is just a basic example.
请记住,查找和实例化是一起完成的,在某些情况下,可以重构查找,但这只是一个基本示例。
Even if you just instantiate, you still get a performance hit:
即使您只是实例化,您仍然会受到性能影响:
30 // no reflection
47 // reflection using one lookup, only instantiating
Again, YMMV.
再次,YMMV。
回答by Elie
Yes, it is significantly slower. We were running some code that did that, and while I don't have the metrics available at the moment, the end result was that we had to refactor that code to not use reflection. If you know what the class is, just call the constructor directly.
是的,它明显变慢了。我们正在运行一些这样做的代码,虽然我目前没有可用的指标,但最终结果是我们不得不重构该代码以不使用反射。如果知道类是什么,直接调用构造函数即可。
回答by Marcus Downing
There is some overhead with reflection, but it's a lot smaller on modern VMs than it used to be.
反射有一些开销,但在现代虚拟机上它比以前小得多。
If you're using reflection to create every simple object in your program then something is wrong. Using it occasionally, when you have good reason, shouldn't be a problem at all.
如果您使用反射来创建程序中的每个简单对象,那么就会出现问题。偶尔使用它,当你有充分的理由时,根本不应该有问题。
回答by aledbf
Yes, always will be slower create an object by reflection because the JVM cannot optimize the code on compilation time. See the Sun/Java Reflection tutorialsfor more details.
是的,通过反射创建对象总是会更慢,因为 JVM 无法在编译时优化代码。有关更多详细信息,请参阅 Sun/Java反射教程。
See this simple test:
看这个简单的测试:
public class TestSpeed {
public static void main(String[] args) {
long startTime = System.nanoTime();
Object instance = new TestSpeed();
long endTime = System.nanoTime();
System.out.println(endTime - startTime + "ns");
startTime = System.nanoTime();
try {
Object reflectionInstance = Class.forName("TestSpeed").newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
endTime = System.nanoTime();
System.out.println(endTime - startTime + "ns");
}
}
回答by kdgregory
"Significant" is entirely dependent on context.
“重要”完全取决于上下文。
If you're using reflection to create a single handler object based on some configuration file, and then spending the rest of your time running database queries, then it's insignificant. If you're creating large numbers of objects via reflection in a tight loop, then yes, it's significant.
如果您使用反射基于某个配置文件创建单个处理程序对象,然后将其余时间用于运行数据库查询,那么这无关紧要。如果您在紧密循环中通过反射创建大量对象,那么是的,这很重要。
In general, design flexibility (where needed!) should drive your use of reflection, not performance. However, to determine whether performance is an issue, you need to profile rather than get arbitrary responses from a discussion forum.
一般来说,设计灵活性(在需要的地方!)应该驱动你使用反射,而不是性能。但是,要确定性能是否是一个问题,您需要进行概要分析,而不是从讨论论坛获得任意响应。
回答by sproketboy
Often you can use Apache commons BeanUtils or PropertyUtils which introspection (basically they cache the meta data about the classes so they don't always need to use reflection).
通常,您可以使用 Apache commons BeanUtils 或 PropertyUtils 进行自省(基本上它们缓存有关类的元数据,因此它们并不总是需要使用反射)。
回答by Peter Lawrey
You may find that A a = new A() is being optimised out by the JVM. If you put the objects into an array, they don't perform so well. ;) The following prints...
您可能会发现 A a = new A() 正在被 JVM 优化掉。如果将对象放入数组中,它们的性能就不会那么好。;) 以下打印...
new A(), 141 ns
A.class.newInstance(), 266 ns
new A(), 103 ns
A.class.newInstance(), 261 ns
public class Run {
private static final int RUNS = 3000000;
public static class A {
}
public static void main(String[] args) throws Exception {
doRegular();
doReflection();
doRegular();
doReflection();
}
public static void doRegular() throws Exception {
A[] as = new A[RUNS];
long start = System.nanoTime();
for (int i = 0; i < RUNS; i++) {
as[i] = new A();
}
System.out.printf("new A(), %,d ns%n", (System.nanoTime() - start)/RUNS);
}
public static void doReflection() throws Exception {
A[] as = new A[RUNS];
long start = System.nanoTime();
for (int i = 0; i < RUNS; i++) {
as[i] = A.class.newInstance();
}
System.out.printf("A.class.newInstance(), %,d ns%n", (System.nanoTime() - start)/RUNS);
}
}
This suggest the difference is about 150 ns on my machine.
这表明我的机器上的差异约为 150 ns。
回答by Bill K
Yes, it's slower.
是的,它更慢。
But remember the damn #1 rule--PREMATURE OPTIMIZATION IS THE ROOT OF ALL EVIL
但请记住该死的 #1 规则——过早优化是万恶之源
(Well, may be tied with #1 for DRY)
(好吧,可能与 DRY 的 #1 并列)
I swear, if someone came up to me at work and asked me this I'd be very watchful over their code for the next few months.
我发誓,如果有人在工作中走过来问我这个问题,我会在接下来的几个月里密切关注他们的代码。
You must never optimize until you are sure you need it, until then, just write good, readable code.
在确定需要优化之前,永远不要进行优化,在那之前,只需编写良好、可读的代码即可。
Oh, and I don't mean write stupid code either. Just be thinking about the cleanest way you can possibly do it--no copy and paste, etc. (Still be wary of stuff like inner loops and using the collection that best fits your need--Ignoring these isn't "unoptimized" programming, it's "bad" programming)
哦,我也不是说写愚蠢的代码。只是考虑一下你可能做的最干净的方式 - 没有复制和粘贴等(仍然要警惕内循环和使用最适合你需要的集合之类的东西 - 忽略这些不是“未优化的”编程,这是“糟糕”的编程)
It freaks me out when I hear questions like this, but then I forget that everyone has to go through learning all the rules themselves before they really get it. You'll get it after you've spent a man-month debugging something someone "Optimized".
当我听到这样的问题时,我吓坏了,但后来我忘记了每个人都必须自己学习所有规则才能真正掌握它。在你花了一个人月调试某人“优化”的东西后,你会得到它。
EDIT:
编辑:
An interesting thing happened in this thread. Check the #1 answer, it's an example of how powerful the compiler is at optimizing things. The test is completely invalid because the non-reflective instantiation can be completely factored out.
在这个线程中发生了一件有趣的事情。检查 #1 答案,这是编译器在优化事物方面有多强大的一个例子。该测试完全无效,因为可以完全排除非反射实例化。
Lesson? Don't EVER optimize until you've written a clean, neatly coded solution and proven it to be too slow.
课?在编写干净、编码整齐的解决方案并证明它太慢之前,不要进行优化。
回答by Esko Luontola
If there reallyis need for something faster than reflection, and it's not just a premature optimization, then bytecode generation with ASMor a higher level library is an option. Generating the bytecode the first time is slower than just using reflection, but once the bytecode has been generated, it is as fast as normal Java code and will be optimized by the JIT compiler.
如果确实需要比反射更快的东西,而且不仅仅是过早的优化,那么使用ASM或更高级别的库生成字节码是一种选择。第一次生成字节码比仅仅使用反射慢,但是一旦生成字节码,它和普通 Java 代码一样快,并且会被 JIT 编译器优化。
Some examples of applications which use code generation:
使用代码生成的一些应用程序示例:
Invoking methods on proxies generated by CGLIBis slightly faster than Java's dynamic proxies, because CGLIB generates bytecode for its proxies, but dynamic proxies use only reflection (I measuredCGLIB to be about 10x faster in method calls, but creating the proxies was slower).
JSerialgenerates bytecode for reading/writing the fields of serialized objects, instead of using reflection. There are some benchmarkson JSerial's site.
I'm not 100% sure (and I don't feel like reading the source now), but I think Guicegenerates bytecode to do dependency injection. Correct me if I'm wrong.
回答by Doradus
Reflection is slow, though object allocation is not as hopeless as other aspects of reflection. Achieving equivalent performance with reflection-based instantiation requires you to write your code so the jit can tell which class is being instantiated. If the identity of the class can't be determined, then the allocation code can't be inlined. Worse, escape analysis fails, and the object can't be stack-allocated. If you're lucky, the JVM's run-time profiling may come to the rescue if this code gets hot, and may determine dynamically which class predominates and may optimize for that one.
反射很慢,尽管对象分配不像反射的其他方面那样无望。使用基于反射的实例化实现等效性能需要您编写代码,以便 jit 可以判断正在实例化哪个类。如果无法确定类的身份,则无法内联分配代码。更糟糕的是,逃逸分析失败,并且对象不能被堆栈分配。如果幸运的话,如果此代码变热,JVM 的运行时分析可能会派上用场,并且可以动态确定哪个类占主导地位,并可能针对该类进行优化。
Be aware the microbenchmarks in this thread are deeply flawed, so take them with a grain of salt. The least flawed by far is Peter Lawrey's: it does warmup runs to get the methods jitted, and it (consciously) defeats escape analysis to ensure the allocations are actually occurring. Even that one has its problems, though: for example, the tremendous number of array stores can be expected to defeat caches and store buffers, so this will wind up being mostly a memory benchmark if your allocations are very fast. (Kudos to Peter on getting the conclusion right though: that the difference is "150ns" rather than "2.5x". I suspect he does this kind of thing for a living.)
请注意,此线程中的微基准测试存在严重缺陷,因此请谨慎对待。到目前为止,缺陷最少的是 Peter Lawrey 的:它执行预热运行以使方法被立即执行,并且(有意识地)击败逃逸分析以确保分配实际发生。但是,即使这样也有其问题:例如,可以预期大量的数组存储会击败缓存和存储缓冲区,因此如果您的分配非常快,这将主要成为内存基准。(不过,感谢 Peter 得出正确的结论:差异是“150ns”而不是“2.5x”。我怀疑他做这种事情是为了谋生。)