11

我试图制作简单的 java profiler 并为此使用 ClassLoader。

这是我的 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(String 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;
    }
}

当我尝试使用此类加载器时,我收到此输出(我尝试使用 1.6_37 和 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.<init>(GC.java:190)
        at sun.misc.GC$LatencyRequest.<init>(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.<clinit>(VMID.java:27)
        at sun.rmi.transport.DGCClient.<clinit>(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.<init>(SecureRandom.java:133)
        at java.rmi.server.UID.<init>(UID.java:92)
        at java.rmi.server.ObjID.<clinit>(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)

我认为问题出在 RMI 服务器上,但我写了另一个 RMI 客户端,它运行良好。有谁知道问题出在哪里以及如何解决(他们)?

4

1 回答 1

2

TL;DR:不要使用像根类加载器这样具有严重副作用的类加载器。

问题是 sun.rmi.transport.DGCClient 类上的 const 字段 gcInterval 在使用之前未初始化(因此显示值为 0)。这样做的原因是您的类加载器通过 RMI 进行调用,从而创建了一个新的 DGCClient 实例。在 DGCClient 的构造函数执行期间,会加载另一个类(参见堆栈跟踪)。对类加载器的第三次调用再次触发 RMI 调用,它不会创建 DGCClient 的新实例,而是使用先前创建的实例并对其进行一些调用。这意味着对半初始化对象进行调用,导致使用这个尚未初始化的常量字段。

我们不能为此责怪 Sun/Oracle,因为每个 Java 类都可以假定它是在没有这种不可预测的副作用的情况下加载的。

于 2013-01-04T09:17:31.523 回答