征服Android面試官路漫漫(二):OutOfMemoryError 可以被 try catch 嗎 ?

南方吳彥祖_藍斯發表於2020-11-02

問題由來:

這是一家公司的面試題目,感覺有點意思,所以面試回來準備測試下什麼情況。

問題論點:

對於這個問題,主要討論兩種OutOfMemory可能性,一種是突然使用了大量記憶體,比如載入了特別巨大的圖片,第二是記憶體洩漏。

然後還有個問題是,一旦發生OOM,引發OOM的操作是否會成功,如果會成功賦值是否會成功呢?理論上操作和賦值都不會成功的,但是我覺得有必要嘗試一下。

目錄

  • OutOfMemoryError 可以被 try catch 嗎?
  • 捕獲 OutOfMemoryError 有什麼意義?
  • JVM 中哪一塊記憶體不會發生 OOM ?

OutOfMemoryError 可以被 try catch 嗎?

群裡小夥伴碰到的一道比較經典的面試題,但我相信很多第一次碰到這個問題的同學應該無法立刻給出答案,最好的辦法肯定還是動手測一測。

注意看下面的 Gif,每點選一次  Allocate 20MB ,都會給陣列容量增加  20*1024*1024,當然應該並不是 20 MB。如下面程式碼所示:

binding.allocate.setOnClickListener {
 try {
  bytes = ByteArray(bytes.size + 1024 * 1024 * 20)
  refreshMemory()
 } catch (e: OutOfMemoryError) {
  binding.oomError.text = "Catch OOM : \n ${e.message}"
 }}
征服Android面試官路漫漫(二):OutOfMemoryError 可以被 try catch 嗎 ?

當點選第 7 次時,發生了  OutOfMemoryError ,並且  catch 程式碼塊執行了。

Catch OOM : Failed to allocate a 146801680 byte allocation with 25165824 free bytes and 133MB until OOM, target footprint 153948888, growth limit 268435456

所以, OutOfMemoryError 是可以 try catch 的。

順道畫了一個思維導圖回顧一下 Java 的異常體系。

征服Android面試官路漫漫(二):OutOfMemoryError 可以被 try catch 嗎 ?

上面的圖片沒有羅列出所有的異常型別,但也基本概括了 Java 異常的繼承體系。所有的異常類都繼承自  Throwable , Throwable 有兩個直接子類  Error 和  Exception 。

Exception 一般指可以/應該捕獲和處理的異常。它的兩個直接子類 IOException 和  RuntimeException 及其子類都是我們在程式碼中經常遇到的一些錯誤。 RuntimeException 是在程式執行中可能發生的異常,我們可以不捕獲它,但可能帶來 Crash 的代價,但是過多的捕獲異常又不利於暴露和除錯異常情況。在開發過程中,我們更多的應該及時暴露問題。除了 RuntimeException 以外,其他異常可以統稱為 非執行時異常 或者  受檢異常,這些異常必須被捕獲,否則編譯期就會報錯。

Error 一般指非正常狀態的,比較嚴重的,不應該被捕獲的系統錯誤。

再回頭看看 OutOfMemoryError 的父類們,

OutOfMemoryError <- VirtualMachineError <- Error

OutOfMemoryError 是一個 Error ,Error 不應該被捕獲。那麼, 捕獲 OutOfMemoryError 有什麼意義呢?

捕獲 OutOfMemoryError 有什麼意義?

一般情況下並沒有什麼太大意義,相信你在開發中也幾乎沒有寫過 catch OOM 的程式碼。

如果你把捕獲 OOM 當做處理 OOM 的一種手段,無疑是不合適的。你無法保證你 catch 的程式碼就是導致 OOM 的原因,可能它只是壓死駱駝的最後一根稻草,甚至你也無法保證你的 catch 程式碼塊中不會再次觸發 OOM 。

我也從來沒有寫過捕獲 OOM 的程式碼,但無意中在 Android 原始碼中發現了這樣的操作。在  View.java 的  buildDrawingCacheImpl() 方法中有這麼一段程式碼:

