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/340461.shtml

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

相关文章

大气校正后的ndvi_Sentinel2 L1C下载、大气校正、重采样

点击蓝字关注我哦1.基本信息(成像仪/重访周期/波段数/分辨率)哨兵2号是高分辨率多光谱成像卫星&#xff0c;携带一枚多光谱成像仪(MSI)&#xff0c;用于陆地监测&#xff0c;可提供植被、土壤和水覆盖、内陆水路及海岸区域等图像&#xff0c;分为2A和2B两颗卫星,哨兵&#xff0…

hello python的代码,python基础教程之Hello World!

Python命令行假设你已经安装好了Python, 那么在Linux命令行输入:代码如下:$python将直接进入python。然后在命令行提示符>>>后面输入:代码如下:>>>print(Hello World!)可以看到&#xff0c;随后在屏幕上输出:代码如下:Hello World!print是一个常用函数&#…

python3 线程隔离_Python的线程隔离实现方法

前段时间看了下flask的源码&#xff0c;对于这样一个轻量级的web框架是怎样支持多线程的感到非常好奇&#xff0c;于是深入了解了一番。flask是依赖werkeug来实现线程间的隔离的&#xff0c;而werkeug最后又使用到了python的内置模块locals来承载数据&#xff0c;看不如写&…

限定通配符和非限定通配符_为什么我不信任通配符以及为什么我们仍然需要通配符...

限定通配符和非限定通配符在将子类型多态性&#xff08;面向对象&#xff09;与参数多态性&#xff08;泛型&#xff09;相结合的任何编程语言中&#xff0c;都会出现方差问题。 假设我有一个字符串列表&#xff0c;键入List<String> 。 我可以将其传递给接受List<Obj…

php strtotime month bug,处理PHP strtotime的BUG

PHP strtotime的BUG处理最近使用了strtotime结合-1 month, 1 month, next month获取上个月或者下个月的日期&#xff0c;不过刚看到一篇文章&#xff0c;才知道原来使用strtotime直接获取日期还是有点小BUGBUG如日期&#xff1a;$today 2020-12-31;echo date("Y-m-d"…

JMetro版本11.5.11和8.5.11发布

你好 具有新JMetro样式的另一个版本&#xff08;深色和浅色版本&#xff09;&#xff1a; 分割菜单按钮 分割窗格 药丸按钮/分段按钮 调整现有样式和错误修复。 继续阅读以获取详细信息。 分割菜单按钮 以下是一个动画&#xff0c;显示了新的“拆分菜单按钮” JMetro浅色…

python远程连接mysql数据库_MySQL数据库之python mysql远程连接

本文主要向大家介绍了MySQL数据库之python mysql远程连接 &#xff0c;通过具体的内容向大家展现&#xff0c;希望对大家学习MySQL数据库有所帮助。第一步&#xff1a;vim /etc/MySQL/my.cnf找到bind-address 127.0.0.1注释掉这行&#xff0c;如&#xff1a;#bind-address 12…

php100并发cpu告警,多线程并发导致CPU100%的一种原因和解决办法

在用自定义线程池的时候&#xff0c;遇到cpu100%&#xff0c;经过验证后&#xff0c;发现问题来源于我定义的子线程。子线程的主要功能是从任务队列(LinkedBlockingQueue)里面持续拿出任务&#xff0c;并且执行。以下为令CPU100的代码。private class WorkThread extends Threa…

excel离散度图表怎么算_一般人不知道的几个excel制图技巧

原标题&#xff1a;一般人不知道的几个excel制图技巧作者&#xff1a;杜雨 公众号&#xff1a;数据小魔方(datamofang)今天这篇&#xff0c;我专注于Excel的作图规则&#xff0c;深入的研究下Excel由数据源到可视化图表之间的关系是如何对应的&#xff0c;倘若你已经在工作中横…

listview控件在php的使用方法,Android_Android编程之控件ListView使用方法,本文实例讲述了Android编程之控 - phpStudy...

