openplanning

Hướng dẫn và ví dụ Java WeakHashMap

  1. WeakHashMap
  2. Primitive Keys
  3. Object Keys
  4. Tại sao chúng ta cần WeakHashMap?

1. WeakHashMap

WeakHashMap là một lớp tương tự như lớp HashMap, chúng đều sử dụng kỹ thuật băm (hashing technique) trong việc lưu trữ và truy xuất dữ liệu. Điều khác biệt là nếu một đối tượng được chỉ định là một khoá của WeakHashMap, nó có thể bị Garbage Collector (GC) (Trình gom rác) loại bỏ ra khỏi bộ nhớ nếu nó không còn được sử dụng tại một nơi nào khác mạnh hơn GC. Một khi khoá bị loại bỏ bởi GC thì ánh xạ tương ứng cũng bị loại bỏ ra khỏi WeakHashMap.
Theo suy nghĩ thông thường, nếu một đối tượng đang được sử dụng tại một nơi nào đó có nghĩa là nó đang có ích và không thể bị loại bỏ ra khỏi bộ nhớ. Tuy nhiên WeakHashMap được thiết kế đặc biệt và được coi là yếu hơn GC, một đối tượng là khoá của nó vẫn có thể bị loại bỏ khỏi bộ nhớ.
public class WeakHashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>
Lớp AnyObject sẽ tham gia vào các ví dụ trong bài viết này:
AnyObject.java
package org.o7planning.beans;

public class AnyObject  {
    private String s;
    public AnyObject(String s)  {
        this.s = s;
    }
    public String getS() {
        return s;
    }
}
Trước hết, hãy xem ví dụ dưới đây, sau đó chúng ta sẽ phân tích xem điều gì đã xẩy ra:
WeakHashMap_objectKey_ex1.java
package org.o7planning.weakhashmap.ex;

import java.util.WeakHashMap;

import org.o7planning.beans.AnyObject;

public class WeakHashMap_objectKey_ex1 {

    public static void main(String[] args) throws InterruptedException {
        // (1) Create a reference objK1 points to AnyObject("S1") object.
        AnyObject objK1 = new AnyObject("S1");
        AnyObject objK2 = new AnyObject("S2");

        // Create WeakHashMap object:
        WeakHashMap<AnyObject,Integer> map = new  WeakHashMap<AnyObject,Integer>();
        
        map.put(objK1, 1000);
        map.put(objK2, 2000);
        
        System.out.println(map);
        
        // (2) Set reference objK1 to null.
        objK1 = null;
        
        // Garbage Collector is called
        System.gc();
        System.out.println("\n --- After Garbage Collector is called: --- \n");
        
        Thread.sleep(3000);
        System.out.println(map);
    }
}
Output:
{org.o7planning.beans.AnyObject@1c4af82c=2000, org.o7planning.beans.AnyObject@5e91993f=1000}

 --- After Garbage Collector is called: ---

{org.o7planning.beans.AnyObject@1c4af82c=2000}
(1) - Ban đầu, tham chiếu objK1 được tạo ra và trỏ tới đối tượng AnyObject("S1"), nó là một khoá của WeakHashMap.
(2) - Tham chiếu objK1 được trỏ tới một giá trị null. Lúc này, không còn bất kỳ một tham chiếu mạnh nào trỏ tới đối tượng AnyObject("S1"), mặc dù nó đang được sử dụng như một khoá của WeakHashMap, nhưng nó vẫn được coi là đủ điều kiện để bị loại bỏ bởi trình gom rác (Garbage Collector). Bởi vì GC được coi là mạnh hơn so với WeakHashMap.
Garbage Collector
Garbage Collector của Java hoạt động một cách tự động để loại bỏ các đối tượng không còn được sử dụng ra khỏi bộ nhớ, bạn có thể chủ động gọi nó bằng phương thức System.gc(), tuy nhiên không đảm bảo rằng nó sẽ có tác dụng ngay lập tức.
  • Java Garbage Collector
