亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

java內(nèi)存管理關(guān)系及內(nèi)存泄露的原理分析

 更新時(shí)間:2021年10月11日 15:13:09   作者:知我飯否  
這篇文章主要介紹了java內(nèi)存管理關(guān)系及內(nèi)存泄露的原理,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

java內(nèi)存管理關(guān)系及內(nèi)存泄露原理

這可能是最近寫的博客中最接近底層的了。閑言少敘,進(jìn)入正題。

java對(duì)象和內(nèi)存的關(guān)系

首先,我們要知道下面幾條真理(自己總結(jié)的)

  • 一個(gè)完整的建立對(duì)象流程是 1聲明對(duì)象,2開辟內(nèi)存空間,3將對(duì)象和內(nèi)存空間建立聯(lián)系。
  • 一個(gè)對(duì)象只能對(duì)應(yīng)一個(gè)內(nèi)存空間,一個(gè)內(nèi)存空間可以對(duì)應(yīng)很多對(duì)象
  • 回收一個(gè)內(nèi)存空間 。如果,這個(gè)內(nèi)存空間沒(méi)有任何一個(gè)對(duì)象和他有聯(lián)系。就可以被回收,或者好幾個(gè)對(duì)象環(huán)形引用,也會(huì)被回收
  • 對(duì)一個(gè)對(duì)象進(jìn)行操作的時(shí)候,是先通過(guò) 對(duì)象 找到 內(nèi)存空間,然后 對(duì)內(nèi)存空間進(jìn)行操作(讀,寫 改 刪)

這是本人總結(jié)出來(lái)的四條經(jīng)驗(yàn)。

特別重要的是,一定要有這種認(rèn)知。不管任何語(yǔ)言,最終都是要物理內(nèi)存上面反映的,對(duì)象 和 內(nèi)存空間 是兩個(gè)不同的個(gè)體。 如果 沒(méi)有的話,那么你會(huì)發(fā)現(xiàn) 下面將的都是什么??!

創(chuàng)建對(duì)象

       Stu one; //只聲明 one對(duì)象 但是沒(méi)有分配內(nèi)存空間
        //用new 開辟新的內(nèi)存空間 oneMemory ,調(diào)用構(gòu)造函數(shù)賦值,并將內(nèi)存空間 oneMemory 與 one對(duì)象建立聯(lián)系。
        one = new Stu("one");
        
        //聲明 two對(duì)象 并開辟內(nèi)存 twoMemory 調(diào)用構(gòu)造函數(shù)賦值,并將內(nèi)存空間 twoMemory與 two對(duì)象建立聯(lián)系
        Stu two = new Stu("two");
        
        //聲明 three對(duì)象, 并找到one 對(duì)象聯(lián)系的內(nèi)存空間 oneMemory。并將 oneMemory與 three 對(duì)象建立聯(lián)系
        Stu three = one;
        
        //此時(shí) 內(nèi)存空間 oneMemory 與兩個(gè)對(duì)象有聯(lián)系。一個(gè)是 one對(duì)象,一個(gè)是three對(duì)象
        System.out.println("three 和 one 是否相等" + (three == one) + " one的哈希值" + one.hashCode() + " three的哈希值" + three.hashCode());

運(yùn)行結(jié)果

在這里插入圖片描述

我們可以發(fā)現(xiàn),three對(duì)象 和one對(duì)象 指向的是同一個(gè)內(nèi)存空間oneMemory。這個(gè)不就是符合上面所說(shuō)的第二個(gè)真理 如果,我們對(duì)one對(duì)象進(jìn)行操作,那么產(chǎn)生的影響,也會(huì)反映到three對(duì)象上。

因?yàn)椋?*他們指向的是同一個(gè)內(nèi)存空間,對(duì)one對(duì)象操作,就是做內(nèi)存空間oneMemory進(jìn)行操作。而three對(duì)象指向的也是oneMemory。這個(gè)符合上面第四條真理。**例子如下

        System.out.println("three 對(duì)象的值" + three.getName() + three.hashCode());
        //修改one的值,第一步 找到one對(duì)象聯(lián)系的內(nèi)存空間 oneMemory , 將內(nèi)存空間oneMemory 中的name值改變
        one.setName("change");
        //讀取three對(duì)象值時(shí)候,先找到three對(duì)象聯(lián)系的內(nèi)存空間oneMemory,讀取其中的name值
        System.out.println("three 對(duì)象的值" + three.getName() + three.hashCode());

