자바 리플렉션 성능
클래스 생성자를 호출하는 대신 리플렉션을 사용하여 객체를 생성하면 성능에 큰 차이가 있습니까?
네 그럼요. 반사를 통해 클래스를 찾는 것은 규모가 비싸다.
리플렉션에 대한 Java 문서 인용 :
리플렉션에는 동적으로 분석되는 유형이 포함되므로 특정 Java 가상 머신 최적화를 수행 할 수 없습니다. 결과적으로, 반사 작업은 비 반사 작업보다 성능이 느리므로 성능에 민감한 응용 프로그램에서 자주 호출되는 코드 섹션에서는 피해야합니다.
다음은 Sun JRE 6u10을 실행하는 내 컴퓨터에서 5 분 안에 해킹 한 간단한 테스트입니다.
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);
}
}
이 결과로 :
35 // no reflection
465 // using reflection
조회와 인스턴스화가 함께 수행되며 경우에 따라 조회를 리팩토링 할 수 있지만 이는 기본적인 예일뿐입니다.
인스턴스화 한 경우에도 여전히 성능 저하가 발생합니다.
30 // no reflection
47 // reflection using one lookup, only instantiating
다시 YMMV.
예, 느립니다.
그러나 빌어 먹을 # 1 규칙을 기억하십시오-프리 마이징 최적화는 모든 사악의 뿌리입니다
(글쎄, DRY의 경우 # 1과 연결될 수 있음)
맹세합니다. 누군가가 직장에서 나에게 와서 나에게 물었다면 앞으로 몇 달 동안 코드를 잘 살펴볼 것입니다.
필요할 때까지 최적화하지 말고 읽을 수있는 코드를 작성하십시오.
아, 그리고 바보 같은 코드를 쓰는 것도 아닙니다. 복사 및 붙여 넣기 등의 작업을 수행 할 수있는 가장 깨끗한 방법에 대해 생각하십시오 (내부 루프와 같은 항목에주의하고 필요에 가장 잘 맞는 컬렉션 사용). "나쁜"프로그래밍)
이런 질문이 들리면 정말 놀랍지 만 모든 규칙을 실제로 배우기 전에 스스로 배우는 것을 잊어 버립니다. 누군가 "최적화 된"무언가를 디버깅하는 데 한 달에 한 번 보낸 후에 얻을 수 있습니다.
편집하다:
이 글에서 흥미로운 일이 일어났습니다. # 1 답변을 확인하십시오. 컴파일러가 최적화하는 데 얼마나 강력한 지 보여주는 예입니다. 비 반사 인스턴스화를 완전히 제거 할 수 있으므로 테스트가 완전히 유효하지 않습니다.
교훈? 깨끗하고 깔끔하게 코딩 된 솔루션을 작성하고 너무 느리다는 것이 입증 될 때까지 절대 최적화하지 마십시오.
JVM에서 A a = new A ()가 최적화되고 있음을 알 수 있습니다. 객체를 배열에 넣으면 성능이 좋지 않습니다. ;) 다음 인쇄
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);
}
}
이것은 내 컴퓨터의 차이가 약 150ns라는 것을 나타냅니다.
"중요한"은 전적으로 맥락에 의존합니다.
리플렉션을 사용하여 일부 구성 파일을 기반으로 단일 처리기 객체를 만든 다음 데이터베이스 쿼리를 실행하는 데 나머지 시간을 소비하는 경우 중요하지 않습니다. 타이트한 루프에서 반사를 통해 많은 수의 객체를 생성하는 경우 중요합니다.
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.
If there really is need for something faster than reflection, and it's not just a premature optimization, then bytecode generation with ASM or 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.
Some examples of applications which use code generation:
Invoking methods on proxies generated by CGLIB is slightly faster than Java's dynamic proxies, because CGLIB generates bytecode for its proxies, but dynamic proxies use only reflection (I measured CGLIB to be about 10x faster in method calls, but creating the proxies was slower).
JSerial generates bytecode for reading/writing the fields of serialized objects, instead of using reflection. There are some benchmarks on JSerial's site.
I'm not 100% sure (and I don't feel like reading the source now), but I think Guice generates bytecode to do dependency injection. Correct me if I'm wrong.
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.
Yes there is a performance hit when using Reflection but a possible workaround for optimization is caching the method:
Method md = null; // Call while looking up the method at each iteration.
millis = System.currentTimeMillis( );
for (idx = 0; idx < CALL_AMOUNT; idx++) {
md = ri.getClass( ).getMethod("getValue", null);
md.invoke(ri, null);
}
System.out.println("Calling method " + CALL_AMOUNT+ " times reflexively with lookup took " + (System.currentTimeMillis( ) - millis) + " millis");
// Call using a cache of the method.
md = ri.getClass( ).getMethod("getValue", null);
millis = System.currentTimeMillis( );
for (idx = 0; idx < CALL_AMOUNT; idx++) {
md.invoke(ri, null);
}
System.out.println("Calling method " + CALL_AMOUNT + " times reflexively with cache took " + (System.currentTimeMillis( ) - millis) + " millis");
will result in:
[java] Calling method 1000000 times reflexively with lookup took 5618 millis
[java] Calling method 1000000 times reflexively with cache took 270 millis
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.
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.)
Interestingly enough, settting setAccessible(true), which skips the security checks, has a 20% reduction in cost.
Without setAccessible(true)
new A(), 70 ns
A.class.newInstance(), 214 ns
new A(), 84 ns
A.class.newInstance(), 229 ns
With setAccessible(true)
new A(), 69 ns
A.class.newInstance(), 159 ns
new A(), 85 ns
A.class.newInstance(), 171 ns
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.
In the doReflection() is the overhead because of Class.forName("misc.A") (that would require a class lookup, potentially scanning the class path on the filsystem), rather than the newInstance() called on the class. I am wondering what the stats would look like if the Class.forName("misc.A") is done only once outside the for-loop, it doesn't really have to be done for every invocation of the loop.
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 tutorials for more details.
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");
}
}
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).
I think it depends on how light/heavy the target method is. if the target method is very light(e.g. getter/setter), It could be 1 ~ 3 times slower. if the target method takes about 1 millisecond or above, then the performance will be very close. here is the test I did with Java 8 and reflectasm :
public class ReflectionTest extends TestCase {
@Test
public void test_perf() {
Profiler.run(3, 100000, 3, "m_01 by refelct", () -> Reflection.on(X.class)._new().invoke("m_01")).printResult();
Profiler.run(3, 100000, 3, "m_01 direct call", () -> new X().m_01()).printResult();
Profiler.run(3, 100000, 3, "m_02 by refelct", () -> Reflection.on(X.class)._new().invoke("m_02")).printResult();
Profiler.run(3, 100000, 3, "m_02 direct call", () -> new X().m_02()).printResult();
Profiler.run(3, 100000, 3, "m_11 by refelct", () -> Reflection.on(X.class)._new().invoke("m_11")).printResult();
Profiler.run(3, 100000, 3, "m_11 direct call", () -> X.m_11()).printResult();
Profiler.run(3, 100000, 3, "m_12 by refelct", () -> Reflection.on(X.class)._new().invoke("m_12")).printResult();
Profiler.run(3, 100000, 3, "m_12 direct call", () -> X.m_12()).printResult();
}
public static class X {
public long m_01() {
return m_11();
}
public long m_02() {
return m_12();
}
public static long m_11() {
long sum = IntStream.range(0, 10).sum();
assertEquals(45, sum);
return sum;
}
public static long m_12() {
long sum = IntStream.range(0, 10000).sum();
assertEquals(49995000, sum);
return sum;
}
}
}
The complete test code is available at GitHub:ReflectionTest.java
참고URL : https://stackoverflow.com/questions/435553/java-reflection-performance
'IT' 카테고리의 다른 글
파이썬 날짜 시간을 읽을 수있는 형식 날짜가있는 문자열로 바꾸려면 어떻게합니까? (0) | 2020.05.31 |
---|---|
행에서 첫 번째와 마지막 TD를 선택하는 방법은 무엇입니까? (0) | 2020.05.31 |
루비 배열에서 문자열로 변환 (0) | 2020.05.31 |
jQuery에서 요소의 전체 너비 (패딩 및 테두리 포함) (0) | 2020.05.31 |
Enter 키를 누른 후 onChange 이벤트를 호출하려면 (0) | 2020.05.31 |