定制网站型网站开发,陇南网站定制开发公司,杭州旅游攻略,长沙网站seo技术厂家怎么清理句柄在Ultra ESB中#xff0c;我们使用特殊的热交换类加载器 #xff0c;该加载器使我们可以按需重新加载Java类。 这使我们能够从字面上热交换我们的部署单元 -加载#xff0c;卸载#xff0c;使用更新的类重新加载#xff0c;以及正常地逐步退出-无需重启JVM。 … 怎么清理句柄 在Ultra ESB中我们使用特殊的热交换类加载器 该加载器使我们可以按需重新加载Java类。 这使我们能够从字面上热交换我们的部署单元 -加载卸载使用更新的类重新加载以及正常地逐步退出-无需重启JVM。 Windows支持禁地 在Ultra ESB Legacy中 加载程序在Windows上运行良好但在较新的X版本上 似乎出现了一些问题。 我们不支持将Windows作为目标平台因此并没有太大的意义-直到最近当我们决定在Windows上支持非生产发行版时。 我们的企业集成IDE UltraStudio在Windows上可以很好地运行因此Windows开发人员都可以使用。 TDD FTW 修复类加载器很容易所有测试都通过了 但是我想用一些额外的测试来支持我的修正所以我写了一些新的测试。 其中大多数涉及在系统temp目录下的子目录中创建一个新的JAR文件并使用热交换类加载器加载放置在JAR中的不同工件。 为了获得更多有关最佳做法的功劳我还确保添加一些清理逻辑以通过FileUtils.deleteDirectory()删除temp子目录。 然后事情变糟了 。 拆解不再了。 在Linux和Windows中所有测试均通过了测试。 但是最终的拆卸逻辑在Windows中失败了就在我删除temp子目录的那一刻。 在Windows上我没有lsof的奢华 幸运的是 Sysinternals已经有了我需要的东西 handle64 。 查找罪魁祸首非常容易在删除目录树之前在tearDown()一个断点然后运行handle64 {my-jar-name}.jar 。 笨蛋 我的测试Java进程持有测试JAR文件的句柄。 寻找泄漏 不行 我没有 自然我的第一个怀疑者是类加载器本身。 我花了将近半小时反复遍历类加载器代码库。 没运气。 一切似乎都坚如磐石。 又名我的死神文件句柄 我最好的镜头是看是什么代码打开了JAR文件的处理程序。 因此我为Java的FileInputStream和FilterInputStream编写了一个快速处理的补丁程序 该补丁程序将转储获取时间的堆栈跟踪快照。 每当线程保持流打开时间过长时。 此“泄漏转储程序”部分受我们的JDBC连接池的启发该连接池检测到未释放的连接受宽限期限制然后转储借用它的线程的堆栈跟踪-回到被借用的时间。 荣誉给Sachini 我以前的同事实习生AdroitLogic 。 泄漏裸露 果然堆栈跟踪揭示了罪魁祸首 id: 174 created: 1570560438355 --filter-- java.io.FilterInputStream.init(FilterInputStream.java: 13 ) java.util.zip.InflaterInputStream.init(InflaterInputStream.java: 81 ) java.util.zip.ZipFile$ZipFileInflaterInputStream.init(ZipFile.java: 408 ) java.util.zip.ZipFile.getInputStream(ZipFile.java: 389 ) java.util.jar.JarFile.getInputStream(JarFile.java: 447 ) sun.net.www.protocol.jar.JarURLConnection.getInputStream(JarURLConnection.java: 162 ) java.net.URL.openStream(URL.java: 1045 ) org.adroitlogic.x.base.util.HotSwapClassLoader.loadSwappableClass(HotSwapClassLoader.java: 175 ) org.adroitlogic.x.base.util.HotSwapClassLoader.loadClass(HotSwapClassLoader.java: 110 ) org.adroitlogic.x.base.util.HotSwapClassLoaderTest.testServiceLoader(HotSwapClassLoaderTest.java: 128 ) sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java: 62 ) sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java: 43 ) java.lang.reflect.Method.invoke(Method.java: 498 ) org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java: 86 ) org.testng.internal.Invoker.invokeMethod(Invoker.java: 643 ) org.testng.internal.Invoker.invokeTestMethod(Invoker.java: 820 ) org.testng.internal.Invoker.invokeTestMethods(Invoker.java: 1128 ) org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java: 129 ) org.testng.internal.TestMethodWorker.run(TestMethodWorker.java: 112 ) org.testng.TestRunner.privateRun(TestRunner.java: 782 ) org.testng.TestRunner.run(TestRunner.java: 632 ) org.testng.SuiteRunner.runTest(SuiteRunner.java: 366 ) org.testng.SuiteRunner.runSequentially(SuiteRunner.java: 361 ) org.testng.SuiteRunner.privateRun(SuiteRunner.java: 319 ) org.testng.SuiteRunner.run(SuiteRunner.java: 268 ) org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java: 52 ) org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java: 86 ) org.testng.TestNG.runSuitesSequentially(TestNG.java: 1244 ) org.testng.TestNG.runSuitesLocally(TestNG.java: 1169 ) org.testng.TestNG.run(TestNG.java: 1064 ) org.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java: 72 ) org.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java: 123 ) 知道了 java.io.FilterInputStream.init(FilterInputStream.java: 13 ) ... sun.net.www.protocol.jar.JarURLConnection.getInputStream(JarURLConnection.java: 162 ) java.net.URL.openStream(URL.java: 1045 ) org.adroitlogic.x.base.util.HotSwapClassLoader.loadSwappableClass(HotSwapClassLoader.java: 175 ) 但是这并不能说明全部情况。 如果URL.openStream()打开JAR为什么我们从try-with-resources块返回时却没有关闭它 try (InputStream is jarURI.toURL().openStream()) { byte [] bytes IOUtils.toByteArray(is); Class? clazz defineClass(className, bytes, 0 , bytes.length); ... logger.trace( 15 , Loaded class {} as a swappable class , className); return clazz; } catch (IOException e) { logger.warn( 16 , Class {} located as a swappable class, but couldnt be loaded due to : {}, trying to load the class as a usual class , className, e.getMessage()); ... } 疯狂 感谢Sun Microsystems使其成为OSS 我可以浏览JDK源代码直到这个令人震惊的评论–一直到java.net.URLConnection private static boolean defaultUseCaches true ; /** * If codetrue/code, the protocol is allowed to use caching * whenever it can. If codefalse/code, the protocol must always * try to get a fresh copy of the object. * p * This field is set by the codesetUseCaches/code method. Its * value is returned by the codegetUseCaches/code method. * p * Its default value is the value given in the last invocation of the * codesetDefaultUseCaches/code method. * * see java.net.URLConnection#setUseCaches(boolean) * see java.net.URLConnection#getUseCaches() * see java.net.URLConnection#setDefaultUseCaches(boolean) */ protected boolean useCaches defaultUseCaches; 是的Java 来自sun.net.www.protocol.jar.JarURLConnection JarURLInputStream class extends FilterInputStream { JarURLInputStream(InputStream var2) { super (var2); } public void close() throws IOException { try { super .close(); } finally { if (!JarURLConnection. this .getUseCaches()) { JarURLConnection. this .jarFile.close(); } } } } 如果 因为 因为 useCaches在默认情况下为true 那么我们感到非常惊讶 让Java缓存其JAR但不要破坏我的测试 JAR缓存可能会提高性能。 但这是否意味着我应该在此之后停止清理-并且在每次测试后都留下杂散的文件 当然我可以说file.deleteOnExit() 但是由于我正在处理目录层次结构因此不能保证顺序删除所有内容并且会保留未删除的目录。 因此我想要一种清理JAR缓存的方法–或至少清除我的JAR条目 完成之后但在JVM关闭之前。 完全禁用JAR缓存-可能不是一个好主意 URLConnection确实提供了一种避免缓存连接条目的选项 /** * Sets the default value of the codeuseCaches/code field to the * specified value. * * param defaultusecaches the new value. * see #getDefaultUseCaches() */ public void setDefaultUseCaches( boolean defaultusecaches) { defaultUseCaches defaultusecaches; } 如上所述如果可以对每个文件/ URL禁用缓存那将是完美的。 我们的类加载器在打开JAR时会立即缓存所有条目因此不再需要再次打开/读取该文件。 但是一旦打开JAR就无法在其上禁用缓存否则将无法对其进行缓存。 因此一旦我们的类加载器打开了JAR就不会摆脱缓存的文件句柄-直到JVM本身关闭 URLConnection还允许您默认禁用所有后续连接的缓存 /** * Sets the default value of the codeuseCaches/code field to the * specified value. * * param defaultusecaches the new value. * see #getDefaultUseCaches() */ public void setDefaultUseCaches( boolean defaultusecaches) { defaultUseCaches defaultusecaches; } 但是如果您一次禁用它则从那时起整个JVM可能会受到影响-因为它可能适用于所有基于URLConnection的实现。 就像我之前说过的那样这可能会妨碍性能-更不用说使测试脱离启用缓存的实际行为了。 在兔子洞下再次从 侵入性最小的选项是在我知道完成后从缓存中删除我自己的JAR。 好消息是缓存sun.net.www.protocol.jar.JarFileFactory已经具有执行该工作的close(JarFile)方法。 但是可悲的是缓存类是程序包专用的。 意味着无法在我的测试代码中进行操作。 反思救援 多亏了反射我所需要的只是一个小的“桥梁”它将代表我访问和调用jarFactory.close(jarFile) JarBridge { class JarBridge { static void closeJar(URL url) throws Exception { // JarFileFactory jarFactory JarFileFactory.getInstance(); Class? jarFactoryClazz Class.forName( sun.net.www.protocol.jar.JarFileFactory ); Method getInstance jarFactoryClazz.getMethod( getInstance ); getInstance.setAccessible( true ); Object jarFactory getInstance.invoke(jarFactoryClazz); // JarFile jarFile jarFactory.get(url); Method get jarFactoryClazz.getMethod( get , URL. class ); get.setAccessible( true ); Object jarFile get.invoke(jarFactory, url); // jarFactory.close(jarFile); Method close jarFactoryClazz.getMethod( close , JarFile. class ); close.setAccessible( true ); //noinspection JavaReflectionInvocation close.invoke(jarFactory, jarFile); // jarFile.close(); ((JarFile) jarFile).close(); } } 在测试中我只需要说 JarBridge.closeJar(jarPath.toUri().toURL()); 就在删除临时目录之前。 那么要点是什么 如果您不直接处理JAR文件那么对您来说无济于事。 但是如果是这样您可能会遇到这种晦涩的“使用中的文件”错误。 这对于其他基于URLConnection的流同样适用。 如果您碰巧像我以前那样不幸请回想一下一个臭名昭著的博客写了一些骇人的“ leak dumper”补丁JAR 可以确切地告诉您JAR或非JAR泄漏的位置。 阿迪耶 翻译自: https://www.javacodegeeks.com/2019/10/jar-file-handles-clean-up-after-your-mess.html怎么清理句柄