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…

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 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…

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

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

安卓php推送消息机制,深入剖析Android消息机制原理

在Android中&#xff0c;线程内部或者线程之间进行信息交互时经常会使用消息&#xff0c;这些基础的东西如果我们熟悉其内部的原理&#xff0c;将会使我们容易、更好地架构系统&#xff0c;避免一些低级的错误。在学习Android中消息机制之前&#xff0c;我们先了解与消息有关的…

oracle数据modeling分类,由浅入深 NoSQL的五种主流数据模型

【IT168 技术】本文内容是对《NoSQL Data Modeling Techniques》一文的简单概述&#xff0c;原文对NoSQL的几种数据模型进行了详细深入的讨论。是了解NoSQL数据模型不过错过的全面资料。NoSQL的一些非功能性的特性&#xff0c;比如扩展性、性能以及一致性的讨论&#xff0c;目前…

dederss.php美国与,Dede经验:全站rss/连载和分类首页模板替换

我用的是Dedecms55 utf-8建的站。首先感谢开源工作者们的劳动&#xff0c;其次要感谢使用者写的N多问题与解答。我一个PHP初用者&#xff0c;能在一个月内&#xff0c;一个人把酷猫网http://www.92kcuat.com 建成今天这样&#xff0c;相信很多朋友都可以。当遇到问题时&#xf…

物联网协议之COAP简介及Java实践

目录 前言 一、COAP简介 1、关于COAP 2、COAP特点 3、基于COAP的NB-IoT接入流程 二、CoAP协议JAVA实践 1、californium介绍 2、Java集成 3、Maven 资源引入 4、定义Server端 5、Client调用 6、运行测试 总结 前言 今天平安夜&#xff0c;祝大家圣诞快乐&#xff0c…

bp配置 sap_SAP转储订单之 STO without delivery

在《SAP转储订单STO小结》一文中&#xff0c;介绍了存转储订单(STO)的几种方式&#xff0c;在SAP Library中有相应的描述&#xff1a;1 Stock Transfer Between Plants in One Step2 Stock Transfer Between Plants in TwoSteps3 Stock Transport Order Without Delivery4 Stoc…

[MEGA DEAL] 2020年完整的Java Master Class Bundle(96%)

通过超过62个小时的培训来掌握最流行的编程语言&#xff0c;从而树立良好的开发生涯 嘿&#xff0c;怪胎&#xff0c; 这一周&#xff0c;我们JCG促销专区 &#xff0c;我们有另一个极端的报价 。我们正在提供一个巨大的96&#xff05;off的完整2020 Python编程认证捆绑 。 立…

linux nc命令测试端口,Linux和Windows下的NC(Netcat)命令测试端口连通性

1、Linux OS 环境下(以Centos为例)&#xff0c;使用nc命令分别测试TCP和UDP端口连通性&#xff1a;css安装方法&#xff1a;nginx在客户端和服务器端分别安装nc工具&#xff0c;安装命令以下&#xff1a;webyum install nc1. Linux OS下使用nc命令&#xff0c;实现TCP方式监听服…

javafx css颜色_JavaFX技巧7:使用CSS颜色常量/派生颜色

javafx css颜色在使用FlexCalendarFX时&#xff0c;我不得不定义一组颜色以可视化不同颜色的不同日历的控件。 每个日历不仅提供一种颜色&#xff0c;还提供几种&#xff1a;用于取消选择/选定/悬停状态的背景和文本颜色。 颜色曾在多个地方使用过&#xff0c;但为了简洁起见&…

linux 查看端口 程序,linux开发:Linux下查看端口占用

前段时间有学生问到&#xff0c;怎么查看linux系统中已经被占用的端口&#xff1f;下面就统一给大家解释一下。提到端口&#xff0c;那首先来回顾端口定义&#xff0c;为了区分一台主机接收到的数据包应该转交给哪个任务来进行处理&#xff0c;使用端口号来区别&#xff1b;我们…

十进制小数化为二进制小数的方法是什么_八进制转换成十进制,十进制转换成八进制...

先来看八进制如何转换成十进制。其方法与二进制转换成十进制差不多&#xff1a;按权相加法&#xff0c;即将八进制每位上的数乘以位权&#xff08;如8,64,512….&#xff09;&#xff0c;然后将得出来的数再加在一起。如将72.45转换为十进制。如图1所示来看看十进制转八进制&am…

c++从字符串中提取数字求和_【函数应用】单元格文本内提取数字并求和

本篇的主题是将单元格内一串文本&#xff0c;找出所有数字并求和&#xff0c;如下图。难度较高&#xff0c;新手建议仅了解下&#xff0c;先学会数组运用&#xff0c;再研究此知识点。废话少说&#xff0c;步入正题。重点说明&#xff1a;本篇只针对文本内整数的数字进行提取并…

iphone已停用怎么解锁_两种无密码解锁iPhone锁屏密码的方法

现在很多手机都配备指纹解锁功能&#xff0c;大家平时用惯了指纹解锁&#xff0c;有时候在需要输入锁屏密码的时候反倒记不清密码是什么了。像是手机重启后就需要输入密码解锁&#xff0c;iPhone锁屏密码忘了怎么办&#xff1f;多次输入错误密码还可能导致手机被停用&#xff0…

linux查找postgre进程,postgresql数据库某一个进程占用大量CPU,问题排查详解

postgresql某一个进程占用大量CPU&#xff0c;问题排查&#xff0c;目前服务器cpu为4核&#xff0c;内存8G1.查下是不是我们的业务SQLSELECTprocpid,START,now() - START AS lap,current_queryFROM (SELECTbackendid,pg_stat_get_backend_pid(S.backendid) AS procp…

华硕z9pa u8 bios下载_教程:图文教学,华硕M8H M8R硬破解支持intel 9代处理器

听说2020年&#xff0c;Intel将要上10代U了&#xff0c;10代U Comet Lake-S的参数整理规格如下:赛扬G5900&#xff1a;2核心2线程&#xff0c;3.4GHz奔腾G6600&#xff1a;2核心4线程&#xff0c;4.2GHz酷睿i3-10100&#xff1a;4核心8线程&#xff0c;3.6-?GHz酷睿i3-10100T&…

苹果mp3软件_flac、WAV、m4a等音频格式转成MP3 ,一键搞定!

在工作生活中&#xff0c;有时我们需要处理各种音频格式转换&#xff0c;有些系统或者是软件不支持特殊的音频格式添加。比如说苹果手机录音格式是M4A的&#xff0c;flac、WAV无损音质格式或者au格式&#xff0c;这些都怎么转成常用的MP3格式呢&#xff1f;有一种快捷的方法就是…