一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - java內存管理關系及內存泄露的原理分析

java內存管理關系及內存泄露的原理分析

2022-02-15 16:00知我飯否 Java教程

這篇文章主要介紹了java內存管理關系及內存泄露的原理,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

java內存管理關系及內存泄露原理

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

java對象和內存的關系

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

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

這是本人總結出來的四條經驗。

特別重要的是,一定要有這種認知。不管任何語言,最終都是要物理內存上面反映的,對象 和 內存空間 是兩個不同的個體。 如果 沒有的話,那么你會發現 下面將的都是什么啊!

創建對象

     Stu one; //只聲明 one對象 但是沒有分配內存空間
      //用new 開辟新的內存空間 oneMemory ,調用構造函數賦值,并將內存空間 oneMemory 與 one對象建立聯系。
      one = new Stu("one");
      
      //聲明 two對象 并開辟內存 twoMemory 調用構造函數賦值,并將內存空間 twoMemory與 two對象建立聯系
      Stu two = new Stu("two");
      
      //聲明 three對象, 并找到one 對象聯系的內存空間 oneMemory。并將 oneMemory與 three 對象建立聯系
      Stu three = one;
      
      //此時 內存空間 oneMemory 與兩個對象有聯系。一個是 one對象,一個是three對象
      System.out.println("three 和 one 是否相等" + (three == one) + " one的哈希值" + one.hashCode() + " three的哈希值" + three.hashCode());

運行結果

java內存管理關系及內存泄露的原理分析

我們可以發現,three對象 和one對象 指向的是同一個內存空間oneMemory。這個不就是符合上面所說的第二個真理 如果,我們對one對象進行操作,那么產生的影響,也會反映到three對象上。

因為,**他們指向的是同一個內存空間,對one對象操作,就是做內存空間oneMemory進行操作。而three對象指向的也是oneMemory。這個符合上面第四條真理。**例子如下

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

null的作用

長久以來,我只知道,將一個值復制成null,那么他就是空的了。但是 完全不知道,為啥。

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

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

運行結果

java內存管理關系及內存泄露的原理分析

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

內存泄露

如果,你明白了上面。那么內存泄露對你來說也很簡單了。

再來學習一個新概念

java內存管理關系及內存泄露的原理分析

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

接下來看代碼

首先,我們定義一個靜態集合 staticList ,他是程序一運行,就會被創建好的。程序結束運行了,它才會被回收。

static List<Stu> staticList = new ArrayList<>();//開辟內存空間 listMemory
     System.out.println("three 對象的值  after" + three.getName() + three.hashCode());
      /*
       內存泄露 是長生命周期的對象 對一個內存空間有聯系,造成內存空間沒有辦法被回收
       */
      /*
      將three對象添加到靜態集合里面,步驟是這樣的,
      第一步 找到three對象聯系的內存空間 oneMemory
      第二步 找到 staticList集合對象聯系的內存空間 listMemory
      第三步 告訴系統 staticList集合對象的部分成員 和內存空間 oneMemory 建立聯系。
       */
      staticList.add(three);
      
      /*
      在這里 即使three對象已經和內存空間 oneMemory 沒有聯系了。
      oneMemory 也不會被回收,因為上面說了內存空間和對象的關系是1對多。
       而回收的條件是 一個內存空間沒有一條和對象的聯系才可以回收。
       此時 內存空間 和staticList集合對象的部分成員 有聯系,所以 內存空間不會被回收。
       又由于staticList 集合對象聯系的內存空間在 靜態存儲區,是伴隨整個程序的。所以 在整個程序生命里面,
       內存空間 oneMemory  就得不到 回收。  就是內存泄露了。
       */
      three = null;
      System.out.println(staticList.get(0).hashCode());

運行結果

java內存管理關系及內存泄露的原理分析

可以看見。在我們將three對象賦值null切斷和內存空間 oneMemory的聯系后。靜態集合staticList對象的部分成員依然和內存空間 oneMemory有聯系。根據上面第三條所說,因為內存空間 oneMemory 還是和對象有聯系的(staticList)。所以不會回收oneMemory內存空間。又由于staticList是靜態的,生命和程序一樣長。 那么在整個程序周期里面,oneMemory內存空間 都不會被回收。就造成了內存泄露。

附上完整的代碼

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

bean對象 即Stu

package com.zfh.test;
public class Stu {
/*  static {
      System.out.println("靜態代碼塊 我只調用一次");
  }*/

  private String name;
  
/*  {
      System.out.println("構造代碼塊");
  }*/

  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("終結了");
  }
}

 

檢測內存泄露的原理

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

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

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

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持服務器之家。

原文鏈接:https://blog.csdn.net/a1064072510/article/details/83148538

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 亚洲高清无在码在线电影 | 四虎影院免费视频 | 99视频在线观看视频 | 经典千人斩一区二区视频 | 成人在线一区二区 | 国产3344视频在线观看免费 | 成人一级黄色大片 | 俄罗斯图书馆无打码久久 | 免费超级乱淫播放手机版 | 亚洲 综合 欧美在线 热 | 91资源在线视频 | 韩国女主播在线大尺无遮挡 | 青青青手机在线视频 | 午夜神器老司机高清无码 | 男人的天堂久久精品激情 | 亚洲欧美午夜 | 91在线精品国产丝袜超清 | 无码AV熟妇素人内射V在线 | 欧美日韩一区二区三区在线播放 | 日韩欧美在线一区二区三区 | 精品无人乱码一区二区三区 | 国产精品久久久久久久久久久久久久 | 国内精品免费一区二区三区 | 性绞姿始动作动态图 | 亚洲视频中文字幕 | 啪啪免费入口网站 | 日本特黄一级午夜剧场毛片 | 国产成人小视频在线观看 | 青青草影院在线观看 | 日本小视频免费 | 国产免费好大好硬视频 | 草啪啪 | 嫩草影院永久一二三入口 | 5x社区发源地最新地址 | 国产在线拍 | 日韩天堂网 | 国产二区视频 | www.日本在线播放 | 特a级片| 爱福利视频一区 | 四虎最新紧急更新地址 |