日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

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

發(fā)布時(shí)間:2023/12/3 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JAR文件句柄:烦恼后清理! 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

在Ultra ESB中,我們使用特殊的熱交換類(lèi)加載器 ,該加載器使我們可以按需重新加載Java類(lèi)。 這使我們能夠從字面上熱交換我們的部署單元 -加載,卸載,使用更新的類(lèi)重新加載,以及正常地逐步退出-無(wú)需重啟JVM。

Windows:支持禁地

在Ultra ESB Legacy中 ,加載程序在Windows上可以正常運(yùn)行,但在較新的X版本上 ,似乎有些困難。 我們不支持將Windows作為目標(biāo)平臺(tái),因此并沒(méi)有太大的意義-直到最近,當(dāng)我們決定在Windows上支持非生產(chǎn)發(fā)行版時(shí)。 (我們的企業(yè)集成IDE UltraStudio在Windows上可以很好地運(yùn)行,因此Windows開(kāi)發(fā)人員都可以使用。)

TDD FTW

修復(fù)類(lèi)加載器很容易,所有測(cè)試都通過(guò)了; 但是我想通過(guò)一些額外的測(cè)試來(lái)支持我的修正,所以我寫(xiě)了一些新的測(cè)試。 其中大多數(shù)涉及在系統(tǒng)temp目錄下的子目錄中創(chuàng)建一個(gè)新的JAR文件,并使用熱交換類(lèi)加載器加載放置在JAR中的不同工件。 為了獲得更多有關(guān)最佳做法的榮譽(yù),我還確保添加一些清除邏輯,以通過(guò)FileUtils.deleteDirectory()刪除temp子目錄。

然后,事情發(fā)瘋了 。

拆解不再了。

在Linux和Windows上,所有測(cè)試都通過(guò)了; 但是最終的拆卸邏輯在Windows中失敗了,就在我刪除temp子目錄的那一刻。

在Windows上,我沒(méi)有l(wèi)sof的奢華; 幸運(yùn)的是, Sysinternals已經(jīng)有了我需要的東西: handle64 。

查找罪魁禍?zhǔn)追浅H菀?#xff1a;在刪除目錄樹(shù)之前,在tearDown()一個(gè)斷點(diǎn),然后運(yùn)行handle64 {my-jar-name}.jar 。

笨蛋

我的測(cè)試Java進(jìn)程持有測(cè)試JAR文件的句柄。

尋找泄漏

不行 我沒(méi)有

自然,我的第一個(gè)懷疑者是類(lèi)加載器本身。 我花了將近半小時(shí)反復(fù)遍歷類(lèi)加載器代碼庫(kù)。 沒(méi)運(yùn)氣。 一切似乎都堅(jiān)如磐石。

又名我的死神文件句柄

我最好的鏡頭是看是什么代碼打開(kāi)了JAR文件的處理程序。 因此,我為Java的FileInputStream和FilterInputStream編寫(xiě)了一個(gè)快速處理的補(bǔ)丁程序 ,該補(bǔ)丁程序?qū)⑥D(zhuǎn)儲(chǔ)獲取時(shí)間的堆棧跟蹤快照。 每當(dāng)線(xiàn)程保持流打開(kāi)時(shí)間過(guò)長(zhǎng)時(shí)。

此“泄漏轉(zhuǎn)儲(chǔ)程序”部分受我們的JDBC連接池的啟發(fā),該連接池檢測(cè)到未釋放的連接(受寬限期限制),然后轉(zhuǎn)儲(chǔ)借用它的線(xiàn)程的堆棧跟蹤-回到被借用的時(shí)間。 (榮譽(yù)給Sachini ,我以前的同事,實(shí)習(xí)生AdroitLogic 。)

泄漏,裸露!

果然,堆棧跟蹤揭示了罪魁禍?zhǔn)?#xff1a;

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 )

但是,這并不能說(shuō)明全部情況。 如果URL.openStream()打開(kāi)JAR,為什么我們從try-with-resources塊返回時(shí)卻沒(méi)有關(guān)閉它?

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源代碼,直到這個(gè)令人震驚的評(píng)論–一直到j(luò)ava.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

來(lái)自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(); } } } }

如果( 因?yàn)?, 因?yàn)?) useCaches在默認(rèn)情況下為true ,那么我們感到非常驚訝!

讓Java緩存其JAR,但不要破壞我的測(cè)試!

JAR緩存可能會(huì)提高性能。 但這是否意味著我應(yīng)該在此之后停止清理-并且在每次測(cè)試后都留下雜散的文件?

(當(dāng)然,我可以說(shuō)file.deleteOnExit() ;但是由于我正在處理目錄層次結(jié)構(gòu),因此無(wú)法保證會(huì)按順序刪除內(nèi)容,并且會(huì)保留未刪除的目錄。)

因此,我想要一種清理JAR緩存的方法–或至少清除我的JAR條目; 完成之后,但在JVM關(guān)閉之前。

完全禁用JAR緩存-可能不是一個(gè)好主意!

URLConnection確實(shí)提供了一種避免緩存連接條目的選項(xiàng):

