2009-05-22 11 views
7

Estoy tratando de cargar un archivo java .class dinámicamente y llamarlo por reflexión.¿La carga de clase de Java es extremadamente lenta?

Tengo una clase llamada Foo; tiene un constructor vacío y tiene un método llamado doit() que toma un argumento de cadena y devuelve una cadena. También invierte la cadena.

Aquí está mi código:

URL url = new URL("file://C:/jtest/"); 
    URLClassLoader loader = new URLClassLoader(new URL[]{url}); 
    Class<?> cl = loader.loadClass("Foo"); 
    Constructor<?> cons = cl.getConstructor((Class[])null); 
    Object ins = cons.newInstance(new Object[]{}); 
    Method meth = cl.getDeclaredMethod("doit", String.class); 
    Object ret = meth.invoke(ins, new Object[]{"!dlroW olleH"}); 
    System.out.println((String)ret); 

Como era de esperar esta imagen Impresiones "Hello World!". Sin embargo, se necesitan aproximadamente 30 segundos para completar. Sé que la reflexión es lenta, pero espero que sea de 10 ms o algo así.

Estoy usando Eclipse con JRE 1.6.0_13, y yo estoy corriendo Windows Vista.

¿Qué estoy haciendo mal aquí?

Gracias.

Edit: He perfilado el código, y todo su tiempo se utiliza en la tercera línea (loadClass()). Todo lo demás sucede al instante.

Edité: He puesto el código en un bucle; la función lenta de alguna manera se optimiza y toma 30 segundos solo en el primer ciclo.

Editar: he encontrado la solución.

En lugar de:

URL url = new URL("file://C:/jtest/");

lo cambié a:

URL url = new URL("file:/C:/jtest/");

ahora funciona perfectamente. No sé por qué funciona, pero no veo cómo yo (y otras 5 personas) podría haberme perdido eso. Ahora me siento tonto ..

+1

loadClass primero comprueba la caché para ver si la clase ya está cargada, si no recuerdo mal. Eso explicaría por qué no tarda mucho en la segunda iteración. –

+0

¿El verdadero "Foo" está en un paquete? Cargando desde el paquete predeterminado (sin paquete) puede tener efectos extraños. Intenta moverte a foo.Foo. – flicken

+0

Eso es interesante ... nueva URL ("archivo:/C:/jtest /"). GetPath() es/C:/jtest /. Me pregunto cómo lo interpreta URLClassLoader. –

Respuesta

5

A los 30 segundos, usted debe ser capaz de "perfil" de su código y ver exactamente dónde está el problema (en la carga de la clase? En la creación de la instancia? En buscar el método? Etc?)

Como tarda 30 segundos (y no es mucho más pequeño, del orden de 10 ms o menos), puede usar System.out.println(new Date()); entre cada línea de su código.

sospecho que encontrará que es el cargador.loadClass (String) tarda tanto tiempo, y sospecho que encontrará que tiene un classpath muy largo o un classpath que incluye un recurso de red de algún tipo.

+2

@unknown: en ese caso (y si resolvió el problema) debe publicar la respuesta que sabe que es correcta y aceptarla. –

5

compruebe el CLASSPATH predeterminado. Tal vez se refiere a una red no disponible o algo así.

+0

¿Cómo hago eso? – Lucky

+0

echo% CLASSPATH% en un símbolo del sistema –

+0

o comprueba la configuración en Eclipse –

2

No hay nada malo con su código ... Yo era capaz de compilar el programa en menos de un segundo. Estoy ejecutando Java 1.6.11 en Vista Business.

Tal vez su método public string doit(string arg) es lo que está tomando tanto tiempo. ¿Puedes intentar invocarlo sin utilizar la reflexión para ver si tarda mucho tiempo? Intente tener doit() simplemente devuelva el parámetro que pasa (en lugar de invertir los caracteres) para ver si su algoritmo de reversión es lo que está desacelerando.

+0

Dijo que tardó 30 segundos en completarse. Supongo que esto significa completar la ejecución, no compilar. –

+0

Se compiló y se ejecutó en menos de un segundo. Es por eso que le dije que intentara ejecutarlo con una implementación diferente de su método doit(). – Cuga

+0

doit() regresa inmediatamente cuando se utiliza el código de no reflexión. – Lucky

3

Es posible que al crear la instancia cause que se carguen muchas otras clases, algunas de las cuales tienen inicializadores estáticos que hacen cosas que llevan mucho tiempo. Ponga este código en un bucle y vea qué cuestan las creaciones subsiguientes de objetos.

Editar: Genial, en función de tu perfil esto parece que te estás acercando a la raíz del problema. Otra cosa a considerar es si está utilizando bibliotecas que tienen grandes costos de inicio. Utilizo iBatis para administrar nuestras interacciones con Oracle, y cuando se inicia, lee en un grupo de archivos XML y procesa los patrones de consulta en ellos. Hay un retraso notable. Sería interesante escuchar lo que descubres, pero podrías decidir que el costo de una vez es tolerable.

2

¿Es posible que usted ponga Foo en el CLASSPATH predeterminado por lo que sólo puede usar algo como:

ClassLoaderTest.class.getClassLoader() 

, donde ClassLoaderTest es la clase que se está ejecutando en O, siempre si estás. no se ejecuta en un contexto estático, entonces:

this.getClass().getClassLoader()` 

Cualquiera de estos ahorrará crear instancias de un nuevo cargador de clases.

Pero no sé si esto incluso te ayudará (tienes que crear un perfil), y la reflexión siempre será notablemente más lenta. No hay forma de evitar eso. Por supuesto, debe usarse mínimamente en producción, por esta y otras razones.

EDITAR: Dado que loadClass ocupa la mayor parte del tiempo, es posible que C: \ jtest esté desordenado. Puede intentar colocar Foo.class solo en un directorio y usarlo como URL. Por supuesto, la razón por la que es más rápido la segunda vez es que Foo ya está cargado.

6

Quizá sea un poco tarde para ti, pero me encontré con el mismo problema y encontré esta publicación.

Parece que // está forzando una búsqueda remota, si ejecuta con -verbose: class hay una excepción UnknownHostException cargada, por lo que debe lanzarse internamente durante la carga de la clase.

He intentado lo siguiente:

URL url = new URL ("file: // localhost/C:/jtest /");

y esto funciona (casi) tan rápido como su solución de barra única.

+0

Me salvó 4 minutos de tiempo de ejecución y horas de tirar del cabello – MonoThreaded

Cuestiones relacionadas