最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

前言

眾所周知, WeakHashMap 中的 key 是弱引用,如果再使用之后沒(méi)有及時(shí) remove 掉這個(gè)key,那么當(dāng)GC時(shí)key就可能會(huì)被回收,導(dǎo)致key對(duì)應(yīng)的value對(duì)象占用的內(nèi)存無(wú)法回收進(jìn)而導(dǎo)致內(nèi)存泄漏,如果有大量的key可能會(huì)導(dǎo)致內(nèi)存溢出、頻繁FullGC等問(wèn)題。

說(shuō)了這么多導(dǎo)致內(nèi)存泄漏是因?yàn)?WeakHashMap 的 key 是弱引用從而導(dǎo)致內(nèi)存泄漏,但是 HashMap 的key是強(qiáng)引用,也會(huì)導(dǎo)致內(nèi)存泄漏嗎?

Java中的四種引用

先簡(jiǎn)單了解一下Java中的四種引用分別是什么

強(qiáng)引用

如果一個(gè)對(duì)象是強(qiáng)引用, GC 不會(huì)回收該對(duì)象(在該對(duì)象可達(dá)時(shí)),就算發(fā)生內(nèi)存溢出也不會(huì)回收該對(duì)象。一般手動(dòng) new 出來(lái)的對(duì)象都是強(qiáng)引用

什么時(shí)候回收強(qiáng)引用對(duì)象呢?平常寫(xiě)代碼中那么多 new 的對(duì)象怎么沒(méi)有發(fā)生內(nèi)存溢出呢

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

其實(shí),強(qiáng)引用對(duì)象也是會(huì)被回收的,根據(jù)GC回收的算法—可達(dá)性分析算法進(jìn)行判斷,當(dāng)一個(gè)對(duì)象不可達(dá)時(shí)就會(huì)被回收(不可達(dá)就不繼續(xù)展開(kāi)啦),或者手動(dòng)將 obj=null ,也會(huì)回收該對(duì)象。因?yàn)閛bj指向這個(gè) new 出來(lái)的對(duì)象,所以是可達(dá)的對(duì)象不會(huì)被回收,但是 obj 一般是在棧中,當(dāng)前線程執(zhí)行完成,該方法棧就會(huì)被回收,所以obj也會(huì)被回收,進(jìn)而導(dǎo)致堆中該對(duì)象沒(méi)有引用可達(dá)而被回收。

軟引用

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

弱引用

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

虛引用

虛引用,正如其名,對(duì)一個(gè)對(duì)象而言,這個(gè)引用形同虛設(shè),有和沒(méi)有一樣,簡(jiǎn)單來(lái)說(shuō)虛引用就相當(dāng)于沒(méi)有引用。它的作用就是在GC的時(shí)候會(huì)發(fā)出一個(gè)系統(tǒng)通知

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

HashMap的 put 方法分析

先分析一下HashMap的put/get

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

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

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

下面的代碼會(huì)導(dǎo)致內(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); // 這里可以直接獲取到對(duì)象嗎?

控制臺(tái)輸出:

null

原因分析

相信注重細(xì)節(jié)的小伙伴已經(jīng)看出來(lái)了,為什么第二次沒(méi)有獲取到對(duì)應(yīng)的 value 呢,這是因?yàn)?Person 沒(méi)有重寫(xiě)** hashcode 和 equals ,導(dǎo)致默認(rèn)的計(jì)算的 hashcode 不同,所以無(wú)法獲取到第一次保存的 value (還記得上面的前戲**嗎?前戲是非常重要的)

簡(jiǎn)單說(shuō)就是兩次計(jì)算的 hashcode 和 equals 不同導(dǎo)致HashMap的 get 方法無(wú)法獲取到對(duì)應(yīng)的 value

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

解決方案

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

修正代碼示例

使用IDEA自動(dòng)生成的hashcode和equals

...
// 加上equals和hashcode =》 IDEA自動(dòng)生成
@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);
}
...

控制臺(tái)輸出:

11

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

看看下面的代碼是否會(huì)導(dǎo)致內(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);
// 修改對(duì)象的name屬性
gay.setName("蓋倫");
// 第二次獲取
Integer y = map.get(gay);
System.out.println(y); // 這里可以獲取到對(duì)象嗎?

控制臺(tái)輸出:

null

原因分析

怎么就改一下 name 就獲取不到了?(其實(shí)還是要看前戲)看見(jiàn)上面的 hashcode 和 equals 了沒(méi)有, key 的 hashcode 是通過(guò) Objects.hash(name, age); 計(jì)算的,hash值和 name 和 age 有關(guān)的,如果其中一個(gè)改變了,那么計(jì)算出來(lái)的 hash 值就不同,計(jì)算出來(lái)的下標(biāo)可能就不同,所以就獲取不到 value

