【轉】經過軟引用和弱引用提高JVM內存使用性能的方法

本文章是基於https://www.cnblogs.com/Skyar/p/5962253.html基礎上修改而來。在其基礎上優化了一下。添加了一些代碼。若有問題,請告知我。及時修改html

首先分清楚三個概念java

強引用、軟引用、弱引用、數據庫

1.強引用緩存

    指向經過new獲得的內存空間的引用叫強引用。好比有String a = newString(「123」);,其中的a就是一個強引用,它指向了一塊內容是123的堆空間。數據結構

2.軟引用ide

    若是一個對象只具備軟引用,而當前虛擬機堆內存空間足夠,那麼垃圾回收器就不會回收它,反之就會回收這些軟引用指向的對象。性能

3.弱引用測試

    與軟引用的區別在於,垃圾回收器一旦發現某塊內存上只有弱引用(必定請注意只有弱引用,沒強引用),無論當前內存空間是否足夠,那麼都會回收這塊內存。優化

2、強引用、軟引用demo網站

1    import java.lang.ref.SoftReference;
2    import java.lang.ref.WeakReference;
3    public class ReferenceDemo {
4        public static void main(String[] args) {
5            // 強引用
6            String str=new String("abc"); 
7            SoftReference<String> softRef=new SoftReference<String>(str);  // 軟引用   
8            str = null;  // 去掉強引用
9            System.gc(); // 垃圾回收器進行回收
10            System.out.println(softRef.get());        
11            // 強引用
12            String abc = new String("123");
13            WeakReference<String> weakRef=new WeakReference<String>(abc); // 弱引用    
14            abc = null;  // 去掉強引用
15            System.gc(); // 垃圾回收器進行回收
16            System.out.println(weakRef.get());
17        }
18    }

3、使用場景

    1.軟引用的使用場景

    

好比在一個博客管理系統裏,爲了提高訪問性能,在用戶在點擊博文時,若是這篇博文沒有緩存到內存中,則須要作緩存動做,這樣其它用戶在點擊一樣這篇文章時,就能直接從內存裏裝載,而不用走數據庫,這樣能下降響應時間。

    咱們能夠經過數據庫級別的緩存在作到這點,這裏也能夠經過軟引用來實現,具體的實現步驟以下。

    第一,能夠經過定義Content類來封裝博文的內容,其中能夠包括文章ID、文章內容、做者、發表時間和引用圖片等相關信息。

    第二,能夠定義一個類型爲HashMap<String, SoftReference<Content>>的對象類保存緩存內容,其中鍵是String類型,表示文章ID,值是指向Content的軟引用。

    第三,當用戶點擊某個ID的文章時,根據ID到第二步定義的HashMap裏去找,若是找到,並且所對應的SoftReference<Content>值內容不是null,則直接從這裏拿數據並作展現動做,這樣不用走數據庫,能夠提高性能。

    第四,若是用戶點擊的某個文章的ID在HashMap裏找不到,或者雖然找到,但對應的值內容是空,那麼就從數據庫去找,找到後顯示這個文章,同時再把它插入到HashMap裏,這裏請注意,顯示後須要撤銷掉這個Content類型對象上的強引用,保證它上面只有一個軟引用。

    來分析下用軟引用有什麼好處?假設咱們用1個G的空間緩存了10000篇文章,這10000篇文章所佔的內存空間上只有軟引用。若是內存空間足夠,那麼咱們能夠經過緩存來提高性能,但萬一內存空間不夠,咱們能夠依次釋放這10000篇文章所佔的1G內存,釋放後不會影響業務流程,最多就是下降些性能。

    對比一下,若是咱們這裏不用軟應用,而是用強引用來緩存,因爲不知道文章什麼時候將被點擊,咱們還沒法得知何時能夠撤銷這些文章對象上的強引用,或者即便咱們引入了一套緩存淘汰流程,但這就是額外的工做了,這就沒剛纔使用「軟引用「那樣方便了。

 

    2.經過WeakHashMap來了解弱引用的使用場景

        WeakHashMap和HashMap很類似,能夠存儲鍵值對類型的對象,但咱們能夠從它的名字上看出,其中的引用是弱引用。經過下面的WeakHashMapDemo.java,咱們來看下它的用法。    

    

