关于 JVM loadClass 预热优化项

78 阅读1分钟

延续上一优化项 关于 Shardingsphere SQL 预热优化项,尽管首次请求有大幅度下降 ( 800ms -> 300ms ),但仍然有部分超时出现,那么来进一步探索下,下来看如下火焰图: image.png

经过多次的采样对比,发现首次请求均有大量的类加载,那我们来优化,but 如何优化?无非就是提前加载 -- 跟 SQL 预热类似的套路,得出两个子问题:1. 加载哪些类;2. 如何加载。 先来看第一个问题,熟悉 Java 的人都知道类是通过 ClassLoader 加载,有如下属性:

// The classes loaded by this class loader. The only purpose of this table
// is to keep the classes from being GC'ed until the loader is GC'ed.
private final Vector<Class<?>> classes = new Vector<>();

没有提供获取的方法,自然通过方法 ( 反射 ) 获取其中加载的类,如下所示:

// fixme filte intrinsic classes such as `BySpringCGLIB`、`$Proxy`、`lambda$`、`$auxiliary$` etc
private Set<String> getLoadedClass(ClassLoader classLoader) {
    return ((Vector<Class>) classesInClassLoader.get(classLoader)).stream()
            .map(Class::getName).collect(Collectors.toSet());
}

OK,搞定第一个问题,那么接下来看第二个问题,方法如下 ( 这个无需多说 ):

try {
    Class.forName(clazz);
} catch (Throwable e) {
    log.info("unexpected occurred while loading class, the detail is {}!", e.getMessage());
}

至此,第二阶优化完成,来看下如下效果: image.png

image.png

写在最后: 从两个阶段的优化来看,尽管首次请求耗时从 800ms -> 300ms -> 70ms 完成两个跃进,但与第二次依旧有差距,查看火焰图: image.png

看来是一些 dynamic invoke,那么这块的优化则是方法首次调用的缓存优化 ( 由 JVM 处理 ),可以考虑通过流量切换的方式。