Sửa đổi ví dụ trên, nếu chúng ta thay thế WeakHashMap bởi HashMap, đối tượng AnyObject("S1") sẽ không bị loại bỏ bởi Garbage Collector. Bởi vì HashMap được coi là mạnh hơn so với Garbage Collector.
HashMap_objectKey_ex1.java
package org.o7planning.weakhashmap.ex;

import java.util.HashMap;

import org.o7planning.beans.AnyObject;

public class HashMap_objectKey_ex1 {

    public static void main(String[] args) throws InterruptedException {
        // (1) Create a reference objK1 points to AnyObject("S1") object.
        AnyObject objK1 = new AnyObject("S1");
        AnyObject objK2 = new AnyObject("S2");

        // Create HashMap object:
        HashMap<AnyObject,Integer> map = new  HashMap<AnyObject,Integer>();
        
        map.put(objK1, 1000);
        map.put(objK2, 2000);
        
        System.out.println(map);
        
        // (2) Set reference objK1 to null.
        objK1 = null;
        
        // Garbage Collector is called
        System.gc();
        System.out.println("\n --- After Garbage Collector is called: --- \n");
        
        Thread.sleep(3000);
        System.out.println(map);
    }
}
Output:
{org.o7planning.beans.AnyObject@1c4af82c=2000, org.o7planning.beans.AnyObject@5e91993f=1000}

 --- After Garbage Collector is called: ---

{org.o7planning.beans.AnyObject@1c4af82c=2000, org.o7planning.beans.AnyObject@5e91993f=1000}
Trong bài viết này chúng ta sẽ thảo luận về các kiểu khoá khác nhau của WeakHashMap và cách GC đối xử với chúng.
No
Key Type
Example
1
Primitive
Integer aKey = 3;
String aKey = "aString";
2
Object
newAnyObject();
newString("aString");
newInteger(3);

2. Primitive Keys

Các đối tượng được tạo ra từ các giá trị nguyên thuỷ không sử dụng toán tử "new" sẽ không bị loại bỏ bởi GC nếu nó đang được sử dụng như một khoá của WeakHashMap.
Primitive Keys
Integer key1 = 1000;
Double key2 = 2000.2;
String key3 = "SomeKey";
WeakHashMap_primitiveKey_ex1.java
package org.o7planning.weakhashmap.ex;

import java.util.WeakHashMap;

public class WeakHashMap_primitiveKey_ex1 {

    public static void main(String[] args) throws InterruptedException {
        // (1) Create a reference objK1 points to a primitive value.
        Integer objK1 = 100;
        Integer objK2 = 200;

        // Create WeakHashMap object:
        WeakHashMap<Integer, String> map = new WeakHashMap<Integer, String>();

        map.put(objK1, "One Hundred");
        map.put(objK2, "Two Hundred");

        System.out.println(map);

        // (2) Set reference objK1 to null.
        objK1 = null;

        // Garbage Collector is called
        System.gc();
        System.out.println("\n --- After Garbage Collector is called: --- \n");

        Thread.sleep(5000);
        System.out.println(map);
    }
}
Output:
{200=Two Hundred, 100=One Hundred}

 --- After Garbage Collector is called: ---

{200=Two Hundred, 100=One Hundred}

3. Object Keys

Các đối tượng được tạo ra bằng toán tử "new" sẽ bị GC loại bỏ khỏi bộ nhớ nếu không có một tham chiếu mạnh nào đó trỏ tới nó, kể cả khi nó đang được sử dụng như một khoá của WeakHashMap. Như đã nói ở trên, GC được coi là mạnh hơn so với WeakHashMap.
Object Keys
AnyObject objK1 = new AnyObject("S1");
String objK2 = new String("S2");
Integer objK3 = new Integer(1000);
String objK4 = new String("S4");
Ví dụ:
WeakHashMap_objectKey_ex2.java
package org.o7planning.weakhashmap.ex;

import java.util.WeakHashMap;

import org.o7planning.beans.AnyObject;

public class WeakHashMap_objectKey_ex2 {