try {
    bitmap = Bitmap.createBitmap(mResources.getDisplayMetrics(),
                        width, height, quality);
    bitmap.setDensity(getResources().getDisplayMetrics().densityDpi);
    if (autoScale) {
        mDrawingCache = bitmap;
     } else {
         mUnscaledDrawingCache = bitmap;
     }
     if (opaque && use32BitCache) bitmap.setHasAlpha(false);} catch (OutOfMemoryError e) {
    // If there is not enough memory to create the bitmap cache, just
    // ignore the issue as bitmap caches are not required to draw the
    // view hierarchy
    if (autoScale) {
        mDrawingCache = null;
    } else {
        mUnscaledDrawingCache = null;}mCachingFailed = true;......

buildDrawingCacheImpl() 方法的大致作用是為當前 View 生成一個 Bitmap 快取。在構建 Bitmap 物件的時候,如果捕捉到了 OOM ,就放棄生成 Bitmap 快取,因為在 View 的繪製過程中 Bitmap Cache 並不是必須存在的。所以在這裡沒有必要丟擲 OOM ,而是自己捕獲就可以了。

在你自己明確知道可能發生 OOM 的情況下設定一個兜底策略,這可能是捕獲 OOM 的唯一意義了。如果你有其他奇淫技巧,歡迎在評論區補充。

JVM 中哪一塊記憶體不會發生 OOM ?

最後補充一道我曾經遇到過的面試題, JVM 中哪一塊記憶體不會發生 OOM ?

當時面試的時候一下沒反應過來,回來之後翻了翻 《深入理解Java虛擬機器》 。但凡是 JVM 的相關問題,基本上都可以在這本書上找到答案。以下內容均總結摘抄自這本書,也可以檢視我的相關讀書筆記:第2章:Java記憶體區域與記憶體移溢位異常 。

Java 虛擬機器在執行 Java 程式的過程中會把它所管理的記憶體劃分為若干個不同的資料區域,如下圖所示:

征服Android面試官路漫漫(二):OutOfMemoryError 可以被 try catch 嗎 ?

Java 虛擬機器棧 。每個方法被執行的時候,Java 虛擬機器棧都會同步建立一個棧幀用於儲存區域性變數表、運算元棧、動態連線、方法出口等資訊。每個方法被呼叫直到執行完畢的過程,就對應著一個棧幀在虛擬機器棧中從入棧到出棧的過程。

如果執行緒請求的棧深度大於虛擬機器所允許的深度,將丟擲 StackOverflowError 異常。如果 Java 虛擬機器棧支援動態擴充套件,當棧擴充套件時無法申請到足夠的記憶體會排丟擲 OutOfMemoryError 異常。

本地方法棧。為虛擬機器使用到的 Native 方法服務。《Java 虛擬機器規範》對本地方法棧中方法使用的語言、使用方式和資料結構並沒有任何強制規定,因此具體的虛擬機器可以根據需要自由實現它。Hotspot 將本地方法棧和虛擬機器棧合二為一。

本地方法棧也會在棧深度溢位和棧擴充套件失敗時分別丟擲 StackOverflowError 和 OutOfMemoryError 。

Java 堆。所有執行緒共享的一塊記憶體區域,在虛擬機器啟動時建立。此記憶體區域的唯一目的就是存放物件例項,Java 世界裡 “幾乎” 所有的物件例項都在這裡分配記憶體。在 《Java 虛擬機器規範》中對 Java 堆的描述是:“所有的物件例項以及陣列都應當在堆上分配”。

Java 堆以處於物理上不連續的記憶體空間,但在邏輯上它應該被視為連續的。但對於大物件(典型的如陣列物件),多數虛擬機器實現出於實現簡單、儲存高效的考慮,很可能會要求連續的記憶體空間。

Java 堆既可以被實現成固定大小,也可以是擴充套件的。如果在 Java 堆中沒有記憶體完成例項分配,並且堆無法再擴充套件時,Java 虛擬機器將會丟擲 OutOfMemoryError 。

方法區。方法區是各個執行緒共享的記憶體區域,它用於儲存已被虛擬機器載入的型別資訊、常量、靜態變數、即時編譯器編譯後的程式碼快取等資料。

雖然《Java 虛擬機器規範》中把方法區描述為堆的一個邏輯部分,但是它卻有一個別名叫做“非堆”,目的是與 Java 堆分開來。

Hotspot 設計之初選擇把垃圾收集器的分代設計擴充套件至方法區,或者說使用永久代來實現方法區而已,使得 HotSpot 的 GC 能夠像管理 Java 堆一樣管理這部分記憶體,但導致 Java 應用更容易遇到記憶體溢位的問題。在 JDK 8 中,徹底廢棄了永久代的概念。

如果方法區無法滿足新的記憶體分配的需求時,將丟擲 OutOfMemoryError 。

執行時常量池。方法區的一部分。Class 檔案的常量池表,用於存放編譯期生成的各種字面量與符號引用,這部分內容將在類載入後方法方法去的執行時常量池。

執行時常量池具有動態性,執行期間也可以將新的常量放入池中,如 String.intern() 。

常量池受到方法區的限制,當無法再申請到記憶體時,會丟擲 OutOfMemoryError 。

唯一一個在《Java虛擬機器規範》中沒有規定任何 OutOfMemoryError 情況的區域是  程式計數器。程式計數器(Program Counter Register)是一塊較小的記憶體空間,它可以看作是當前執行緒所執行的位元組碼的行號指示器。如果執行緒正在執行的是一個Java方法,這個計數器記錄的是正在執行的虛擬機器位元組碼指令的地址;如果正在執行的是本地(Native)方法,這個計數器值則應為空(Undefined)。

征服Android面試官路漫漫

有些東西你不僅要懂,而且要能夠很好地表達出來,能夠讓面試官認可你的理解,例如Handler機制,這個是面試必問之題。有些晦澀的點,或許它只活在面試當中,實際工作當中你壓根不會用到它,但是你要知道它是什麼東西。

對於程式設計師來說,要學習的知識內容、技術有太多太多,要想不被環境淘汰就只有不斷提升自己, 從來都是我們去適應環境,而不是環境來適應我們!

最後我在這裡分享一下這段時間從朋友,大佬那裡收集到的一些 2019-2020BAT 面試真題解析,裡面內容很多也很系統,包含了很多內容: Android 基礎、Java 基礎、Android 原始碼相關分析、常見的一些原理性問題等等,可以很好地幫助我們深刻理解Android相關知識點的原理以及面試相關知識。

1、確定好方向,梳理成長路線圖

不用多說,相信大家都有一個共識:無論什麼行業,最牛逼的人肯定是站在金字塔端的人。所以,想做一個牛逼的程式設計師,那麼就要讓自己站的更高,成為技術大牛並不是一朝一夕的事情,需要時間的沉澱和技術的積累。

關於這一點,在我當時確立好Android方向時,就已經開始梳理自己的成長路線了,包括技術要怎麼系統地去學習,都列得非常詳細。

征服Android面試官路漫漫(二):OutOfMemoryError 可以被 try catch 嗎 ?

知識梳理完之後,就需要進行查漏補缺,所以針對這些知識點,我手頭上也準備了不少的電子書和筆記,這些筆記將各個知識點進行了完美的總結:

征服Android面試官路漫漫(二):OutOfMemoryError 可以被 try catch 嗎 ?

2、透過原始碼來系統性地學習

只要是程式設計師,不管是Java還是Android,如果不去閱讀原始碼,只看API文件,那就只是停留於皮毛,這對我們知識體系的建立和完備以及實戰技術的提升都是不利的。

真正最能鍛鍊能力的便是直接去閱讀原始碼,不僅限於閱讀各大系統原始碼,還包括各種優秀的開源庫。

征服Android面試官路漫漫(二):OutOfMemoryError 可以被 try catch 嗎 ?
《486頁超全面Android開發相關原始碼精編解析》

3、閱讀前輩的一些技術筆記

征服Android面試官路漫漫(二):OutOfMemoryError 可以被 try catch 嗎 ?
《960全網最全Android開發筆記》

4、刷題備戰,直通大廠

歷時半年,我們整理了這份市面上最全面的安卓面試題解析大全
包含了騰訊、百度、小米、阿里、樂視、美團、58、360、新浪、搜狐等一線網際網路公司面試被問到的題目。熟悉本文中列出的知識點會大大增加透過前兩輪技術面試的機率。

如何使用它?

1.可以透過目錄索引直接翻看需要的知識點,查漏補缺。
2.五角星數表示面試問到的頻率,代表重要推薦指數

征服Android面試官路漫漫(二):OutOfMemoryError 可以被 try catch 嗎 ?
《379頁Android開發面試寶典》

以上文章中的資料,均可以免費分享給大家來學習,無論你是零基礎還是工作多年,現在開始就不會晚。

以上內容均放在了開源專案: github  中已收錄,大家可以自行獲取。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69983917/viewspace-2731736/,如需轉載,請註明出處,否則將追究法律責任。

相關文章