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

Java內(nèi)存泄漏問(wèn)題排查與解決

 更新時(shí)間:2022年01月26日 15:16:40   作者:文曉武  
大家好,本篇文章主要講的是Java內(nèi)存泄漏問(wèn)題排查與解決,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下

前言

Java 最牛逼的一個(gè)特性就是垃圾回收機(jī)制,不用像 C++ 需要手動(dòng)管理內(nèi)存,所以作為 Java 程序員很幸福,只管 New New New 即可,反正 Java 會(huì)自動(dòng)回收過(guò)期的對(duì)象。。。

那么 Java 都自動(dòng)管理內(nèi)存了,那怎么會(huì)出現(xiàn)內(nèi)存泄漏,難道 Jvm 有 bug? 不要急,且聽(tīng)我慢慢道來(lái)。。

1. 怎么判斷可以被回收

先了解一下 Jvm 是怎么判斷一個(gè)對(duì)象可以被回收。一般有兩種方式,一種是引用計(jì)數(shù)法,一種是可達(dá)性分析。

引用計(jì)數(shù)法:每個(gè)對(duì)象有一個(gè)引用計(jì)數(shù)屬性,新增一個(gè)引用時(shí)計(jì)數(shù)加1,引用釋放時(shí)計(jì)數(shù)減1,計(jì)數(shù)為0時(shí)可以回收。

這個(gè)辦法看起來(lái)挺簡(jiǎn)單的,但是如果出現(xiàn) A 引用了 B,B 又引用了 A,這時(shí)候就算他們都不再使用了,但因?yàn)橄嗷ヒ?計(jì)算器=1 永遠(yuǎn)無(wú)法被回收。

此方法簡(jiǎn)單,無(wú)法解決對(duì)象相互循環(huán)引用的問(wèn)題。

可達(dá)性分析(Reachability Analysis):從 GC Roots 開(kāi)始向下搜索,搜索所走過(guò)的路徑稱為引用鏈。當(dāng)一個(gè)對(duì)象到 GC Roots 沒(méi)有任何引用鏈相連時(shí),則證明此對(duì)象是不可用的,那么虛擬機(jī)就判斷是可回收對(duì)象。

可達(dá)性分析可以解決循環(huán)引用的問(wèn)題。

那么 gc roots 對(duì)象是哪些呢

虛擬機(jī)棧中引用的對(duì)象

方法區(qū)中類靜態(tài)屬性引用的對(duì)象

方法區(qū)中常量引用的對(duì)象

本地方法棧中JNI[即一般說(shuō)的Native]引用的對(duì)象

目前主流的虛擬機(jī)中大多使用可達(dá)性分析的方式來(lái)判定對(duì)象是否可被 GC 回收。

2. 什么情況下會(huì)出現(xiàn)內(nèi)存泄漏

既然可達(dá)性分析好像已經(jīng)很牛逼的樣子了,怎么可能還會(huì)出現(xiàn)內(nèi)存泄漏呢,那我們?cè)賮?lái)看一下內(nèi)存泄漏的定義。

內(nèi)存泄露就是指一個(gè)不再被程序使用的對(duì)象或變量一直被占據(jù)在內(nèi)存中。

有可能此對(duì)象已經(jīng)不使用了,但是還有其它對(duì)象保持著此對(duì)象的引用,就會(huì)導(dǎo)致 GC 不能回收此對(duì)象,這種情況下就會(huì)出現(xiàn)內(nèi)存泄漏。

寫一個(gè)程序讓出現(xiàn)內(nèi)存泄漏

①長(zhǎng)生命周期的對(duì)象持有短生命周期對(duì)象的引用就很可能發(fā)生內(nèi)存泄露,盡管短生命周期對(duì)象已經(jīng)不再需要,但是因?yàn)殚L(zhǎng)生命周期對(duì)象持有它的引用而導(dǎo)致不能被回收。

public class Simple {
    Object object;
    public void method1(){
        object = new Object();
        //...其他代碼
    }
}

