1. 程式人生 > 程式設計 >jvm系列筆記-命令列引數

jvm系列筆記-命令列引數

java命令列

執行java程式

兩種用法:

  • 一種是使用-cp引數,然後傳入一大堆jar包,接著傳入主類,最後傳入程式引數。例如:
    • java -Dkey1=val1 -Dkey2=val2 -cp a.jar:b.jar:c.jar aaa.bbb.ccc.Main arg1 arg2 arg3
    • 上述的方式中,-D引數定義的是系統屬性,在java中可以通過System.getProperty(key1)的方式讀取,arg1 arg2 arg3定義的是程式引數,最後也就是main函式的args陣列引數。
  • 第二種是使用-jar引數,然後傳入一個jar包,接著就是程式引數。例如:
    • java -Dkey1=val1 -Dkey2=val2 -jar xxx.jar arg1 arg2 arg3
    • 這種方式只能使用一個jar包,並且jar包需要META_INF目錄下的manifest.MF檔案中定義了Main-Class,會自動去啟動這個main類

順便說一句,System.getEnv()獲取的是環境變數,就是通常意義上的程式的環境變數(jvm實質就是個程式),System.getProperty(key1)獲取的是系統屬性,這個是java自己定義的一種引數,arg1 arg2是程式引數,這個對應於main函式接收的引數。

虛擬機器器引數

執行模式、棧空間大小、棧幀的結構

-client -server

  • 表示虛擬機器器的執行模式,server模式的啟動慢,但是總體執行的效能比client高,優化更多(所以熱身時間長)

-Xss1024k

  • 表示執行緒的棧空間大小是1024k,執行緒的棧深度實際上是沒有限制的(可能有別的限制我不知道),之所以出現StackOverflow的異常,是因為棧空間的大小達到了限制。
  • 執行緒棧的每個元素都是棧幀,棧幀的大小不是固定的(每個方法呼叫對應一個棧幀)。所以如果棧幀大,那麼棧的深度就會小,棧幀小,棧的深度就會大(因為 棧空間 = 棧幀大小 * 棧深)。
  • 棧幀可以理解成一段記憶體,棧幀本身並不是棧式的結構,是可以在棧幀中進行隨機訪問的讀寫的。
  • 棧幀至少包含有區域性變量表運算元棧幀資料區

棧幀的結構

區域性變量表

就很簡單,我們的方法的引數、方法裡面宣告的區域性變數,都存在區域性變量表裡。區域性變數中,會有一堆插槽,一個插槽可以放一個資料(一個byte、一個char、一個short...等,以及一個reference)。虛擬機器器通過插槽索引來使用區域性變量表裡的資料(比如說引用插槽0,可能對應一個數字3,引用插槽2,可能對應一個物件的引用)

運算元棧

運算元棧是棧幀裡的一個棧(就是說,棧幀裡有好幾塊不同的區域,其中一塊區域作為一個棧來使用了,就是運算元棧)。主要是用來儲存計算的中間結果,以及作為計算過程中變數的臨時儲存空間(因為一些指令操作的模式都是入棧出棧然後計算)。

幀資料區

棧幀除了上面的資料之外,還需要一些資料來支援常量池解析,方法返回和異常處理等。比如我們返回值存放在哪裡,異常處理表存放在哪裡,這些都需要放在幀資料區,jvm還可以實現一些別的資料區存放在這裡。

棧上分配

這是個優化技術,基本思想是,對於不可能被其它執行緒訪問到的物件,可以打散分配在棧上。比如我們在方法內建立的一個臨時物件,接收物件引用的也是個區域性變數,物件也沒有傳遞給其它方法。那麼這個物件就不會發生逃逸,就可能會被以棧的方式進行分配。(棧空間很小,所以只能分配小物件)好處是棧幀被回收的時候,物件直接就會被回收了。

元空間引數

-XX:MaxMetaspaceSize=10m

  • 表示jvm使用的元空間大小是10M
  • 元空間中儲存的是類的資訊,比如類的欄位、方法、常量池等
  • 元空間所屬的記憶體是堆外記憶體。如果不指定大小,java8中預設沒有設定元空間上限,會在需要時增長直到耗盡系統記憶體。
  • 元空間越大,系統可以載入的類越多(因為載入的類的資訊都在這裡,所以一些需要動態代理的框架或程式對元空間會有一些消耗)

GC引數

-XX:+PrintGC

  • 列印GC資訊,當遇到GC後,JVM就會打印出GC資訊,如下圖
  • 有了GC資訊之後,我們就比較容易監控GC的用時和效果了

