怎么清理句柄_JAR文件句柄:混乱后清理!

怎么清理句柄

在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

怎么清理句柄

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/331529.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

XML——生成 XML 文档(读入 XML的反向过程)

【0】README 0.1&#xff09; 本文文字描述转自 core java volume 2 &#xff0c; 旨在理解 XML——生成 XML 文档&#xff08;读入 XML的反向过程&#xff09; 的基础知识 &#xff1b; 0.2&#xff09; for source code, please visit https://github.com/pacosonTang/core…

算法四之冒泡排序

一、冒泡排序&#xff08;Bubble Sort&#xff09;思想 &#xff08;1&#xff09;冒泡排序&#xff08;Bubble Sort&#xff09;&#xff0c;是一种计算机科学领域的较简单的排序算法。它重复地走访过要排序的数列&#xff0c;一次比较两个元素&#xff0c;如果他们的顺序错误…

java 使用sdk_使用SDKMAN管理多个Java SDK! 轻松

java 使用sdkSDKMAN&#xff01; 是用于管理多个软件开发套件的并行版本的便捷工具。 该工具对Java开发人员特别有用&#xff0c;因为它支持JVM的SDK&#xff0c;例如Java&#xff0c;Groovy&#xff0c;Scala&#xff0c;Kotlin和Ceylon。 还支持Gradle&#xff0c;Maven&…

ftp服务器在线查看文件内容,ftp服务器PDF文件在线查看的实现方法

URL形式&#xff1a;// http://localhost:2692/PDFVIEWER/web/viewer.html?filehttp://localhost:2692/TOV/DASystem/GetStreaem?path%3Dftp://ftp账号:ftp密码IP地址/0001/E_File3/2017526/test.PDF注意&#xff1a;PDFVIEWER/web/viewer.html是一个PDF插件地址链接后台方法…

XML——写出XML文档(XSLT+StAX)

【0】README 0.1&#xff09; 本文描述部分转自 core java volume 2 &#xff0c; 旨在理解 XML——写出XML文档&#xff08;XSLTStAX&#xff09; 的基础知识 &#xff1b; 0.2&#xff09; for source code, please visit https://github.com/pacosonTang/core-java-volume…

算法五之快速排序

一、快速排序&#xff08;Quicksort&#xff09;思想设要排序的数组是A[0]……A[N-1]&#xff0c;首先任意选取一个数据&#xff08;通常选用数组的第一个数&#xff09;作为关键数据&#xff0c;然后将所有比它小的数都放到它前面&#xff0c;所有比它大的数都放到它后面&…

jvm 助记符_您的JVM是否泄漏文件描述符-像我的一样?

jvm 助记符前言&#xff1a;此处描述的两个问题是在一年前发现并修复的。 本文仅用作历史证明&#xff0c;也是有关解决Java中文件描述符泄漏的初学者指南。 在Ultra ESB中&#xff0c;我们使用内存RAM磁盘文件缓存来进行快速且无垃圾的有效负载处理。 一段时间以前&#xff0…

08r2系统服务器开索引,SQLSERVER2008R2正确使用索引