null的作用

長(zhǎng)久以來(lái),我只知道,將一個(gè)值復(fù)制成null,那么他就是空的了。但是 完全不知道,為啥。

還是接著上面的例子,看一段代碼;

         //讀取three對(duì)象值時(shí)候,先找到three對(duì)象聯(lián)系的內(nèi)存空間oneMemory,讀取其中的name值
        System.out.println("three 對(duì)象的值 before" + three.getName() + three.hashCode());
        /*
         此時(shí) 如果 我們把one 對(duì)象 設(shè)置為null的。 對(duì)內(nèi)存空間 oneMemory 是沒(méi)有影響的
         =null的作用是 將one對(duì)象自己本身 對(duì)內(nèi)存空間的聯(lián)系去除,并不會(huì)影響到內(nèi)存空間和其他對(duì)象的聯(lián)系
         */
        one = null;
        System.out.println("three 對(duì)象的值  after" + three.getName() + three.hashCode());

運(yùn)行結(jié)果

在這里插入圖片描述

我們會(huì)發(fā)現(xiàn) 將one對(duì)象賦值為空后,three對(duì)象還是和先前一樣。以前一直認(rèn)為null,是將對(duì)象和他的內(nèi)存空間清楚。但現(xiàn)在不是!。代碼注釋里面寫的很清楚了。null 并不是清除內(nèi)存空間,他只是把對(duì)象自己本身和內(nèi)存空間的聯(lián)系切斷了

內(nèi)存泄露

如果,你明白了上面。那么內(nèi)存泄露對(duì)你來(lái)說(shuō)也很簡(jiǎn)單了。

再來(lái)學(xué)習(xí)一個(gè)新概念

在這里插入圖片描述

上面這么多呢。在本篇文章里面,你只需要記住 被static關(guān)鍵詞修飾的變量,類,方法的生命周期是伴隨整個(gè)程序的。就是程序活多久,他們就活多久

接下來(lái)看代碼

首先,我們定義一個(gè)靜態(tài)集合 staticList ,他是程序一運(yùn)行,就會(huì)被創(chuàng)建好的。程序結(jié)束運(yùn)行了,它才會(huì)被回收。

static List<Stu> staticList = new ArrayList<>();//開辟內(nèi)存空間 listMemory
       System.out.println("three 對(duì)象的值  after" + three.getName() + three.hashCode());
        /*
         內(nèi)存泄露 是長(zhǎng)生命周期的對(duì)象 對(duì)一個(gè)內(nèi)存空間有聯(lián)系,造成內(nèi)存空間沒(méi)有辦法被回收
         */
        /*
        將three對(duì)象添加到靜態(tài)集合里面,步驟是這樣的,
        第一步 找到three對(duì)象聯(lián)系的內(nèi)存空間 oneMemory
        第二步 找到 staticList集合對(duì)象聯(lián)系的內(nèi)存空間 listMemory
        第三步 告訴系統(tǒng) staticList集合對(duì)象的部分成員 和內(nèi)存空間 oneMemory 建立聯(lián)系。
         */
        staticList.add(three);
        
        /*
        在這里 即使three對(duì)象已經(jīng)和內(nèi)存空間 oneMemory 沒(méi)有聯(lián)系了。
        oneMemory 也不會(huì)被回收,因?yàn)樯厦嬲f(shuō)了內(nèi)存空間和對(duì)象的關(guān)系是1對(duì)多。
         而回收的條件是 一個(gè)內(nèi)存空間沒(méi)有一條和對(duì)象的聯(lián)系才可以回收。
         此時(shí) 內(nèi)存空間 和staticList集合對(duì)象的部分成員 有聯(lián)系,所以 內(nèi)存空間不會(huì)被回收。
         又由于staticList 集合對(duì)象聯(lián)系的內(nèi)存空間在 靜態(tài)存儲(chǔ)區(qū),是伴隨整個(gè)程序的。所以 在整個(gè)程序生命里面,
         內(nèi)存空間 oneMemory  就得不到 回收。  就是內(nèi)存泄露了。
         */
        three = null;
        System.out.println(staticList.get(0).hashCode());