-XX:+PrintGCDetails

  • 列印詳細GC資訊,GC的資訊更加詳細和豐富。它會在JVM退出前,列印堆的詳細資訊。
  • PSYoungGen表示年輕代中的eden+from區
  • ParOldGen表示老年代
  • space 65536K,2% used [0x000000076ab00000,0x000000076acb4e98,0x000000076eb00000) 三個數字表示記憶體的下界、當前上界、上界。

-XX:+PrintHeapAtGC

  • 每次在GC後,都列印堆的詳細資訊,這個就比上面的detail更加詳細了。

-XX:+PrintGCTimeStamps

  • 每次列印GC日誌的時候,還要輸出時間資訊(系統啟動後的時間)。

-XX:+PrintGCApplicationConcurrentTime

  • 列印應用程式的執行時間(到達安全點safepoint的時間),一般是跟下面的引數一起使用

-XX:+PrintGCApplicationStoppedTime

  • 列印應用程式因為GC停頓的時間(stw機制)

-XX:+PrintReferenceGC

  • 列印引用相關的GC。這個可以跟蹤系統內的軟引用、弱引用、虛引用和Finallize佇列。

-Xloggc:log/gc.log

  • 預設情況GC日誌是直接在控制檯中輸出(標準輸出),使用這個引數可以將其輸出到我們指定的地方,這裡是說當前目錄的log目錄下的gc.log檔案中。

類資訊引數

-verbose:class 或 -XX:+TraceClassLoading 與 -XX:+TraceClassUnloading

  • 這兩類引數是等價的,都是跟蹤類的載入和解除安裝
  • -verbose:class 跟蹤類的載入和解除安裝
  • -XX:+TraceClassLoading 跟蹤類的載入
  • -XX:+TraceClassUnloading 跟蹤類的解除安裝

-XX:+PrintClassHistogram

  • 允許列印和檢視系統中類的分佈資訊
  • 開啟了這個引數後,在java的控制檯中按下Ctrl + Break,控制檯就會顯示當前的類資訊柱狀圖。如下顯示了當前佔用空間最多的類,例項個數、空間大小等

系統引數本身的相關引數

-XX:+PrintFlagsFinal

  • 列印所有系統引數

-XX:+PrintCommandLineFlags

  • 列印傳遞給虛擬機器器的顯式和隱式引數,隱式引數未必是命令列給的,可能是虛擬機器器啟動時自行設定的。

堆空間引數

-Xms10m -Xmx20m

  • 最小堆記憶體10m
  • 最大堆記憶體20m
  • 生產專案很多都是直接設定成一樣大的,直接全都設定成最大的記憶體。這樣一開始就是使用的最大記憶體,減少GC次數提升效能,並且防止記憶體的複製(擴大記憶體的時候可能會發生)

-Xmn3m

  • 設定新生代大小是3m
  • 一般設定為整個堆空間大小的1/3到1/4

-XX:SurvivorRatio=2

  • 表示新生代中,eden/from(以及eden/to)的比值是2
  • 比如說新生代40m記憶體,那麼eden區就是20m,from和to都是10m

-XX:NewRatio=3

  • 表示 老年代/新生代 的比值是3
  • 比如說一共堆記憶體是40m,那麼新生代10m,老年代30m

堆溢位處理引數

-XX:+HeapDumpOnOutOfMemoryError

  • 在記憶體溢位時,匯出整個堆的資訊

-XX:HeapDumpPath=d:/a.dump

  • 記憶體溢位時,匯出堆的資訊到這個檔案裡。與上面的引數配合使用
  • 匯出了dump檔案後,可以使用MAT工具進行分析

-XX:OnOutOfMemoryError=d:/tools/printstack.bat

  • 記憶體溢位的時候,執行這個指令碼
  • 一般用來報警、通知、儲存Thread Dump或者 Core Dump檔案來進行分析

直接記憶體引數

-XX:MaxDirectMemorySize=100m

  • 最大直接記憶體是100m
  • 這個值預設是跟堆記憶體的最大值一樣的

本地執行緒分配緩衝引數

-XX:+UseTLAB

  • 使用本地執行緒分配緩衝
  • 現在的JVM版本都是預設啟用(jdk1.4.2之後)
  • 每個執行緒在堆上都分配了一個本地執行緒分配緩衝(Thread Local Allocation Buffer)空間,分配新物件時,預設從這個緩衝裡進行分配,這樣就解決了多執行緒同時分配物件時候的競爭問題。當這個分配緩衝用完了時候,執行緒會再去向堆申請緩衝,這時候才需要再進行同步操作。