1. Java的內存模型以及GC算法java
1、Java內存模型算法
Java 虛擬機具備一個堆,堆是運行時數據區域,全部類實例和數組的內存均今後處分配sql
JVM主要管理兩種類型內存:堆和非堆,堆內存(Heap Memory)是在 Java 虛擬機啓動時建立,非堆內存(Non-heap Memory)是在JVM堆以外的內存數組
簡單來講,堆是Java代碼可及的內存,留給開發人員使用的;非堆是JVM留給本身用的,包含方法區、JVM內部處理或優化所需的內存(如 JITCompiler,Just-in-time Compiler,即時編譯後的代碼緩存)、每一個類結構(如運行時常數池、字段和方法數據)以及方法和構造方法的代碼緩存
JVM 內存包含以下幾個部分:服務器
堆內存(Heap Memory): 存放Java對象網絡
非堆內存(Non-Heap Memory): 存放類加載信息和其它meta-data架構
其它(Other): 存放JVM 自身代碼等併發
Method Area 和 Heap 是線程共享的jvm
JVM初始運行的時候都會分配好 Method Area(方法區) 和Heap(堆),而JVM 每遇到一個線程,就爲其分配一個 Program Counter Register(程序計數器) , VM Stack(虛擬機棧)和Native Method Stack (本地方法棧), 當線程終止時,三者(虛擬機棧,本地方法棧和程序計數器)所佔用的內存空間也會被釋放掉。
2、Java內存分配
Java的內存管理實際上就是變量和對象的管理,其中包括對象的分配和釋放。
JVM內存申請過程以下:
JVM 會試圖爲相關Java對象在Eden中初始化一塊內存區域
當Eden空間足夠時,內存申請結束;不然到下一步
JVM 試圖釋放在Eden中全部不活躍的對象(這屬於1或更高級的垃圾回收),釋放後若Eden空間仍然不足以放入新對象,則試圖將部分Eden中活躍對象放入Survivor區
Survivor區被用來做爲Eden及OLD的中間交換區域,當OLD區空間足夠時,Survivor區的對象會被移到Old區,不然會被保留在Survivor區
當OLD區空間不夠時,JVM 會在OLD區進行徹底的垃圾收集(0級)
徹底垃圾收集後,若Survivor及OLD區仍然沒法存放從Eden複製過來的部分對象,致使JVM沒法在Eden區爲新對象建立內存區域,則出現」out of memory」錯誤
4、GC分代劃分
爲了進行高效的垃圾回收,虛擬機把堆內存劃分紅新生代(Young Generation)、老年代(Old Generation)和永久代(Permanent Generation)3個區域。
1) 在Young Generation中,有一個叫Eden Space的空間,主要是用來存放新生的對象,還有兩個Survivor Spaces(from、to),它們的大小老是同樣,它們用來存放每次垃圾回收後存活下來的對象
2) 在Old Generation中,主要存放應用程序中生命週期長的內存對象
3) 在Young Generation塊中,垃圾回收通常用Copying的算法,速度快。每次GC的時候,存活下來的對象首先由Eden拷貝到某個SurvivorSpace,當Survivor Space空間滿了後,剩下的live對象就被直接拷貝到OldGeneration中去。所以,每次GC後,Eden內存塊會被清空。
4) 在Old Generation塊中,垃圾回收通常用mark-compact的算法,速度慢些,但減小內存要求
5) 垃圾回收分多級,0級爲所有(Full)的垃圾回收,會回收OLD段中的垃圾;1級或以上爲部分垃圾回收,只會回收Young中的垃圾,內存溢出一般發生於OLD段或Perm段垃圾回收後,仍然無內存空間容納新的Java對象的狀況
2. jvm性能調優都作了什麼
JVM啓動參數:調整各代的內存比例和垃圾回收算法,提升吞吐量
目標:
GC的時間足夠的小
GC的次數足夠的少
發生Full GC的週期足夠的長
實踐:
(1)針對JVM堆的設置,通常能夠經過-Xms -Xmx限定其最小、最大值,爲了防止垃圾收集器在最小、最大之間收縮堆而產生額外的時間,咱們一般把最大、最小設置爲相同的值
(2)年輕代和年老代將根據默認的比例(1:2)分配堆內存,能夠經過調整兩者之間的比率NewRadio來調整兩者之間的大小,也能夠針對回收代,好比年輕代,經過 -XX:newSize -XX:MaxNewSize來設置其絕對大小。一樣,爲了防止年輕代的堆收縮,咱們一般會把-XX:newSize -XX:MaxNewSize設置爲一樣大小
(3)年輕代和年老代設置多大才算合理?這個我問題毫無疑問是沒有答案的,不然也就不會有調優。咱們觀察一下兩者大小變化有哪些影響
更大的年輕代必然致使更小的年老代,大的年輕代會延長普通GC的週期,但會增長每次GC的時間;小的年老代會致使更頻繁的Full GC
更小的年輕代必然致使更大年老代,小的年輕代會致使普通GC很頻繁,但每次的GC時間會更短;大的年老代會減小Full GC的頻率
如何選擇應該依賴應用程序對象生命週期的分佈狀況:若是應用存在大量的臨時對象,應該選擇更大的年輕代;若是存在相對較多的持久對象,年老代應該適當增大。但不少應用都沒有這樣明顯的特性,在抉擇時應該根據如下兩點:(A)本着Full GC儘可能少的原則,讓年老代儘可能緩存經常使用對象,JVM的默認比例1:2也是這個道理 (B)經過觀察應用一段時間,看其餘在峯值時年老代會佔多少內存,在不影響Full GC的前提下,根據實際狀況加大年輕代,好比能夠把比例控制在1:1。但應該給年老代至少預留1/3的增加空間
(4)在配置較好的機器上(好比多核、大內存),能夠爲年老代選擇並行收集算法: -XX:+UseParallelOldGC ,默認爲Serial收集
(5)線程堆棧的設置:每一個線程默認會開啓1M的堆棧,用於存放棧幀、調用參數、局部變量等,對大多數應用而言這個默認值太了,通常256K就足用。理論上,在內存不變的狀況下,減小每一個線程的堆棧,能夠產生更多的線程,但這實際上還受限於操做系統。
程序算法:改進程序邏輯算法提升性能
調優原則:
一、多數的Java應用不須要在服務器上進行GC優化;
二、多數致使GC問題的Java應用,都不是由於咱們參數設置錯誤,而是代碼問題;
三、在應用上線以前,先考慮將機器的JVM參數設置到最優(最適合);
四、減小建立對象的數量;
五、減小使用全局變量和大對象;
六、GC優化是到最後不得已才採用的手段;
七、在實際使用中,分析GC狀況優化代碼比優化GC參數要多得多;
調優目的:
一、將轉移到老年代的對象數量下降到最小;
二、減小full GC的執行時間;
調優手段:
一、減小使用全局變量和大對象;
二、調整新生代的大小到最合適;
三、設置老年代的大小爲最合適;
四、選擇合適的GC收集器;
調優步驟:
1,監控GC的狀態
2,分析結果,判斷是否須要優化
若是各項參數設置合理,系統沒有超時日誌出現,GC頻率不高,GC耗時不高,那麼沒有必要進行GC優化;若是GC時間超過1-3秒,或者頻繁GC,則必須優化;
注:若是知足下面的指標,則通常不須要進行GC:
Minor GC執行時間不到50ms;
Minor GC執行不頻繁,約10秒一次;
Full GC執行時間不到1s;
Full GC執行頻率不算頻繁,不低於10分鐘1次;
3,調整GC類型和內存分配
4,不斷的分析和調整
5,全面應用參數
3. 介紹JVM中7個區域,而後把每一個區域可能形成內存的溢出的狀況說明
順口溜:器池堆,棧棧區區;棄池堆,站站曲曲(一離開衛生間,肚子疼的站不起身)
虛擬機規範中的7個內存區域分別是三個線程私有的和四個線程共享的內存區
線程私有內存區域:與線程具備相同生命週期,分別是:指令計數器、線程棧和本地線程棧
四個共享區:是全部線程共享的,在JVM啓動時就會分配,分別是:方法區、 常量池、直接內存區和堆(即咱們一般所說的JVM的內存分爲堆和棧中的堆,後者就是前面的線程棧)
4. jvm 如何分配直接內存,常量池解析
直接內存:並非虛擬機運行時數據區的一部分,但直接內存被頻繁使用也可能致使OutOfMemoryError異常出現。在NIO中,引入了一種基於通道和緩衝區的I/O方式,它可使用native函數直接分配堆外內存,而後經過一個存儲在java堆中的DirectByteBuffer對象做爲這塊內存的引用進行操做
-XX:MaxDirectMemorySize設置最大值,默認與java堆最大值同樣
常量池:JVM爲每一個已加載的類型維護一個常量池,常量池就是這個類型用到的常量的一個有序集合。包括直接常量(基本類型,String)和對其餘類型、方法、字段的符號引用。池中的數據和數組同樣經過索引訪問。因爲常量池包含了一個類型全部的對其餘類型、方法、字段的符號引用,因此常量池在Java的動態連接中起了核心做用。常量池存在於堆中
5. 數組多大放在JVM老年代
虛擬機提供了一個-XX:PretenureSizeThreshold參數,令大於這個設置值的對象直接在老年代中分配。這樣作的目的是避免在Eden區及兩個Survivor區之間發生大量的內存拷貝
新生代經歷minor gc ,年齡到達閾值。過大對象。minor gc後仍存在大量對象,分配survivor不能存儲的對象到老年代。某種相同年齡對象相加綜合大於survivor一半,大於或等於這個年齡值的對象直接到老年代
對應的年輕代回收機制是:標記-複製。(圖片來自網絡)
對應的老年代回收機制是:標記-清除(或標記-整理)。(圖片來自網絡)
1. 標記-清除
2. 標記-複製
3. 標記-整理
4. 分代回收
Java架構資料分享,獲取方式請到架構圈圈:948368769 領取(有高可用、高併發、高性能及分佈式、Jvm性能調優、Spring源碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個知識點的架構資料)