最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

記一次生產(chǎn)頻繁發(fā)生FullGC問題

2023-03-16 16:07 作者:吳小敏63  | 我要投稿

問題發(fā)現(xiàn)

早上過來,飯都沒來的及吃,運維就給我發(fā)來信息,說是某個接口調(diào)用大量超時。因為最近這個接口調(diào)用量是翻倍了,所以我就去檢查了下慢SQL,發(fā)現(xiàn)確實是有較多的慢SQL,所以我就縮減了查詢的時間范圍,但是效果并不好。

過了一會發(fā)現(xiàn),這個服務fullGC是有問題的,太頻繁了,這個應該是導致接口超時的根本問題,因為時間也是對的上的。

這個是最近三個小時fullGC的監(jiān)控圖:

這個是最近三天fullGC的監(jiān)控圖:

對比一下,就不難發(fā)現(xiàn),fullGC數(shù)量是從3月15號晚上9點開始增加的,也是這個接口對外開放的時間。

?

解決思路

1、首先去服務器上面下載dump文件,分析是哪里造成了內(nèi)存泄漏,頻繁觸發(fā)fullGC。首先找出服務器內(nèi)java文件的PID,然后保存dump文件,我們公司java服務是固定端口號:1

使用top命令:

然后執(zhí)行命令:jmap -dump:file=202303160924.dump,format=b 1 ,保存dump文件

2、根據(jù)dump文件,分析出堆內(nèi)對象的分布情況


    • 下載一個可以分析dump文件的工具,這里我下載是Jprofiler

    • 查看大對象的分析,發(fā)現(xiàn)是java.lang.ApplicationShutdownHooks的hooks占用太大內(nèi)存,并且得知改熟悉是一個Map

  • 分析這個Map里面的元素引用關系,可以看到這個map里面存的都是線程對象,并且大部分都是一個名為java.util.concurrent.ScheduledThreadPoolExecutor@59648a61的線程池對象,到了這里就定位到問題代碼了,是這次新加的接口里面有一個異步操作,用的guava并發(fā)包里面的一個超時等待功能的接口,具體思路就是啟用一個定時任務線程池去定時去檢查在規(guī)定時間內(nèi),是否返回結(jié)果。

??

3、看看我的業(yè)務代碼是哪里出現(xiàn)了問題

//異步執(zhí)行某個查詢方法,并且在規(guī)定時間內(nèi)返回查詢結(jié)果public <T> T asyncWithTimeout(ScheduledThreadPoolExecutor executor, Callable<T> callable, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?long time, TimeUnit unit) { ? ?try { ? ? ? ?ListeningExecutorService listeningExecutorService = MoreExecutors.listeningDecorator(threadPoolExecutor); ? ? ? ?ListenableFuture<T> future = listeningExecutorService.submit(callable); ? ? ? ?//這里是創(chuàng)建一個定時任務線程,去定時檢查是否在規(guī)定時間內(nèi)查詢完畢,應該就是這個去添加了鉤子函數(shù),進去看看 ? ? ? ?ScheduledExecutorService scheduledExecutorService = MoreExecutors.getExitingScheduledExecutorService(executor); ? ? ? ?return Futures.withTimeout(future, time, unit, scheduledExecutorService).get(time, unit); ? ?} catch (InterruptedException | ExecutionException | TimeoutException e) { ? ? ? ?log.warn("異步方法執(zhí)行失敗,error:{}", e.getMessage()); ? ?} ? ?return null; }//=======================guava并發(fā)包代碼=======================@Beta@GwtIncompatible // TODOpublic static ScheduledExecutorService getExitingScheduledExecutorService( ? ?ScheduledThreadPoolExecutor executor) { ?//每次都去創(chuàng)建一個新的對象 ?return new Application().getExitingScheduledExecutorService(executor); }final ScheduledExecutorService getExitingScheduledExecutorService( ? ?ScheduledThreadPoolExecutor executor) { ?return getExitingScheduledExecutorService(executor, 120, TimeUnit.SECONDS); }final ScheduledExecutorService getExitingScheduledExecutorService( ? ?ScheduledThreadPoolExecutor executor, long terminationTimeout, TimeUnit timeUnit) { ?useDaemonThreadFactory(executor); ?ScheduledExecutorService service = Executors.unconfigurableScheduledExecutorService(executor); ?//添加構(gòu)造函數(shù)的地方,進去看看 ?addDelayedShutdownHook(executor, terminationTimeout, timeUnit); ?return service; }final void addDelayedShutdownHook( ? ?final ExecutorService service, final long terminationTimeout, final TimeUnit timeUnit) { ?checkNotNull(service); ?checkNotNull(timeUnit); ?//繼續(xù)點進去 ?addShutdownHook( ? ? ?MoreExecutors.newThread( ? ? ? ? ?//線程名字對上了,就在對象引用的截圖里面出現(xiàn)過 ? ? ? ? ?"DelayedShutdownHook-for-" + service, ? ? ? ? ?new Runnable() { ? ? ? ? ? ?@Override ? ? ? ? ? ?public void run() { ? ? ? ? ? ? ?try { ? ? ? ? ? ? ? ?// We'd like to log progress and failures that may arise in the ? ? ? ? ? ? ? ?// following code, but unfortunately the behavior of logging ? ? ? ? ? ? ? ?// is undefined in shutdown hooks. ? ? ? ? ? ? ? ?// This is because the logging code installs a shutdown hook of its ? ? ? ? ? ? ? ?// own. See Cleaner class inside {@link LogManager}. ? ? ? ? ? ? ? ?service.shutdown(); ? ? ? ? ? ? ? ?service.awaitTermination(terminationTimeout, timeUnit); ? ? ? ? ? ? ?} catch (InterruptedException ignored) { ? ? ? ? ? ? ? ?// We're shutting down anyway, so just ignore. ? ? ? ? ? ? ?} ? ? ? ? ? ?} ? ? ? ? ?})); }@VisibleForTestingvoid addShutdownHook(Thread hook) { ?Runtime.getRuntime().addShutdownHook(hook); }//=======================guava并發(fā)包代碼=======================public void addShutdownHook(Thread hook) { ? ?SecurityManager sm = System.getSecurityManager(); ? ?if (sm != null) { ? ? ? ?sm.checkPermission(new RuntimePermission("shutdownHooks")); ? ?} ? ?//定位到問題了,就是這里添加的鉤子函數(shù) ? ?ApplicationShutdownHooks.add(hook); }static synchronized void add(Thread hook) { ? ?if(hooks == null) ? ? ? ?throw new IllegalStateException("Shutdown in progress"); ? ?if (hook.isAlive()) ? ? ? ?throw new IllegalArgumentException("Hook already running"); ? ?if (hooks.containsKey(hook)) ? ? ? ?throw new IllegalArgumentException("Hook previously registered"); ? ?//存在到 hooks 這個map對象里面,就是這個大對象 ? ?hooks.put(hook, hook); }

