【軟件構造】第八章第一節 軟件構造性能的度量原理 Java 內存區域和GC機制

第八章第一節 軟件構造性能的度量原理

本章是課程覆蓋的第5個質量指標:時空性能html

這是你們最熟悉的指標,雖然很重要,但並不是軟件構造中最重要的指標,當其餘指標得以優化以後,再去考慮性能問題。java

Outline

  • 性能度量指標
  • 存儲性能
  • 內存管理:
    • 對象管理模型:靜態、堆、棧
    • 內存管理模型:
  • Java垃圾回收機制
    • 基本概念
    • GC的四種基本算法
  • JVM中的GC
  • JVM GC性能調優

 Notes

## 性能度量指標

  • 時間性能
    • 每條指令、每一個控制 結構、整個程序的執行時間
    • 不一樣語句或控制結構執行時間的分佈狀況
    • 時間瓶頸在哪裏
  • 空間性能
    • 每一個變量、每一個複雜結構、整個程序的內存消耗
    • 不一樣變量/數據結構的相對消耗
    • 空間瓶頸在哪裏
    • 隨時間的變化狀況

## 內存管理

【對象管理模型】算法

  • 三者的差別在於:如何與什麼時候在程序對象與內存對象之間創建聯繫
  • 靜態
    • 定義:靜態內存是指在程序開始運行時由編譯器分配的內存,它的分配是在程序開始編譯時完成的,不佔用CPU資源。
    • 程序中的各類變量,在編譯時系統已經爲其分配了所需的內存空間,當該變量在做用域內使用完畢時,系統會自動釋放所佔用的內存空間;
    • 不支持遞歸,不支持動態建立可變長的複雜數據類型;
    • 在程序執行期內實體至多關聯一個運行時對象
    • eg: 基本類型,數組
  • 動態-基於棧
    • 棧定義:方法調用和局部變量的存儲位置,保存基本類型
      • 若是一個方法被調用,它的棧幀被放到調用棧的頂部
      • 棧幀保存方法的狀態,包括執行哪行代碼以及全部局部變量的值
      • 棧頂始終是當前運行方法
    • 一個實體能夠在運行時連續地鏈接到多個對象,而且運行時機制以堆棧中的後進先出順序分配和釋放這些對象
    • 棧沒法支持複雜數據結構

  • 動態-基於堆
    • 堆定義:在一塊內存裏分爲多個小塊,每塊包含 一個對象,或者未被佔用
    • 自由模式的內存管理,動態分配,可管理複雜的動態數據結構
    • 代碼中的一個變量能夠在不一樣時間被關聯到不一樣的內存對象上,沒法在編譯階段肯定。內存對象也能夠進一步指向其餘對象

 【Java內存管理模型(Java Memory Model)】數組

  • Java內存模型(簡稱JMM)自己是一種抽象的概念,並不真實存在,它描述的是一組規則或規範,經過這組規範定義了程序中各個變量(包括實例字段,靜態字段和構成數組對象的元素)的訪問方式。即回答:JVM如何管理內存
    • 如何在堆上建立新對象
    • 當某個對象再也不有reference指向他,如何闡述對象、釋放內存

  • 上圖右側是通過簡化CPU與內存操做的硬件處理器簡易圖;多線程的執行最終都會映射到硬件處理器上進行執行,其框架結構與內存硬件框架關係如全圖所示。 
  • 線程棧:每一個線程建立時JVM都會爲其建立一個工做內存(有些地方稱爲棧空間),
    • 每一個線程有本身的棧,管理其局部數據,各棧之間彼此不可見
    • 全部局部的基本數據類型都在棧上建立
    • 多線程之間傳遞數據,是經過複製而非引用
  •  堆:全部對象(即便是局部變量的object)都是在堆上建立的
    • 主內存可被多線程共享

  • 對上建立的對象可被全部線程共享引用;
  • 可訪問對象,就能夠訪問對象內的成員變量;
  • 若是兩個線程調用同一個對象上的某個方法,它們分別保留該方法的局部變量的拷貝;
  • JMM例子:
    • 一個基本類型的局部變量,一直被保存在 線程棧
    • 局部變量引用的對象,保存在 線程棧 中,對象自己存在 堆 
    • 對象可能包含方法,這些方法可能包含局部變量。 這些局部變量存儲在線程棧上,而且該方法所屬的對象存儲在_堆
    • 對象的原始成員變量存儲在堆上。 若是一個成員變量是一個對象的引用,它將被存儲在堆
    • 靜態類變量保存在堆上

