欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java中的HashMap內(nèi)存泄漏問題詳解

 更新時間:2023年09月07日 10:19:34   作者:唐宋xy  
這篇文章主要介紹了Java中的HashMap內(nèi)存泄漏問題詳解,WeakHashMap中的key是弱引用,如果再使用之后沒有及時remove掉這個key,那么當GC時key就可能會被回收,導致key對應的value對象占用的內(nèi)存無法回收進而導致內(nèi)存泄漏,需要的朋友可以參考下

前言

眾所周知, WeakHashMap 中的 key 是弱引用,如果再使用之后沒有及時 remove 掉這個key,那么當GC時key就可能會被回收,導致key對應的value對象占用的內(nèi)存無法回收進而導致內(nèi)存泄漏,如果有大量的key可能會導致內(nèi)存溢出、頻繁FullGC等問題。

說了這么多導致內(nèi)存泄漏是因為 WeakHashMap 的 key 是弱引用從而導致內(nèi)存泄漏,但是 HashMap 的key是強引用,也會導致內(nèi)存泄漏嗎?

Java中的四種引用

先簡單了解一下Java中的四種引用分別是什么

強引用

如果一個對象是強引用, GC 不會回收該對象(在該對象可達時),就算發(fā)生內(nèi)存溢出也不會回收該對象。一般手動 new 出來的對象都是強引用

什么時候回收強引用對象呢?平常寫代碼中那么多 new 的對象怎么沒有發(fā)生內(nèi)存溢出呢

平常代碼中有大量的例如:Objedt obj = new Object() 這種手動創(chuàng)建的對象既然都是強引用,不會被回收,JVM還不是分分鐘爆炸?

其實,強引用對象也是會被回收的,根據(jù)GC回收的算法—可達性分析算法進行判斷,當一個對象不可達時就會被回收(不可達就不繼續(xù)展開啦),或者手動將 obj=null ,也會回收該對象。因為obj指向這個 new 出來的對象,所以是可達的對象不會被回收,但是 obj 一般是在棧中,當前線程執(zhí)行完成,該方法棧就會被回收,所以obj也會被回收,進而導致堆中該對象沒有引用可達而被回收。

軟引用

如果堆內(nèi)存空間充足,則一般GC時不會回收軟引用對象,只有在堆內(nèi)存空間不足的時候才會回收軟引用內(nèi)存空間的對象。使用場景:(和弱引用相同的作用)一般可以用作本地緩存,防止緩存中的數(shù)據(jù)量太大導致內(nèi)存溢出

弱引用

無論堆內(nèi)存空間是否充足,在每次GC的時候都可能會對弱引用對象進行回收。

虛引用

虛引用,正如其名,對一個對象而言,這個引用形同虛設,有和沒有一樣,簡單來說虛引用就相當于沒有引用。它的作用就是在GC的時候會發(fā)出一個系統(tǒng)通知

HashMap內(nèi)存泄漏場景

HashMap的 put 方法分析

先分析一下HashMap的put/get

HashMap 的 put 方法在保存數(shù)據(jù)的時候會根據(jù)** key 計算出來hash值**,然后根據(jù)hash值獲取到數(shù)組的下標的位置,沒有發(fā)生hash沖突就只直接保存,如果發(fā)生hash沖突則形成鏈表。

get 方法是根據(jù)key的hash值得到數(shù)組的下標,然后直接獲取或者遍歷鏈表并調(diào)用 equals 方法來獲取數(shù)據(jù)。

第一種:內(nèi)存泄漏代碼

下面的代碼會導致內(nèi)存泄漏嗎

class Person {
  String name;
  int age;
  //省略getter/setter方法
  ...
}
HashMap<Person, Integer> map = new HashMap<>();
Person gay = new Person("gay倫", 18);
Person yase = new Person("亞瑟", 10);
Person timo = new Person("提莫", 11);
// 保存
map.put(gay, 1);
map.put(yase, 2);
map.put(timo, 3);
// 第一次獲取
Integer x = map.get(gay);
System.out.println(x);
Integer y = map.get(new Person("gay倫", 18));
System.out.println(y); // 這里可以直接獲取到對象嗎?

控制臺輸出:

null

原因分析

相信注重細節(jié)的小伙伴已經(jīng)看出來了,為什么第二次沒有獲取到對應的 value 呢,這是因為 Person 沒有重寫** hashcode 和 equals ,導致默認的計算的 hashcode 不同,所以無法獲取到第一次保存的 value (還記得上面的前戲**嗎?前戲是非常重要的)

簡單說就是兩次計算的 hashcode 和 equals 不同導致HashMap的 get 方法無法獲取到對應的 value

如果 HashMap 一直沒有回收(例如定義為常量、靜態(tài)變量),那么無法 remove 對應的key,就會導致value一直被HashMap引用,從而導致占用的內(nèi)存無法回收,導致內(nèi)存泄漏。

解決方案

解決方案:如果是自定義的類,最好重寫 hashcode 和 equals ,如果是保存到Map中,則一定要重寫 hashcode 和 equals

修正代碼示例

使用IDEA自動生成的hashcode和equals

...
// 加上equals和hashcode =》 IDEA自動生成
@Override
public boolean equals(Object o) {
  if (this == o) return true;
  if (o == null || getClass() != o.getClass()) return false;
  Person person = (Person) o;
  return age == person.age &&
  Objects.equals(name, person.name);
}
@Override
public int hashCode() {
	return Objects.hash(name, age);
}
...

控制臺輸出:

11

第二種:內(nèi)存泄漏代碼

