Apache POI 4.0.1 入门超级慢... 15 分钟或更长时间。怎么了?
Apache POI 4.0.1 super slow getting started ... 15 minutes or more. What is wrong?
POI 在 Java 8 和 Windows 10 上 Tomcat 8 实例中初始化其第一个工作簿需要 15 分钟或更长时间。基于在调试器中中断进程并查看堆栈,它正在 classloader 中花费时间,由 xbeans 驱动。
编辑
这给人一种 class 加载程序问题的感觉,因为当我为 POI 库(如下)实施解决方法时,其他 classes 开始表达同样的问题。
编辑
堆栈跟踪看起来与此错误最相似:https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8022063
正如您在 jvisualvm 中看到的,进程线程 taskExecutor-1 中没有 classloader 争用。
我有一个解决方法,但应该没有必要。接受的答案将解释为什么会发生这种情况。
编辑:解决方法
在应用程序初始化期间,我将此行添加到配置 bean 的静态 class 初始化程序中,以强制 classloader 预加载 classes 和资源。它在那里立即执行,当调用实际流程时它也立即执行。
XSSFWorkbook preload = new XSSFWorkbook ();
最后的解决方法是预加载所有与 SSL、安全和 POI 相关的 jar 文件。据我所知,当在 spring 批处理上下文中加载较晚时(可能是红鲱鱼),classloader 试图使用在 bouncycastle [=] 中编码的算法验证 bouncycastle jar 文件48=]es,并且是 运行 解释模式下 SSL 堆栈中的所有内容。
Reflections reflections = new Reflections("org.bouncycastle", this.getClass().getClassLoader(), new SubTypesScanner(false));
Set<Class<? extends Object>> bouncyCastleClasses = reflections.getSubTypesOf(Object.class);
reflections = new Reflections("sun.security", this.getClass().getClassLoader(), new SubTypesScanner(false));
Set<Class<? extends Object>> sunSecurityClasses = reflections.getSubTypesOf(Object.class);
reflections = new Reflections("javax.ssl", this.getClass().getClassLoader(), new SubTypesScanner(false));
Set<Class<? extends Object>> javaSslClasses = reflections.getSubTypesOf(Object.class);
reflections = new Reflections("org.apache.poi", this.getClass().getClassLoader(), new SubTypesScanner(false));
Set<Class<? extends Object>> poiClasses = reflections.getSubTypesOf(Object.class);
POM 摘录:
<poi.version>4.0.1</poi.version>
<!-- apache poi / poi-ooxml -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>${poi.version}</version>
</dependency>
代码:
public void beforeJob(JobExecution jobExecution) {
if (logger.isDebugEnabled()) {
workbookname = "debuginfo-" + System.currentTimeMillis() + ".xlsx";
logger.debug("Debug statistics dump file will be saved at [{}]", workbookname);
debugStatsDump = new XSSFWorkbook(); // This line takes several minutes to run, apparently in the classloader???
}
}
事实证明,在某些情况下,当您混合来自不同版本 java(比如 1.5 和 1.8)的 jar 时,JIT 会在解释模式下抛出它的手和 运行。 Oracle 网站上列出了一些关于此行为的报告 "we don't know why it's doing this, we won't fix it".
我下载了未在 1.8 中编译并从源构建的库的源代码,一切正常。
POI 在 Java 8 和 Windows 10 上 Tomcat 8 实例中初始化其第一个工作簿需要 15 分钟或更长时间。基于在调试器中中断进程并查看堆栈,它正在 classloader 中花费时间,由 xbeans 驱动。
编辑 这给人一种 class 加载程序问题的感觉,因为当我为 POI 库(如下)实施解决方法时,其他 classes 开始表达同样的问题。
编辑 堆栈跟踪看起来与此错误最相似:https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8022063
正如您在 jvisualvm 中看到的,进程线程 taskExecutor-1 中没有 classloader 争用。
我有一个解决方法,但应该没有必要。接受的答案将解释为什么会发生这种情况。
编辑:解决方法
在应用程序初始化期间,我将此行添加到配置 bean 的静态 class 初始化程序中,以强制 classloader 预加载 classes 和资源。它在那里立即执行,当调用实际流程时它也立即执行。
XSSFWorkbook preload = new XSSFWorkbook ();
最后的解决方法是预加载所有与 SSL、安全和 POI 相关的 jar 文件。据我所知,当在 spring 批处理上下文中加载较晚时(可能是红鲱鱼),classloader 试图使用在 bouncycastle [=] 中编码的算法验证 bouncycastle jar 文件48=]es,并且是 运行 解释模式下 SSL 堆栈中的所有内容。
Reflections reflections = new Reflections("org.bouncycastle", this.getClass().getClassLoader(), new SubTypesScanner(false));
Set<Class<? extends Object>> bouncyCastleClasses = reflections.getSubTypesOf(Object.class);
reflections = new Reflections("sun.security", this.getClass().getClassLoader(), new SubTypesScanner(false));
Set<Class<? extends Object>> sunSecurityClasses = reflections.getSubTypesOf(Object.class);
reflections = new Reflections("javax.ssl", this.getClass().getClassLoader(), new SubTypesScanner(false));
Set<Class<? extends Object>> javaSslClasses = reflections.getSubTypesOf(Object.class);
reflections = new Reflections("org.apache.poi", this.getClass().getClassLoader(), new SubTypesScanner(false));
Set<Class<? extends Object>> poiClasses = reflections.getSubTypesOf(Object.class);
POM 摘录:
<poi.version>4.0.1</poi.version>
<!-- apache poi / poi-ooxml -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>${poi.version}</version>
</dependency>
代码:
public void beforeJob(JobExecution jobExecution) {
if (logger.isDebugEnabled()) {
workbookname = "debuginfo-" + System.currentTimeMillis() + ".xlsx";
logger.debug("Debug statistics dump file will be saved at [{}]", workbookname);
debugStatsDump = new XSSFWorkbook(); // This line takes several minutes to run, apparently in the classloader???
}
}
事实证明,在某些情况下,当您混合来自不同版本 java(比如 1.5 和 1.8)的 jar 时,JIT 会在解释模式下抛出它的手和 运行。 Oracle 网站上列出了一些关于此行为的报告 "we don't know why it's doing this, we won't fix it".
我下载了未在 1.8 中编译并从源构建的库的源代码,一切正常。