java學習(十九)

1、jdk1.6之前和現在jdk1.8哪些地方做了改動(JVM),爲什麼要這樣做?

PermGen space”其實指的就是方法區。不過方法區和“PermGen space”又有着本質的區別。前者是 JVM 的規範,而後者則是 JVM 規範的一種實現。

元空間的本質和永久代類似,都是對JVM規範中方法區的實現。不過元空間與永久代之間最大的區別在於:元空間並不在虛擬機中,而是使用本地內存。

jdk1.6中存在永久代(PermGen),JDK 1.8 中實現了從永久代向元空間的轉換。

原因:

  • 字符串存在永久代中,容易出現性能問題和內存溢出;
  • 類及方法的信息很難確定其大小,因此對於永久代的大小指定比較困難,太小容易出現永久代溢出,太大容易導致老年代溢出;
  • 永久代會爲GC帶來不必要的複雜度,並且回收效率極低;
  •  Oracle 可能會將HotSpot 與 JRockit 合二爲一。

2、常見的垃圾回收器,說一下CMS或者G1.

Serial、Serial old、ParNew、Parallel Scavenge、Parallel old、CMS、G1

CMS:它是一種以獲取最短回收停頓時間爲目標的收集器,基於標記-清楚算法實現的,是一種老年代收集器,
通常與ParNew一起使用;
CMS的垃圾收集分爲4個過程:初始標記、併發標記、重新標記、併發清除
CMS的優缺點:
優點:以獲取最短回收停頓時間爲目標的收集器,具有併發收集、停頓時間低的優點;
缺點:對CPU資源非常敏感,收集過程會產生浮動垃圾,標記-清除方式會產生內存碎片。
G1收集器:將新生代和老年代取消了,將堆劃分爲若干的區域,屬於分代收集器,區域的一部分包含新生代,
一部分包含老年代,新生代採用複製算法,老年代採用標記-整理算法。每次根據回收時間來優先回收價值最大的
region(回收所獲得的空間大小以及回收所需時間的經驗值)
G1垃圾收集器分爲4個過程:初始標記、併發標記、最終標記、篩選回收。
G1的特點:並行與併發、分代收集、空間整合(不會產生內存碎片)、可預測的停頓。

3、頻繁GC的原因和解決方案

  • 人爲原因:例如在代碼中調用System#GC或者Runtime#GC方法。
  • 框架原因:在java程序調用相關框架時,框架內部調用了GC方法。
  • 內存原因:當heap大小設置比較小時,會引起頻繁的GC,所以在類似於Spark這樣對內存性能要求比較高的應用程序運行時,應可能給heap分配較大的內存,這樣可以減少頻繁的GC現象的發生。

方案即爲儘量避免如上做法。

4、高併發後臺怎麼優化GC

  • CMS+ParNew組合進行垃圾回收(‘壓測期間’性能效果更優,可以提高系統吞吐量);
  • -XX:NewRatio=3:設置新生代與老年代的比例(需要根據企業產品的特徵來做不同的設置:程序開闢內存駐留時間長短,手動銷燬內存等);
  • -XX:MaxTenuringThreshold:設置對象進入老年代的年齡閥值;
  • -XX:SurvivorRatio=8:設置新生代中Eden與Survivor的比例(需要根據企業產品的特徵來做不同的設置,一般需要在測試環境做不同配置參數的壓力測試,從而得到最佳配置)
  • 設置 -Xms6144M 、-Xmx6144M 、-XX:PermSize=128M 、-XX:MaxPermSize=256M 、-XX:MetaspaceSize(JDK8裏面的)等參數(需要在測試環境做不同配置參數的壓力測試,從而得到最佳配置)

註釋:eg如下(jvm調優)

ava -server -Xms4G -Xmx4G -Xmn2G -XX:SurvivorRatio=1 -XX:+UseConcMarkSweepGC -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=1100 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -jar c1000k.jar&

這臺機器是一個4G內存的機器。

-Xms4G 是指: JVM啓動時整個堆(包括年輕代,年老代)的初始化大小。