/** * 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禁用緩存,那將是完美的。 我們的類(lèi)加載器在打開(kāi)JAR時(shí)會(huì)立即緩存所有條目,因此不再需要再次打開(kāi)/讀取該文件。 但是,一旦打開(kāi)JAR,就無(wú)法禁用緩存。 因此,一旦我們的類(lèi)加載器打開(kāi)了JAR,就不會(huì)擺脫緩存的文件句柄-直到JVM本身關(guān)閉!

URLConnection還允許您默認(rèn)禁用所有后續(xù)連接的緩存:

/** * 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; }

但是,如果您一次禁用它,則從那時(shí)起,整個(gè)JVM可能會(huì)受到影響-因?yàn)樗赡苓m用于所有基于URLConnection的實(shí)現(xiàn)。 正如我之前說(shuō)過(guò)的那樣,這可能會(huì)妨礙性能-更不用說(shuō)使測(cè)試脫離啟用緩存的實(shí)際行為了。

在兔子洞下(再次!):從

侵入性最小的選項(xiàng)是在我知道完成后從緩存中刪除我自己的JAR。

好消息是,緩存sun.net.www.protocol.jar.JarFileFactory已經(jīng)具有執(zhí)行該工作的close(JarFile)方法。

但是可悲的是,緩存類(lèi)是程序包專(zhuān)用的。 意味著無(wú)法在我的測(cè)試代碼中進(jìn)行操作。

反思救援!

多虧了反射,我所需要的只是一個(gè)小的“橋梁”,它將代表我訪(fǎng)問(wèn)和調(diào)用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(); } }

在測(cè)試中,我只需要說(shuō):

JarBridge.closeJar(jarPath.toUri().toURL());

就在刪除臨時(shí)目錄之前。

那么,要點(diǎn)是什么?

如果您不直接處理JAR文件,那么對(duì)您來(lái)說(shuō)無(wú)濟(jì)于事。 但是如果是這樣,您可能會(huì)遇到這種晦澀的“使用中的文件”錯(cuò)誤。 (這對(duì)于其他基于URLConnection的流同樣適用。)

如果您碰巧像我那樣(不幸),請(qǐng)回想一下,一個(gè)臭名昭著的博客寫(xiě)了一些駭人聽(tīng)聞的“ leak dumper”補(bǔ)丁JAR ,可以確切地告訴您JAR(或非JAR)泄漏的位置。

阿迪耶

翻譯自: https://www.javacodegeeks.com/2019/10/jar-file-handles-clean-up-after-your-mess.html

總結(jié)

以上是生活随笔為你收集整理的JAR文件句柄:烦恼后清理!的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 超碰午夜 | 欧美少妇一级片 | 久久精品这里只有精品 | 91网入口| 在线免费一区二区 | 免费毛片小视频 | 一区二区三区网站 | 国产精品18久久久久久无码 | 国产精品成人久久久久久久 | 亚洲免费视频大全 | 欧美国产综合视频 | 国产成人精品一区二区三区在线 | 中文字幕女同女同女同 | 亚洲天堂一二三 | 精品人妻伦一二三区久久 | 中文av一区二区三区 | 台湾150部性三级 | 中文字幕精品一区二区三区视频 | 亚洲天堂2015 | 免费在线观看不卡av | 中文字幕有码视频 | 无套内谢少妇毛片 | 在线a天堂 | 99久久久无码国产精品 | 日本裸体动漫 | 国产精品视频在线观看免费 | 亚洲国产小视频 | 国产一区二区自拍视频 | 国产又粗又猛又黄又爽的视频 | 欧洲精品视频在线 | 乱老熟女一区二区三区 | 亚洲精品欧洲 | 一级大片视频 | 日韩av在线一区 | 国产一区欧美日韩 | 97av视频| 丰满少妇一区二区 | 国精品无码一区二区三区 | 色老头综合网 | 潘金莲一级淫片a.aaaaa播放 | 日韩最新中文字幕 | 亚欧精品视频一区二区三区 | 中文文字幕文字幕高清 | 久久久免费高清视频 | 国产精品久久AV无码 | 久久网av | 少妇人妻无码专区视频 | 全黄性高潮 | 自拍欧美亚洲 | 亚洲午夜激情视频 | 日韩精品一区二区在线观看 | 五月天婷婷导航 | 美美女高清毛片视频免费观看 | 免费不卡视频 | 中日韩在线 | 视频精品一区 | 人妻一区二区三区在线 | 伊人69| 三级免费网址 | 在线免费观看福利 | 中文字字幕一区二区三区四区五区 | 精品三区| 狠狠精品干练久久久无码中文字幕 | 手机在线一区二区三区 | 爱爱视频网址 | 国产精品系列在线播放 | 三上悠亚亚洲一区 | 国产中文一区二区三区 | 污污网站免费在线观看 | 艳妇臀荡乳欲伦交换在线播放 | 欧美老肥妇做.爰bbww视频 | 在线免费观看污网站 | 海角国产乱辈乱精品视频 | 午夜一区二区视频 | 日韩av影片 | 高潮爽爆喷水h | 日本乱子伦 | 超碰神马| 法国空姐在线观看视频 | 韩国色网 | xxx黄色片| 国产一线二线在线观看 | 久热在线 | 日韩女优在线视频 | 国产原创视频 | 在线观看你懂的网站 | 天天干视频在线观看 | 国模丫头1000人体 | 日韩黄色片网站 | 久久久精彩视频 | 欧美精品五区 | 少妇无码av无码专区在线观看 | 一级片麻豆 | 天天操夜夜操视频 | 黄色午夜网站 | 亚洲av无码专区在线播放中文 | 三级黄色在线 | 成人在线一区二区三区 | 医生强烈淫药h调教小说视频 |