《Java性能權威指南》學習筆記

英文版《Java Performance The Definitive Guide》,出版於2014年,中文版出版於2016年,相比於《深入理解Java虛擬機》,講的更加透徹、準確。適合讀一遍,然後做長期參考。

作者Scott Oaks是Oracle公司的一位架構師,專注研究Oracle中間件軟件的性能。加入Oracle之前,他曾於Sun Microsystem公司任職多年,在多個技術領域都有建樹,包括SunOS的內核、網絡程序設計、Windows系統的遠程方法調用(RPC)以及 OPEN LOOK虛擬窗口管理器。1996年,Scott成爲Sun公司的Java佈道師,並於2001年加入Sun公司的Java性能小組——從那時起他就一直專注於Java的性能提升。此外,Scott也在O'Reilly出版社出版了多部書籍,包括Java Security、Java Threads、JXTA in a Nutshell和Jini in a Nutshell。

大約兩年半之前,根據同事推薦看了《深入理解Java虛擬機》,這是一本入門JVM的好書,但也發現看書的過程中,很多自己想要更深入、更準確、更廣泛瞭解的地方,還是要查詢互聯網,但是這本書,可以統統掃除那些疑惑。

看完這本書,大約用了1個月,斷斷續續,中間經過一個端午節,還速讀了7部小說《明朝那些事》,大致看了《南渡北歸》,掃了幾眼《人類簡史》,很多也是,看了,忘了,真是想找到一種克服遺忘的辦法。但是據說,克服遺忘最好的辦法是重複,重複,需要的是時間,於是也越發感覺時光如電。。。

關於遺忘,也可以從學習方法角度找下原因,此前有小總結:http://blog.csdn.net/puma_dong/article/details/45345397#t0


第一章 導論


本書的側重點是拓展知識的深度。本書的知識分爲兩大類:JVM自身的調優,Java平臺(既指Java語言,比如線程和同步,也指JavaAPI,比如XML解析性能)的特性對性能的影響。

請記住,JVM只佔整體性能的一小部分,更多的是操作系統、數據庫,其他系統,不過本書不是講整體分析的,本書假設性能瓶頸在Java。


第二章 性能測試方法


原則1:測試真實應用;

原則2:理解批處理流逝時間、吞吐量和響應時間

原則3:用統計方法應對性能的變化

原則4:儘早頻繁測試

本章的目的,是給出一些性能測試的理論和定量方法。


第三章 Java性能調優工具箱


欲善其事,先利其器。幾個月前做了幾個網線,不知道壓線是否通了,每條都試一下,真是麻煩,於是買了的測線儀,壓完就知道結果,方便多了。

看Java,看JVM,我們也需要這樣的工具。

1、看操作系統的:CPU、磁盤、網絡,用linux的操縱系統命令。

2、看Java的:線程、類、堆、棧,用jstack,jcmd等命令。

3、採樣、探查

4、JFR,Java飛行記錄儀,收費版JVM纔有的。

常用的Linux操作系統命令,以及Java性能相關的命令:jcmd、jstack、jmap、jstat、jinfo的常用參數,要純熟。


第四章 JIT編譯器


JIT編譯,我們的調優參數並不多,還是要學習熟悉一下。

以前看《深入理解Java虛擬機》時,想知道機器默認是使用client/server模式,查了很多資料,未必準確,而本章就有介紹。


第五章 垃圾收集入門


本章介紹了垃圾收集的入門知識。比如選擇GC算法,調整堆、永久代,控制併發等。

看過《深入理解Java虛擬機》,可能依然形成一些粗略瞭解形成的錯誤觀念。舉兩個例子。

1、Stop The World,請問新生代的GC,會STW嗎?老年代呢?

實際的結果是,所有的新生代GC算法,都是100%的STW,因爲時間短,一般都是幾毫秒~幾十毫秒左右,對於大部分應用都是可以接受的。

老年代GC算法,常用的CMS,會盡量不STW,只有在經過一定失敗條件,CMS無法繼續進行時,會退化成使用SerialOld,纔會STW,一次老年代GC,大約需要1秒,於存活對象大小成正比,如果老年代全部STW,這個1秒,真是無法承受。

我寫過一個測試代碼,驗證過STW,如下:

/**
 * java -Xms1000m -Xmx1000m Test2
 * 可以看到,每次垃圾回收(Young GC),都是Stop the world,導致方法的執行時間 > 15毫秒,增加的時間=Young GC耗費的時間
 */
public class Test2 {
	public static void main(String[] args) throws Exception {
		
		final List<Object> os = new ArrayList<Object>();
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				int i = 0;
				while(true) {
					for(int j=0;j<1000;j++)
						os.add(new java.util.concurrent.ConcurrentHashMap<String, String>());
					try {
						Thread.sleep(1);
					} catch (InterruptedException e) {
					}
					i++;
					if(i == 300) {
						os.clear();
						i = 0;
					}
				}
				
			}
		}).start();
		
		while(true) {
			
			long start = System.currentTimeMillis();
			Thread.sleep(15);
			long m = System.currentTimeMillis() - start;
			if(m>20)
				System.out.println(m);
		}
	}
}

