1. 程式人生 > >Java中的異常處理機制

Java中的異常處理機制

條件 order 什麽是 浪費 sun color 越界 details 區域

基本框架如下

Java中的異常處理機制只要實現自Throwable接口,繼承關系如下:

技術分享圖片

如上圖可以看出這個機制的處理對象主要分為兩種:主要區別

error 表示恢復不是不可能但很困難的情況下的一種嚴重問題。比如說底層系統出新錯誤。不可能指望程序能處理這樣的情況。

exception 表示一種設計或實現問題。也就是說,它表示如果程序運行正常,從不會發生的情況。

Exection又可以分為兩種:

被檢查的異常(Checked Exception)

運行時異常(RuntimeException)

下面進行詳細分析

(01) 運行時異常
定義 : RuntimeException及其子類都被稱為運行時異常。
特點

: Java編譯器不會檢查它。 也就是說,當程序中可能出現這類異常時,倘若既"沒有通過throws聲明拋出它",也"沒

有用try-catch語句捕獲它",還是會編譯通過。例如,除數為零時產生的ArithmeticException異常,數組越界時產生的IndexOutOfBoundsException

異常,fail-fail機制產生的ConcurrentModificationException異常等,都屬於運行時異常。
雖然Java編譯器不會檢查運行時異常,但是我們也可以通過throws進行聲明拋出,也可以通過try-catch對它進行捕獲處理。
如果產生運行時異常,則需要通過修改代碼來進行避免。 例如,若會發生除數為零的情況,則需要通過代碼避免該情況的發生!

(02) 被檢查的異常
定義 : Exception類本身,以及Exception的子類中除了"運行時異常"之外的其它子類都屬於被檢查異常。
特點 : Java編譯器會檢查它。 此類異常,要麽通過throws進行聲明拋出,要麽通過try-catch進行捕獲處理,否則不能通過編

譯。例如,CloneNotSupportedException就屬於被檢查異常。當通過clone()接口去克隆一個對象,而該對象對應的類沒有實現

Cloneable接口,就會拋出CloneNotSupportedException異常。
被檢查異常通常都是可以恢復的。

(03) 錯誤
定義 : Error類及其子類。

特點 : 和運行時異常一樣,編譯器也不會對錯誤進行檢查。
當資源不足、約束失敗、或是其它程序無法繼續運行的條件發生時,就產生錯誤。程序本身無法修復這些錯誤的。例如,

VirtualMachineError就屬於錯誤。
按照Java慣例,我們是不應該是實現任何新的Error子類的!

什麽是OOM?

先了解下JVM的幾個運行時數據區:

1.方法區

用於存儲已被JVM加載的類信息,常量,靜態變量,即時編譯器編譯後的代碼,線程共享。

2.運行時常量池

方法區一部分。存放編譯期生成的各種字面量和符號引用。

3.虛擬機棧

內部創建棧幀,來存放局部變量表,操作數棧,動態鏈接,方法出口等,線程私有。

4.本地方法棧(HotSpot不區分虛擬機棧和本地方法棧)

類似虛擬機棧,但是只為Native方法服務。

5.堆

存放實例對象和數組,線程共享。

6.程序計數器

存放當前線程執行的字節碼的行號。

OOM就是說,當JVM因為沒有足夠的內存來為對象分配空間並且垃圾回收器也已經沒有空間可回收時,就會拋出這個Error


2.為什麽會OOM

原因不外乎有兩點:

(1)分配的少了:比如虛擬機本身可使用的內存(一般通過啟動時的VM參數指定)太少。

(2)應用用的太多,並且用完沒釋放,浪費了。此時就會造成內存泄露或者內存溢出。

內存泄露:申請使用完的內存沒有釋放,導致虛擬機不能再次使用該內存,此時這段內存就泄露了。

內存溢出:申請的內存超出了JVM能提供的內存大小,此時稱之為溢出。