T1表 10000000万条数据,(插入时间36分钟&#xff0c;count(*)查询19秒&#xff0c;空间占用670M左右)sql1.真正充分的利用索引好比like 张% 就是符合SARG(符合扫描参数)标准而like %张 就不符合该标准数据库通配符%在字符串首字符的使用会致使索引没法使用&#xff0c;虽然实际…

算法六之直接插入排序

一、直接插入排序基本思想 直接插入排序(straight insertion sort)的做法是&#xff1a;每次从无序表中取出第一个元素&#xff0c;把它插入到有序表的合适位置&#xff0c;使有序表仍然有序。第一趟比较前两个数&#xff0c;然后把第二个数按大小插入到有序表中&#xff1b; 第…

网络——连接到server

【0】README 0.1&#xff09; 本文描述部分转自 core java volume 2 &#xff0c; 旨在理解 网络——连接到server 的基础知识 &#xff1b; 0.2&#xff09; for source code, please visit https://github.com/pacosonTang/core-java-volume/blob/master/coreJavaAdvanced/…

文件服务器监控用户修改信息,文件服务器监控日志软件

文件服务器监控日志软件 内容精选换一换有以下几种现象&#xff1a;将制作好的SD卡插入开发者板并上电后&#xff0c;开发者板LED1与LED2灯状态信息异常。将制作好的SD卡插入开发者板&#xff0c;并通过USB方式连接Ubuntu服务器&#xff0c;上电、开发者板启动完成后&#xff0…

java –cp ./:_成为Java流大师–第3部分:终端操作

java –cp ./:比尔盖茨曾经说过&#xff1a;“我选择一个懒惰的人去做一件困难的事情&#xff0c;因为一个懒惰的人会找到一个简单的方法来做。” 关于流&#xff0c;没有什么比这更真实了。 在本文中&#xff0c;您将学习Stream如何通过在调用终端操作之前不对源元素执行任何计…

Oracle入门(四)之查询基本信息

一、查询基本信息 &#xff08;1&#xff09; 查询实例服务SQL> show parameter instance name&#xff08;2&#xff09;查询数据库名字SQL> show parameter db_name;&#xff08;3&#xff09;查询数据库名字SQL>select name from v$database;&#xff08;4&#xf…

网络——实现服务器

【0】README 0.1&#xff09; 本文描述部分转自 core java volume 2 &#xff0c; 旨在理解 网络——实现服务器 的基础知识 &#xff1b; 0.2&#xff09; for source code, please visit https://github.com/pacosonTang/core-java-volume/blob/master/coreJavaAdvanced/ch…

image 闪烁 c# ajax updatepanel,Why doesn't asp:UpdatePanel refresh an Image?

问题I have the following UpdatePanel that gets an image from an ashx handler all of which works fine when the page is refreshed. However, when the timer fires, the label is refreshed with current time, but never the image.ImageUrl"~/getImage.ashx?cam…

kafka处理流式数据_通过Apache Kafka集成流式传输大数据

kafka处理流式数据从实时过滤和处理大量数据&#xff0c;到将日志数据和度量数据记录到不同来源的集中处理程序中&#xff0c;Apache Kafka越来越多地集成到各种系统和解决方案中。 使用CData Sync &#xff0c;可以轻松地将此类解决方案应用于任何CRM&#xff0c;ERP或Analyti…

Oracle入门(十一)之SQL

一、SQL组成 SQL的组成 核心SQL主要有四个部分&#xff1a; 1、数据定义语言即SQL DDL(Data Definition Language )&#xff0c;用于定义 SQL模式、基本表、视图、索引等结构。 CREATE、ALTER、DROP、REVOKE、GRANT2、数据操纵语言 即SQL DML(Data Manipulation Language)。 数…

常见 Java 字节码 指令 助记符

转自&#xff1a; 常见java字节码 有时候为了能理解JVM对程序所做的优化等&#xff0c;需要查看程序的字节码&#xff0c;因此知道了解一些常见的指令集很重要&#xff01; 指令码 助记符 说明 0x00 nop 什么都不做 0x01 aconst_null 将null推送至栈顶 0x02…

等价关系和等价类_确定Java等价性的新时代?

等价关系和等价类几个月前&#xff0c;我读了一篇题为“确定Java等价性的新时代&#xff1f;”的博客文章。 这在某种程度上与我当时在我那令人沮丧的副项目Java :: Geci中开发的内容非常吻合 。 我建议您暂停阅读&#xff0c;阅读原始文章&#xff0c;然后再返回此处&#xff…

数据结构(一)之链表

一、链表链表是一种物理存储单元上非连续、非顺序的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接次序实现的。 链表由一系列结点&#xff08;链表中每一个元素称为结点&#xff09;组成&#xff0c;结点可以在运行时动态生成。每个结点包括两个部分&#xff…