jvm系列筆記-命令列引數
阿新 • • 發佈:2019-12-31
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)空間,分配新物件時,預設從這個緩衝裡進行分配,這樣就解決了多執行緒同時分配物件時候的競爭問題。當這個分配緩衝用完了時候,執行緒會再去向堆申請緩衝,這時候才需要再進行同步操作。