日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

java性能权威指南中文_Java性能权威指南读书笔记--之一

發(fā)布時間:2025/4/5 java 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java性能权威指南中文_Java性能权威指南读书笔记--之一 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

JIT(即時編譯)

解釋型代碼:程序可移植,相同的代碼在任何有適當(dāng)解釋器的機(jī)器上,都能運(yùn)行,但是速度慢。

編譯型代碼:速度快,電視不同CPU平臺的代碼無法兼容。

java則是使用java的編譯器先將其編譯為class文件,也就是字節(jié)碼;然后將字節(jié)碼交由jvm(java虛擬機(jī))解釋執(zhí)行。由于這個編譯是在程序執(zhí)行時進(jìn)行的,因此被稱為“即使編譯”。

熱點(diǎn)編譯

對于程序來說,通常只有一部分代碼被經(jīng)常執(zhí)行,而應(yīng)用的性能就取決于這些代碼執(zhí)行得有多快。這些關(guān)鍵代碼段被稱為應(yīng)用的熱點(diǎn),代碼執(zhí)行得越多就被認(rèn)為是越熱。

因此JVM執(zhí)行代碼時,并不會立即編譯代碼:

如果代碼只執(zhí)行一次,那編譯完全就是浪費(fèi)精力。對于只執(zhí)行一次的代碼,解釋執(zhí)行Java字節(jié)碼比先編譯然后執(zhí)行的速度快。

JVM執(zhí)行特定方法或者循環(huán)的次數(shù)越多,它就會越了解這段代碼。這使得JVM可以在編譯代碼時進(jìn)行大量優(yōu)化。

分層編譯

Client編譯器和server編譯器主要的區(qū)別在于編譯代碼的時機(jī)不同。client編譯器開啟編譯比server編譯器要早。這意味著在代碼執(zhí)行的開始階段,client編譯器比server編譯器要快,因為它的編譯代碼相比server編譯器而言要多。

分層編譯是綜合了client和server的優(yōu)點(diǎn)。在開啟分層編譯(-XX:+TieredCompilation)后代碼先由client編譯器編譯,隨著代碼變熱,由server編譯器重新編譯。

調(diào)優(yōu)代碼緩存

JVM編譯代碼時,會在代碼緩存中保留編譯之后的匯編語言指令集。代碼緩存的大小固定,所以一旦填滿,JVM就不能編譯更多代碼了。

也就是說,如果代碼緩存過小,那么就會有一些熱點(diǎn)代碼被編譯了,而其他沒有,最終導(dǎo)致應(yīng)用的大部分代碼都是解釋運(yùn)行(非常慢)。這個問題在使用client編譯器或進(jìn)行分層編譯時很常見。

當(dāng)代碼緩存填滿時,JVM通常會發(fā)出以下警告:

Java HotSopt(TM) 64-Bit Server VM warning:CodeCache is full.Compiler has bean disabled.

Java HotSopt(TM) 64-Bit Server VM warning:Try increasing the code cache size using -XX:ReservedCodeCacheSize=

各平臺代碼緩存的默認(rèn)大小:

jvm

jdk版本

大小

32位client

Java8

32MB

32位client

分層編譯,Java8

240MB

64位client

分層編譯,Java8

240MB

32位client

Java7

32MB

32位server

Java7

32MB

64位server

Java7

48MB

64位server

分層編譯,Java7

48MB

如果代碼緩存設(shè)為1GB,JVM就會保留1GB的本地內(nèi)存空間。如果是32位JVM,那么進(jìn)程占用的總內(nèi)存不能超過4GB(包括Java堆、JVM自身所有代碼占用空間、分配給應(yīng)用的本地內(nèi)存、代碼緩存)。

通過jconsole Memory(內(nèi)存)面板的Memory Pool Code Cache圖表,可以監(jiān)控代碼緩存。

編譯閾值

一旦代碼執(zhí)行到一定次數(shù),且達(dá)到了編譯閾值,編譯器就可以獲得足夠的信息編譯代碼了。

編譯是基于兩種JVM計數(shù)器的:方法調(diào)用計數(shù)器和方法中的循環(huán)回邊計數(shù)器。回邊實際上可以看作是循環(huán)完成執(zhí)行的次數(shù)。

棧上替換:JVM可以在方法循環(huán)運(yùn)行時進(jìn)行編譯,并在循環(huán)代碼編譯結(jié)束之后,JVM替換還在棧上的代碼,循環(huán)的下一次迭代就會執(zhí)行快的多的代碼。

標(biāo)準(zhǔn)編譯由-XX:CompileThreshold=N標(biāo)志觸發(fā)。使用client編譯器時,N的默認(rèn)值是1500,使用server編譯器時為10000。

計數(shù)器會隨著時間而減少,所以計數(shù)器只是方法或循環(huán)最新熱度的度量。由此帶來一個副作用是,執(zhí)行不太頻繁的代碼可能永遠(yuǎn)不會編譯。

檢測編譯過程

-XX:+PrintCompilation

如果開啟PrintCompilation,每次編譯一個方法(或循環(huán))時,JVM就會打印一行被編譯的內(nèi)容信息。

絕大多數(shù)編譯日志的行具有以下格式:

timestamp compilation_id attributes (tiered_level) method_name size deopt

timestamp表示編譯完成的時間

compilation_id內(nèi)部的任務(wù)ID