更多內容參考  zejiang的博客數據結構

除了堆棧外,咱們還須要JVM使用的本地方法棧、PC代碼行號治時期 和 用於存儲被VM加載的類信息、常量、靜態變量等的Method Area多線程

## Java垃圾回收機制併發

【內存回收的三種方式】框架

    ①靜態模式下的內存回收:在靜態內存分配模式下,無需進行內存回收:全部都是已肯定的。post

    ②在棧模式下的內存回收:按block(某個方法)總體進行性能

    ③在堆模式下的內存回收:在heap上進行內存空間回收,最複雜——沒法提早預知某個object是否已經變得無用。

 

 【動態垃圾回收 相關概念】轉自 長安蒹葭的博客

  • GC(Garbage Collection):識別垃圾並釋放其佔用的內存
    • 垃圾回收器根據對象的「活性」(從root的可達性)來決定是否回收該對象的內存,」死「的對象是須要回收的垃圾
  • Root
    • 根集合由root對象和局部對象構成
    • root對象:Class(不能被回收)、Thread、Java方法/接口的本地變量或參數、全局接口引用等
  • 可達/不可達對象(Reachable/Unreachable):free模式
    • 從根能夠直接或間接到達的對象爲可達的,不然爲不可達的
    • 從根開始,不斷將指向的對象加入活動集,剩下的是垃圾
  • 活動/死亡對象(Live/dead):
    • 在stack和free的結合模式下,對象的引用被視爲有向圖,能夠從根訪問的對象爲活動對象,不然爲死亡對象。

【GC的四種算法】

  • 引用計數
    • 基本思想:爲每一個object存儲一個計數RC,當有其餘 reference指向它時,RC++;當其餘reference與其斷開時,RC--;如 果RC==0,則回收它。
    • 優勢:簡單、計算代價分散,「幽靈時間」短 爲0
    • 缺點:不全面(容易漏掉循環引用的對象)、併發支 持較弱、佔用額外內存空間、等
  • Mark-Sweep(標記-清除)算法
    • 基本思想:爲每一個object設定狀態位(live/dead)並記錄,即mark階段;將標記爲dead的對象進行清理,即sweep可階段。
    • 優勢:能夠處理循環調用,指針操做無開銷,對象不變
    • 缺點:複雜度爲O(heap),高 堆的佔用比高時影響性能,容易形成碎片,須要找到root

  • Copying(複製)算法
    • 基本思想:爲了解決Mark-Sweep算法的缺陷,Copying算法就被提了出來。它將可用內存按容量劃分爲大小相等的兩塊,每次只使用其中的一塊。當這一塊的內存用完了,就將還存活着的對象複製到另一塊上面,而後再把已使用的內存空間一次清理掉,這樣一來就不容易出現內存碎片的問題。
    • 優點:運行高效、不易產生內存碎片
    • 缺點:複製花費大量的時間,犧牲內存空間

  • Mark-Compact(標記-整理)算法
    • 基本思想:爲了解決Copying算法的缺陷,充分利用內存空間,提出了Mark-Compact算法。該算法標記階段和Mark-Sweep同樣,可是在完成標記以後,它不是直接清理可回收對象,而是將存活對象都向一端移動,而後清理掉端邊界之外的內存。

 

 

## JVM中的GC

關於該部分的內容 請參考  Java 內存區域和GC機制

Java GC將堆分爲不一樣的區域,各區域採用不一樣的GC策略,以提升GC的效率