問題解決

經(jīng)過上面問題的排查,造成hooks大對象的原因找到了,就是每次調(diào)用接口的時候,都會往hooks里面put一個對象。

所以,解決辦法很簡單,就是不用每次都去生成一個ScheduledExecutorService對象,類初始化的時候創(chuàng)建一次就行了

改造后的代碼如下:

private ListeningExecutorService listeningExecutorService;private ScheduledExecutorService scheduledExecutorService;public static AsyncUtils getInstance() { ? ?return ThreadHolder.INSTANCE.getAsyncWithCallback(); }@SuppressWarnings("UnstableApiUsage")private AsyncUtils() { ? ?listeningExecutorService = MoreExecutors.listeningDecorator(ThreadPoolConstant.THREAD_POOL_EXECUTOR); ? ?scheduledExecutorService = MoreExecutors.getExitingScheduledExecutorService(ThreadPoolConstant.SCHEDULED_THREAD_POOL_EXECUTOR); }@SuppressWarnings("UnstableApiUsage")public <T> T asyncWithTimeout(Callable<T> callable, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?long time, TimeUnit unit) { ? ?try { ? ? ? ?ListenableFuture<T> future = listeningExecutorService.submit(callable); ? ? ? ?return Futures.withTimeout(future, time, unit, scheduledExecutorService).get(time, unit); ? ?} catch (InterruptedException | ExecutionException | TimeoutException e) { ? ? ? ?log.warn("異步方法執(zhí)行失敗,error:{}", e.getMessage()); ? ?} ? ?return null; }private enum ThreadHolder { ? ?/** ? ? * 線程持有類 INSTANCE ? ? */ ? ?INSTANCE; ? ?private final AsyncUtils asyncUtils; ? ?ThreadHolder() { ? ? ? ?asyncUtils = new AsyncUtils(); ? ?} ? ?public AsyncUtils getAsyncWithCallback() { ? ? ? ?return asyncUtils; ? ?} }


記一次生產(chǎn)頻繁發(fā)生FullGC問題的評論 (共 條)

分享到微博請遵守國家法律
临邑县| 信阳市| 广西| 吉林市| 德昌县| 厦门市| 富平县| 珲春市| 兴山县| 汶上县| 桐庐县| 西昌市| 南木林县| 奈曼旗| 望奎县| 衡东县| 庐江县| 章丘市| 茂名市| 巴彦县| 卓资县| 扎赉特旗| 鹤岗市| 金溪县| 石阡县| 福泉市| 汉阴县| 彰化县| 闽侯县| 穆棱市| 连云港市| 新巴尔虎右旗| 射洪县| 老河口市| 正定县| 日喀则市| 内乡县| 盈江县| 巩义市| 泸水县| 四会市|