App下載

詳解Java虛擬機是如何進行調(diào)優(yōu)的

猿友 2021-08-03 11:32:55 瀏覽數(shù) (2396)
反饋

    Java 虛擬機(JVM)的存在,才使得 Java 能夠在各個平臺運行,具備跨平臺的特性。Java 既然是在 JVM 中編譯執(zhí)行的,那么有沒有方法優(yōu)化 JVM,使得 Java 的運行效率再次提升呢?下面,我將和大家分享關(guān)于 JVM 的調(diào)優(yōu)方法的介紹。

    JVM常用命令行參數(shù)

    1. 查看參數(shù)列表

    虛擬機參數(shù)分為基本和擴展兩類,在命令行中輸入 JAVA_HOMEinjava就可得到基本參數(shù)列表。
    在命令行輸入 JAVA_HOMEinjava –X就可得到擴展參數(shù)列表。

    2. 基本參數(shù)說明:

    • -client,-server: 兩種Java虛擬機啟動方式,client模式啟動比較快,但是性能和內(nèi)存管理相對較差,server模式啟動比較慢,但是運行性能比較高,windos上采用的是client模式,Linux采用server模式
    • -classpath -cp: 虛擬機在運行一個類的時候,需要將其轉(zhuǎn)入內(nèi)存中,虛擬機搜索類的方式和順序:Bootstrap classes、Extension classes、User classes。
    • Bootstrap 中的路徑是虛擬機自帶的 jar 或 zip 文件,虛擬機首先搜索這些包文件,用System.getProperty(“sun.boot.class.path”)可得到虛擬機搜索的包名。
    • Extension 是位于 jrelibext 目錄下的 jar 文件,虛擬機在搜索完 Bootstrap 后就搜索該目錄下的 jar 文件。用 System. getProperty("java.ext.dirs”)可得到虛擬機使用
    • Extension 搜索路徑。
    • User classes 搜索順序為當(dāng)前目錄、環(huán)境變量 CLASSPATH、-classpath。
    • -classpath: 告訴虛擬機搜索目錄名、jar文檔名、zip文檔名
    • -verbose[:class|gc|jni]: 在輸出設(shè)備上顯示虛擬機運行信息
    • -verbose:class 輸出虛擬機裝入的類的信息
    • -verbose:gc 在虛擬機發(fā)生內(nèi)存回收時輸出設(shè)備信息,用來監(jiān)視虛擬機內(nèi)存回收的情況
    • -verbose:jni 虛擬機調(diào)用native方法時輸出設(shè)置顯示信息,用來監(jiān)視虛擬機調(diào)用本地方法的情況
    • -version:顯示可運行的虛擬機版本信息
    • -showversion:顯示版本信息以及幫助信息

    3. 擴展參數(shù)說明:

    • -Xmixed: 設(shè)置 -client 模式虛擬機對使用頻率高的方式進行 Just-In-Time 編譯和執(zhí)行,對其他方法使用解釋方式執(zhí)行,該方式是虛擬機缺省模式
    • -Xint: 設(shè)置-client模式下運行的虛擬機以解釋方式執(zhí)行類的字節(jié)碼,不將字節(jié)碼編譯為本機碼,有可能會損失性能
    • -Xbootclasspath:path、-Xbootclasspath/a:path、-Xbootclasspath/p:path: 改變虛擬機裝載系統(tǒng)運行包 rt.jar,而從-Xbootclasspath 中設(shè)定的搜索路徑中裝載系統(tǒng)運行類。除非你自己能寫一個運行時,否則不會用到該參數(shù)。
    • /a:將在缺省搜索路徑后加上 path 中的搜索路徑。
    • /p:在缺省搜索路徑前先搜索 path 中的搜索路徑。
    • -Xnoclassgc: 關(guān)閉虛擬機對 class 的垃圾回收功能,有可能會導(dǎo)致OutOfMemoryError
    • -Xincgc: 啟動增量垃圾收集器,缺省是關(guān)閉的,增量垃圾收集器能減少偶然發(fā)生的長時間的垃圾回收造成的暫停時間,但增量垃圾收集器和應(yīng)用程序并發(fā)執(zhí)行,會占用部分CPU在應(yīng)用程序上的功能
    • -Xloggc:file: 將虛擬機每次垃圾回收的信息寫到日志文件中,文件名由file指定,文件格式是平文件,內(nèi)容和-verbose:gc 輸出內(nèi)容相同
    • -Xms20M: 設(shè)置虛擬機可用內(nèi)存堆的初始大小為20M,大小為1024的整數(shù)倍并且要大于1MB,可用K或者M為單位來設(shè)置較大內(nèi)存數(shù),初始堆大小為2MB,例如:-Xms256M
    • -Xmx20M: 設(shè)置虛擬機內(nèi)存堆的最大可用大小,該值必須為1024的整數(shù)倍,并且要大于2MB,可用K或者M為單位來設(shè)置較大的內(nèi)存數(shù),例如:-Xmx81920K,-Xmx80M,當(dāng)應(yīng)用程序申請了大內(nèi)存運行時虛擬機拋出 java.lang.OutOfMemoryError,就需要使用-Xmx來設(shè)置了
    • -Xss128K: 設(shè)置線程棧的大小,和-Xmx類似,可以用K或M來設(shè)置較大的值,也可以在Java中創(chuàng)建線程對象時設(shè)置棧的大小
    • -Xoss128k: 設(shè)置本地方法棧的大小為128K,不過HotSpot不區(qū)分虛擬機棧和本地方法棧,所以這個參數(shù)對于HotSpot是無效的
    • -XX:PermSize=10M: 表示JVM初始分配的永久代的容量,必須以M為單位
    • -XX:MaxPermSize=10M: 表示JVM允許分配的永久代的最大容量,必須以M為單位,大部分情況下這個參數(shù)默認為64M
    • -XX:NewRatio=4: 表示設(shè)置年輕代:老年代的大小比值為1:4,這意味著年輕代占整個堆的1/5
    • -XX:SurvivorRatio=8: 表示設(shè)置2個Survivor區(qū):1個Eden區(qū)的大小比值為2:8,這意味著Survivor區(qū)占整個年輕代的1/5,這個參數(shù)默認為8
    • -Xmn20M: 表示設(shè)置年輕代的大小為20M
    • -XX:+HeapDumpOnOutOfMemoryError: 表示可以讓虛擬機在出現(xiàn)內(nèi)存溢出異常時Dump出當(dāng)前的堆內(nèi)存轉(zhuǎn)儲快照
    • -XX:+UseG1GC: 表示讓JVM使用G1垃圾收集器
    • -XX:+PrintGCDetails: 表示在控制臺上打印出GC具體細節(jié)
    • -XX:+PrintGC: 表示在控制臺上打印出GC信息
    • -XX:PretenureSizeThreshold=3145728 表示對象大于3145728(3M)時直接進入老年代分配,這里只能以字節(jié)作為單位
    • -XX:MaxTenuringThreshold=1: 表示對象年齡大于1,自動進入老年代

    虛擬機參數(shù)分類

    標準: - 開頭,所有的HotSpot都支持
    非標準:-X 開頭,特定版本HotSpot支持特定命令
    不穩(wěn)定:-XX 開頭,下個版本可能取消

    例如:java -version、java -X

    import java.util.List;
    import java.util.LinkedList;
    
    public class HelloGC {
      public static void main(String[] args) {
        System.out.println("HelloGC!");
        List list = new LinkedList();
        for(;;) {
          byte[] b = new byte[1024*1024];
          list.add(b);
        }
      }
    }
    

    在這里插入圖片描述

    • 區(qū)分概念:內(nèi)存泄漏memory leak,內(nèi)存溢出out of memory
    • java -XX:+PrintCommandLineFlags HelloGC
    • java -Xmn10M -Xms40M -Xmx60M -XX:+PrintCommandLineFlags -XX:+PrintGC HelloGC
    • PrintGCDetails PrintGCTimeStamps PrintGCCauses
    • java -XX:+UseConcMarkSweepGC -XX:+PrintCommandLineFlags HelloGC
    • java -XX:+PrintFlagsInitial 默認參數(shù)值
    • java -XX:+PrintFlagsFinal 最終參數(shù)值
    • java -XX:+PrintFlagsFinal | grep xxx 找到對應(yīng)的參數(shù)
    • java -XX:+PrintFlagsFinal -version |grep GC

    調(diào)優(yōu)前的基礎(chǔ)概念

    1. 吞吐量: 用戶代碼時間 /(用戶代碼執(zhí)行時間 + 垃圾回收時間)
    2. 響應(yīng)時間: STW(Stop The World)越短,響應(yīng)時間越好

    所謂的調(diào)優(yōu),首先自己要明確,想要的是什么,是吞吐量還是響應(yīng)時間,還是在滿足一定的響應(yīng)時間的情況下,要求達到多大的吞吐量,一般來說根據(jù)業(yè)務(wù)類型去選擇對應(yīng)的調(diào)優(yōu)方式,比如網(wǎng)站需要的是響應(yīng)時間優(yōu)先,JDK1.8盡量選G1,那如果是數(shù)據(jù)挖掘的需要使用的是吞吐量。

    什么是調(diào)優(yōu)

    在沒有接觸過調(diào)優(yōu)之前我們理解的JVM調(diào)優(yōu)就是解決OOM問題,OOM只是JVM調(diào)優(yōu)的一部分

    一般是根據(jù)需求進行JVM規(guī)劃和預(yù)調(diào)優(yōu)優(yōu)化運行JVM運行環(huán)境(慢,卡頓)解決JVM運行過程中出現(xiàn)的各種問題(OOM)

    首先的話,調(diào)優(yōu)是從業(yè)務(wù)場景開始的,如果沒有業(yè)務(wù)場景的JVM調(diào)優(yōu)都是不靠譜的,比如有時間在實際項目中,有很多個類,成千上萬個代碼,你怎么知道具體是哪個代碼有問題,就算我們知道有段代碼頻繁的full gc,但是可能過一段時間就OOM了。

    1.調(diào)優(yōu)步驟:

    • 熟悉業(yè)務(wù)場景,選定垃圾回收器(沒有最好的垃圾回收器,只有最合適的垃圾回收器)
    • 響應(yīng)時間、停頓時間 [CMS G1 ZGC] (需要給用戶作響應(yīng))吞吐量 = 用戶時間 /( 用戶時間 + GC時間) [PS]
    • 選擇回收器組合
    • 計算內(nèi)存需求(設(shè)置內(nèi)存大小 1.5G 16G)
    • 選定CPU:越高越好
    • 設(shè)定年代大小、升級年齡
    • 設(shè)定日志參數(shù)

    -Xloggc:/opt/xxx/logs/xxx-xxx-gc-%t.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=20M -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCCause或者每天產(chǎn)生一個日志文件

    在生產(chǎn)環(huán)境中日志文件,后面日志名字,按照系統(tǒng)時間產(chǎn)生,循環(huán)產(chǎn)生,日志個數(shù)五個,每個大小20M,這樣的好處在于整體大小100M,能 控制整體文件大小

    觀察日志情況

    2.調(diào)優(yōu)案例

    2.1案例一

    垂直電商,最高每日百萬訂單,處理訂單系統(tǒng)需要什么樣的服務(wù)器配置

    這個問題比較雞肋,因為很多不同的服務(wù)器配置都能夠支撐

    比如有一百萬個訂單,每個小時不會產(chǎn)生很高的并發(fā)量,我們尋找高峰時間,做一個假設(shè)100W訂單有72W訂單在高峰期產(chǎn)生,比如一個小時平均36W訂單,所以我們內(nèi)存選擇大小是按照巔峰時間選擇的,很多時間我們可能就是去做壓測,實在不行就添加CPU和內(nèi)存

    2.2案例二

    12306遭遇春節(jié)大規(guī)模搶票應(yīng)該如何支撐

    訂單信息每天固定,可以丟到緩存中,不同的業(yè)務(wù)邏輯有不同的業(yè)務(wù)設(shè)計,12306應(yīng)該是中國并發(fā)量最大的秒殺網(wǎng)站,號稱并發(fā)100W,就是說每秒進行百萬次的業(yè)務(wù)邏輯的處理,估計淘寶一年最高的是54W并發(fā)。

    如果解決這個問題呢,看下面所示:

    CDN -》 LVS -》 NGINX -》 業(yè)務(wù)系統(tǒng) -》每臺機器1W并發(fā)

    普通電商的下單流程一般是:

    訂單 -》下單-》 訂單系統(tǒng)減庫存 -》 等待用戶付款

    這個事務(wù)如果同步的方式完成,TPS是支撐不了多長時間的

    但是在12306里面的模型是

    下單-》 減庫存和訂單同時異步進行 -》 等待付款

    異步是當(dāng)你下完訂單之后,它一個線程去減庫存,另外一個線程直接把你下單的信息扔到kafka或者redis里面直接返回OK,你下單成功后等待你付款,什么時候你付款完成后面那些個訂單處理線程就會去里面拿數(shù)據(jù),這個處理完了就會持久化到Hbase或者是mysql,一般大流量的處理方法核心思想就是:分而治之

    JVM優(yōu)化

    比如我有一個50萬PV的資料類網(wǎng)站(從磁盤提取文檔到內(nèi)存)原服務(wù)器32位,1.5G的堆,用戶反饋網(wǎng)站比較緩慢,如果對它進行升級,新服務(wù)器64位,16G的堆內(nèi)存,用戶還是反饋卡頓,而且還比之前更嚴重,這個是因為什么呢?一般來說很多用戶去瀏覽數(shù)據(jù),很多數(shù)據(jù)會load到內(nèi)存中,導(dǎo)致內(nèi)存不足,頻繁的GC,STW時間過長,響應(yīng)時間就會變慢,那我們應(yīng)該怎么辦呢,使用 PS-> PN+CMS或者G1。

    還有一個就是系統(tǒng)CPU經(jīng)常100%,我們要如何進行調(diào)優(yōu)呢?

    首先我們可以想到CPU100%那么一定有線程在占用系統(tǒng)資源

    1.找出哪個進程CPU高 (top命令)

    2.該進程中的哪個線程CPU高(top -Hp)

    3.導(dǎo)出該線程的堆棧

    4.查找哪個方法(棧掙)比較消耗時間

    5.工作線程占比高 | 垃圾回收線程占比高

    總結(jié)

    今天我們只是講解了一些基本的操作,具體怎么操作該怎么辦呢?這一部分小農(nóng)會在下一部分中進行講解,今天主要帶大家了解一些常用的參數(shù),告訴大家怎么去使用和一些前置知識。

    到此這篇關(guān)于 Java 虛擬機進行調(diào)優(yōu)的具體方法的文章就介紹到這了,想要了解更多相關(guān) Java虛擬機的內(nèi)容請搜索W3Cschool以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,也希望大家以后多多支持!

    0 人點贊