而在Java語言中,由於存在了垃圾自動回收機制,所以,我們一般不用去主動釋放不用的對象所占的內存,也就是理論上來說,

是不會存在“內存泄露”的。但是,如果編碼不當,比如,將某個對象的引用放到了全局的Map中,雖然方法結束了,但是由

於垃圾回收器會根據對象的引用情況來回收內存,導致該對象不能被及時的回收。如果該種情況出現次數多了,就會導致內存

溢出,比如系統中經常使用的緩存機制

3 OOM的常見類型

按照JVM規範,JAVA虛擬機在運行時會管理以下的內存區域:

程序計數器:當前線程執行的字節碼的行號指示器,線程私有。

JAVA虛擬機棧:Java方法執行的內存模型,每個Java方法的執行對應著一個棧幀的進棧和出棧的操作。

本地方法棧:類似“ JAVA虛擬機棧 ”,但是為native方法的運行提供內存環境。

JAVA:對象內存分配的地方,內存垃圾回收的主要區域,所有線程共享。可分為新生代,老生代。

方法區:用於存儲已經被JVM加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。Hotspot中的“永久代”。

運行時常量池:方法區的一部分,存儲常量信息,如各種字面量、符號引用等。

直接內存:並不是JVM運行時數據區的一部分, 可直接訪問的內存, 比如NIO會用到這部分。

JVM除了程序計數器不會拋出OOM外,其他各個內存區域都可能會拋出OOM

最常見的OOM類型有以下幾種:

3.1 Java堆內存溢出

Java堆用於存儲對象實例,我們只要不斷的創建對象,並且保證GC Roots到對象之間有可達路徑來避免垃圾回收機制清除這些對象,

就會在對象數量達到最大堆容量限制後產生內存溢出異常

出現這種異常,一般手段是先通過內存映像分析工具(如Eclipse Memory Analyzer)對dump出來的堆轉存快照進行分析,重點是確認

內存中的對象是否是必要的,先分清是因為內存泄漏(Memory Leak)還是內存溢出(MemoryOverflow)。

如果是內存泄漏,可進一步通過工具查看泄漏對象到GC Roots的引用鏈。於是就能找到泄漏對象時通過怎樣的路徑與GC Roots相關

聯並導致垃圾收集器無法自動回收。

如果不存在泄漏,那就應該檢查虛擬機的參數(-Xmx與-Xms)的設置是否適當。

3.2 虛擬機棧和本地方法棧溢出

如果線程請求的棧深度大於虛擬機所允許的最大深度,將拋出StackOverflowError異常。

如果虛擬機在擴展棧時無法申請到足夠的內存空間,則拋出OutOfMemoryError異常

這裏需要註意當棧的大小越大可分配的線程數就越少。

3.3 運行時常量池溢出

如果要向運行時常量池中添加內容,最簡單的做法就是使用String.intern()這個Native方法。該方法的作用是:如果池中已經包含一個

等於此String的字符串,則返回代表池中這個字符串的String對象;否則,將此String對象包含的字符串添加到常量池中,並且返回此

String對象的引用。由於常量池分配在方法區內,我們可以通過-XX:PermSize和-XX:MaxPermSize限制方法區的大小,從而間接限制

其中常量池的容量

3.4 方法區溢出

方法區用於存放Class的相關信息,如類名、訪問修飾符、常量池、字段描述、方法描述等。

方法區溢出也是一種常見的內存溢出異常,一個類如果要被垃圾收集器回收,判定條件是很苛刻的。在經常動態生成大量Class的應用中,

要特別註意這點。

什麽是SOF?

即StackOverFlowError,指棧溢出,如果線程請求的棧深度大於虛擬機所允許的最大深度,將拋出StackOverflowError異常。


參考博客:http://blog.csdn.net/qq_40983603/article/details/78559468

http://blog.csdn.net/maclaren001/article/details/44046631


Java中的異常處理機制