Java类的急切加载

9
我试图对运行在各种硬件和操作系统平台上的JVM进行一些基准测试。 我已经创建了一个算法来运行我感兴趣的JVM部分,并打算多次运行此算法以找到合理的平均值。
当我运行基准测试时,我发现第一次运行比后续运行时间要长得多:
132ms
86ms
77ms
89ms
72ms

我的怀疑是类是懒加载的,这会在第一次运行时产生很大的开销。虽然我认为这确实是每个JVM独有的特性,但这并不是我目前感兴趣的。
是否有标准的命令行选项或属性可以急切地加载类?还是有人有其他理论吗?
6个回答

5
如果您想强制加载类,请按以下方式操作:
public class Main
{
    static
    {
        loadClasses();
    }

    public static void main(final String[] argv)
    {
        // whatever
    }

    private static void loadClasses()
    {
        final String[] classesToLoad;

        // even better, read them from a file and pass the filename to this method
        classesToLoad = new String[]
        {
            "foo.bar.X",
            "foo.bar.Y",
        }

        for(final String className : classesToLoad)
        {
            try
            {
                // load the class
                Class.forName(className);
            }
            catch(final ClassNotFoundException ex)
            {
                // do something that makes sense here
                ex.printStackTrace();
            }
        }
    }
}

如果想在主类加载时动态加载类,可以使用import语句,那么为什么还需要使用Class.forName呢? - lowLatency
@Naroji 导入仅在编译时起作用,运行时不会有任何影响。 - TofuBeer
请验证...类可以通过ClassLoader、Class.forName或new Operator、DI、Factory(内部都使用ClassLoader)加载到jvm方法区中... new、DI、Factory也会创建对象。 - lowLatency
所有这些都可以做到。但是没有必要创建一个类的实例来加载类。例如,System.out.println(X.staticVariable);将导致类被加载。如果你只想让类被加载,那么只需要使用Class.forName就可以了。 - TofuBeer

5
最简单的方法是忽略第一次运行。(如果这是有效的做法) 注意:如果您运行相同的代码10,000次,它将进一步编译代码并获得更好的结果,因此您可能需要忽略某些微基准测试的前10K个结果。
一些JVM支持急切加载,但我不认为Sun的JVM支持。
JWrapper支持AOT https://www.jwrapper.com/features

我无法进一步编译代码。请详细说明原因,为什么只有运行10000次后,才能得到适当的微基准测试结果(平均值)。 - lowLatency
默认的“编译器阈值”为10000。请参见-XX:CompileThreshold=10000。这意味着在运行10,000次之前,代码可能没有完全优化,您可能希望忽略这些运行以获得最佳结果。无论如何,我建议至少运行测试2-10秒钟。 - Peter Lawrey
如果我设置-XX:CompileThreshold=10,那么我们可以测试一个10000次循环(是的,我知道它只会运行几毫秒)。 - lowLatency
1
@PeterLawrey,哪些JVM支持急切的类加载? - caduceus
@caduceus 我相信 https://www.jwrapper.com/features 支持AOT,并且如果还没有添加到Gradle VM中,它将被添加进去。 - Peter Lawrey

4

换句话说:如果你忽略它变慢的时候,Java 是快的 :-) - marcus

3

使用java -XX:+TraceClassLoading来跟踪类的加载。

使用java -XX:+PrintCompilation来跟踪方法何时被JIT编译。


1

一旦你加载了类,为了避免运行解释器,可以使用-Xcomp(在Sun实现中)仅运行编译代码。以这种方式运行普通应用程序可能会非常缓慢,因为所有代码都必须被编译,而不仅仅是一些片段。


0

没有标准的命令行选项,因为那不是JVM规范的一部分。懒加载类是(明确允许的)JVM规范的一部分。在运行之前使用Class.forName()来加载你知道的类是可能的,但它不是自动传递的。

HotSpot编译也会影响前几次运行 - 一个方法在被编译之前会被解释几次,而编译过程需要一些时间。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接