五月天青色头像情侣网名,国产亚洲av片在线观看18女人,黑人巨茎大战俄罗斯美女,扒下她的小内裤打屁股

歡迎光臨散文網(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問題的評論 (共 條)

分享到微博請遵守國家法律
武鸣县| 渭南市| 微山县| 翼城县| 剑阁县| 江安县| 彝良县| 城固县| 思南县| 黔江区| 茶陵县| 建阳市| 革吉县| 汕头市| 安仁县| 奉贤区| 巴南区| 芜湖市| 永新县| 鄱阳县| 巨野县| 柏乡县| 安远县| 上高县| 铁岭市| 灵武市| 英德市| 多伦县| 汉阴县| 汉寿县| 永和县| 沈阳市| 宁明县| 垣曲县| 义马市| 革吉县| 镇康县| 高碑店市| 砚山县| 麻栗坡县| 通化县|