1. 程式人生 > 遊戲 >模擬經營遊戲《角鬥士公會經理》EA開啟 支援簡中

模擬經營遊戲《角鬥士公會經理》EA開啟 支援簡中

C和C++工程師掌握建立和銷燬記憶體空間的權力,並維護記憶體中每一個物件從始至終的生命。但Java工程師可以不再繁瑣的進行記憶體控制,並且更不容易出現記憶體洩露和溢位的問題,但如果不瞭解Java是如何自動對記憶體進行控制的,在出現問題後更難定位。

JVM記憶體執行時資料區域

JVM執行時會將所管轄的記憶體劃分為不同區域做不同功能,有的隨虛擬機器程序而生死,有的因執行緒而生死,虛擬機器規範規定記憶體中要包含以下幾個區域:

  • 程式計數器:較小空間的記憶體,用於記錄當前執行緒所執行的位元組碼檔案所在行號。通過改變這裡的行號來實現指令中的分支、迴圈、跳轉等操作。因為Java的多執行緒是多個執行緒輪流切換執行的,在執行另一執行緒時要讓來源執行緒記錄好自己所處的行號,所以每個執行緒都有自己獨立的程式計數器,相互之間不會干擾,這種每個執行緒獨有的空間就叫執行緒私有空間。當執行的是Natvie方法時,此空間記錄為空,並且此空間是唯一不存在記憶體溢位錯誤的空間。

  • 棧:執行緒私有且生命週期與執行緒相同,在執行每個方法時都會建立棧幀壓入到棧裡,方法的呼叫開始就是棧幀入棧,完成就是棧幀出棧。棧幀中記錄著方法的區域性變量表、操作棧、動態連結和方法出口等資訊。區域性變量表存放了編譯期間可知的基本型別和引用型別,還有就是returnAddress型別,表示一條位元組碼指令的地址。其中64位長度的變數資料將佔用兩個變數空間,其餘佔用一個變數空間。區域性變量表會在編譯期間完成分配,當進入一個方法開始指向的時候,這個方法所需要的區域性記憶體就已經確定了,不會再改變了。當方法呼叫深度過深,比如出現了自呼叫情況時,就會出現棧溢位異常,當然一些虛擬機器棧會動態擴大棧深度,當無法再獲取記憶體資源來擴充套件時會出現記憶體溢位異常。

  • 本地方法棧:跟虛擬機器棧很類似,採用同樣的儲存方式也會有同樣的異常,但是用來執行Native方法的,沒有規定其中方法所使用的語言,所以可以自由的實現它。有的虛擬機器例如HotSpot,就把虛擬機器棧和本地方法棧合二為一了。

  • 堆:堆是JVM中最大的空間,是所有執行緒都會共享的空間,將在虛擬機器啟動時建立,也就是與程序同生死。對裡存放物件的例項,所有的物件例項都將在這裡進行分配。垃圾回收也將主要在堆中執行,為了更快的回收還會把堆中的物件進行各種分類。堆是物理不連續的記憶體空間,只要邏輯上是連續的就可以,這類似磁碟空間。當堆沒法再去獲得空間將出現記憶體溢位異常。

  • 方法區:儲存被虛擬機器載入的類資訊、常量、靜態變數、即時編譯器編譯後的程式碼等,是執行緒共享的。雖然Java規範將其形容為堆的一個區域,但只是一個邏輯分割槽。HotSpot虛擬機器把方法區在垃圾回收中設定為永生代,但其他的虛擬機器並不存在永生代的說法,之所以這麼叫是因為其中的內容很少會被清理,清理起來也是比較的麻煩,Java官方Bug清單中就出現了很多因為此處空間導致的記憶體洩露。它像堆一樣是不連續的實體記憶體,可以固定或擴充套件大小,可以選擇不實現垃圾回收,當無法獲取記憶體時也會出現記憶體溢位異常。

  • 執行時常量池:它是方法區的一部分,Class檔案中除了類的版本、欄位、方法、介面等描述資訊外還有就是常量池,用於存放編譯期間生成的字面量和符號引用,在類載入後放入此處空間。JVM對Class檔案的每個部分都做了嚴格的規定,每個位元組儲存哪種資料都必須規範才會被JVM認可、裝載和執行,但對於常量池JVM規範並沒有特殊要求,不同的提供商可以自己實現這塊區域,所以通常除了存放字面量和符號引用外,直接引用也會存在這裡。Class檔案常量池必須具備動態性,也就是並不一定在編譯期間產生,執行時也可以放入新的常量。因為是方法區的一部分,所以當空間無法擴充套件也會出現記憶體溢位異常。

  • 直接記憶體:並不是JVM執行時記憶體的一部分,並且也並未在JVM規範中定義,但這塊記憶體也被頻繁使用並且也會出現記憶體溢位異常。JDK1.4中引入了NIO,非阻塞的IO方式,可以讓Native函式直接分配堆外的記憶體,然後通過堆裡的DirectByteBuffer物件作為引用指向這款記憶體,從而避免在堆和Native堆中切換複製資料,從而提高效能。堆外記憶體實在本機記憶體中的,雖然不會收到JVM的影響但空間和定址空間的限制。當記憶體區域大於實體記憶體限制的時候會導致記憶體溢位異常。