【问题标题】:How to explore which classes are loaded from which JARs?如何探索从哪些 JAR 加载哪些类?
【发布时间】:2010-10-31 03:17:16
【问题描述】:

有没有办法确定在运行时从哪些 JAR 加载哪些类?

我敢肯定,我们以前都经历过 JAR 地狱。我在项目中遇到过很多对ClassNotFoundExceptions 和NoClassDefFoundErrors 进行故障排除的问题。我想避免在 JAR 中找到一个类的所有实例,并避免在导致 CNFE 找到罪魁祸首的代码上使用消除过程。

任何分析或管理工具会为您提供此类信息吗?

这个问题非常烦人,纯粹是因为我们应该在加载类的时候就知道这些信息。必须有办法找到它,或者记录下来并找到它,但我不知道有什么方法可以做到这一点,是吗?

我知道 OSGi 和版本化的捆绑包/模块旨在解决这个问题......但它似乎不会很快消失。

注意:我发现这个 question 是我的问题的一个子集,与从版本化 jar 加载的类有关。

有点相关,这篇文章解释了在 JAR(在当前目录下)或 M2_REPO 中搜索类的策略:JarScan, scan all JAR files in all subfolders for specific class

也有点相关,JBoss Tattletale

【问题讨论】:

标签: java profiling classloader jar


【解决方案1】:

-verbose:class 开关传递给java 命令将打印每个加载的类及其加载位置。

Joops 也是提前查找缺失课程的好工具。

【讨论】:

  • 不错 - Joops 看起来很酷 - 你知道它是否足够聪明,可以关注通过 Class.forName 引用的类吗?
  • 如果它可以遵循 Class.forName 引用,我会感到惊讶,但我不确定。 Joops 还有一个which 命令,因此您可以使用它手动按名称检查类。
  • +1 非常感谢!帮助我找出冲突的类加载!
  • 已经在运行我无法控制的 JVM 怎么样?即我无法使用 -verbose:class 选项重新启动它。可能是不可能的,因为 jvm 在没有 -verbose:class 选项的情况下永远不会记录它,只是想知道。
【解决方案2】:

您可以从代码中调用:

myObject.getClass().getProtectionDomain().getCodeSource()

(注意,getProtectionDomain 可能会不幸返回 null(糟糕的设计),因此“正确的代码”会检查这一点。)

【讨论】:

  • 这对 CNFE 或 NCDFE 没有帮助;你什么时候做的?
  • 大概如果你得到一个 ClassNotFoundException 那么你大概已经知道这个类不在任何罐子里。 (假设您没有使用类加载器。)如果在类加载过程中出现其他错误,例如缺少超类,则可能会出现问题。
  • @TomHawtin-tackline 如果上述类的静态初始化程序抛出异常,您可以获得ClassNotFoundException。那么jar文件中有类,但是ClassLoader加载不出来。
  • 如果您不想(或不能)修改代码,可以在 Eclipse 调试表达式中使用它。
【解决方案3】:

上面 Jason Day 提到的 JVM 标志有一个 MBean。

如果您使用的是 JBoss,如果您将本机 JMX MBean 服务器添加到您的配置中,您可以使用 JMX 按需调整它。添加以下 -D:

-Dcom.sun.management.jmxremote.port=3333
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
-Djboss.platform.mbeanserver 
-Djavax.management.builder.initial=org.jboss.system.server.jmx.MBeanServerBuilderImpl
-DJBOSS_CLASSPATH="../lib/jboss-system-jmx.jar"

然后您可以在 java.lang:Classloading MBean 下看到此设置,并可以即时将其打开/关闭。如果您只想在执行某段代码时打开它,这将很有帮助。

还有一个 MBean,它允许您输入一个完全限定的类名并查看它在类层次结构中的加载位置。 MBean 称为 LoaderRepository,您需要调用 displayClassInfo() 操作,传入 FQCN。

【讨论】:

    【解决方案4】:

    在 WebSphere (WAS) 中,您可以使用称为“类加载器查看器”的功能

    首先通过单击服务器 > 服务器类型 > WebSphere 应用程序服务器 > server_name > 类加载器查看器服务启用类加载器查看器,启用该服务并重新启动服务器。

    然后您可以转到疑难解答>类加载器查看器并搜索您的类或包名称。

    https://www-01.ibm.com/support/knowledgecenter/SSAW57_8.5.5/com.ibm.websphere.nd.doc/ae/ttrb_classload_viewer.html?lang=en

    【讨论】:

      【解决方案5】:

      您可以轻松导出 JMX 操作以访问进程中任何已加载类的包信息,例如:

        public static final class Jmx {
      
          @JmxExport
          public static Reflections.PackageInfo getPackageInfo(@JmxExport("className") final String className) {
            return Reflections.getPackageInfo(className);
          }
        }
      

      这里有一个简单的单元测试来导出和调用它:

        @Test
        public void testClassLocator() throws IOException, InstanceNotFoundException, MBeanException, ReflectionException {
          Registry.export(Jmx.class);
          Reflections.PackageInfo info = (Reflections.PackageInfo) Client.callOperation(
                  "service:jmx:rmi:///jndi/rmi://:9999/jmxrmi",
                  Jmx.class.getPackage().getName(),
                  Jmx.class.getSimpleName(), "getPackageInfo", Registry.class.getName());
          System.out.println(info);
          Assert.assertNotNull(info);
        }
      

      这一切都基于使用 spf4j (http://www.spf4j.org) 中的一些小型实用程序库

      你可以看到这段代码at和测试at

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-01-11
        • 1970-01-01
        • 2011-05-21
        • 2010-09-13
        • 1970-01-01
        • 1970-01-01
        • 2011-08-20
        相关资源
        最近更新 更多