2、System.gc

這行Java代碼,會不會導致GC。很多人說不會,可能是看過一些資料,或者人云亦云。

遇到這種問題,不用爭論,寫行代碼試驗一下就知道了,實踐出真知。

答案是:這行代碼會產生GC,回收所有的堆空間,新生代、老年代、永久代。通過 jstat -gccause -h10 pid 1000 可以清楚的看到。

技術是件很嚴肅的事情。


第六章 垃圾收集算法


詳細講了3類GC收集器的原理:以最大CPU利用率爲目的的Throughput(parallel),最小響應延遲爲目的的CMS,和CMS類似但是適用於大內存的G1。

一些高級調優選項。

在看本章的P116,“CMS收集器的永久代調優”時,從JVM角度消除了一個生產報警。場景描述如下:

有一個調用頻繁的服務,一天大約調用100萬次,每次都是通過反射,從Spring容器拿出一個對象(因爲這個服務有狀態,所以必須是prototype),經過代碼跟蹤,發現這種方式,在生成新對象的過程中,總會產生一個類似這樣的新類: sun.reflect.GeneratedSerializationConstructorAccessor10079,大約5個小時,256m的永久代就會超過90%,監控開始報警,然後permgen到達100%時,會進行full gc,回收所有的堆(新生代、老年代、永久代)。


-XX:+CMSPermGenSweepingEnabled

-XX:CMSInitiatingPermOccupancyFraction=70

CMS接管Perm,70%時併發進行垃圾收集,避免滿了再FullGC。在70%時,只會回收新生代和永久代,不會回收老年代。



第七章 堆內存最佳實踐


本章首先介紹了使用jmap命令或者其他工具進行堆轉儲,然後通過Eclipse Memory Analyze工具分析轉儲文件,理解淺內存、保留內存、深內存,及EMA的用法。

然後介紹了幾種常見的內存溢出場景。

然後從減少內存使用絕度(減小對象大小、延遲初始化、不可變對象和標準化對象、字符串的保留),以及對象生命週期管理(對象重用、弱引用/軟引用/其他引用)絕度,討論了堆內存最佳實踐方法。

以上實際都是編程的絕度。


第八章 原生內存最佳實踐


首先介紹了原生內存使用的理論知識。

在Java8中,通過開啓-XX:NativeMemoryTracking=off|summary|detail,然後可以通過命令 jcmd pid VM.native_memory summary查看原生內存使用情況。

介紹了操作系統級別的JVM優化:大頁和壓縮的OOP。

頁是OS管理內存的一個單元,也是OS分配內存的最小單元:要分配一個字節,OS一定會分配一個整頁。

介紹了Linux大頁和Linux透明大頁(從2.6.32開始):cat(echo always > ) /sys/kernel/mm/transparent_hugepage/enabled

壓縮的OOP(ordinary object pointer 普通對象指針),在java7和更新的版本中,只要堆小於32G,壓縮的oop默認就是啓用的,不用參數-XX:+UseCompressedOops,如果堆大於32G,要大於一定數值,因爲需要額外的空間彌補非壓縮引用所使用的空間,平均而言,對象引用會佔用20%的堆空間,38G是個不錯的起點。


第九章 線程於同步的性能


拿到這本書後,基於對線程知識的理解(過去看過關於線程的書,查詢過很多資料,總結過博客:http://blog.csdn.net/puma_dong/article/details/37597261),所以最先看的就是這一章。

本章開始講了線程池、ThreadPoolExecutor(根據應用場景:CPU/IO密集等,設置最大線程數、最小線程數、線程池任務大小、設置ThreadPoolExecutor大小)、ForkJoinPool、線程同步,以及JVM線程調優(調節線程棧大小、偏向鎖、自旋鎖、線程優先級)。

最後講解了線程監視於分析,最常用的是jstack,我們需要數量的看懂jstack的輸出結果,並能快速的分析出存在的問題。


第十章 Java EE性能調優


本章介紹了一些Java EE技術,及常見的調優,比如Servlet、Jsp、Http、EJB(過時的技術)、數據交換技術(XML、JSON)、對象序列化、數據傳輸(Http、WebService、RESTFull)

本章,個人覺得,其他簡單看看,着重看看對象序列化就行了。


第十一章 數據庫性能的最佳實踐


本章所講的理論,更像是數據庫相關的理論。

在日常的應用開發中,關於數據庫相關,我們往往是應用持久化框架,所以從開發絕度考慮DB性能調優,更多的是在持久化框架配置方面。


第十二章 Java SE API技巧


本章講解了一些Java SE API技巧。

比如緩衝式I/O、類加載(雙親委派、並行加載)、隨機數(僞隨機、真正的隨機)、Java原生接口、異常、字符串的性能、日誌、集合類等。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章