Java: ottieni un elenco di tutte le classi caricate nella JVM

Vorrei avere una lista di tutte le classi appartenenti ad un certo pacchetto e tutti i loro figli. Le classi possono o non possono essere già caricate nella JVM.

Non è una soluzione programmatica ma puoi correre

java -verbose:class .... 

e la JVM scaricherà ciò che sta caricando e da dove.

 [Opened /usr/java/j2sdk1.4.1/jre/lib/rt.jar] [Opened /usr/java/j2sdk1.4.1/jre/lib/sunrsasign.jar] [Opened /usr/java/j2sdk1.4.1/jre/lib/jsse.jar] [Opened /usr/java/j2sdk1.4.1/jre/lib/jce.jar] [Opened /usr/java/j2sdk1.4.1/jre/lib/charsets.jar] [Loaded java.lang.Object from /usr/java/j2sdk1.4.1/jre/lib/rt.jar] [Loaded java.io.Serializable from /usr/java/j2sdk1.4.1/jre/lib/rt.jar] [Loaded java.lang.Comparable from /usr/java/j2sdk1.4.1/jre/lib/rt.jar] [Loaded java.lang.CharSequence from /usr/java/j2sdk1.4.1/jre/lib/rt.jar] [Loaded java.lang.String from /usr/java/j2sdk1.4.1/jre/lib/rt.jar] 

Vedi qui per maggiori dettagli.

usando la libreria Reflections , è facile come:

 Reflections reflections = new Reflections("my.pkg", new SubTypesScanner(false)); 

Verrebbe eseguita la scansione di tutte le classi nell’URL / s che contiene il pacchetto my.pkg.

  • il parametro false significa: non escludere la class Object, che è esclusa per impostazione predefinita.
  • in alcuni scenari (contenitori diversi) potresti passare il classLoader oltre che un parametro.

Quindi, ottenere tutte le classi sta effettivamente ottenendo tutti i sottotipi di Object, in modo transitorio:

 Set allClasses = reflections.getStore().getSubTypesOf(Object.class.getName()); 

(Il modo ordinario reflections.getSubTypesOf(Object.class) provocherebbe il caricamento di tutte le classi in PermGen e probabilmente genererebbe OutOfMemoryError. Non vuoi farlo …)

Se vuoi ottenere tutti i sottotipi diretti di Object (o qualsiasi altro tipo), senza ottenere tutti i sottotipi transitivi tutti in una volta, usa questo:

 Collection directSubtypes = reflections.getStore().get(SubTypesScanner.class).get(Object.class.getName()); 

Ci sono più risposte a questa domanda, in parte a causa di domande ambigue: il titolo parla di classi caricate dalla JVM, mentre il contenuto della domanda dice “può o non può essere caricato dalla JVM”.

Supponendo che OP abbia bisogno di classi che vengono caricate dalla JVM da un determinato classloader, e solo quelle classi – anche il mio bisogno – c’è una soluzione ( qui elaborata ) che va così:

 import java.net.URL; import java.util.Enumeration; import java.util.Iterator; import java.util.Vector; public class CPTest { private static Iterator list(ClassLoader CL) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { Class CL_class = CL.getClass(); while (CL_class != java.lang.ClassLoader.class) { CL_class = CL_class.getSuperclass(); } java.lang.reflect.Field ClassLoader_classs_field = CL_class .getDeclaredField("classs"); ClassLoader_classs_field.setAccessible(true); Vector classs = (Vector) ClassLoader_classs_field.get(CL); return classs.iterator(); } public static void main(String args[]) throws Exception { ClassLoader myCL = Thread.currentThread().getContextClassLoader(); while (myCL != null) { System.out.println("ClassLoader: " + myCL); for (Iterator iter = list(myCL); iter.hasNext();) { System.out.println("\t" + iter.next()); } myCL = myCL.getParent(); } } } 

Una delle cose migliori è che puoi scegliere un classloader arbitrario che vuoi controllare. È tuttavia probabile che si interrompano i componenti interni della class di modifica del classloader, quindi deve essere utilizzato come strumento diagnostico unico.

Un approccio alternativo a quelli descritti sopra sarebbe quello di creare un agente esterno utilizzando java.lang.instrument per scoprire quali classi vengono caricate ed eseguire il programma con l’ -javaagent :

 import java.lang.instrument.ClassFileTransformsr; import java.lang.instrument.IllegalClassFormatException; import java.security.ProtectionDomain; public class SimpleTransformsr implements ClassFileTransformsr { public SimpleTransformsr() { super(); } public byte[] transform(ClassLoader loader, String className, Class redefiningClass, ProtectionDomain domain, byte[] bytes) throws IllegalClassFormatException { System.out.println("Loading class: " + className); return bytes; } } 

Questo approccio ha il vantaggio di fornire informazioni su quale ClassLoader ha caricato una determinata class.

Ti suggerisco anche di scrivere un agente -javagent , ma usare il metodo getAllLoadedClasses invece di trasformare qualsiasi class.

Per sincronizzarsi con il codice client (normale codice Java), creare un socket e comunicare con l’agente attraverso di esso. Quindi puoi triggersre un metodo “elenca tutte le classi” ogni volta che ne hai bisogno.

Un modo se conosci già il percorso di livello superiore del pacchetto è utilizzare OpenPojo

 final List pojoClasses = PojoClassFactory.getPojoClassesRecursively("my.package.path", null); 

Quindi puoi andare oltre la lista ed eseguire qualsiasi funzionalità che desideri.

Potresti essere in grado di ottenere un elenco di classi che vengono caricate tramite il programma di caricamento classi, ma questo non includerebbe le classi che non sono ancora state caricate ma che si trovano sul classpath.

Per ottenere TUTTE le classi sul tuo classpath devi fare qualcosa come la tua seconda soluzione. Se vuoi veramente le classi che sono attualmente “caricate” (in altre parole, le classi a cui hai già fatto riferimento, a cui hai effettuato l’accesso o l’istanza), allora devi perfezionare la tua domanda per indicarlo.

Esegui il tuo codice sotto una JVM JRockit, quindi usa JRCMD print_class_summary

Questo genererà tutte le classi caricate, una su ogni riga.

Bene, quello che ho fatto è stato semplicemente elencando tutti i file nel classpath. Potrebbe non essere una soluzione gloriosa, ma funziona in modo affidabile e mi dà tutto ciò che voglio, e altro ancora.

Questo programma stampa tutte le classi con il suo percorso fisico. l’uso può semplicemente copiarlo su qualsiasi JSP se è necessario analizzare il caricamento della class da qualsiasi server web / applicativo.

 import java.lang.reflect.Field; import java.util.Vector; public class TestMain { public static void main(String[] args) { Field f; try { f = ClassLoader.class.getDeclaredField("classs"); f.setAccessible(true); ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); Vector classs = (Vector) f.get(classLoader); for(Class cls : classs){ java.net.URL location = cls.getResource('/' + cls.getName().replace('.', '/') + ".class"); System.out.println("

"+location +"

"); } } catch (Exception e) { e.printStackTrace(); } } }