運(yùn)行結(jié)果

在這里插入圖片描述

可以看見。在我們將three對(duì)象賦值null切斷和內(nèi)存空間 oneMemory的聯(lián)系后。靜態(tài)集合staticList對(duì)象的部分成員依然和內(nèi)存空間 oneMemory有聯(lián)系。根據(jù)上面第三條所說(shuō),因?yàn)閮?nèi)存空間 oneMemory 還是和對(duì)象有聯(lián)系的(staticList)。所以不會(huì)回收oneMemory內(nèi)存空間。又由于staticList是靜態(tài)的,生命和程序一樣長(zhǎng)。 那么在整個(gè)程序周期里面,oneMemory內(nèi)存空間 都不會(huì)被回收。就造成了內(nèi)存泄露。

附上完整的代碼

package com.zfh.test;
import java.util.ArrayList;
import java.util.List;
public class JavaMain {
    static List<Stu> staticList = new ArrayList<>();//開辟內(nèi)存空間 listMemory
    public static void main(String[] args) {
        Stu one; //只聲明 one對(duì)象 但是沒(méi)有分配內(nèi)存空間
        //用new 開辟新的內(nèi)存空間 oneMemory ,調(diào)用構(gòu)造函數(shù)賦值,并將內(nèi)存空間 oneMemory 與 one對(duì)象建立聯(lián)系。
        one = new Stu("one");
        //聲明 two對(duì)象 并開辟內(nèi)存 twoMemory 調(diào)用構(gòu)造函數(shù)賦值,并將內(nèi)存空間 twoMemory與 two對(duì)象建立聯(lián)系
        Stu two = new Stu("two");
        //聲明 three對(duì)象, 并找到one 對(duì)象聯(lián)系的內(nèi)存空間 oneMemory。并將 oneMemory與 three 對(duì)象建立聯(lián)系
        Stu three = one;
        //此時(shí) 內(nèi)存空間 oneMemory 與兩個(gè)對(duì)象有聯(lián)系。一個(gè)是 one對(duì)象,一個(gè)是three對(duì)象
        System.out.println("three 和 one 是否相等" + (three == one) + " one的哈希值" + one.hashCode() + " three的哈希值" + three.hashCode());
        System.out.println("three 對(duì)象的值" + three.getName() + three.hashCode());
        //修改one的值,第一步 找到one對(duì)象聯(lián)系的內(nèi)存空間 oneMemory , 將內(nèi)存空間oneMemory 中的name值改變
        one.setName("change");
        //讀取three對(duì)象值時(shí)候,先找到three對(duì)象聯(lián)系的內(nèi)存空間oneMemory,讀取其中的name值
        System.out.println("three 對(duì)象的值 before" + three.getName() + three.hashCode());
        /*
         此時(shí) 如果 我們把one 對(duì)象 設(shè)置為null的。 對(duì)內(nèi)存空間 oneMemory 是沒(méi)有影響的
         =null的作用是 將one對(duì)象自己本身 對(duì)內(nèi)存空間的聯(lián)系去除,并不會(huì)影響到內(nèi)存空間和其他對(duì)象的聯(lián)系
         */
        one = null;
        System.out.println("three 對(duì)象的值  after" + three.getName() + three.hashCode());
        /*
         內(nèi)存泄露 是長(zhǎng)生命周期的對(duì)象 對(duì)一個(gè)內(nèi)存空間有聯(lián)系,造成內(nèi)存空間沒(méi)有辦法被回收
         */
        /*
        將three對(duì)象添加到靜態(tài)集合里面,步驟是這樣的,
        第一步 找到three對(duì)象聯(lián)系的內(nèi)存空間 oneMemory
        第二步 找到 staticList集合對(duì)象聯(lián)系的內(nèi)存空間 listMemory
        第三步 告訴系統(tǒng) staticList集合對(duì)象的部分成員 和內(nèi)存空間 oneMemory 建立聯(lián)系。
         */
        staticList.add(three);
        /*
        在這里 即使three對(duì)象已經(jīng)和內(nèi)存空間 oneMemory 沒(méi)有聯(lián)系了。
        oneMemory 也不會(huì)被回收,因?yàn)樯厦嬲f(shuō)了內(nèi)存空間和對(duì)象的關(guān)系是1對(duì)多。
         而回收的條件是 一個(gè)內(nèi)存空間沒(méi)有一條和對(duì)象的聯(lián)系才可以回收。
         此時(shí) 內(nèi)存空間 和staticList集合對(duì)象的部分成員 有聯(lián)系,所以 內(nèi)存空間不會(huì)被回收。
         又由于staticList 集合對(duì)象聯(lián)系的內(nèi)存空間在 靜態(tài)存儲(chǔ)區(qū),是伴隨整個(gè)程序的。所以 在整個(gè)程序生命里面,
         內(nèi)存空間 oneMemory  就得不到 回收。  就是內(nèi)存泄露了。
         */
        three = null;
        System.out.println(staticList.get(0).hashCode());
    }
}

