HashMap內存泄露


背景

HashMap對於Java開發人員來說,應該是一種非常非常熟悉的數據結構了,應用場景相當廣泛。
本文重點不在於介紹如何使用HashMap,而是關注在使用HashMap過程中,可能會導致內存泄露的情況,下面將以示例的形式展開具體介紹。

注意:理解本文的前提需要先熟悉HashMap原理。
為了更快的看到java.lang.OutOfMemoryError: Java heap space,我們可以配置下IDEA的JVM參數,簡單配置下初始堆和最大堆參數為3M,-Xmx3m -Xms3m,如下圖

場景一:重寫hashcode、equals,put同一個對象,但是put前成員屬性值發生了改變

直接上示例代碼:

public class Test {
    public static void main(String[] args) {
        Map<Person, Integer> map = new HashMap<>();
        Person p = new Person("0", 10);

        for (int i = 0; i < 50000; i++) {
            p.setName(String.valueOf(i));
            map.put(p, 1);
            System.out.println(map.size());
        }

        System.out.println("end.");
    }
}

class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }

        if (obj instanceof Person) {
            Person personValue = (Person) obj;
            if (personValue.getName() == null && name == null) {
                return true;
            }
            return personValue.getName() != null && personValue.getName().equals(name);
        }
        return false;
    }

    @Override
    public int hashCode() {
        return name.hashCode();
    }
}

直接點擊運行,查看結果,發現當put第49153個對象時,報了java.lang.OutOfMemoryError: Java heap space

49152
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at java.util.HashMap.resize(HashMap.java:703)
	at java.util.HashMap.putVal(HashMap.java:662)
	at java.util.HashMap.put(HashMap.java:611)
	at app.Test.main(Test.java:23)

結果分析:本來,HashMap put同一個對象,理論上是會覆蓋的,不會導致內存泄露,這里之所以出現這種情況,主要是因為我們put的並不是同一個對象(重寫了hashcode和equals方法,且hashcode發生了改變),然后一直put,就導致對象越來越多,最終觸發OutOfMemoryError。

場景二:沒有重寫hashcode、equals,put的對象每次都是new出來的

直接上示例代碼:

public class Test {
    public static void main(String[] args) {
        Map<Person, Integer> map = new HashMap<>();
        for (int i = 0; i < 500000; i++) {
            map.put(new Person("0", 10), 1);
            System.out.println(map.size());
        }

        System.out.println("end.");
    }
}

class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

同樣,直接點擊運行,查看結果,發現也報了java.lang.OutOfMemoryError: Java heap space

39951Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
	at java.nio.CharBuffer.wrap(CharBuffer.java:373)
	at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:265)
	at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125)
	at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207)
	at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129)
	at java.io.PrintStream.newLine(PrintStream.java:545)
	at java.io.PrintStream.println(PrintStream.java:737)
	at app.Test.main(Test.java:21)

結果分析:這個沒啥好說,Object默認的hashcode對於new出來的對象都是不同的,然后一直put,就導致對象越來越多,最終觸發OutOfMemoryError。

總結

當使用HashMap執行put操作的時候,如果你期望的結果是覆蓋這個key,那么你要再三確認put的時候,key對象的hashcode有沒有發生變化,否則可能會有意想不到的結果;
建議,當想要使用對象作為HashMap的key時,可以考慮使用不可變對象作為HashMap的key,如常用的String類型,或者確保使用不可變的成員屬性來生成hashcode;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM