ClassLoader avec invocation RMI

J’essaie de créer un profileur java simple et d’utiliser ClassLoader pour cela.

Voici mon implémentation de ClassLoader:

import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; public class CustomClassLoader extends ClassLoader { private Notifier notifier; public CustomClassLoader() { super(); } public CustomClassLoader(ClassLoader parent) { super(parent); } private void initNotifier() { if (notifier != null) return; try { System.out.println("2"); Registry registry = LocateRegistry.getRegistry(Const.registryPort); System.out.println("3"); notifier = (Notifier) registry.lookup(Const.stubName); System.out.println("4"); } catch (Exception e) { e.printStackTrace(); System.exit(1); } } @Override protected synchronized Class loadClass(Ssortingng name, boolean resolve) throws ClassNotFoundException { System.out.println("0"); Class clazz = super.loadClass(name, resolve); System.out.println("1"); initNotifier(); System.out.println("5"); try { notifier.classLoaded(name); System.out.println("6"); } catch (RemoteException e) { e.printStackTrace(); System.exit(1); } return clazz; } } 

Lorsque j’essaie d’utiliser ce chargeur de classe, je reçois cette sortie (j’ai essayé d’utiliser 1.6_37 et 1.7_10 jkd):

 C:\Users\Scepion1d>java -cp C:\Users\Scepion1d\Dropbox\Workspace\IntellijIDEA\pr ofiler\out\artifacts\loader\loader.jar;C:\Users\Scepion1d\Dropbox\Workspace\Inte llijIDEA\app\out\production\app -Djava.system.class.loader=CustomClassLoader Main 0 1 2 0 1 2 3 0 1 2 3 java.lang.IllegalArgumentException: Non-positive latency: 0 at sun.misc.GC$LatencyRequest.(GC.java:190) at sun.misc.GC$LatencyRequest.(GC.java:156) at sun.misc.GC.requestLatency(GC.java:254) at sun.rmi.transport.DGCClient$EndpointEntry.lookup(DGCClient.java:212) at sun.rmi.transport.DGCClient.registerRefs(DGCClient.java:120) at sun.rmi.transport.ConnectionInputStream.registerRefs(ConnectionInputS tream.java:80) at sun.rmi.transport.StreamRemoteCall.releaseInputStream(StreamRemoteCal l.java:138) at sun.rmi.transport.StreamRemoteCall.done(StreamRemoteCall.java:292) at sun.rmi.server.UnicastRef.done(UnicastRef.java:431) at sun.rmi.registry.RegistryImpl_Stub.lookup(Unknown Source) at CustomClassLoader.initNotifier(CustomClassLoader.java:22) at CustomClassLoader.loadClass(CustomClassLoader.java:35) at java.lang.ClassLoader.loadClass(ClassLoader.java:247) at sun.security.jca.ProviderConfig$3.run(ProviderConfig.java:234) at java.security.AccessController.doPrivileged(Native Method) at sun.security.jca.ProviderConfig.doLoadProvider(ProviderConfig.java:22 5) at sun.security.jca.ProviderConfig.getProvider(ProviderConfig.java:205) at sun.security.jca.ProviderList.getProvider(ProviderList.java:215) at sun.security.jca.ProviderList.getService(ProviderList.java:313) at sun.security.jca.GetInstance.getInstance(GetInstance.java:140) at java.security.Security.getImpl(Security.java:659) at java.security.MessageDigest.getInstance(MessageDigest.java:129) at java.rmi.dgc.VMID.computeAddressHash(VMID.java:140) at java.rmi.dgc.VMID.(VMID.java:27) at sun.rmi.transport.DGCClient.(DGCClient.java:66) at sun.rmi.transport.ConnectionInputStream.registerRefs(ConnectionInputS tream.java:80) at sun.rmi.transport.StreamRemoteCall.releaseInputStream(StreamRemoteCal l.java:138) at sun.rmi.transport.StreamRemoteCall.done(StreamRemoteCall.java:292) at sun.rmi.server.UnicastRef.done(UnicastRef.java:431) at sun.rmi.registry.RegistryImpl_Stub.lookup(Unknown Source) at CustomClassLoader.initNotifier(CustomClassLoader.java:22) at CustomClassLoader.loadClass(CustomClassLoader.java:35) at java.lang.ClassLoader.loadClass(ClassLoader.java:247) at sun.security.jca.ProviderConfig$3.run(ProviderConfig.java:234) at java.security.AccessController.doPrivileged(Native Method) at sun.security.jca.ProviderConfig.doLoadProvider(ProviderConfig.java:22 5) at sun.security.jca.ProviderConfig.getProvider(ProviderConfig.java:205) at sun.security.jca.ProviderList.getProvider(ProviderList.java:215) at sun.security.jca.ProviderList$3.get(ProviderList.java:130) at sun.security.jca.ProviderList$3.get(ProviderList.java:125) at java.util.AbstractList$Itr.next(AbstractList.java:345) at java.security.SecureRandom.getPrngAlgorithm(SecureRandom.java:522) at java.security.SecureRandom.getDefaultPRNG(SecureRandom.java:165) at java.security.SecureRandom.(SecureRandom.java:133) at java.rmi.server.UID.(UID.java:92) at java.rmi.server.ObjID.(ObjID.java:71) at java.rmi.registry.LocateRegistry.getRegistry(LocateRegistry.java:158) at java.rmi.registry.LocateRegistry.getRegistry(LocateRegistry.java:106) at java.rmi.registry.LocateRegistry.getRegistry(LocateRegistry.java:73) at CustomClassLoader.initNotifier(CustomClassLoader.java:20) at CustomClassLoader.loadClass(CustomClassLoader.java:35) at java.lang.ClassLoader.loadClass(ClassLoader.java:247) 

Je pensais que le problème venait du serveur RMI, mais j’ai écrit un autre client RMI et cela fonctionne bien. Est-ce que quelqu’un sait où est le (s) problème (s) et comment le (les) résoudre?

TL; DR: N’utilisez pas de chargeur de classe ayant des effets secondaires aussi lourds que le chargeur de classe racine.

Le problème est que le champ const gcInterval de la classe sun.rmi.transport.DGCClient n’est pas initialisé avant son utilisation (et affiche donc la valeur 0). La raison en est que votre chargeur de classes effectue l’appel via RMI, ce qui crée une nouvelle instance de DGCClient. Lors de l’exécution du constructeur de DGCClient, une autre classe est chargée (voir trace de la stack). Ce troisième appel au chargeur de classes déclenche à nouveau l’appel RMI qui ne crée pas une nouvelle instance de DGCClient mais utilise celle créée précédemment et l’appelle. Cela signifie qu’un appel est effectué sur un object semi-initialisé, ce qui conduit à l’utilisation de ce champ constant non encore initialisé.

Nous ne pouvons en aucun cas blâmer Sun / Oracle, car chaque classe Java peut supposer qu’elle est chargée sans ces effets secondaires imprévisibles.