天天看點

jvm 工具 類加載機制Dart文法

jmap -heap < pid>  列印堆的使用情況

D:\後端開發\hsa-mbs-nation-bj-svc>jmap -heap 3556
Attaching to process ID 3556, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.131-b11

using thread-local object allocation.
Parallel GC with 8 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 1073741824 (1024.0MB)
   NewSize                  = 44564480 (42.5MB)
   MaxNewSize               = 357564416 (341.0MB)
   OldSize                  = 89653248 (85.5MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 216006656 (206.0MB)
   used     = 102553440 (97.80258178710938MB)
   free     = 113453216 (108.19741821289062MB)
   47.476981450053096% used
From Space:
   capacity = 56098816 (53.5MB)
   used     = 0 (0.0MB)
   free     = 56098816 (53.5MB)
   0.0% used
To Space:
   capacity = 73400320 (70.0MB)
   used     = 0 (0.0MB)
   free     = 73400320 (70.0MB)
   0.0% used
PS Old Generation
   capacity = 716177408 (683.0MB)
   used     = 716104952 (682.9309005737305MB)
   free     = 72456 (0.06909942626953125MB)
   99.98988295369406% used

101213 interned Strings occupying 11923408 bytes.


           

類從被加載到虛拟機記憶體中開始,到解除安裝出記憶體為止,它的整個生命周期

包括:加載(Loading)、驗證(Verification)、準備(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和解除安裝(Unloading)7個階段。

其中驗證、準備、解析3個部分統稱為連接配接(Linking)

于初始化階段,虛拟機規範則是嚴格規定了有且隻有5種情況必須立即對類進行“初始化”(而加載、驗證、準備自然需要在此之前開始):

1)遇到new、getstatic、putstatic或invokestatic這4條位元組碼指令時,如果類沒有進行過初始化,則需要先觸發其初始化。生成這4條指令的最常見的Java代碼場景是:使用new

關鍵字執行個體化對象的時候、讀取或設定一個類的靜态字段(被final修飾、已在編譯期把結果放入常量池的靜态字段除外)的時候,以及調用一個類的靜态方法的時候。

2)使用java.lang.reflect包的方法對類進行反射調用的時候,如果類沒有進行過初始化,則需要先觸發其初始化。

3)當初始化一個類的時候,如果發現其父類還沒有進行過初始化,則需要先觸發其父類的初始化。

4)當虛拟機啟動時,使用者需要指定一個要執行的主類(包含main()方法的那個類),虛拟機會先初始化這個主類。

5)當使用JDK1.7的動态語言支援時,如果一個java.lang.invoke.MethodHandle執行個體最後的解析結果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且這個方法句柄所對應的類沒有進行過初始化,則需要先觸發其初始化。注意:對于靜态字段,隻有直接定義這個字段的類才會被初始化,是以通過其子類來引用父類中定義的靜态字段,隻會觸發父類的初始化而不會觸發子類的初始化。常量HELLOWORLD,但其實在編譯階段通過常量傳播優化,已經将此常量的值“helloworld”存儲到了NotInitialization類的常量池中,以後NotInitialization對常量ConstClass.HELLOWORLD的引用實際都被轉化為NotInitialization類對自身常量池的引用了。也就是說,實際上NotInitialization的Class檔案之中并沒有ConstClass類的符号引用入口,這兩個類在編譯成Class之後就不存在任何聯系了。

加載階段虛拟機需要完成以下3件事情:

1)通過一個類的全限定名來擷取定義此類的二進制位元組流。

2)将這個位元組流所代表的靜态存儲結構轉化為方法區的運作時資料結構。

3)在記憶體中生成一個代表這個類的java.lang.Class對象,作為方法區這個類的各種資料的通路入口。

驗證是連接配接階段的第一步,這一階段的目的是為了確定Class檔案的位元組流中包含的資訊符合目前虛拟機的要求,并且不會危害虛拟機自身的安全。但從整體上看,驗證階段大緻上會完成下面4個階段的檢驗動作:檔案格式驗證、中繼資料驗證、位元組碼驗證、符号引用驗證。

