學習參考資料:周志明老師的著作《深入理解Java虛擬機(第3版)》
1.大記憶體硬體上的程式部署
一般單體應用在較大記憶體的硬體方式,有下面兩種:
1)通過一個單獨的Java虛擬機實體來管理大量的Java堆記憶體
2)同時使用若干個Java虛擬機,建立邏輯集群利用資源
第一種方法,看似沒有太大問題,但前提是要把Full GC的頻率降得很低(在夜間設定定時任務專門進行回收),因為在一個Java虛擬機管理較大堆記憶體時,一次Full GC會導致很長時間停頓,此外還應該考慮下面可能發生的問題:
- 必須確保應用程式足夠穩定,如果這種大型單體應用發生了記憶體溢位,幾乎無法產生堆轉儲快照(要產生十幾GB甚至更大的檔案)
- 大記憶體必須要有64位Java虛擬機的支持,但由于壓縮指標、處理器快取行容量等因素,64位虛擬機的性能測驗結果普遍略低于32位虛擬機
- 相同程式在64位虛擬機中消耗的記憶體一般比32位更大,這是因為指標膨脹以及資料型別對齊補白等因素導致的,可以開啟(默認開啟)壓縮指標功能開緩解
第二種方法,做法就是在一臺機器上啟動多個服務器行程分配其不同的埠號,然后在前端搭建一個負載均衡,以反向代理的方式分配請求,但這種方法也并不是完美的,也應該考慮下面可能發生的問題:
- 大量使用本地快取,會造成大量資源浪費,因為每一個邏輯結點都有一份快取,解決方法是改為集中式快取
- 很難比較高效的利用某些資源池,譬如連接池,一般在各個結點建立連接池,假如一個結點連接池滿了,另一個結點還有剩余,
- 如果使用32位Java虛擬機會受到限制,如在32為Windows平臺中每個行程只能使用2GB記憶體空間,在某些Linux和Unix系統中可以開到3或4GB,但是32位仍然會受到最高記憶體4GB(2的32位)限制
2.其他問題
-
當使用集群方式部署,如果有需求要進行同步(例如最后一次訪問或操作時間),那么一個頁面就可能出現多個請求,也就會導致各個結點之間網路通信會非常頻繁,假如網路情況不滿足傳輸需求,重發資料在記憶體中不斷堆積,就可能會導致記憶體溢位,(集群同步導致記憶體溢位)
-
直接記憶體溢位,Java虛擬機也會對直接記憶體進行回收,但是直接記憶體即將溢位時,并不會通知Java虛擬機觸發GC,只能等待老年代滿了以后觸發
Full GC幫它順便清理,如果有需要可以catch塊里面通過System.gc()進行主動觸發回收,(堆外記憶體導致的溢位錯誤) -
在應用程式中應該避免經常使用“fork”這類系統呼叫,我們知道“fork”會復制一個和當前環境變數相同的行程,會浪費很多處理器和記憶體的開銷,(外部命令導致系統緩慢)
-
當雙方服務處理速度完全不對等(比如2分鐘),若一個服務受到請求需要告知另一個服務,時間越長就累計了越多web服務沒有呼叫完成,導致在等待的執行緒和Socket連接越來越多,最終超過虛擬機的承受能力后導致虛擬機行程崩潰,可以通過生產者/消費者模式的訊息佇列來解決,(服務器虛擬機行程崩潰)
-
大量資料使用不恰當的資料結構,比如數以百萬計的
HashMap<Long,Long>,一個long型要占8位元組,封裝成Long物件后,還要加上8位元組的Mark Word、8位元組的Klass指標;然后兩個
Long物件還要封裝成Entry物件,還要加上16位元組物件頭、8位元組next欄位和4位元組的int型hash欄位;為了對齊必須加4位元組的空白填充;
最后還有HashMap對它的參考8位元組,這樣實際損耗就是88位元組,
16/88=18%,有效資料占比太低了,(不恰當資料結構導致記憶體占用過大)

-
GUI桌面程式,最小化時記憶體明顯減小,而虛擬記憶體沒有變化,也就是它的作業記憶體被交換到磁盤的頁面檔案之中了,這樣發生垃圾收集器時就有可能因為回復頁面檔案的操作導致不正常的垃圾收集停頓,可以設定啟動檔案中相關引數等于true,保證程式在恢復最小化時能夠立即回應,(由Windows虛擬記憶體導致的長時間停頓)

后面還會陸陸續續更新這系列的讀書筆記,期待您的關注~~
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/375106.html
標籤:其他