Android编程之控件ListView使用方法本文实例讲述了Android编程之控件ListView使用方法。分享给大家供大家参考。具体分析如下&#xff1a;控件ListView是一个重要的控件&#xff0c;可以被用作用户列表等显示&#xff0c;下面进行它的操作测试。下面代码实现了生成了一个ListVi…

使用ElasticSearch进行近实时索引

选择索引策略很困难。 Elasticsearch 文档的确有一些一般性建议 &#xff0c;并且有其他公司的 一些技巧 &#xff0c;但这也取决于特定的用例。 在典型情况下&#xff0c;您有一个数据库作为事实的来源&#xff0c;并且有一个使事物可搜索的索引。 您可以采用以下策略&#xf…

air什么意思中文_Air 的中文意思是什么?

展开全部air释义&#xff1a;(1)作为名词&#xff0c;空气&#xff1b;32313133353236313431303231363533e59b9ee7ad9431333431366261天空&#xff1b;(飞行的)空中。(2)作为动词&#xff0c;晾&#xff1b;晾干&#xff1b;(使)通风&#xff1b;公开发表。读音&#xff1a;英[…

python xlutils函数,python3:xlrd、xlwt、xlutils处理excel文件

1.xlrd读取excel请参考上篇博客https://www.cnblogs.com/shapeL/p/9075843.html2.xlwt生成excel安装下载&#xff1a;pip install xlwt导入&#xff1a;import xlwt参考&#xff1a;生成excel文件test1.xlsfile_name ../dataconfig/test1.xlswbk xlwt.Workbook() #初始化work…

前端实现炫酷动效_20个网页动效设计的炫酷神器

如今很多 UI 设计师不是正在做动效&#xff0c;就正在学着做动效。动效现在已经无处不在了。有的动效可能是一个微妙的悬停效果&#xff0c;使用 Bttn.css 或者是 Hover Buttons 的帮助下&#xff0c;添加到按钮上&#xff0c;有的则可能是使用引人瞩目的动态渐变背景抓住用户的…

php7设置最大连接数,sybase 15.7 修改 number of user connections 最大连接数

sp_configure number of user connectionssp_configure number of user connections,200修改Sybase最大连接数的方法有两种&#xff0c;如下&#xff1a;一、 进Sybase central, 鼠标右键选择数据库服务器(要处理的服务器)&#xff0c;然后选择右键菜单中的配置选项&#xff0c…

jsr303 自定义消息_JSR 303从I18N属性文件加载消息

jsr303 自定义消息总览 本文将说明如何适应JSR 303验证API来从I18n属性文件加载消息&#xff0c;并通过保留国际化的所有好处和对多种语言的支持来实现这一点。 为此&#xff0c;我们将实现一个基于Spring API的自定义MessageInterpolator&#xff0c;用于管理I18N消息。 依存…

最优隐神经元数目 算法 matlab,BP算法的改进在Matlab的实现研究

BP 算法的改进在M a tlab 的实现研究姚文俊(中南民族大学电子信息工程学院 湖北武汉 430074)摘 要:利用M atlab 中的神经网络工具箱提供的丰富网络学习和训练函数,对BP 网络和BP 算法的优化方案进行仿真,得到较优的BP 算法。关键词:人工神经网络;BP 网络;M atlab ;N eu ral …

php 导出excel分段导出_PHP 导出excel 数据量大时

public functionceshiexcel1(){set_time_limit(0);$filename 病毒日志;header(Content-Type: application/vnd.ms-excel);header(Content-Disposition: attachment;filename".$filename..csv");header(Cache-Control: max-age0);//原生链接mysql//数据库配置$mysql_…

通过Apache Kafka集成流式传输大数据

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

php7 获取数据流,stream_socket_accept()

stream_socket_accept()(PHP 5, PHP 7)接受由stream_socket_server()创建的套接字连接说明stream_socket_accept(resource$server_socket[,float$timeout ini_get("default_socket_timeout")[,string&$peername]]):resource接受由stream_socket_server()创建的套…