解決方案

在重寫(xiě) hashcode 和 equals 方法的時(shí)候,一定要注意參與計(jì)算的相關(guān)字段,最好不要更改參與計(jì)算的字段屬性值,如果可以的話,可以將參與計(jì)算的字段定義為 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值通過(guò)final定義的age計(jì)算
    return Objects.hash(age);
  }
}

控制臺(tái)輸出:

11

小總結(jié)

WeakHashMap 在某些情況下因?yàn)?key 是弱引用,所以會(huì)導(dǎo)致內(nèi)存溢出。

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

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

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

相關(guān)文章

  • Java使用Random類生成隨機(jī)數(shù)示例

    Java使用Random類生成隨機(jī)數(shù)示例

    這篇文章主要介紹了Java使用Random類生成隨機(jī)數(shù),結(jié)合實(shí)例形式分析了java基于Random類生成隨機(jī)數(shù)與遍歷輸出相關(guān)操作技巧,需要的朋友可以參考下
    2019-07-07
  • Java之stream流求字段累計(jì)和的3種方法總結(jié)

    Java之stream流求字段累計(jì)和的3種方法總結(jié)

    Java中Stream流用來(lái)幫助處理集合,類似于數(shù)據(jù)庫(kù)中的操作,下面這篇文章主要介紹了Java之stream流求字段累計(jì)和的3種方法,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-09-09
  • 關(guān)于Java下奇怪的Base64詳解

    關(guān)于Java下奇怪的Base64詳解

    這篇文章主要給大家介紹了關(guān)于Java下奇怪的Base64的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • Mybatis返回int或者Integer類型報(bào)錯(cuò)的解決辦法

    Mybatis返回int或者Integer類型報(bào)錯(cuò)的解決辦法

    這篇文章主要介紹了Mybatis返回int或者Integer類型報(bào)錯(cuò)的解決辦法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2016-12-12
  • Java集合排序規(guī)則接口Comparator用法解析

    Java集合排序規(guī)則接口Comparator用法解析

    這篇文章主要介紹了Java集合排序規(guī)則接口Comparator用法解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-09-09
  • logback-spring.xml配置解析(最新整理)

    logback-spring.xml配置解析(最新整理)

    logback-spring.xml用于配置Spring?Boot項(xiàng)目日志,定義輸出格式、路徑、級(jí)別,支持動(dòng)態(tài)參數(shù)讀?。ㄈ鏢pringProperty),實(shí)現(xiàn)日志隔離與滾動(dòng)策略(按天+大小),并通過(guò)logger繼承機(jī)制控制日志輸出目的地,對(duì)logback-spring.xml配置相關(guān)知識(shí)感興趣的朋友一起看看吧
    2025-07-07
  • Java發(fā)展史之Java由來(lái)

    Java發(fā)展史之Java由來(lái)

    本文主要給大家簡(jiǎn)單講解了一下java的發(fā)展史,詳細(xì)說(shuō)明了java的由來(lái)以及如何一步步發(fā)展起來(lái)的,想了解的小伙伴可以來(lái)參考下
    2016-10-10
  • Java拆箱與裝箱實(shí)例詳解

    Java拆箱與裝箱實(shí)例詳解

    這篇文章主要介紹了Java拆箱與裝箱,結(jié)合實(shí)例形式詳細(xì)分析了Java拆箱與裝箱相關(guān)的數(shù)據(jù)類型轉(zhuǎn)換操作技巧,需要的朋友可以參考下
    2019-11-11
  • SpringBoot整合weixin-java-pay實(shí)現(xiàn)微信小程序支付的示例代碼

    SpringBoot整合weixin-java-pay實(shí)現(xiàn)微信小程序支付的示例代碼

    微信小程序支付是常見(jiàn)的一種功能,本文主要介紹了SpringBoot整合weixin-java-pay實(shí)現(xiàn)微信小程序支付的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-05-05
  • java環(huán)境變量如何配置

    java環(huán)境變量如何配置

    這篇文章主要為大家詳細(xì)介紹了java環(huán)境變量配置教程,感興趣的小伙伴們可以參考一下
    2016-06-06

最新評(píng)論

泾川县| 常山县| 启东市| 皮山县| 彭山县| 山阳县| 抚顺县| 衡东县| 紫阳县| 卢龙县| 山东| 洛扎县| 石河子市| 二手房| 吴旗县| 元朗区| 临高县| 上饶市| 中超| 大理市| 华坪县| 富民县| 永济市| 教育| 大竹县| 苏州市| 广南县| 星子县| 板桥市| 横峰县| 长宁县| 望都县| 罗田县| 黔江区| SHOW| 方正县| 光山县| 通道| 武清区| 江源县| 黄山市|