這里的 object 實(shí)例,其實(shí)我們期望它只作用于 method1() 方法中,且其他地方不會(huì)再用到它,但是,當(dāng)method1()方法執(zhí)行完成后,object 對(duì)象所分配的內(nèi)存不會(huì)馬上被認(rèn)為是可以被釋放的對(duì)象,只有在 Simple 類創(chuàng)建的對(duì)象被釋放后才會(huì)被釋放,嚴(yán)格的說(shuō),這就是一種內(nèi)存泄露。

解決方法就是將 object 作為 method1() 方法中的局部變量。

public class Simple {
    Object object;
    public void method1(){
        object = new Object();
        //...其他代碼
        object = null;
    }
}

當(dāng)然大家有可能會(huì)想就這一個(gè)方法也不會(huì)有多大影響,但如果在某些項(xiàng)目中,一個(gè)方法在一分鐘之內(nèi)調(diào)用上萬(wàn)次的時(shí)候,就會(huì)出現(xiàn)很明顯的內(nèi)存泄漏現(xiàn)象。

②集合中的內(nèi)存泄漏,比如 HashMap、ArrayList 等,這些對(duì)象經(jīng)常會(huì)發(fā)生內(nèi)存泄露。比如當(dāng)它們被聲明為靜態(tài)對(duì)象時(shí),它們的生命周期會(huì)跟應(yīng)用程序的生命周期一樣長(zhǎng),很容易造成內(nèi)存不足。

下面給出了一個(gè)關(guān)于集合內(nèi)存泄露的例子。

Vector v=new Vector(10);
for (int i=1;i<100; i++)
{
    Object o=new Object();
    v.add(o);
    o=null;
}
//此時(shí),所有的Object對(duì)象都沒(méi)有被釋放,因?yàn)樽兞縱引用這些對(duì)象。

在這個(gè)例子中,我們循環(huán)申請(qǐng) Object 對(duì)象,并將所申請(qǐng)的對(duì)象放入一個(gè) Vector 中,如果我們僅僅釋放引用本身,那么 Vector 仍然引用該對(duì)象,所以這個(gè)對(duì)象對(duì) GC 來(lái)說(shuō)是不可回收的。

因此,如果對(duì)象加入到 Vector 后,還必須從 Vector 中刪除,最簡(jiǎn)單的方法就是將 Vector 對(duì)象設(shè)置為 null。

以上兩種是最常見(jiàn)的內(nèi)存泄漏案例。當(dāng)然還有一些內(nèi)存泄漏的例子,這里就不再一一例舉了,感興趣的同學(xué)可以在網(wǎng)上找找資料。

3. 如何檢測(cè)內(nèi)存泄漏

3.1 模擬內(nèi)存泄漏代碼

package com.wenxiaowu.solution;
 
import java.util.ArrayList;
import java.util.List;
 
/**
 * Java 內(nèi)存泄露模擬
 */
public class TestMemoryLeak {
    public static void main(String[] args) throws InterruptedException {
        List<SimpleObject> list = new ArrayList<>();
        Runtime run = Runtime.getRuntime();
        int i = 1;
 
        while (true) {
            SimpleObject simpleObject = new SimpleObject();
            list.add(simpleObject);
            simpleObject = null;
 
            if (i++ % 1000 == 0) {
                System.out.print(i + ": 最大內(nèi)存=" + run.maxMemory() / 1024 / 1024 + "M,");
                System.out.print("已分配內(nèi)存=" + run.totalMemory() / 1024 / 1024 + "M,");
                System.out.print("剩余空間內(nèi)存=" + run.freeMemory() / 1024 / 1024 + "M");
                System.out.println("最大可用內(nèi)存=" + (run.maxMemory() - run.totalMemory() + run.freeMemory()) / 1024 / 1024 + "M");
            }
            Thread.sleep(1);
        }
    }
}
 
class SimpleObject {
    // 初始化占用1M內(nèi)存的數(shù)組
    private int[] arr = new int[1024 * 8];
    public int[] getArr() {
        return arr;
    }
}

3.2 生成dump文件

java -jar -Xmx1G -Xms1G -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp wenxiaowu-java.jar

3.3 用JProfile加載dump文件

可以看到,紅框處的int數(shù)組占用空間最大

3.4 定位最大占用內(nèi)存的原始位置

最終找到這個(gè)對(duì)象是在main方法中創(chuàng)建的。