bean對(duì)象 即Stu

package com.zfh.test;
public class Stu {
  /*  static {
        System.out.println("靜態(tài)代碼塊 我只調(diào)用一次");
    }*/
  
    private String name;
    
  /*  {
        System.out.println("構(gòu)造代碼塊");
    }*/
  
    public Stu(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void sout(){
        System.out.println(this.name+this.hashCode());
    }
    public void printer() {
        System.out.println(Stu.class.hashCode());
    }
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("終結(jié)了");
    }
}

檢測(cè)內(nèi)存泄露的原理

檢測(cè)內(nèi)存泄漏的關(guān)鍵是要能截獲住對(duì)分配內(nèi)存和釋放內(nèi)存的函數(shù)的調(diào)用。截獲住這兩個(gè)函數(shù),我們就能跟蹤每一塊內(nèi)存的生命周期,比如,每當(dāng)成功的分配一塊內(nèi)存后,就把它的指針加入一個(gè)全局的list中;每當(dāng)釋放一塊內(nèi)存,再把它的指針從list中刪除。這樣,當(dāng)程序結(jié)束的時(shí)候,list中剩余的指針就是指向那些沒(méi)有被釋放的內(nèi)存。這里只是簡(jiǎn)單的描述了檢測(cè)內(nèi)存泄漏的基本原理,詳細(xì)的算法可以參見Steve Maguire的<<Writing Solid Code>>。  

如果要檢測(cè)堆內(nèi)存的泄漏,那么需要截獲住malloc/realloc/free和new/delete就可以了(其實(shí)new/delete最終也是用malloc/free的,所以只要截獲前面一組即可)。對(duì)于其他的泄漏,可以采用類似的方法,截獲住相應(yīng)的分配和釋放函數(shù)。比如,要檢測(cè)BSTR的泄漏,就需要截獲SysAllocString/SysFreeString;要檢測(cè)HMENU的泄漏,就需要截獲CreateMenu/ DestroyMenu。(有的資源的分配函數(shù)有多個(gè),釋放函數(shù)只有一個(gè),比如,SysAllocStringLen也可以用來(lái)分配BSTR,這時(shí)就需要截獲多個(gè)分配函數(shù))。