(使用「-verbose:gc」在控制檯或日誌文件中輸出JVM進行GC的全過程)

  • Java內存分配和回收的機制歸納的說,就是:分代分配,分代回收。
  • 對象將根據存活的時間被分爲:年輕代(Young Generation)、年老代(Old Generation)、永久代(Permanent Generation,也就是方法區)

 

  • 年輕代:
    • 對象被建立時,內存的分配首先發生在年輕代(大對象能夠直接 被建立在年老代)
    • 大部分的對象在建立後很快就再也不使用,所以很快變得不可達,因而被年輕代的GC機制清理掉(IBM的研究代表,98%的對象都是很快消 亡的)
    • 爲減小GC代價,使用copying算法
    • 具體過程
      1. 絕大多數剛建立的對象會被分配在Eden區,其中的大多數對象很快就會消亡。Eden區是連續的內存空間,所以在其上分配內存極快;
      2. 當Eden區滿的時候,執行Minor GC,將消亡的對象清理掉,並將剩餘的對象複製到一個存活區Survivor0(此時,Survivor1是空白的,兩個Survivor總有一個是空白的);
      3. 此後,每次Eden區滿了,就執行一次Minor GC,並將剩餘的對象都添加到Survivor0;
      4. 當Survivor0也滿的時候,將其中仍然活着的對象直接複製到Survivor1,之後Eden區執行Minor GC後,就將剩餘的對象添加Survivor1(此時,Survivor0是空白的)。
      5. 當兩個存活區切換了幾回(HotSpot虛擬機默認15次,用-XX:MaxTenuringThreshold控制,大於該值進入老年代)以後,仍然存活的對象(其實只有一小部分,好比,咱們本身定義的對象),將被複制到老年代。
  • 年老代:
    • 對象若是在年輕代存活了足夠長的時間而沒有被清理掉,則會被複制到年老代,年老代的空間通常比年輕代大,能存放更多的對象,在年老代上發生的GC次數也比年輕代少。
    • 使用Mark-Sweep或Mark-Compact算法;
    • Minor GC和full GC獨立進行,減少代價;
    • 當perm generation滿了以後,沒法存儲更多的元數據,也啓動full GC。

 

 

 ## GVM GC性能調優

  • 儘量減小GC時間,通常不超過程序執行時間的5%
  • 一旦初始分配給程序的內存滿了,就拋出內存溢出異常,
  • 在啓動程序時,可爲其配置內存分配的具體大小

  • 堆的大小決定着VM將會以何種頻度進行GC、每次GC的時間多長。
    • 這兩個指標具體取值多少爲「優」,須要針對特定應用進行分析。
    • 較大的heap會致使較少發生GC,但每次GC時間很長
    • 若是根據程序須要來設置heap大小,則須要頻繁GC,但每次GC的時間較短
  •  設定堆的大小的具體方法
    • Xmx/-Xms:指定年輕代和老年代空間的初始值和最大值;Xms小於Xmx時,年輕代和老年代所消耗的空間量能夠根據應用程序的需求增加或收縮;Java堆的增加不會比Xms大,也不會比Xmx小
    • XX: NewSize=<n>[g|m|k]:年輕代空間的初始和最小尺寸,<n>是大小,[g | m | k]指示大小是否應解釋爲千兆字節,兆字節或千字節
    • XX: MaxNewSize=<n>[g|m|k]:年輕代空間的最大值
    • Xmn<n>[g|m|k]:將年輕代的初始值、最小值、最大值設爲同一值
  • GC模式選擇
    • 增加或收縮年輕代或老年代的空間時須要Full GC
    • Full GC可能會下降吞吐量並致使超出指望的延遲
    • 串行收集器(-XX:+UseSerialGC):使用單個線程執行全部垃圾收集工做
    • 並行收集器(-XX:+UseParallelGC):並行執行Minor GC,顯著減小垃圾收集開銷
    • 併發低暫停收集器(-XX:+UseConcMarkSweepGC):收集持久代,與執行應用程序同時執行大部分收集,在收集期間會暫停一小段時間
    • 增量低暫停收集器(-XX:+UseTrainGC):收集每一個Minor的部分老年代,並儘可能減小Major的大停頓
    • -verbose:gc:打印GC信息
相關文章
相關標籤/搜索