解決辦法 

使用完之后,先刪除對(duì)應(yīng)的引用,再將對(duì)象置為null,即可正常進(jìn)行內(nèi)存回收。

總結(jié)

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

相關(guān)文章

  • Java RabbitMQ的TTL和DLX全面精解

    Java RabbitMQ的TTL和DLX全面精解

    過(guò)期時(shí)間TTL表示可以對(duì)消息設(shè)置預(yù)期的時(shí)間,在這個(gè)時(shí)間內(nèi)都可以被消費(fèi)者接收獲??;過(guò)了之后消息將自動(dòng)被刪除。DLX, 可以稱之為死信交換機(jī),當(dāng)消息在一個(gè)隊(duì)列中變成死信之后,它能被重新發(fā)送到另一個(gè)交換機(jī)中,這個(gè)交換機(jī)就是DLX ,綁定DLX的隊(duì)列就稱之為死信隊(duì)列
    2021-09-09
  • Java的WeakHashMap源碼解析及使用場(chǎng)景詳解

    Java的WeakHashMap源碼解析及使用場(chǎng)景詳解

    這篇文章主要介紹了Java的WeakHashMap源碼解析及使用場(chǎng)景詳解,Map本身生命周期很長(zhǎng),需要長(zhǎng)期貯留內(nèi)存中,但Map中的Entry可以刪除,使用時(shí)可以從其它地方再次取得,需要的朋友可以參考下
    2023-09-09
  • SpringBoot實(shí)現(xiàn)PDF添加水印的三種方法

    SpringBoot實(shí)現(xiàn)PDF添加水印的三種方法

    本文主要介紹了SpringBoot實(shí)現(xiàn)PDF添加水印的三種方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • 兩個(gè)jar包下相同包名類名引入沖突的解決方法

    兩個(gè)jar包下相同包名類名引入沖突的解決方法

    本文主要介紹了兩個(gè)jar包下相同包名類名引入沖突的解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • SpringMVC前后端參數(shù)映射

    SpringMVC前后端參數(shù)映射

    在web開(kāi)發(fā)中我們都要進(jìn)行前端傳參后端取參的過(guò)程,本文主要介紹了SpringMVC前后端參數(shù)映射,針對(duì)GET,?POST,?PUT,?DELETE?請(qǐng)求的參數(shù)該如何映射,感興趣的可以了解一下
    2023-08-08
  • 圖書(shū)管理系統(tǒng)java版

    圖書(shū)管理系統(tǒng)java版

    這篇文章主要為大家詳細(xì)介紹了java版的圖書(shū)管理系統(tǒng),通過(guò)實(shí)例為大家快速掌握數(shù)據(jù)庫(kù)編程技術(shù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-06-06
  • Java 匿名對(duì)象與匿名內(nèi)部類的使用

    Java 匿名對(duì)象與匿名內(nèi)部類的使用

    很多小伙伴對(duì)匿名對(duì)象和匿名內(nèi)部類的寫法有點(diǎn)陌生,本文主要介紹了Java 匿名對(duì)象與匿名內(nèi)部類的使用,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-01-01
  • @insert mybatis踩坑記錄,實(shí)體接收前端傳遞的參數(shù)

    @insert mybatis踩坑記錄,實(shí)體接收前端傳遞的參數(shù)

    這篇文章主要介紹了@insert mybatis踩坑記錄,實(shí)體接收前端傳遞的參數(shù)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • java開(kāi)發(fā)之Jdbc分頁(yè)源碼詳解

    java開(kāi)發(fā)之Jdbc分頁(yè)源碼詳解

    這篇文章主要介紹了java開(kāi)發(fā)之Jdb分頁(yè)源碼詳解,需要的朋友可以參考下
    2020-02-02
  • 解決SpringMVC 返回Java8 時(shí)間JSON數(shù)據(jù)的格式化問(wèn)題處理

    解決SpringMVC 返回Java8 時(shí)間JSON數(shù)據(jù)的格式化問(wèn)題處理

    本篇文章主要介紹了解決SpringMVC 返回Java8 時(shí)間JSON數(shù)據(jù)的格式化問(wèn)題處理,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-02-02

最新評(píng)論