usar sirve settitle que para definicion codigo java performance reflection

sirve - Reflexión de Java: ¿Por qué es tan lento?



para que sirve settitle en java (7)

El código JITted para instanciar B es increíblemente liviano. Básicamente necesita asignar suficiente memoria (lo cual es solo incrementar un puntero a menos que se requiera un GC) y eso es todo; no hay un código de constructor para llamar realmente; No sé si el JIT se salta o no, pero de cualquier manera no hay mucho que hacer.

Compare eso con todo lo que la reflexión tiene que hacer:

  • Compruebe que hay un constructor sin parámetros
  • Verifique la accesibilidad del constructor sin parámetros
  • Compruebe que la persona que llama tiene acceso para usar la reflexión en absoluto
  • Calcular (en tiempo de ejecución) cuánto espacio debe asignarse
  • Llame al código del constructor (porque no sabrá de antemano que el constructor está vacío)

... y probablemente otras cosas que ni siquiera he pensado.

Por lo general, la reflexión no se usa en un contexto de rendimiento crítico; si necesita un comportamiento dinámico como ese, podría usar algo como BCEL en BCEL lugar.

Siempre he evitado la reflexión de Java basada en su reputación de lentitud. Llegué a un punto en el diseño de mi proyecto actual, donde poder utilizarlo haría que mi código fuera mucho más legible y elegante, así que decidí probarlo.

Simplemente me sorprendió la diferencia, noté a veces casi 100 veces más tiempo de ejecución. Incluso en este ejemplo simple en el que solo instancia una clase vacía, es increíble.

class B { } public class Test { public static long timeDiff(long old) { return System.currentTimeMillis() - old; } public static void main(String args[]) throws Exception { long numTrials = (long) Math.pow(10, 7); long millis; millis = System.currentTimeMillis(); for (int i=0; i<numTrials; i++) { new B(); } System.out.println("Normal instaniation took: " + timeDiff(millis) + "ms"); millis = System.currentTimeMillis(); Class<B> c = B.class; for (int i=0; i<numTrials; i++) { c.newInstance(); } System.out.println("Reflecting instantiation took:" + timeDiff(millis) + "ms"); } }

Entonces realmente, mis preguntas son

  • ¿Por qué es esto lento? ¿Hay algo que estoy haciendo mal? (incluso el ejemplo anterior demuestra la diferencia). Me cuesta creer que realmente puede ser 100 veces más lento que la instanciación normal.

  • ¿Hay algo más que se pueda utilizar mejor para tratar el código como datos (tenga en cuenta que estoy atascado con Java por ahora)?


El código de @Tim Bender da estos resultados en mi máquina (jdk_1.8_45, os_x 10.10, i7, 16G):

Reflecting instantiation took:1139ms Normal instaniation took: 4969ms

por lo que parece en JVM moderna, el código de reflexión también se optimizará.


La reflexión es lenta por algunas razones obvias:

  1. El compilador no puede hacer ninguna optimización ya que no puede tener una idea real de lo que está haciendo. Esto también vale para el JIT
  2. Todo lo que se invoca / crea debe ser descubierto (es decir, las clases se buscaron por nombre, se buscaron los métodos para buscar coincidencias, etc.)
  3. Los argumentos deben ser disfrazados a través de boxeo / unboxing, empaque en matrices, Exceptions envueltas en InvocationTargetException y relanzados, etc.
  4. Todo el procesamiento que menciona Jon Skeet aquí .

El hecho de que algo sea 100 veces más lento no significa que sea demasiado lento para asumir que la reflexión es la "manera correcta" de diseñar su programa. Por ejemplo, imagino que los IDE hacen un uso intensivo de la reflexión y mi IDE está más que correcto desde una perspectiva de rendimiento.

Después de todo, es probable que la sobrecarga de la reflexión sea insignificante cuando se compara con , digamos, analizar XML o acceder a una base de datos .

Otro punto para recordar es que los micro-puntos de referencia son un mecanismo notoriamente defectuoso para determinar qué tan rápido algo está en la práctica . Además de los comentarios de Tim Bender , la JVM tarda un tiempo en "calentarse", el JIT puede volver a optimizar los puntos de acceso de código sobre la marcha, etc.


Parece que si haces que el constructor sea accesible, se ejecutará mucho más rápido. Ahora solo es unas 10-20 veces más lento que la otra versión.

Constructor<B> c = B.class.getDeclaredConstructor(); c.setAccessible(true); for (int i = 0; i < numTrials; i++) { c.newInstance(); } Normal instaniation took: 47ms Reflecting instantiation took:718ms

Y si usa Server VM, puede optimizarlo más, de modo que solo sea 3-4 veces más lento. Este es un rendimiento bastante típico. El artículo que vincula Geo es una buena lectura.

Normal instaniation took: 47ms Reflecting instantiation took:140ms

Pero si habilita el reemplazo escalar con -XX: + DoEscapeAnalysis, entonces la JVM podrá optimizar la instanciación regular (será de 0-15 ms) pero la creación de la instancia reflexiva se mantendrá igual.


Su prueba puede ser defectuosa. En general, aunque la JVM puede optimizar la instanciación normal, pero no pudo realizar optimizaciones para el caso de uso reflexivo .

Para aquellos que se preguntan cuáles fueron los tiempos, agregué una fase de calentamiento y usé una matriz para mantener los objetos creados (más similar a lo que podría hacer un programa real). Ejecuté el código de prueba en mi sistema OSX, jdk7 y obtuve esto:

La creación de reflejos tomó: 5180ms
La instancia normal tomó: 2001ms

Prueba modificada:

public class Test { static class B { } public static long timeDiff(long old) { return System.nanoTime() - old; } public static void main(String args[]) throws Exception { int numTrials = 10000000; B[] bees = new B[numTrials]; Class<B> c = B.class; for (int i = 0; i < numTrials; i++) { bees[i] = c.newInstance(); } for (int i = 0; i < numTrials; i++) { bees[i] = new B(); } long nanos; nanos = System.nanoTime(); for (int i = 0; i < numTrials; i++) { bees[i] = c.newInstance(); } System.out.println("Reflecting instantiation took:" + TimeUnit.NANOSECONDS.toMillis(timeDiff(nanos)) + "ms"); nanos = System.nanoTime(); for (int i = 0; i < numTrials; i++) { bees[i] = new B(); } System.out.println("Normal instaniation took: " + TimeUnit.NANOSECONDS.toMillis(timeDiff(nanos)) + "ms"); } }



  • La reflexión fue muy lenta cuando se introdujo por primera vez, pero se ha acelerado considerablemente en los nuevos JRE
  • Aún así, podría no ser una buena idea usar la reflexión en un bucle interno
  • El código basado en reflexiones tiene un bajo potencial para optimizaciones basadas en JIT
  • La reflexión se utiliza principalmente para conectar componentes poco acoplados, es decir, al buscar clases y métodos concretos, donde solo se conocen interfaces: marcos de inyección de dependencias, creación de instancias de clases de implementación JDBC o analizadores XML. Estos usos a menudo se pueden hacer una vez al inicio del sistema, ¡así que la pequeña ineficiencia no importa de todos modos!