怎么清理句柄

在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的FileInputStreamFilterInputStream编写了一个快速处理的补丁程序 ,该补丁程序将转储获取时间的堆栈跟踪快照。 每当线程保持流打开时间过长时。

此“泄漏转储程序”部分受我们的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 couldn't 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 <code>true</code>, the protocol is allowed to use caching * whenever it can. If <code>false</code>, the protocol must always * try to get a fresh copy of the object. * <p> * This field is set by the <code>setUseCaches</code> method. Its * value is returned by the <code>getUseCaches</code> method. * <p> * Its default value is the value given in the last invocation of the * <code>setDefaultUseCaches</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 <code>useCaches</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 <code>useCaches</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

怎么清理句柄

怎么清理句柄_JAR文件句柄:混乱后清理!相关推荐

  1. JAR文件句柄:烦恼后清理!

    在Ultra ESB中,我们使用特殊的热交换类加载器 ,该加载器使我们可以按需重新加载Java类. 这使我们能够从字面上热交换我们的部署单元 -加载,卸载,使用更新的类重新加载,以及正常地逐步退出-无 ...

  2. 怎样清理苹果手机内存空间_这才是正确清理苹果手机内存的方法,清理后和新机一样流畅...

    分享最实在的玩机技巧,洞察最前沿的科技资讯!大家好,这里是手机科技园~~~ 建议大家买手机一定要买大内存的,不然空间不足的时候,天天都要清理.今天就来教大家,正确清理苹果手机内存的方法,清理后和新机一 ...

  3. 扫地机器人狗毛_扫地机器人:我是清理狗毛的!不是清理狗屎的!

    原标题:扫地机器人:我是清理狗毛的!不是清理狗屎的! 扫地机器人可以清洁地面和角落里的垃圾,对于满是毛毛的铲屎官家庭来说,简直就是福音呐! 不过最近,槽点却有点多:家里买的扫地机器人不仅一点也不智能, ...

  4. win7怎么清理java缓存文件夹_win7系统清理电脑缓存文件的方法

    ‍‍ 电脑在使用的过程中会堆积很多缓存垃圾,必须要经常清理不然会导致系统运行速度变慢.内存不足等问题.也有很多win7专业版系统的新用户不知道如何清理电脑内的缓存文件,小编在这里整理一些清理缓存文件的 ...

  5. android垃圾清理动画,[Android开发实战]金山清理大师(猎豹清理大师)一键加速快捷方式动画实现...

    转载请注明出处:http://blog.csdn.net/ruils/article/details/16922557 最近有人问我金山清理大师桌面上的一键加速的动画是如何实现的,我下了个金山清理大师 ...

  6. 苹果内存其他怎么清理_手机内存不足怎么清理才有效?

    手机使用时间长了,特别是使用微信.在线看电影.小视频等,会产生很多缓存数据保存在手机内,不能自动消失,时间久了就形成了很多垃圾.其次,软件安装过多,也会占用很多内存,造成手机内存不足,解决办法只有清理 ...

  7. 一键清空朋友圈软件_微信清理朋友圈app下载-微信清理朋友圈下载v1.8.0 安卓版-西西软件下载...

    微信清理朋友圈app一款强大的微信清理工具,当你想要重置自己的微信号并删除所有的内容之时,又觉得一条条删太麻烦了,就可以用到这款神器工具,可以帮助你一键删除掉朋友圈内的所有内容,帮助你轻松的重置微信号 ...

  8. linux有必要清理内存么,Linux系统需要清理垃圾文件和优化系统吗?

    Linux系统需要清理垃圾文件和优化系统吗? 前言 首先这个是windows带来的习惯,Linux系统并不需要清理垃圾文件和优化系统! 清理垃圾 Linux下没有什么垃圾文件,最多的就是程序的缓存文件 ...

  9. CleanMyMac x for Mac系统清理工具简单安装,彻底清理!

    CleanMyMac x for Mac系统清理工具简单安装,彻底清理!CleanMyMac x for Mac是应用在MacOS上的一款系统清理工具,释放RAM,运行维护脚本,管理登录项,启动代理和 ...

最新文章

  1. .bat脚本自动yes_推荐|PyToBI自动标注韵律
  2. PHP for Linux之xml2-config这个文件没找到
  3. smarty模板截取字符串乱码问题完美解决```````
  4. Contest Leaderboard(分组、聚合、连接)
  5. Linux操作系统安装字体笔记
  6. Apache Derby数据库JVM安全策略
  7. 应理解计算机专业的学科特点,职业高中计算机专业测验试题编写的策略初探.doc...
  8. Windows下JDK开发环境搭建及环境变量配置
  9. Vivado设计流程(一)新建工程
  10. 第 6 章 存储 - 039 - Data Volume 之 bind mount
  11. 零件缝隙平行线距离检测2
  12. cv2中函数名的规则
  13. Invalid prop: type check failed for prop “index“. Expected String with value “145“...
  14. 中国农业大学研究生计算机学院宿舍,中国农业大学宿舍条件怎么样
  15. git config之后仍无法commit,提示 “fatal: empty ident name“
  16. 著者四角号码查询_著者姓名汉语拼音与四角号码数字混编书次号的研究
  17. PHP 代码覆盖率分析
  18. (转)对冲基金:Citadel如何屹立市场20年不倒
  19. 022 PHP获取从周一到周日的时间戳
  20. uniapp 小程序 加载显示激励视频广告

热门文章

  1. Sum of Log(2020上海C)
  2. P2567 [SCOI2010]幸运数字
  3. cf1512F. Education
  4. 天气情况图像分类练习赛 第三阶段(赛中感)
  5. [POJ 3709] K-Anonymous Sequence(斜率优化dp / 动态维护凸包)
  6. 【学习笔记】原根 / BSGS / 扩展BSGS证明及模板
  7. CF600F:Edge coloring of bipartite graph(二分图、构造)
  8. jzoj3237-间谍派遣【最小生成树,并查集】
  9. 【主席树】可持久化数组(金牌导航 可持久化数据结构-3)
  10. 2019.01.28【NOIP普及组】模拟赛C组总结