<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Java內存溢出時,還能正常處理請求嗎?

      當你被問到“當Java程序發生內存溢出時,進程還能正常處理請求嗎?”這樣的面試題,會不會很懵?這里分享一次網友車轍在當初剛畢業那幾年,意義風發,總覺得天下沒有自己不會的面試題。然后在一次字節的面試中,徹徹底底的翻車的面試過程,希望提供大家一些面試經驗。

      Java 的優勢有什么

      面試官一上來,直接進入主題:你覺得在內存管理上,Java 有什么優勢?

      我:小菜一碟。相對于需要手動釋放內存的 C 語言,Java 則通過垃圾回收機制實現了自動內存管理。這一機制能夠自動辨識并清理不再使用的內存資源,從而省去了繁瑣的手動釋放過程,極大地簡化了開發人員的工作。這讓開發者能夠將精力更專注地放在業務邏輯的構建上,而不必過多憂慮內存管理的問題,從而大大簡化了開發人員的工作量。

      什么是 OOM

      面試官:請問您是否熟悉內存溢出(OOM)情況?

      我:在我的經驗中,我在線上經常遇到內存溢出的情況。特別是在使用Java編寫的應用程序中,OOM通常是指內存溢出異常,即當應用程序需要為新對象分配內存空間時,可用內存不足以滿足需求,從而導致了OOM異常的拋出。

      什么情況會產生 OOM

      面試官:好小子,線上的事故代碼不會都是你寫的吧,那你能談談是什么情況會導致內存溢出呢?

      我:當然,一個常見的情況就是堆內存溢出。在創建對象時,大部分情況下都是占用 JVM 的堆內存。一旦堆內存無法滿足對象的分配需求,就會拋出OOM異常。

      錯誤信息通常是:java.lang.OutOfMemoryError: Java heap space

      堆內存溢出的具體場景

      面試官:你這個太抽象了,能不能具體點?

      我:嗯,常見導致內存溢出的情況有這么幾種:

      對象生命周期過長:如果某個對象的生命周期過長,而且該對象占用的內存很大,那么在不斷創建新對象的過程中,堆內存會被耗盡,從而導致內存溢出。這種情況一般出現在用集合當緩存,卻忽略了緩存的淘汰機制。

      無限遞歸:遞歸調用中缺少退出條件或遞歸深度過大,會導致空間耗盡,引發溢出錯誤。往往在測試環境就會發現該問題,不會暴露在生產環境

      大數據集合:在處理大量數據時,如果沒有正確管理內存,例如加載過大的文件、查詢結果集過大等,會導致內存溢出。

      JVM配置不當:如果JVM的內存參數配置不合理,例如堆內存設置過小,無法滿足應用程序的內存需求,也會導致內存溢出。

      下面的這個例子就是無限循環導致內存溢出。

      List list = new ArrayList();
      while (true) {
          list.add(1);
      }

      什么是內存泄漏

      面試官:你知道在我們的程序里,有可能會出現內存泄漏,你對它了解嗎?

      我:對的,和內存溢出的情況不同,還有一種特殊場景,叫做內存泄漏(本質上還是內存溢出,只不過是錯誤的內存溢出),指的是程序在運行過程中無法釋放不再使用的內存,導致內存占用不斷增加,最終耗盡系統資源,這種情況就被稱為內存泄漏。

      這一次,我提前搶答了, 常見導致內存泄漏的情況包括:

      對象的引用未被正確釋放:如果在使用完一個對象后,忘記將其引用置為 null 或者從數據結構中移除,那么該對象將無法被垃圾回收,導致內存泄漏。比如 ThreadLocal。

      長生命周期的對象持有短生命周期對象的引用:如果一個長生命周期的對象持有了一個短生命周期對象的引用,即使短生命周期對象不再使用,由于長生命周期對象的引用仍然存在,短生命周期對象也無法被垃圾回收,從而造成內存泄漏。

      過度使用第三方庫:某些第三方庫可能存在內存泄漏或者資源未正確釋放的問題,如果使用不當或者沒有適當地管理這些庫,可能會導致內存溢出。

      集合類使用不當:在使用集合類時,如果沒有正確地清理元素,當集合不再需要時,集合中的對象也不會被釋放,導致內存泄漏。

      資源未正確釋放:如果程序使用了諸如文件、數據庫連接、網絡連接等資源,在不再需要這些資源時沒有正確釋放,會導致資源泄漏,最終導致內存泄漏。

      下面的這個例子就是長生命周期的對象持有短生命周期對象的引用, 導致內存泄漏。

      List list2 = new ArrayList();
      
      @GetMapping("/headOOM2")
      public String headOOM2() throws InterruptedException {
          while (true) {
              list2.add(1);
          }
      }
      

      還有其他情況嗎

      面試官:你說的都是堆的內存溢出,還有其他情況嗎?

      遞歸調用導致棧溢出

      當遞歸調用的層級過深,棧空間無法容納更多的方法調用信息時,會引發 StackOverflowError 異常,這也是一種 OOM 異常。例如,以下示例中的無限遞歸調用會導致棧溢出。

      public class RecursiveExample {
          public static void recursiveFunction() {
              recursiveFunction();
          }
      
          public static void main(String[] args) {
              recursiveFunction();
          }
      } 

      元空間(Metaspace)耗盡

      元空間是 Java 8 及以后版本中用來存儲類元數據的區域。它取代了早期版本中的永久代(PermGen)。元空間主要用于存儲類的結構信息、方法信息、靜態變量以及編譯后的代碼等。

      當程序加載和定義大量類、動態生成類、使用反射頻繁操作類等情況下,可能會導致元空間耗盡。常見導致元空間耗盡的情況包括:

      類加載過多:如果應用程序動態加載大量的類或者使用動態生成類的方式,會導致元空間的使用量增加。如果無法及時卸載這些類,元空間可能會耗盡。

      字符串常量過多:Java中的字符串常量會被存儲在元空間中。如果應用程序中使用了大量的字符串常量,尤其是較長的字符串,可能會導致元空間的耗盡。

      頻繁使用反射:反射操作需要大量的元數據信息,會占用較多的元空間。如果應用程序頻繁使用反射進行類的操作,可能會導致元空間耗盡。

      大量動態代理:動態代理是一種使用反射創建代理對象的技術。如果應用程序大量使用動態代理,將會生成大量的代理類,占用較多的元空間。

      未正確限制元空間大小:默認情況下,元空間的大小是不受限制的,它會根據需要動態擴展。如果沒有正確設置元空間的大小限制,或者限制過小,可能會導致元空間耗盡。

      下面的這個例子就是類加載過多導致的內存泄漏。

      public class OOMExample {
          public static void main(String[] args) {
              while (true) {
                  ClassLoader classLoader = new CustomClassLoader();
                  classLoader.loadClass("com.example.LargeClass");
              }
          }
      }
      

      終極問題

      面試官滿意地點了點頭,表示我對Java線程處理請求時的情況了解得很多。接著,他提出了一個問題:“當Java線程在處理請求時,發生了OOM異常,整個進程還能繼續處理請求嗎?”這個Java面試題可不簡單哦!

      在我正準備回答的時候,面試官卻提醒我這個問題涉及的內容較為復雜,不是簡單的是與否問題。他建議我先整理一下思路。

      面試官的眼神透露出這道題目有些深意。經過一番思考,我給出了我的答案:“我認為OOM并不會導致整個進程崩潰。”

      面試官隨即追問:“你是怎么理解的?OOM難道不是內存不足的表現嗎?既然內存不足,進程還能繼續處理請求嗎?”

      我解釋道:“盡管出現OOM,但通過垃圾回收機制仍有可能釋放一些內存。”

      面試官卻反駁:“不是因為在垃圾回收后,發現內存依然不足才會拋出OOM異常嗎?這難道不意味著垃圾回收已經無法繼續執行,導致內存不足,進而觸發了OOM異常嗎?整個流程是內存不足,垃圾回收無效,最終OOM。”

      我只能在內心默默吐槽,面對這樣的組合拳,我有些措手不及。很遺憾,面試最終以失敗告終。面試官在我離開時似乎在暗示:“這種情況我也不愿意發生,只能怪編程經驗不夠豐富。”

      實戰

      回到家,我馬上去進行了代碼實戰,用來測試 OOM。

      環境是:OpenJdk 11 -Xms100m -Xmx100m -XX:+PrintGCDetails

      堆內存溢出

      首先我們創建一個方法,調用它,每隔一秒不停的循環打印控制臺信息,它的主要作用是模擬其他線程處理請求。

      @GetMapping("/writeInfo")
      public String writeInfo() throws InterruptedException {
          while (true) {
              Thread.sleep(1000);
              System.out.println("正在輸出信息");
          }
      }
      

      接著再創建一個死循環往 List 中放入對象的方法,它的主要作用是模擬導致OOM的那個線程。

      @GetMapping("/headOOM")
      public String headOOM() throws InterruptedException {
          List list = new ArrayList();
          while (true) {
              list.add(1);
          }
      }
      

      最終結果是headOOM拋出了 OOM 異常,但是控制臺還在不停的打印。【這邊截圖太大了,就不貼出來了】

      這就是答案嗎?其實不是,在第一步中,僅僅是在控制臺打印出了日志,并沒有創建明確的對象。將它稍微改動下,加一行,每次打印前先創建 10M 的對象。

      public String writeInfo() throws InterruptedException {
          while (true) {
              Thread.sleep(1000);
              Byte[] bytes = new Byte[1024 * 1024 * 10];
              System.out.println("正在輸出信息");
          }
      }
      

      結果依舊會繼續打印。看到這里有些人可能會說,答案確實是”還能繼續執行”,我只能說你是 Too Young Too Simple 。往下看

      堆內存泄漏

      老規矩,還是上面的方法

      public String writeInfo() throws InterruptedException {
          while (true) {
              Thread.sleep(1000);
              Byte[] bytes = new Byte[1024 * 1024 * 10];
              System.out.println("正在輸出信息");
          }
      }
      

      創建一個內存泄漏的方法,list2 作用域是在類對象級別,從而產生內存泄漏

      List list2 = new ArrayList();
      @GetMapping("/headOOM2")
      public String headOOM2() throws InterruptedException {
          while (true) {
              list2.add(1);
          }
      }
      

      然后繼續執行,結果首先是headOOM2這個方法對應的線程拋出 OOM。

      接著是 WriteInfo這個方法對應的線程拋出OOM,所以我猜測現在整個進程基本都不能處理請求了。

      為了印證這個猜測,再去調用下 writeInfo這個方法,直接拋出 OOM 異常。說明我們的猜測是對的。

      這時候你如果把那個 10M 改成1M,writeInfo 這個方法就又能執行下去了,不信的話就去試試看吧。

      這說明內存泄漏的情況,其他線程能否繼續執行下去,取決于這些線程的執行邏輯是否會占用大量內存。

      不發生內存泄漏的情況下,為什么頻繁創建對象會導致OOM,GC 不是會把對象給回收嗎

      1. 堆內存限制:Java程序的堆內存有大小限制。如果頻繁創建對象且無法及時回收,堆空間可能被耗盡。垃圾回收器會嘗試回收不再使用的對象,但若創建速度超過回收速度,堆內存不足將導致OOM。
      2. 垃圾回收開銷:盡管垃圾回收器回收不再使用的對象,但垃圾回收本身消耗時間和計算資源。頻繁創建臨時對象會增加回收時間,降低應用程序效率。
      3. 內存碎片化:頻繁創建和銷毀對象會導致內存空間碎片化。即使總的空閑內存足夠,若沒有足夠的連續大塊內存分配給新對象,也會發生OOM。

      通過觀察GC日志,可能發現OOM發生時,堆大小未達到閾值,進一步說明其他因素導致了OOM異常。

      總結

      首先,我們為你闡述了何為OOM以及其發生場景,包括內存溢出和內存泄漏,然后引出了問題:當Java線程在處理請求時,拋出OOM異常,整個進程是否仍能處理請求

      隨后,我們進行了代碼實驗,模擬了內存溢出和內存泄漏兩種情況,得出以下結論:

      1. 內存溢出情況下,若GC速度跟不上內存分配速度,導致OOM并殺死線程,通常整個進程仍能繼續處理請求。
      2. 內存泄漏情況下,未能回收的內存可能引發OOM,繼而殺死線程以防止無法回收對象繼續產生。此時,那些不占用大量內存的線程可能會繼續執行,但那些耗費大量內存的線程可能會無法執行。極端情況下,進程可能會崩潰。

      以上結論反映了OOM對進程的影響,并指出了在不同情況下,進程是否仍能處理請求。

      posted @ 2023-08-15 11:32  Java潘老師  閱讀(356)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 国产国产人免费人成免费| 亚洲国产成人AⅤ片在线观看| 夜夜躁狠狠躁日日躁| 国产成人综合色在线观看网站 | 国产成人久久精品二三区| 邵东县| 亚洲综合网国产精品一区| 日本一区二区三区专线| 日本一区二区三区免费播放视频站| 少妇人妻av毛片在线看| yy111111在线尤物| 国产精品女生自拍第一区| 在线视频精品中文无码| 国产爽视频一区二区三区| 国产精品区一区第一页| 色综合一本到久久亚洲91| 久爱无码精品免费视频在线观看| 亚洲国产制服丝袜先锋| 济宁市| 国产办公室秘书无码精品99| 欧美老人巨大XXXX做受视频 | 人人爽人人爽人人片av东京热| 骚虎视频在线观看| 亚洲综合网国产精品一区| 国产专区精品三级免费看| 国产精品一区中文字幕| 99精品国产中文字幕| 少妇人妻偷人精品系列| 国产精品一区二区久久毛片 | 亚洲美女被黑人巨大在线播放| 最新AV中文字幕无码专区| 精品人妻中文字幕av| 日本久久一区二区免高清| 人妻有码av中文字幕久久琪| 国内少妇偷人精品免费| 亚洲成AV人片在线观高清| 欧美黑人添添高潮a片www| 九九久久自然熟的香蕉图片| 99在线精品免费视频| 四虎亚洲国产成人久久精品| 91国产自拍一区二区三区|