    public static void main(String[] args) throws InterruptedException {
        // (1) Create a reference objK1 points to String object.
        AnyObject objK1 = new AnyObject("S1");
        String objK2 = new String("S2");
        Integer objK3 = new Integer(1000);
        String objK4 = new String("S4");

        // Create WeakHashMap object:
        WeakHashMap<Object,Integer> map = new  WeakHashMap<Object,Integer>();
        
        map.put(objK1, 1000);
        map.put(objK2, 2000);
        map.put(objK3, 3000);
        map.put(objK4, 4000);
        
        System.out.println(map);
        
        // (2) Set references objK1, objK2, objK3 to null.
        objK1 = null;
        objK2 = null;
        objK3 = null;
        
        // Garbage Collector is called
        System.gc();
        System.out.println("\n --- After Garbage Collector is called: --- \n");
        
        Thread.sleep(3000);
        System.out.println(map);
    }
}
Output:
{S2=2000, org.o7planning.beans.AnyObject@5e91993f=1000, S4=4000, 1000=3000}

 --- After Garbage Collector is called: ---

{S4=4000}
Các đối tượng được tạo ra bằng toán tử "new" sẽ bị GC loại bỏ khỏi bộ nhớ nếu chương trình đang cần thêm bộ nhớ và nếu không có tham chiếu mạnh nào trỏ tới nó. Trường hợp này bạn không cần chủ động gọi phương thức System.gc().
Trong ví dụ dưới đây chúng ta cố gắng làm cho bộ nhớ đầy bằng cách tạo ra rất nhiều đối tượng một cách liên tục.
WeakHashMap_objectKey_ex3.java
package org.o7planning.weakhashmap.ex;

import java.util.WeakHashMap;

import org.o7planning.beans.AnyObject;

public class WeakHashMap_objectKey_ex3 {

    public static void main(String[] args) throws InterruptedException {
        // (1) Create a reference objK1 points to String object.
        AnyObject objK1 = new AnyObject("S1");
        String objK2 = new String("S2");
        Integer objK3 = new Integer(1000);
        String objK4 = new String("S4");
        String objK5 = "S5"; // Primitive Key

        // Create WeakHashMap object:
        WeakHashMap<Object,Integer> map = new  WeakHashMap<Object,Integer>();
        
        map.put(objK1, 1000);
        map.put(objK2, 2000);
        map.put(objK3, 3000);
        map.put(objK4, 4000);
        map.put(objK5, 5000);
        
        int ORIGIN_MAP_SIZE = map.size();
        
        System.out.println(map);  
        
        int i = 0;
        while(true)  {
            if(map.size() < ORIGIN_MAP_SIZE) {
                System.out.println("WeakHashMap Size: " + map.size());
                System.out.println(map);
                break;
            }
            i++;
            // (2) Make the memory full by creating lots of Strings
            String s = new String("String" + i);
            System.out.println("Create new String: " + s);
            System.out.println("   >>> Now WeakHashMap size is: " + map.size());
        }
    }
}
Output:
{S2=2000, org.o7planning.beans.AnyObject@5e91993f=1000, S5=5000, S4=4000, 1000=3000}
Create new String: String1

....

Create new String: String347615
   >>> Now WeakHashMap size is: 5
Create new String: String347616
   >>> Now WeakHashMap size is: 5
Create new String: String347617
   >>> Now WeakHashMap size is: 5
WeakHashMap Size: 1
{S5=5000}

4. Tại sao chúng ta cần WeakHashMap?

Về cơ bản, WeakHashMap là một giải pháp để tiết kiệm bộ nhớ, nó hữu ích nếu bạn cần một đối tượng Map để lưu trữ các ánh xạ ứng với các khoá mà bạn biết trong một thời điểm, các khoá không còn cần thiết sẽ bị loại bỏ một cách tự động bởi GC.
Ngoài các đặc điểm đã được đề cập ở trên, WeakHashMap có đầy đủ các đặc điểm của một Map thông thường, bạn có thể xem thêm cách sử dụng Map trong bài viết dưới đây:
Xem thêm:

Các hướng dẫn Java Collections Framework

Show More