基本類型
|
大小(字節)
|
取值範圍
|
裝箱基本類型
|
int
|
4
|
-2^31 ~ 2^31-1
|
Integer
|
char
|
2
|
|
Character
|
byte
|
1
|
-2^7 ~ 2^7-1
|
Byte
|
short
|
2
|
-2^15 ~ 2^15-1
|
Short
|
long
|
8
|
-2^63 ~ 2^63-1
|
Long
|
float
|
4
|
|
Float
|
double
|
8
|
|
Double
|
boolean
|
1或4
|
true~false
|
Boolean
|
JVM簡單調優及工作中例子記錄
前序:
Jvm調優需要我們對系統有所瞭解,其中比較關鍵的是對核心業務的理解,特別是會造成頻繁GC的部分,比如高併發造成的不及時回收。
要知道爲什麼會造成頻繁GC,首先我們要懂怎麼估算java類的大小
下面列舉各個基本類型和字符串估算的表格,以及測試的類
註釋:1字節(byte) = 8比特(bit),1kb = 1024字節。爲什麼boolea需要4個字節,原則上只需要1個比特,但是操作系統以字節作爲單位,所以至少要一個字節。又因爲jvm用int替代boolean,所以需要4字節。
講解完基礎類型,我們大概看一下String字符是多少字節(默認就是2字節):
Java規定了字符的內碼要用UTF-16編碼,一個字符是2個字節。外碼字符所佔字節取決於具體編碼。幾種常見的編碼換算如下:
-- ASCII編碼 1字節編碼,只有英文字符,不能編碼漢字。
-- GBK編碼 英文1字節 漢字2字節。
-- UTF-8編碼 英文1字節 漢字3字節。
-- Unicode編碼 英文2字節 漢字2字節。
下面可以藉助getObjectSize()來獲取類的大小(以下是一個demo,可自行測試):
public class Test01 {
public Test01() {
Demo1 demo1 = new Demo1();
long objectSize = getObjectSize(demo1);
System.out.println(objectSize);
}
public static void main(String[] args) throws UnsupportedEncodingException {
new Test01();
// 字符串大小
System.out.println("測試".getBytes("ISO8859-1").length);
}
}
/**
* 對齊填充(8字節補齊)
* 這裏有一個注意點:如果大小未滿8字節的倍數,會自動補齊8字節倍數。
*/
class Demo1 {
private String str;
// private int intVal;
// private int intVal2;
//private double doubleVal;
}
接下來就舉一個簡單的jvm調優參考例子:
---------------調優前-----------------
假設有一個日活過億的電商網站(日均活躍用戶500w,平均每人點擊20-30次),下單率10%,也就是
下單人數 = 10% * 500w = 50w
把這一千萬在整天進行平攤(考慮到用戶活躍時間一般只有4-5小時(具體數字見自己系統的分析),所以在這5小時進行平攤)
每秒下單數 = 50w / (5 * 60 * 60) = 27
如果只有27這個數字是很小的,如果把當前服務部署成3個,那每個每秒就9個下單請求,基本不會對系統造成影響,JVM的垃圾回收肯定是正常的。
但是這些系統一般會有一個高峯,在短時間內產品了一天的下單量,比如搞活動雙十一啥的,這時候公式計算就變成下面這樣:
下單人數 = 10% * 500w = 50w
用戶活躍時間就不是4-5個小時了,可能是幾分鐘內涌入大量訂單
每秒下單數 = 30w / 幾分鐘 = 1000多
這時候我們也部署成3個,每臺300來個訂單,這時候300明顯遠大於正常情況,假如三臺都是(4G8核)的機器,我們使用2G作爲老年代,1G作爲新生代(Eden區:s1區:s2區 = 8:1:1 = 800M:100M:100M)
高峯導致的問題(平時都是很平穩的):
高峯的時候會發現系統頻繁FullGC告警,頻繁FullGC會導致STW(stop the world),讓頁面產生卡頓,對用戶體驗不好(正常情況下STW不應該這麼頻繁)。
假設這是一個訂單需求上線後纔出現的(也做過jvm參數的變更)。這時候可以從jvm參數來入手如下:
-- 1 估算下單時產生的內存大小
假定每個訂單有幾十個字段,每個字段8個字節,放大來看:8 * 100 = 800byte,約等於1KB(我們就當它1KB算了)
每秒300個請求就是: 300 * 1KB = 300KB 的對象生成,
然後還有一起雜七雜八的庫存、優惠券、積分等,放大20倍:300KB * 20 = 6MB,然後雜七雜八的查詢再放大10倍:6MB * 10 = 60MB
然後Eden=800MB,所以每13秒會minor GC一次:800MB / 60MB = 13,並且由於第13次的60M是可能還執行完,其引用還存在(CG root可達),這時候第15次的60MB不會被minor GC,本來應該會從移動s區,但是由於s區只有100MB,60MB 大於 100MB*50%,所以這個60MB會直接移動到老年代,導致老年代一點點上上漲,最後滿了觸發fullGC,導致STW。
--------------調優解決方法--------------------------------
將新生代設置爲2GB,其中Eden=1.8GB,s1=s2=200MB。這樣的話60MB放進s區的時候也不會超過50%,隨後就能被minorGC掉,從而不會放到老年代中,導致老年代短時間一直漲。導致系統卡頓。
其他編程中的小例子:
1 編程中假設查詢出來僅僅有兩個字段,就儘量使用只有兩個成員變量的類來接收這些數據,不要爲了方便用一個含幾十個成員變量的類來接收,這樣會很佔用內容。
2 編程中性能會涉及到網絡IO、磁盤IO等,比如我們查數據庫,把數據從一次從庫查出來和循環中一個個查出來很大區別,主要體現在查數據庫的時候有網絡IO和磁盤IO,多次查詢的網絡IO+磁盤IO累計起來很可怕。
3 使用中不要以爲用redis就一定很快,比如你用redis在循環中查數據,假設每次吊redis耗時1ms,但是數據一多(假設2w條)就會變成2w * 1ms = 20s,這樣是無法忍受的,相反一次性從redis查兩位萬條可能就1-50ms之間,性能完全沒得比。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.