1    import java.util.HashMap;  
2    import java.util.Iterator;  
3    import java.util.Map;  
4    import java.util.WeakHashMap;    
5    public class WeakHashMapDemo {  
6        public static void main(String[] args) throws Exception {  
7            String a = new String("a");  
8            String b = new String("b");  
9            Map weakmap = new WeakHashMap();  
10            Map map = new HashMap();  
11            map.put(a, "aaa");  
12            map.put(b, "bbb");            
13            weakmap.put(a, "aaa");  
14            weakmap.put(b, "bbb");            
15            map.remove(a);            
16            a=null;  
17            b=null;            
18            System.gc();  
19            Iterator i = map.entrySet().iterator();  
20            while (i.hasNext()) {  
21                Map.Entry en = (Map.Entry)i.next();              System.out.println("map:"+en.getKey()+":"+en.getValue());  
22            }    
23            Iterator j = weakmap.entrySet().iterator();  
24            while (j.hasNext()) {  
25                Map.Entry en = (Map.Entry)j.next();System.out.println("weakmap:"+en.getKey()+":"+en.getValue());                
26            }  
27        }  
28    }

 

行號

針對內存的操做以及輸出結果

7

在堆空間裏分配一塊空間(假設首地址是1000),在其中寫入String類型的a,並用a這個強引用指向這塊空間。

8

在堆空間裏分配一塊空間(假設首地址是2000),在其中寫入String類型的b,並用b這個強引用指向這塊空間。

11,12

在HashMap裏了插入兩個鍵值對,其中鍵分別是a和b引用,這樣1000號和2000號內存上就分別多加了一個強引用了(有兩個強引用了)。

13,14

在WeakHashMap裏了插入兩個鍵值對,其中鍵分別是a和b引用,這樣1000號和2000號內存上就分別多加了一個弱引用了(有兩個強引用,和一個弱引用)。

15

從HashMap裏移出鍵是a引用的鍵值對,這時1000號內存上有一個String類型的強引用和一個弱引用。

16

撤銷掉1000號內存上的a這個強引用,此時1000號內存上只有一個弱引用了。

17

撤銷掉2000號內存上的b這個強引用,此時2000號內存上有一個HashMap指向的強引用和一個WeakHashMap指向的弱引用。

18

經過System.gc()回收內存

19~22

遍歷並打印HashMap裏的對象,這裏爭議不大,在11和12行放入了a和b這兩個強引用的鍵,在第15行移出a,因此會打印map:b:bbb。

23~25

遍歷並打印WeakHashMap裏的對象,這裏的輸出是weakmap:b:bbb。

雖然咱們沒有從WeakHashMap裏移除a這個引用,但以前a所對應的1000號內存上的強引用全都已經被移除,只有一個弱引用,因此在第18行時,1000號內存裏的內存已經被回收,因此WeakHashMap裏也看不到a了,只能看到b。

 3. 弱引用的使用場景2

好比在某個電商網站項目裏,咱們會用Coupan這個類來保存優惠券信息,在其中咱們能夠定義優惠券的打折程度,有效日期和所做用的商品範圍等信息。當咱們從數據庫裏獲得全部的優惠券信息後,會用一個List<Coupan>類型的coupanList對象來存儲全部優惠券。

    並且,咱們想要用一種數據結構來保存一個優惠券對象以及它所關聯的全部用戶,這時咱們能夠用WeakHashMap<Coupan, <List<WeakReference <User>>>類型的weakCoupanHM對象。其中它的鍵是Coupan類型,值是指向List<User>用戶列表的弱引用。

    你們能夠想象下,若是有100個優惠券,那麼它們會存儲於List<Coupan>類型的coupanList,同時,WeakHashMap<Coupan, <List<WeakReference <User>>>類型的weakCoupanHM對象會以鍵的形式存儲這100個優惠券。並且,若是有1萬個用戶,那麼咱們能夠用List<User>類型的userList對象來保存它們,假設coupan1這張優惠券對應着100個用戶,那麼咱們必定會經過以下的代碼存入這種鍵值對關係,weakCoupanHM.put(coupan1,weakUserList);,其中weakUserList裏以弱引用的方式保存coupan1所對應的100個用戶。

    這樣的話,一旦當優惠券或用戶發生變動,它們的對應關係就能自動地更新,具體表現以下。

    1 當某個優惠券(假設對應於coupan2對象)失效時,咱們能夠從coupanList裏去除該對象,coupan2上就沒有強引用了,只有weakCoupanHM對該對象還有個弱引用,這樣coupan2對象能在下次垃圾回收時被回收,從而weakCoupanHM裏就看不到了。

    2 假設某個優惠券coupan3用弱引用的方式指向於100個用戶,當某個用戶(假設user1)註銷帳號時,它會被從List<User>類型的userList對象中被移除。這時該對象上只有weakCoupanHM裏的值(也就是<List<WeakReference <User>>)這個弱引用,該對象一樣能在下次垃圾回收時被回收,這樣coupan3的關聯用戶就會自動地更新爲99個。

    若是不用弱引用,而是用常規的HashMap<Coupan,List<User>>來保存對應關係的話,那麼一旦出現優惠券或用戶的變動的話,那麼咱們就不得不手動地更新這個表示對應關係的HashMap對象了,這樣,代碼就會變得複雜,並且咱們頗有可能因疏忽而忘記在某個位置添加更新代碼。相比之下,弱引用給咱們帶來的「自動更新「就能給咱們帶來很大的便利。

/***
 * 用戶
 */
public class User {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
}
/**
 * 優惠券
 */
public class Coupan {
    private String coupanName;

    public String getCoupanName() {
        return coupanName;
    }

    public void setCoupanName(String coupanName) {
        this.coupanName = coupanName;
    }

    @Override
    public String toString() {
        return "Coupan{" +
                "coupanName='" + coupanName + '\'' +
                '}';
    }
}
import java.lang.ref.WeakReference;
import java.util.*;

public class TestCoupan {
    public static void main(String[] args) {
        WeakHashMap<Coupan, List<WeakReference<User>>> weakCoupanHM = new WeakHashMap<>();
        List<Coupan> coupans = new ArrayList<>();
        List<User> users = new ArrayList<>();

        for (int i = 1 ; i< 100 ; i ++){
            Coupan coupan = new Coupan();
            coupan.setCoupanName("優惠券編碼>"+i);
            coupans.add(coupan);
        }

        for (int i = 1; i < 10000; i++){
            User user = new User();
            user.setName("用戶>"+i);
            users.add(user);
        }

        List<WeakReference<User>> weakUsers = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            User u = users.get(i);
            WeakReference<User> weakUser = new WeakReference<User>(u); ;
            weakUsers.add(weakUser);
        }
        weakCoupanHM.put(coupans.get(1),weakUsers);

        printWeakCoupanHM(weakCoupanHM);
//        coupans.remove(1); //能夠用這一行測試優惠券
        users.remove(1);
        System.gc();
        printWeakCoupanHM(weakCoupanHM);
    }
    private static void printWeakCoupanHM(WeakHashMap<Coupan, List<WeakReference<User>>> weakCoupanHM){
        Iterator<Map.Entry<Coupan, List<WeakReference<User>>>> weakCoupanItr = weakCoupanHM.entrySet().iterator();
        while(weakCoupanItr.hasNext()){
            Map.Entry<Coupan, List<WeakReference<User>>> entry = weakCoupanItr.next();
            Coupan coupan = entry.getKey();
            System.out.println(coupan);

            for (WeakReference<User> weakU: entry.getValue()
                    ) {
                System.out.println(weakU.get());
            }
        }
    }
}

 

研讀過程當中,畫了一些草圖,但願對讀者有必定的幫助

 

相關文章
相關標籤/搜索