看看下面的代碼是否會導致內(nèi)存泄漏

class Person {
  String name;
  int age;
  //省略getter/setter方法
  ...
  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Person person = (Person) o;
    return age == person.age &&
    Objects.equals(name, person.name);
  }
  @Override
  public int hashCode() {
    return Objects.hash(name, age);
  }
}
HashMap<Person, Integer> map = new HashMap<>();
Person gay = new Person("gay倫", 18);
Person yase = new Person("亞瑟", 10);
Person timo = new Person("提莫", 11);
// 保存
map.put(gay, 1);
map.put(yase, 2);
map.put(timo, 3);
// 第一次獲取
Integer x = map.get(gay);
System.out.println(x);
// 修改對象的name屬性
gay.setName("蓋倫");
// 第二次獲取
Integer y = map.get(gay);
System.out.println(y); // 這里可以獲取到對象嗎?

控制臺輸出:

null

原因分析

怎么就改一下 name 就獲取不到了?(其實還是要看前戲)看見上面的 hashcode 和 equals 了沒有, key 的 hashcode 是通過 Objects.hash(name, age); 計算的,hash值和 name 和 age 有關的,如果其中一個改變了,那么計算出來的 hash 值就不同,計算出來的下標可能就不同,所以就獲取不到 value

解決方案

在重寫 hashcode 和 equals 方法的時候,一定要注意參與計算的相關字段,最好不要更改參與計算的字段屬性值,如果可以的話,可以將參與計算的字段定義為 final 類型

修正代碼演示

class Person {
  String name;
  // 定義為final
  final int age;
  //省略getter/setter方法
  ...
  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Person person = (Person) o;
    // 只判斷name相同即可
    return age == person.age;
  }
  @Override
  public int hashCode() {
    // hash值通過final定義的age計算
    return Objects.hash(age);
  }
}

控制臺輸出:

11

小總結

WeakHashMap 在某些情況下因為 key 是弱引用,所以會導致內(nèi)存溢出。

ThreadLocal 為什么會內(nèi)存溢出呢?

就是因為其內(nèi)部類 Entry 繼承了 WeakReference ,所以一般使用完成之后要及時的 remove HashMap在某些情況下也會內(nèi)存溢出,所以在自定義類的時候要注意重寫equals和hashcode方法。

到此這篇關于Java中的HashMap內(nèi)存泄漏問題詳解的文章就介紹到這了,更多相關HashMap內(nèi)存泄漏內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • SpringBoot整合Swagger Api自動生成文檔的實現(xiàn)

    SpringBoot整合Swagger Api自動生成文檔的實現(xiàn)

    本文主要介紹了SpringBoot整合Swagger Api自動生成文檔的實,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-06-06
  • SpringBoot上傳文件到本服務器 目錄與jar包同級問題

    SpringBoot上傳文件到本服務器 目錄與jar包同級問題

    這篇文章主要介紹了SpringBoot上傳文件到本服務器 目錄與jar包同級問題,需要的朋友可以參考下
    2018-11-11
  • java 基礎知識之網(wǎng)絡通信(TCP通信、UDP通信、多播以及NIO)總結

    java 基礎知識之網(wǎng)絡通信(TCP通信、UDP通信、多播以及NIO)總結

    這篇文章主要介紹了java 基礎知識之網(wǎng)絡通信總結的相關資料,包括TCP通信、UDP通信、多播以及NIO,需要的朋友可以參考下
    2017-03-03
  • Java中實現(xiàn)String字符串分割的3種方法

    Java中實現(xiàn)String字符串分割的3種方法

    這篇文章主要介紹了Java中實現(xiàn)String字符串分割的3種方法,文章底部介紹了JAVA?截取字符串的三種方法subString,StringUtils,split,本文結合實例代碼給大家介紹的非常詳細,需要的朋友可以參考下
    2023-05-05
  • WebService教程詳解(一)

    WebService教程詳解(一)

    WebService,顧名思義就是基于Web的服務。它使用Web(HTTP)方式,接收和響應外部系統(tǒng)的某種請求,接下來通過本文給大家介紹WebService教程詳解(一),對webservice教程感興趣的朋友一起學習吧
    2016-03-03
  • Java負載均衡策略的實現(xiàn)詳解

    Java負載均衡策略的實現(xiàn)詳解

    這篇文章主要介紹了Java負載均衡策略的實現(xiàn),負載均衡在Java領域中有著廣泛深入的應用,不管是大名鼎鼎的nginx,還是微服務治理組件如dubbo,feign等,負載均衡的算法在其中都有著實際的使用,需要的朋友可以參考下
    2022-07-07
  • Java HashMap 如何正確遍歷并刪除元素的方法小結

    Java HashMap 如何正確遍歷并刪除元素的方法小結

    這篇文章主要介紹了Java HashMap 如何正確遍歷并刪除元素的方法小結,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-05-05
  • SpringBoot 增量部署發(fā)布的實現(xiàn)步驟

    SpringBoot 增量部署發(fā)布的實現(xiàn)步驟

    本文介紹了通過拆分項目jar包和使用類加載器實現(xiàn)Spring Boot的增量部署,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2024-12-12
  • java必學必會之線程(2)

    java必學必會之線程(2)

    本文對java線程進行深入學習,重點介紹了線程同步問題、線程死鎖問題,感興趣的小伙伴們可以參考一下
    2015-12-12
  • Java輸入年份和月份判斷多少天實例代碼

    Java輸入年份和月份判斷多少天實例代碼

    這篇文章主要給大家介紹了關于Java輸入年度和月份判斷多少天的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-12-12

最新評論