-Xmx4G 是指: JVM啓動時整個堆的最大值。

-Xmn2G是指:年輕代的空間大小,剩下的是年老代的空間。

-XX:SurvivorRatio=1是指:年輕代空間中2個Survivor空間與Eden空間的大小比例。此處爲1:1:1,算法如下:比如整個年輕代空間爲2G,如果比例爲1,那麼2/3,則S0/S1/Eden的空間大小是同樣的,爲666M。

該值不設置,則JDK默認爲比例爲8,那麼是1:1:8,通過上面的算法可以得出S0/S1的大小。我們可以看到官方通過增大Eden區的大小,來減少YGC發生的次數,但有時我們發現,雖然次數減少了,但Eden區滿

的時候,由於佔用的空間較大,導致釋放緩慢,此時stop-the-world的時間較長,因此需要按照程序情況去調優。

-XX:+UseConcMarkSweepGC是指:使用GC的回收類型。這裏是CMS類型,JDK1.7以後推薦使用+UseG1GC,被稱爲G1類型(或Garbage First)的回收器。

我們可以通過jvisualvm.exe中的Visual GC插件查看GC的圖形,我們也可以再服務器上執行:jstat -gc 15016 1000,看到每1秒鐘java進程號爲15016的GC回收情況。

[root@yxdevapp04 c1000k]# jstat -gc 15016 1000

S0C S1C S0U S1U EC EU OC OU PC PU YGC YGCT FGC FGCT GCT

699008.0 699008.0 29980.4 0.0 699136.0 116881.6 2097152.0 660769.4 21248.0 20071.0 354 54.272 0 0.000 54.272

699008.0 699008.0 29980.4 0.0 699136.0 118344.8 2097152.0 660769.4 21248.0 20071.0 354 54.272 0 0.000 54.272

699008.0 699008.0 29980.4 0.0 699136.0 119895.5 2097152.0 660769.4 21248.0 20071.0 354 54.272 0 0.000 54.272

699008.0 699008.0 29980.4 0.0 699136.0 121383.1 2097152.0 660769.4 21248.0 20071.0 354 54.272 0 0.000 54.272

S0C 是指:Survivor0區的分配空間

S0U 是指:Survivor0區的已經使用的空間

S1C 是指:Survivor1區的分配空間

S1U 是指:Survivor1區的已經使用的空間

EC是指:Eden區所使用的空間

EU是指:Eden區當前使用的空間

OC是指:老年代分配的空間

OU是指:老年代當前使用的空間

PC是指:持久待分配的空間

PU是指:持久待當前使用的空間

YGC是指:年輕代發生的次數,這裏是354次

YGCT是指:年輕代發送的總時長,這裏是54.272秒,因此每次年輕代發生GC,即平均每次stop-the-world的時長爲54.272/354=0.153秒。

FGC是指:年老代回收的次數,或者成爲FullGC的次數。

FGCT是指:年老代發生回收的總時長。

GCT是指:包括年輕代YGC及年老代FGC的總時間長。

常結合圖形或數據,我們可以看到當EU即將等於EC的時候,此時發生YGC,因此YGC次數+1,YGCT時間增加。

經過實際的調優測試我們發現,當發生YGC的時候,如果S0U或S1U區如果有任意一個區域爲0的時候,此時YGC的速度很快,相反如果S0U或者S1U中都有數據,或相對滿的時候,此時YGC的時間邊長,這就是因爲S0/S1及Eden區的比例問題導致的。

經過一定時間的調優,我們基本上可以使得YGC的次數非常少,時間非常快,很長時間,數天都不會發生FGC,此時JVM的調優算是一個好的結果。在MAC電腦上可以通過jconsole調出圖形化分析工具。

jvm調優工具:jstack jconsole等。

5、HashSet和HashMap的區別

