java每秒执行一次_Java性能权威指南
[2020年5月15日]
開坑。左右看看,可能這本書更加適合作為休息讀物,不要求連續花長時間去理解內容,更加偏實踐。
書中示例代碼:https://github.com/scottoaks/javaperformancetuning
[2020年5月17日]
第1章 導論
概述
本書涉及的知識主要分兩類。1. 如何對Java虛擬機自身的性能進行調優,即如何通過jvm的配置來影響程序的各種性能指標;2. 理解Java平臺的特性對性能影響,包括Java語言(線程、同步等)和Java標準api(例如解析XML 性能)
jvm調優標志
JVM 主要接受2類調優標志:布爾標志和附帶參數的標志。
布爾標志采用如下語法:-XX:+FlagName 表示開啟, -XX:-FlagName表示關閉。
附帶參數的標志采用以下語法:-XX:FlagName=value, 表示將FlagName的值設置為value。
全面的性能調優
[2020年5月20日]
第2章 性能測試方法 主要討論了4條性能測試的原則。
原則1:測試真實的應用:在實際使用的場景中測試性能。性能測試分微基準測試、宏基準測試、介基準測試3種,各有優劣,選擇適用于實際應用的才能取得最好的效果。
微基準測試:測試微小代碼單元的性能,如同步、非同步比較,線程創建與線程池使用的代價比較等。需要注意的是,所測單元的代碼需要實際執行,而不能因為編譯優化導致被測代碼并為執行,如下例子:
public void doTest(){double l;long then=System.currentTimeMills();for(int i=0;i<nLoops;i++){l=fibnacci(50);}long now=System.currentTimeMills();System.out.println("time: "(now-then)/1000); }這段代碼被編譯優化后,如下
public void doTest(){double l;long then=System.currentTimeMills();long now=System.currentTimeMills();System.out.println("time: "(now-then)/1000); }因為它并為改變程序的任何狀態。將局部變量 l 的定義改變為實例變量(并用volatile聲明),就能測試這個方法的性能了。
使用合理的隨機數,不要使用測試程序以外的測試數據,如要測試階乘,卻傳入負數,使得程序拋出異常或者被if語句忽略。
盡量減少被測代碼模塊中的無效代碼,如println等輔助函數的調用。
熱身期問題:Java的一個特點是代碼執行的越多性能越好。微基準測試需要熱身期,否則測量的是編譯代碼而不是被測代碼的性能。
宏基準測試:測試應用程序的整體性能,而非某一段代碼。
介基準測試:測試某個功能某塊的代碼,如socket管理代碼、讀取請求模塊代碼等。
原則2:理解批處理時間、吞吐量、響應時間。
由于JIT(及時編譯)的存在,研究Java性能優化需要關注熱身期——在運行代碼足夠長的時間,已經編譯且優化之后再測性能。
批處理關注的是,從處理開始到結束時整體的性能。某個批處理前段時間性能<后段時間,這種情況并無大礙。
吞吐量測試:基于一段時間內所能完成的工作量,常見指標,每秒事務數TPS, 每秒請求數RPS,每秒操作數OPS。 在客戶端、服務端的測試中存在著“客戶端不能足夠快的向服務端發送數據”的問題。
響應時間:客戶端從發送請求至接收到響應之間流逝的時間。衡量方法:平均值,或者百分位數(超過90%響應時間是多少)。
原則3:統計方法應對性能變化
由于測試數據的隨機性、操作系統調度等原因,即使相同的代碼,性能也會有差別。對于性能的差異,利用 學生t檢驗 、置信度、顯著性等概念來比較測試結果。
因代碼改變而進行的測試——回歸測試。回歸測試中,原來的代碼成為基線代碼(baseline)、新代碼稱為試樣(specimen)。
原則4:盡早頻繁測試
盡早開發、盡早測試。
自動化測試、測試一切(各種信息采集,如CPU使用率、磁盤使用率、網絡使用率,內存使用率)、真實系統上運行。
第3章 Java性能調優工具箱
本章主要關注操作系統的性能分析工具和jdk提供的程序分析工具。
操作系統提供的工具包括:
CPU使用率:vmstat(virtual memory statistics)"vmstat 1" 每隔1s給出統計數據。CPU使用率表示程序以多高的效率使用CPU,所以數字越大,性能越好。man page 鏈接
CPU隊列:vmstat 命令。如果試圖運行的線程數超過了可用的CPU,性能就會下降。
性能檢查時,首先應該審查CPU時間;優化代碼的目的是提升而不是降低CPU使用率。
磁盤使用率:iostat(input/output statistics)"iostat -xm 2 6" 每隔2秒計算一次,計算6次,m表示結果按照megabite顯示。 man page。
網絡使用率:
* netstat 顯示網絡連接、routing table、網絡接口統計數據、多播成員組;https://linux.die.net/man/8/netstat
* nicstat:打印網絡traffic statistics。nicstat 5 每隔5s統計一次數據。區別帶寬代碼bps和其他使用單位Bps(字節每秒)https://docs.oracle.com/cd/E86824_01/html/E54763/nicstat-1.html
網絡無法支持100%使用率,對于局域網來說,承受的網絡使用率超過40%就意味著接口飽和了。
[2020年5月28日]
這一章主要介紹了Linux 系統性能監控工具(上面已經介紹)&&Java application&&JVM 性能監控工具(如下) 以及一些優化思想。
java 性能監控工具:jcmd、jconsole、jhat、jmap、jinfo、jstack、jstat、jvisualvm。可以用man命令看如何使用。
這些工具的功能可以概括為:
- 基本VM信息
- 線程信息
- 類信息
- 實時GC分析
- 堆轉儲的事后處理
- JVM性能分析
還介紹了采樣分析與探查分析。重要思想方法:
- 所有性能分析必然對應用原來的性能產生影響。
- 采樣分析可能發生嚴重偏差(采樣點存在bias,比如兩個線程交叉執行,每次采樣都是threadB,由此得出threadB執行時間更長)
- 找到用時最長的系統性原因,而不是局限于某一個、幾個用時最長的線程的優化
Java任務控制,jmc 命令,對Java程序有更精細的統計數據,但是需要在運行Java程序的時候添加啟用jmc的標志。Java flight recorder JFR是jmc的核心特征。
[2020年6月10日]
之前看的是第4章 JIT 編譯器。 這一章主要介紹了JVM編譯器以及優化執行相關知識。
- JVM 有兩種編譯器類型、多種版本、三種編譯類型
- client編譯器(C1編譯器)和server 編譯器(C2編譯器)
- 有32位、64位不同版本(和平臺有關,并非所有編譯器類型在各個尋址大小下都有)
- 僅使用client編譯(只有在C1編譯器下才有效,c2編譯器上只能server編譯),僅使用server編譯(只能在C2編譯器下進行),分層編譯(先c1后c2,只能在C2編譯器下進行)。Java8中默認開啟分層編譯。
- client編譯策略:編譯開啟時間早,在任務運行的早起開始階段速度較快
- server編譯策略:編譯開啟的晚,在任務過了熱身階段之后,對熱代碼(高頻執行的)編譯優化,使得整個熱身之后的執行速度都比client編譯要快
- 分層編譯:早期使用client編譯,之后使用server編譯
編譯優化基于兩種JVM 計數器:方法調用計數器和方法中的循環回邊計數器。方法計數器超過閾值,該方法被放入編譯隊列進行編譯(這個編譯與代碼執行是異步的,這個過程一般叫做標準編譯),在方法下次被調用時執行編譯后的代碼。循環回邊計數器超過閾值,執行循環的代碼也會被編譯。這部分被編譯的代碼會替換還在棧上的代碼(叫做棧上替換,Over Stack Replacement, OSR),下次循環迭代時使用編譯后的代碼。
可用-XX:+PrintCompilation選項,每次編譯一個方法或者循環時,JVM就會打印一行被編譯的內容信息。jstat 也能檢測編譯器內部工作情況。
短小、被頻繁調用的代碼會被直接內聯執行,代碼內聯是默認開啟的。如getter、setter會被自動內聯。
高級編譯優化還包括:
- 逆優化
- 代碼被棄置:新編譯策略生成的代碼會使得之前運行的代碼被丟棄
- 逆向化僵尸代碼:被棄置的類長期不被調用會被標記為僵尸代碼,然后他們可以被從代碼緩存中移除。
- 分層編譯級別,共5種執行級別。client 編譯器有3中級別
- 0:解釋代碼
- 1:簡單C1編譯代碼
- 2:受限的C1編譯代碼
- 3:完全C1編譯代碼
- 4:C2編譯代碼
代碼期望編譯順序為:級別0 --》 3--》4 。按照這個順序性能可以達到最優。
當編譯級別4中server隊列滿了,就會從server隊列中取出方法,以級別2進行編譯,C1編譯器使用方法調用計數器和回邊計數器。當方法的調用計數器達到閾值之后就被編譯為級別3,最終當server 編譯器隊列不太忙的時候就被編譯為級別4.
如果方法經常被編譯為級別2,并且還有額外的可用CPU周期,那就可以考慮增加編譯器線程數,從而減少server編譯器隊列的長度。
從調優的角度看,簡單的選擇就是對所有應用都使用server編譯器和分層編譯,能夠解決90%與編譯器相關的性能問題。
有沒有final關鍵字,都不會影響應用的性能。
需要編譯的代碼在編譯隊列中,隊列中代碼越多,程序達到最佳性能時間越久。
[2020年6月21日]
近期主要看了 “第5章 JVM 垃圾收集入門”。
四個主流垃圾收集器:
- Serial收集器(常用于單CPU)
- Throughput(或者Parallel)收集器
- CMS(concurrent 收集器)
- G1收集器(concurrent收集器)
垃圾收集主要包含兩步:
- 查找不再使用的對象(如果僅僅用“不再被引用”作為判定對象是否使用,會有問題。比如一個不再使用的循環鏈表,永遠無法被釋放)
- 釋放這些對象占用的內存
邏輯上,JVM 線程分成兩類:應用程序線程,垃圾收集器線程。垃圾回收時移動內存中的對象,此時需要確保應用程序線程不再繼續使用這些對象。
時空停頓(stop-the-world):所有應用線程停止。
所有GC算法都將老年代分成:
- 老年代
- 新生代
- Eden
- survivor
新生代是堆的一部分,對象首先在新生代分配。新生代填滿時,垃圾收集器會暫停所有應用線程,回收新生代空間。不再使用的對象會被回收,仍然在使用的對象會被移動到其他地方,這種操作時Minor GC. 仍然使用的對象被移動到survivor空間或者老年代。所有算法對新生代進行垃圾回收時都存在“時空停頓”現象。
當老年代被填滿時,JVM需要找出老年代中不再被使用的對象,并對它們進行回收。——對老年代的垃圾回收方法,是垃圾收集算法差異最大的地方。
Full GC:停掉所有應用線程,找出不再使用的對象進行回收,接著對堆空間進行整理。——導致較大的停頓。
CMS和G1收集器以消耗更多CPU為代價,在應用程序線程運行的時候找出不再使用的對象。但是他們也會遭遇長時間的Full GC停頓。
Java提供System.gc()來強制進行GC。
四種垃圾收集算法:
- Serial收集器:常用語僅有單CPU可用以及其他程序會干擾GC的情況(通常是默認GC)
- Throughput收集器:在其他虛擬機上是默認值,它能最大化應用程序的總吞吐量,但是有些操作可能遇到較長的停頓。
- CMS收集器:能夠在應用線程運行的同時并行地對老年代的垃圾進行收集。如果CPU的計算能力足以支撐后臺垃圾收集線程的運行,該算法能夠避免應用程序發送full gc
- G1收集器:也能在應用程序運行的同時并發地對老年代的垃圾進行收集,在某種程度上能夠減少發生full gc的風險.G1的設計理念使得它比CMS更不容易遭遇full gc。
使用Throughput收集器處理應用程序的批量任務能最大程度地利用CPU處理能力,通常能獲得更好的性能。
如果批量任務并沒有使用機器上所有可用的CPU資源,那么切換到concurrent收集器往往能夠取得更好的性能。
CMS收集器和G1收集器之間的抉擇:一般情況下,堆空間小于4GB時,CMS收集器的性能比G1收集器好。使用大型堆或者巨型堆時,由于G1收集器可以分割工作,通常它比CMS收集器表現得更好。(G1收集器將老年代劃分成不同區域,使用更多的線程分擔掃描老年代空間的任務)。
GC調優基礎:
- 堆過小,導致頻繁gc;堆過大,每次gc停頓時間較長
- 調整堆大小首要原則是堆的容量<機器物理內存。一臺機器上有多個jvm實例時,它們堆大小之和<物理內存。除此之外,還有預留系統內存(通常是1G)
- 設置堆大小的參數,初始值(-Xms N設置),最大值(-Xmx N設置)。
- jvm發現如果使用初始堆的大小會頻繁發生gc,則它會嘗試增大堆的空間,直到jvm gc頻率回到正常范圍,或者達到堆的最大值。
- 調優時盡量考慮調整gc算法的性能目標,而非微調堆的大小,來提升性能
代空間的調整
- 所有用于調整代空間的命令行標志調整的都是新生代空間,新生代空間剩下的所有空間都被老年代占用。可以通過NewRatio、NewSize、MaxNewSize、XmnN等標志調整代空間大小。
永久代和元空間的調整
- 永久代(jdk 7以及以前)或者元空間(jdk8及以后)保存著類的元數據(并非類本體數據)。它以分離的堆的形式存在。
- 典型應用程序在啟動之后不需要載入新的類,這個區域的初始值可以依據所有類都加載后的情況設置。使用優化初始值能夠加速啟動過程。
- 開發中的應用服務器(或者任何需要頻繁重新載入類的環境)上經常能碰到由于永久代或元空間耗盡觸發的full gc,這時老的元數據會被丟棄回收。
自適應調整:
- jvm在堆的內部如何調整新生代和老年代的百分比是由自適應調整機制控制的
- 通常情況下,我們應該開啟自適應調整,因為垃圾回收算法依賴于調整后的代的大小來達到它停頓時間的性能目標。
垃圾回收監控工具
- 開啟GC 日志功能,-XX:+PrintGCDetails或者-XX:+PrintGCTimeStamps 或者-XX:+PrintGCDateStamps.
- 使用GC Histogram 工具分析
- 或者使用jconsole、jstat分析。
總結
以上是生活随笔為你收集整理的java每秒执行一次_Java性能权威指南的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: pdo插入mysql数据出错_php中通
- 下一篇: MySQL双主io线程起不来_解决mas