JVM調優總結(十一)-反思

zhoubangding發表於2017-02-15

轉自:http://pengjiaheng.iteye.com/blog/558619

垃圾回收的悖論

    所謂“成也蕭何敗蕭何”。Java的垃圾回收確實帶來了很多好處,為開發帶來了便利。但是在一些高效能、高併發的情況下,垃圾回收卻成為了制約Java應用的瓶頸。目前JDK的垃圾回收演算法,始終無法解決垃圾回收時的暫停問題,因為這個暫停嚴重影響了程式的響應時間,造成擁塞或堆積。這也是後續JDK增加G1演算法的一個重要原因。

    當然,上面是從技術角度出發解決垃圾回收帶來的問題,但是從系統設計方面我們就需要問一下了:

    我們需要分配如此大的記憶體空間給應用嗎?

    我們是否能夠通過有效使用記憶體而不是通過擴大記憶體的方式來設計我們的系統呢?   

我們的記憶體中都放了什麼

    記憶體中需要放什麼呢?個人認為,記憶體中需要放的是你的應用需要在不久的將來再次用到到的東西。想想看,如果你在將來不用這些東西,何必放記憶體呢?放檔案、資料庫不是更好?這些東西一般包括:

1. 系統執行時業務相關的資料。比如web應用中的session、即時訊息的session等。這些資料一般在一個使用者訪問週期或者一個使用過程中都需要存在。

2. 快取。快取就比較多了,你所要快速訪問的都可以放這裡面。其實上面的業務資料也可以理解為一種快取。

3.  執行緒。

    因此,我們是不是可以這麼認為,如果我們不把業務資料和快取放在JVM中,或者把他們獨立出來,那麼Java應用使用時所需的記憶體將會大大減少,同時垃圾回收時間也會相應減少。

    我認為這是可能的。

 

 

解決之道

 

資料庫、檔案系統

    把所有資料都放入資料庫或者檔案系統,這是一種最為簡單的方式。在這種方式下,Java應用的記憶體基本上等於處理一次峰值併發請求所需的記憶體。資料的獲取都在每次請求時從資料庫和檔案系統中獲取。也可以理解為,一次業務訪問以後,所有物件都可以進行回收了。

    這是一種記憶體使用最有效的方式,但是從應用角度來說,這種方式很低效。

 

 

記憶體-硬碟對映

    上面的問題是因為我們使用了檔案系統帶來了低效。但是如果我們不是讀寫硬碟,而是寫記憶體的話效率將會提高很多。

    資料庫和檔案系統都是實實在在進行了持久化,但是當我們並不需要這樣持久化的時候,我們可以做一些變通——把記憶體當硬碟使。

    記憶體-硬碟對映很好很強大,既用了快取又對Java應用的記憶體使用又沒有影響。Java應用還是Java應用,他只知道讀寫的還是檔案,但是實際上是記憶體。

    這種方式兼得的Java應用與快取兩方面的好處。memcached的廣泛使用也正是這一類的代表。

 

 

同一機器部署多個JVM

    這也是一種很好的方式,可以分為縱拆和橫拆。縱拆可以理解為把Java應用劃分為不同模組,各個模組使用一個獨立的Java程式。而橫拆則是同樣功能的應用部署多個JVM。

    通過部署多個JVM,可以把每個JVM的記憶體控制一個垃圾回收可以忍受的範圍內即可。但是這相當於進行了分散式的處理,其額外帶來的複雜性也是需要評估的。另外,也有支援分散式的這種JVM可以考慮,不要要錢哦:)

 

 

程式控制的物件生命週期

    這種方式是理想當中的方式,目前的虛擬機器還沒有,純屬假設。即:考慮由程式設計方式配置哪些物件在垃圾收集過程中可以直接跳過,減少垃圾回收執行緒遍歷標記的時間。

    這種方式相當於在程式設計的時候告訴虛擬機器某些物件你可以在*時間後在進行收集或者由程式碼標識可以收集了(類似C、C++),在這之前你即便去遍歷他也是沒有效果的,他肯定是還在被引用的。

    這種方式如果JVM可以實現,個人認為將是一個飛躍,Java即有了垃圾回收的優勢,又有了C、C++對記憶體的可控性。

 

 

執行緒分配

    Java的阻塞式的執行緒模型基本上可以拋棄了,目前成熟的NIO框架也比較多了。阻塞式IO帶來的問題是執行緒數量的線性增長,而NIO則可以轉換成為常數執行緒。因此,對於服務端的應用而言,NIO還是唯一選擇。不過,JDK7中為我們帶來的AIO是否能讓人眼前一亮呢?我們拭目以待。

 

 

其他的JDK

    本文說的都是Sun的JDK,目前常見的JDK還有JRocket和IBM的JDK。其中JRocket在IO方面比Sun的高很多,不過Sun JDK6.0以後提高也很大。而且JRocket在垃圾回收方面,也具有優勢,其可設定垃圾回收的最大暫停時間也是很吸引人的。不過,系統Sun的G1實現以後,在這方面會有一個質的飛躍。


相關文章