*HashMap* *HashSet*
HashMap實現了Map接口 HashSet實現了Set接口
HashMap儲存鍵值對 HashSet僅僅存儲對象
使用put()方法將元素放入map中 使用add()方法將元素放入set中
HashMap中使用鍵對象來計算hashcode值 HashSet使用成員對象來計算hashcode值,對於兩個對象來說hashcode可能相同,所以equals()方法用來判斷對象的相等性,如果兩個對象不同的話,那麼返回false
HashMap比較快,因爲是使用唯一的鍵來獲取對象 HashSet較HashMap來說比較慢

6、HashMap中初始化大小爲什麼是16?

  • 減少hash碰撞
  • 提高map查詢效率
  • 分配過小防止頻繁擴容
  • 分配過大浪費資源

7、爲什麼8的時候樹化,4(或者其他數)不可以嗎?

TreeNodes佔用空間是普通Nodes的兩倍,所以只有當bin包含足夠多的節點時纔會轉成TreeNodes,而是否足夠多就是由TREEIFY_THRESHOLD的值決定的。當bin中節點數變少時,又會轉成普通的bin。並且我們查看源碼的時候發現,鏈表長度達到8就轉成紅黑樹,當長度降到6就轉成普通bin。

當hashCode離散性很好的時候,樹型bin用到的概率非常小,因爲數據均勻分佈在每個bin中,幾乎不會有bin中鏈表長度會達到閾值。但是在隨機hashCode下,離散性可能會變差,然而JDK又不能阻止用戶實現這種不好的hash算法,因此就可能導致不均勻的數據分佈。不過理想情況下隨機hashCode算法下所有bin中節點的分佈頻率會遵循泊松分佈,我們可以看到,一個bin中鏈表長度達到8個元素的概率爲0.00000006,幾乎是不可能事件。

通俗點將就是put進去的key進行計算hashCode時 只要選擇計算hash值的算法足夠好(hash碰撞率極低),從而遵循泊松分佈,使得桶中掛載的bin的數量等於8的概率非常小,從而轉換爲紅黑樹的概率也小,反之則概率大。

注:由鏈表變成紅黑樹也只是當前桶掛載的bin會進行轉換,不會影響其它桶的數據結構

8、HashMap擴容爲什麼選擇紅黑樹,別的樹不可以嗎?

  • 紅黑樹不追求"完全平衡",即不像AVL那樣要求節點的 |balFact| <= 1,它只要求部分達到平衡,但是提出了爲節點增加顏色,紅黑是用非嚴格的平衡來換取增刪節點時候旋轉次數的降低,任何不平衡都會在三次旋轉之內解決,而AVL是嚴格平衡樹,因此在增加或者刪除節點的時候,根據不同情況,旋轉的次數比紅黑樹要多。
  • 就插入節點導致樹失衡的情況,AVL和RB-Tree都是最多兩次樹旋轉來實現復衡rebalance,旋轉的量級是O(1)
    刪除節點導致失衡,AVL需要維護從被刪除節點到根節點root這條路徑上所有節點的平衡,旋轉的量級爲O(logN),而RB-Tree最多只需要旋轉3次實現復衡,只需O(1),所以說RB-Tree刪除節點的rebalance的效率更高,開銷更小!
  • AVL的結構相較於RB-Tree更爲平衡,插入和刪除引起失衡,如2所述,RB-Tree復衡效率更高;當然,由於AVL高度平衡,因此AVL的Search效率更高啦。
  • 針對插入和刪除節點導致失衡後的rebalance操作,紅黑樹能夠提供一個比較"便宜"的解決方案,降低開銷,是對search,insert ,以及delete效率的折衷,總體來說,RB-Tree的統計性能高於AVL.
  • 故引入RB-Tree是功能、性能、空間開銷的折中結果
    *AVL更平衡,結構上更加直觀,時間效能針對讀取而言更高;維護稍慢,空間開銷較大。
    *紅黑樹,讀取略遜於AVL,維護強於AVL,空間開銷與AVL類似,內容極多時略優於AVL,維護優於AVL。

    基本上主要的幾種平衡樹看來,紅黑樹有着良好的穩定性和完整的功能,性能表現也很不錯,綜合實力強
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章