在Windows平臺(tái)下,檢測(cè)內(nèi)存泄漏的工具常用的一般有三種,MS C-Runtime Library內(nèi)建的檢測(cè)功能;外掛式的檢測(cè)工具,諸如,Purify,BoundsChecker等;利用Windows NT自帶的Performance Monitor。這三種工具各有優(yōu)缺點(diǎn),MS C-Runtime Library雖然功能上較之外掛式的工具要弱,但是它是免費(fèi)的;Performance Monitor雖然無(wú)法標(biāo)示出發(fā)生問(wèn)題的代碼,但是它能檢測(cè)出隱式的內(nèi)存泄漏的存在,這是其他兩類工具無(wú)能為力的地方。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • SpringBoot-RestTemplate實(shí)現(xiàn)調(diào)用第三方API的方式

    SpringBoot-RestTemplate實(shí)現(xiàn)調(diào)用第三方API的方式

    RestTemplate?是由?Spring?提供的一個(gè)?HTTP?請(qǐng)求工具,它提供了常見的REST請(qǐng)求方案的模版,例如?GET?請(qǐng)求、POST?請(qǐng)求、PUT?請(qǐng)求、DELETE?請(qǐng)求以及一些通用的請(qǐng)求執(zhí)行方法?exchange?以及?execute,下面看下SpringBoot?RestTemplate調(diào)用第三方API的方式
    2022-12-12
  • Java中不常用但很好用的開發(fā)小技巧分享

    Java中不常用但很好用的開發(fā)小技巧分享

    其實(shí)干 Java 開發(fā),必然離不開一些計(jì)算,所以就會(huì)經(jīng)常用到 BigDecimal ,今天小編就來(lái)給大家分項(xiàng)一下那些不怎么常用,但是非常有用的方法,需要的可以參考一下
    2023-04-04
  • 并發(fā)編程之Java內(nèi)存模型volatile的內(nèi)存語(yǔ)義

    并發(fā)編程之Java內(nèi)存模型volatile的內(nèi)存語(yǔ)義

    這篇文章主要介紹了并發(fā)編程之Java內(nèi)存模型volatile的內(nèi)存語(yǔ)義,理解volatile特性的一個(gè)好辦法是把對(duì)volatile變量的單個(gè)讀/寫,看成是使用同一個(gè)鎖對(duì)單個(gè)讀/寫操作做了同步。下面我們一起進(jìn)入文章看看具體例子吧,需要的小伙伴可以參考下
    2021-11-11
  • Mybatis全局配置及映射關(guān)系的實(shí)現(xiàn)

    Mybatis全局配置及映射關(guān)系的實(shí)現(xiàn)

    本文主要介紹了Mybatis全局配置及映射關(guān)系的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • Java后端向前端返回文件流實(shí)現(xiàn)下載功能

    Java后端向前端返回文件流實(shí)現(xiàn)下載功能

    后端可以使用Java中servlet提供的HttpServletResponse,核心步驟是要設(shè)置響應(yīng)的數(shù)據(jù)類型,設(shè)置為某一類文件類型或二進(jìn)制格式,以及響應(yīng)頭,然后用ServletOutputStream將文件以流的形式發(fā)送到前端,本文介紹Java后端向前端返回文件流實(shí)現(xiàn)下載功能,感興趣的朋友一起看看吧
    2023-12-12
  • 智能 AI 代碼生成工具 Cursor 安裝和使用超詳細(xì)教程

    智能 AI 代碼生成工具 Cursor 安裝和使用超詳細(xì)教程

    Cursor.so 是一個(gè)集成了 GPT-4 的國(guó)內(nèi)直接可以訪問(wèn)的,優(yōu)秀而強(qiáng)大的免費(fèi)代碼生成器,可以幫助你快速編寫、編輯和討論代碼,這篇文章主要介紹了智能 AI 代碼生成工具 Cursor 安裝和使用介紹,需要的朋友可以參考下
    2023-05-05
  • SpringMVC的執(zhí)行過(guò)程淺析

    SpringMVC的執(zhí)行過(guò)程淺析

    這篇文章主要給大家介紹了關(guān)于SpringMVC的執(zhí)行過(guò)程的相關(guān)資料,文中通過(guò)圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者使用SpringMVC具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • Java的Socket網(wǎng)絡(luò)編程基礎(chǔ)知識(shí)入門教程

    Java的Socket網(wǎng)絡(luò)編程基礎(chǔ)知識(shí)入門教程

    這篇文章主要介紹了Java的Socket網(wǎng)絡(luò)編程基礎(chǔ)知識(shí)入門教程,包括基于TCP/IP和UDP協(xié)議的簡(jiǎn)單實(shí)例程序講解,需要的朋友可以參考下
    2016-01-01
  • 詳解Spring Retry實(shí)現(xiàn)原理

    詳解Spring Retry實(shí)現(xiàn)原理

    這篇文章主要介紹了詳解Spring Retry實(shí)現(xiàn)原理,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-04-04
  • java判斷ftp目錄是否存在的方法

    java判斷ftp目錄是否存在的方法

    這篇文章主要為大家詳細(xì)介紹了java判斷ftp目錄是否存在的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-04-04

最新評(píng)論