Rendimiento de reflexión de Java
¿La creación de un objeto mediante la reflexión en lugar de llamar al constructor de la clase produce diferencias significativas en el rendimiento?
Si, absolutamente. Buscar una clase mediante reflexión es, por magnitud , más caro.
Citando la documentación de Java sobre la reflexión :
Dado que la reflexión implica tipos que se resuelven dinámicamente, no se pueden realizar determinadas optimizaciones de la máquina virtual Java. En consecuencia, las operaciones reflectantes tienen un rendimiento más lento que sus contrapartes no reflectantes y deben evitarse en secciones de código que se llaman con frecuencia en aplicaciones sensibles al rendimiento.
Aquí hay una prueba simple que hackeé en 5 minutos en mi máquina, ejecutando 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);
}
}
Con estos resultados:
35 // no reflection
465 // using reflection
Tenga en cuenta que la búsqueda y la creación de instancias se realizan juntas y, en algunos casos, la búsqueda se puede refactorizar, pero este es solo un ejemplo básico.
Incluso si simplemente crea una instancia, aún obtendrá un impacto en el rendimiento:
30 // no reflection
47 // reflection using one lookup, only instantiating
De nuevo, YMMV.
Sí, es más lento.
Pero recuerde la maldita regla número uno: LA OPTIMIZACIÓN PREMATURA ES LA RAÍZ DE TODO MAL
(Bueno, puede estar empatado con el número 1 para SECO)
Lo juro, si alguien se acercara a mí en el trabajo y me preguntara esto, estaría muy atento a su código durante los próximos meses.
Nunca debes optimizar hasta que estés seguro de que lo necesitas; hasta entonces, simplemente escribe un código bueno y legible.
Ah, y tampoco me refiero a escribir código estúpido. Simplemente piense en la forma más limpia en la que pueda hacerlo: sin copiar y pegar, etc. (Aun así, tenga cuidado con cosas como bucles internos y use la colección que mejor se adapte a sus necesidades; ignorarlos no es programación "no optimizada"). , es una programación "mala")
Me asusta cuando escucho preguntas como esta, pero luego olvido que todos tienen que aprender todas las reglas por sí mismos antes de entenderlas realmente. Lo obtendrá después de haber pasado un mes-hombre depurando algo que alguien "optimizó".
EDITAR:
En este hilo sucedió algo interesante. Verifique la respuesta n.° 1, es un ejemplo de cuán poderoso es el compilador para optimizar cosas. La prueba es completamente inválida porque la instanciación no reflexiva se puede descartar por completo.
¿Lección? NUNCA optimice hasta que haya escrito una solución limpia y claramente codificada y haya demostrado que es demasiado lenta.
Es posible que descubra que la JVM está optimizando A a = new A(). Si colocas los objetos en una matriz, no funcionan tan bien. ;) Las siguientes impresiones...
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);
}
}
Esto sugiere que la diferencia es de aproximadamente 150 ns en mi máquina.
Si realmente se necesita algo más rápido que la reflexión, y no se trata solo de una optimización prematura, entonces la generación de código de bytes con ASM o una biblioteca de nivel superior es una opción. Generar el código de bytes la primera vez es más lento que simplemente usar la reflexión, pero una vez que se ha generado el código de bytes, es tan rápido como el código Java normal y será optimizado por el compilador JIT.
Algunos ejemplos de aplicaciones que utilizan generación de código:
Invocar métodos en proxies generados por CGLIB es ligeramente más rápido que los proxies dinámicos de Java , porque CGLIB genera código de bytes para sus proxies, pero los proxies dinámicos solo usan reflexión ( mediré que CGLIB era aproximadamente 10 veces más rápido en las llamadas a métodos, pero la creación de los proxies fue más lenta).
JSerial genera código de bytes para leer/escribir los campos de objetos serializados, en lugar de utilizar la reflexión. Hay algunos puntos de referencia en el sitio de JSerial.
No estoy 100% seguro (y no tengo ganas de leer la fuente ahora), pero creo que Guice genera código de bytes para realizar la inyección de dependencia. Corrígeme si estoy equivocado.