attributes是一組5個字符長的串,表示代碼編譯的狀態(tài)。如果給定的編譯被賦予了特定屬性,就會打印下面列表中所顯示的字符,否則該屬性就打印一個空格。

* % :編譯為OSR

* s :方法是同步的

* !:方法有異常處理器

* b :阻塞模式時發(fā)生的編譯

* n:為封裝本地方法所發(fā)生的編譯

tiered_level 如果程序沒有使用分成編譯的方式運(yùn)行則為空,否則為數(shù)字,表明所完成編譯的級別

method_name格式為:ClassName::method

然后是編譯后代碼大小(單位是字節(jié))

最后,在某些情況下,編譯日志的結(jié)尾會有一條信息,表明發(fā)生了某種逆優(yōu)化,通常是“made not entrant”或”made zombie”

135 1 n 0 java.lang.Thread::currentThread (native) (static)

136 2 3 java.util.Arrays::copyOf (19 bytes)

136 7 3 sun.nio.cs.UTF_8$Encoder::encode (359 bytes)

137 8 2 java.lang.String::hashCode (55 bytes)

使用jstat -compiler 進(jìn)程ID 也可以看有多少方法被編譯

使用jstat -printcompilation 5003 1000 表示進(jìn)程ID為5003的程序每1秒輸出一次最近被編譯的方法

編譯器線程

當(dāng)方法(或循環(huán))適合編譯時,就會進(jìn)入到編譯隊列。隊列則由一個或多個后臺線程處理。編譯隊列是一種優(yōu)先隊列,即調(diào)用計數(shù)次數(shù)多的方法有更高的優(yōu)先級。

當(dāng)開啟分層編譯時,JVM默認(rèn)開啟多個client和server線程。

cpu數(shù)量

C1的線程數(shù)(client)

C2的線程數(shù)(server)

1

1

1

2

1

1

4

1

2

8

1

2

16

2

6

32

3

7

64

4

8

128

4

10

編譯器的線程數(shù)可通過-XX:CICompilerCount=N標(biāo)志來設(shè)置。對于分層編譯來說,設(shè)置的值中三分之一將用來處理client編譯器隊列,其余的線程(至少一個)用來處理server編譯器隊列。

使用分層編譯時,線程數(shù)很容易超過系統(tǒng)限制,特別是有多個JVM同時運(yùn)行的時候。在這種情況下,減少線程數(shù)有助于提高整體的吞吐量(盡管代價可能是熱身期會持續(xù)得更長)。

方法內(nèi)聯(lián)

public class Point{

private int x,y;

public int getX(){ return x; }

public void setX(int i){ x = i;}

}

如果你寫下面的代碼

Point p = getPoint();

p.setX(p.getX()*2);

編譯后的代碼本質(zhì)上執(zhí)行的是:

Point p = getPoint();

p.x = p.x *2;

方法是否內(nèi)聯(lián)取決于它有多熱以及它的大小。

-XX:MaxInlineSize=N默認(rèn)是35字節(jié),即只有方法小于35字節(jié)時第一次調(diào)用方法時就會被內(nèi)聯(lián)。

-XX:MaxFreqInlineSize=N默認(rèn)是325字節(jié),即只有當(dāng)一個方法頻繁被調(diào)用并且小于325字節(jié)時會被內(nèi)聯(lián)。

逃逸分析

-XX:+DoEscapeAnalysis默認(rèn)為true。逃逸分析可以讓JVM對一個對象根據(jù)代碼來進(jìn)行優(yōu)化。

棧上分配

我們都知道Java中的對象都是在堆上分配的,而垃圾回收機(jī)制會回收堆中不再使用的對象,但是篩選可回收對象,回收對象還有整理內(nèi)存都需要消耗時間。如果能夠通過逃逸分析確定某些對象不會逃出方法之外,那就可以讓這個對象在棧上分配內(nèi)存,這樣該對象所占用的內(nèi)存空間就可以隨棧幀出棧而銷毀,就減輕了垃圾回收的壓力。

同步消除

如果發(fā)現(xiàn)某個對象只能從一個線程可訪問,那么在這個對象上的操作可以不需要同步。

標(biāo)量替換

Java虛擬機(jī)中的原始數(shù)據(jù)類型(int,long等數(shù)值類型以及reference類型等)都不能再進(jìn)一步分解,它們就可以稱為標(biāo)量。相對的,如果一個數(shù)據(jù)可以繼續(xù)分解,那它稱為聚合量,Java中最典型的聚合量是對象。如果逃逸分析證明一個對象不會被外部訪問,并且這個對象是可分解的,那程序真正執(zhí)行的時候?qū)⒖赡懿粍?chuàng)建這個對象,而改為直接創(chuàng)建它的若干個被這個方法使用到的成員變量來代替。拆散后的變量便可以被單獨(dú)分析與優(yōu)化,可以各自分別在棧幀或寄存器上分配空間,原本的對象就無需整體分配空間了。

小結(jié)

不用擔(dān)心小方法,特別是getter和setter,因為它們?nèi)菀變?nèi)聯(lián)。

需要編譯的代碼在編譯隊列中,隊列中代碼越多,程序打到最佳性能的時間越久。

雖然代碼緩存的大小可以調(diào)整,但它仍然是有限的資源

代碼越簡單,優(yōu)化越多。

總結(jié)

以上是生活随笔為你收集整理的java性能权威指南中文_Java性能权威指南读书笔记--之一的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。