準備階段是正式為類變量配置設定記憶體并設定類變量初始值的階段,這些變量所使用的記憶體都将在方法區中進行配置設定。這個階段中有兩個容易産生混淆的概念需要強調一下,首先,這時候進行記憶體配置設定的僅包括類變量(被static修飾的變量),而不包括執行個體變量,執行個體變量将會在對象執行個體化時随着對象一起配置設定在Java堆中。其次,這裡所說的初始值“通常情況”下是資料類型的零值,假設一個類變量的定義為:publicstaticintvalue=123;那變量value在準備階段過後的初始值為0而不是123,因為這時候尚未開始執行任何Java方法,而把value指派為123的putstatic指令是程式被編譯後,存放于類構造器<clinit>()方法之中,是以把value指派為123的動作将在初始化階段才會執行。表7-1列出了Java中所有基本資料類型的零值。假設上面類變量value的定義變為:publicstaticfinalintvalue=123;編譯時Javac将會為value生成ConstantValue屬性,在準備階段虛拟機就會根據ConstantValue的設定将value指派為123。

解析階段是虛拟機将常量池内的符号引用替換為直接引用的過程類初始化階段是類加載過程的最後一步,前面的類加載過程中,除了在加載階段使用者應用程式可以通過自定義類加載器參與之外,其餘動作完全由虛拟機主導和控制。

到了初始化階段,才真正開始執行類中定義的Java程式代碼在準備階段,變量已經賦過一次系統要求的初始值,而在初始化階段,則根據程式員通過程式制定的主觀計劃去初始化類變量和其他資源,或者可以從另外一個角度來表達:初始化階段是執行類構造器<clinit>()方法的過程。<clinit>()方法是由編譯器自動收集類中的所有類變量的指派動作和靜态語句塊(static{}塊)中的語句合并産生的,編譯器收集的順序是由語句在源檔案中出現的順序所決定的。<clinit>()方法對于類或接口來說并不是必需的,如果一個類中沒有靜态語句塊,也沒有對變量的指派操作,那麼編譯器可以不為這個類生成<clinit>()方法。虛拟機會保證一個類的<clinit>()方法在多線程環境中被正确地加鎖、同步,如果多個線程同時去初始化一個類,那麼隻會有一個線程去執行這個類的<clinit>()方法,其他線程都需要阻塞等待,直到活動線程執行<clinit>()方法完畢。如果在一個類的<clinit>()方法中有耗時很長的操作,就可能造成多個程序阻塞

static表示“全局”或者“靜态”的意思,用來修飾成員變量和成員方法,也可以形成靜态static代碼塊。

static修飾的變量習慣稱為靜态變量,static修飾的方法稱為靜态方法,static修飾的代碼塊叫做靜态代碼塊。

static的意義在于友善在沒有建立對象的情況下來進行調用(方法/變量)。

靜态的優缺點

對對象的共享資料提供單獨空間的存儲,節省空間,沒有必要每一個對象都存儲一份

可以直接被類名調用,不用在堆記憶體建立對象

靜态成員可以通過類名直接通路,相對建立對象通路成員友善

弊端:

通路出現局限性。(靜态雖好,但隻能通路靜态)

而static方法是編譯時靜态綁定的。tatic方法跟類的任何執行個體都不相關,

Dart文法

一、static、final、const

static

表示成員在類本身上可用,而不是在類的執行個體上。 這就是它的意思,并沒有用于其他地方。 static修飾成員。

final

表示單一指派:final變量或字段必須初始化。 一旦指派,就不能改變final變量的值。 final修飾變量。

const

含義在Dart中有點複雜和微妙。 const修飾值。 您可以在建立集合時使用它,例如const [1,2,3] ,以及構造對象(代替new),比如const Point(2,3)。這裡,const意味着對象的整個深度狀态可以在編譯時完全确定,并且對象将被當機并完全不可變。

二、const對象的屬性和限制

  1. 必須根據可在編譯時計算的資料建立它們。 const對象無法通路運作時需要計算的任何内容。 1 + 2是一個合法的const表達式,但new DateTime.now()不是(合法的const表達式)。
  2. 它們是深層不能改變的。 如果你有包含集合的final字段,則該集合仍然可變。 如果你有一個const集合,那麼它中的所有東西也必須是遞歸的const。
  3. 它們是規範化的。 這有點像字元串内聯:對于任何給定的const值,無論const表達式被計算多少次,都将建立并重用單個const對象。 也就是說:
getConst() => const [1, 2];
main() {
  var a = getConst();
  var b = getConst();
  print(a === b); // true
}
           

我認為Dart在保持語義和關鍵字清晰明确方面做得非常好。 (曾經有一段時間将const用于const和final。這令人困惑。)

唯一的缺點是,當你想指出一個單一指派的成員和類本身時,你必須使用兩個關鍵字:static final。