<kbd id="5sdj3"></kbd>
<th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>

    一篇搞懂,Java內(nèi)存泄漏

    共 1534字,需瀏覽 4分鐘

     ·

    2020-11-16 23:03

    點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號”

    優(yōu)質(zhì)文章,第一時間送達(dá)

    ? 作者?|??Luckyp1ng

    來源 |? urlify.cn/fIvuQr

    66套java從入門到精通實戰(zhàn)課程分享

    Java中的內(nèi)存管理

    要了解Java中的內(nèi)存泄漏,首先就得知道Java中的內(nèi)存是如何管理的。

    在Java程序中,我們通常使用 new 為對象分配內(nèi)存,而這些內(nèi)存空間都在堆上。

    Java判斷對象是否可以回收使用的而是可達(dá)性分析算法。

    這個算法的基本思路就是通過一系列名為 "GC Roots" 的對象作為起始點(diǎn),從這些節(jié)點(diǎn)開始向下搜索,搜索所走過的路徑稱為引用鏈(Reference Chain),當(dāng)一個對象到 GC Roots 沒有任何引用鏈相連時,則證明此對象是不可用的,下圖對象 object5, object6, object7 雖然有互相判斷,但它們到 GC Roots 是不可達(dá)的,所以它們將會判定為是可回收對象。

    在 Java 語言中,可作為 GC Roots 對象的包括如下幾種:

    • 虛擬機(jī)棧(棧幀中的本地變量表)中引用的對象

    • 本地方法棧(Native方法)中引用的對象

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

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

    什么是Java中的內(nèi)存泄漏

    Java 中的內(nèi)存泄漏,廣義并通俗的說,就是:不再會被使用的對象的內(nèi)存不能被回收,就是內(nèi)存泄漏。

    Java 中的內(nèi)存泄漏與 C++ 中的表現(xiàn)有所不同。

    在 C++ 中,所有被分配了內(nèi)存的對象,不再使用之后,都必須程序員手動的去釋放他們。但是在 Java 中,我們不用自己釋放內(nèi)存,無用的內(nèi)存由 GC 自動清理,這也極大的簡化了我們的編程工作。但實際有時候一些不再會被使用的對象在 GC 看來不能被釋放就會造成內(nèi)存泄漏。

    對象都是有生命周期的,有的長,有的短。如果長生命周期的對象持有短生命周期的引用,就很可能會出現(xiàn)內(nèi)存泄漏。例如:

    public?class?Test?{
    ?Object?object;
    ?public?void?method()?{
    ??object?=?new?Object();
    ??//?...
    ?}
    }

    這里的 object 實例,其實我們期望它只作用于 method() 方法中,且其他地方也不會再用到它,但是當(dāng) method() 方法執(zhí)行完之后,object對象所分配的內(nèi)存不會馬上被認(rèn)為是可以被釋放的對象。只有在 Test 類創(chuàng)建的對象被釋放后才會被釋放。嚴(yán)格地說,這就是一種內(nèi)存泄漏。解決辦法就是將 object 作為 method() 方法中的局部變量。當(dāng)然也可以在使用完 object 之后 將其置為 null。

    public?class?Test?{
    ?Object?object;
    ?public?void?method()?{
    ??object?=?new?Object();
    ??//?...
    ??object?=?null;
    ?}
    }

    這樣,之前 new Object() 分配的內(nèi)存就可以被 GC 回收。

    Java中內(nèi)存泄漏的例子

    1. 靜態(tài)集合類

    如HashMap、LinkedList等等。如果這些容器為靜態(tài)的,那么它們的生命周期與程序一致,則容器中的對象在程序結(jié)束之前將不能被釋放,從而造成內(nèi)存泄漏。簡單而言,長生命周期的對象持有短生命周期對象的引用,盡管短生命周期的對象不再使用,但是因為長生命周期對象持有它的引用而導(dǎo)致不能被回收。

    static?Vector?v?=?new?Vector();?
    for?(int?i?=?1;?i<100;?i++)?
    {?
    ????Object?o?=?new?Object();?
    ????v.add(o);?
    ????o?=?null;?
    }

    在這個例子中,代碼棧中存在 Vector 對象的引用 v 和 Object 對象的引用 o 。在 For 循環(huán),我們不斷的生成新的對象,然后將其添加到 Vector 對象中,之后將 o 引用置空。問題是當(dāng) o 引用被置空后,如果發(fā)生 GC,我們創(chuàng)建的 Object 對象是否能夠被 GC 回收呢?答案是否定的。因為, GC 在跟蹤代碼棧中的引用時,會發(fā)現(xiàn) v 引用,而繼續(xù)往下跟蹤,就會發(fā)現(xiàn) v 引用指向的內(nèi)存空間中又存在指向 Object 對象的引用。也就是說盡管o 引用已經(jīng)被置空,但是 Object 對象仍然存在其他的引用,是可以被訪問到的,所以 GC 無法將其釋放掉。如果在此循環(huán)之后, Object 對象對程序已經(jīng)沒有任何作用,那么我們就認(rèn)為此 Java 程序發(fā)生了內(nèi)存泄漏。

    1. 各種連接,如數(shù)據(jù)庫連接、網(wǎng)絡(luò)連接和IO連接等

    在對數(shù)據(jù)庫進(jìn)行操作的過程中,首先需要建立與數(shù)據(jù)庫的連接,當(dāng)不再使用時,需要調(diào)用close方法來釋放與數(shù)據(jù)庫的連接。只有連接被關(guān)閉后,垃圾回收器才會回收對應(yīng)的對象。否則,如果在訪問數(shù)據(jù)庫的過程中,對Connection、Statement或ResultSet不顯性地關(guān)閉,將會造成大量的對象無法被回收,從而引起內(nèi)存泄漏。

    1. 變量不合理的作用域

    一般而言,一個變量的定義的作用范圍大于其使用范圍,很有可能會造成內(nèi)存泄漏。另一方面,如果沒有及時地把對象設(shè)置為null,很有可能導(dǎo)致內(nèi)存泄漏的發(fā)生。

    public?class?UsingRandom?{
    ?private?String?msg;
    ?public?void?receiveMsg(){?
    ??readFromNet();//?從網(wǎng)絡(luò)中接受數(shù)據(jù)保存到msg中
    ??saveDB();//?把msg保存到數(shù)據(jù)庫中
    ?}
    }

    如上面這個偽代碼,通過 readFromNet() 方法把接受的消息保存在變量 msg 中,然后調(diào)用 saveDB() 方法把 msg 的內(nèi)容保存到數(shù)據(jù)庫中,此時 msg 已經(jīng)就沒用了,由于 msg 的生命周期與對象的生命周期相同,此時 msg 還不能回收,因此造成了內(nèi)存泄漏。

    實際上這個 msg 變量可以放在 receiveMsg() 方法內(nèi)部,當(dāng)方法使用完,那么 msg 的生命周期也就結(jié)束,此時就可以回收了。還有一種方法,在使用完 msg 后,把 msg 設(shè)置為 null,這樣垃圾回收器也會回收 msg 的內(nèi)存空間。

    1. 內(nèi)部類持有外部類

    如果一個外部類的實例對象的方法返回了一個內(nèi)部類的實例對象,這個內(nèi)部類對象被長期引用了,即使那個外部類實例對象不再被使用,但由于內(nèi)部類持有外部類的實例對象,這個外部類對象將不會被垃圾回收,這也會造成內(nèi)存泄露。

    1. 改變哈希值

    當(dāng)一個對象被存儲進(jìn) HashSet 集合中以后,就不能修改這個對象中的那些參與計算哈希值的字段了,否則,對象修改后的哈希值與最初存儲進(jìn) HashSet 集合中時的哈希值就不同了,在這種情況下,即使在 contains 方法使用該對象的當(dāng)前引用作為的參數(shù)去 HashSet 集合中檢索對象,也將返回找不到對象的結(jié)果,這也會導(dǎo)致無法從 HashSet 集合中單獨(dú)刪除當(dāng)前對象,造成內(nèi)存泄露。

    public?static?void?main(String[]?args)?
    {?
    ????Set?set?=?new?HashSet();?
    ????Person?p1?=?new?Person("唐僧","pwd1",25);?
    ????Person?p2?=?new?Person("孫悟空","pwd2",26);?
    ????Person?p3?=?new?Person("豬八戒","pwd3",27);?
    ????set.add(p1);?
    ????set.add(p2);?
    ????set.add(p3);?
    ????System.out.println("總共有:"+set.size()+"?個元素!");?//結(jié)果:總共有:3 個元素!
    ????p3.setAge(2);?//修改p3的年齡,此時p3元素對應(yīng)的hashcode值發(fā)生改變?
    ????set.remove(p3);?//此時remove不掉,造成內(nèi)存泄漏
    ????set.add(p3);?//重新添加,居然添加成功?
    ????System.out.println("總共有:"+set.size()+"?個元素!");?//結(jié)果:總共有:4 個元素!
    ????for?(Person?person?:?set)?
    ????{?
    ????????System.out.println(person);?
    ????}?
    }
    1. 單例對象在被初始化后將在JVM的整個生命周期中存在(以靜態(tài)變量的方式),如果單例對象持有外部對象的引用,那么這個外部對象將不能被jvm正常回收,導(dǎo)致內(nèi)存泄露

    2. 緩存泄漏

    內(nèi)存泄漏的另一個常見來源是緩存,一旦你把對象引用放入到緩存中,他就很容易遺忘,對于這個問題,可以使用 WeakHashMap 代表緩存,此種 Map 的特點(diǎn)是,當(dāng)除了自身有對 key 的引用外,此 key 沒有其他引用那么此 map 會自動丟棄此值

    1. 監(jiān)聽器和回調(diào)

    內(nèi)存泄漏第三個常見來源是監(jiān)聽器和其他回調(diào),如果客戶端在你實現(xiàn)的 API 中注冊回調(diào),卻沒有顯示的取消,那么就會積聚。需要確?;卣{(diào)立即被當(dāng)作垃圾回收的最佳方法是只保存他的弱引用,例如將他們保存成為 WeakHashMap 中的鍵。

    內(nèi)存泄露解決的原則

    1.盡量減少使用靜態(tài)變量,類的靜態(tài)變量的生命周期和類同步的。

    2.聲明對象引用之前,明確內(nèi)存對象的有效作用域,盡量減小對象的作用域,將類的成員變量改寫為方法內(nèi)的局部變量;

    3.減少長生命周期的對象持有短生命周期的引用;

    4.使用StringBuilder和StringBuffer進(jìn)行字符串連接,Sting和StringBuilder以及StringBuffer等都可以代表字符串,其中String字符串代表的是不可變的字符串,后兩者表示可變的字符串。如果使用多個String對象進(jìn)行字符串連接運(yùn)算,在運(yùn)行時可能產(chǎn)生大量臨時字符串,這些字符串會保存在內(nèi)存中從而導(dǎo)致程序性能下降。

    5.對于不需要使用的對象手動設(shè)置null值,不管GC何時會開始清理,我們都應(yīng)及時的將無用的對象標(biāo)記為可被清理的對象;

    6.各種連接(數(shù)據(jù)庫連接,網(wǎng)絡(luò)連接,IO連接)操作,務(wù)必顯示調(diào)用close關(guān)閉。





    粉絲福利:實戰(zhàn)springboot+CAS單點(diǎn)登錄系統(tǒng)視頻教程免費(fèi)領(lǐng)取

    ???

    ?長按上方微信二維碼?2 秒
    即可獲取資料



    感謝點(diǎn)贊支持下哈?

    瀏覽 28
    點(diǎn)贊
    評論
    收藏
    分享

    手機(jī)掃一掃分享

    分享
    舉報
    評論
    圖片
    表情
    推薦
    點(diǎn)贊
    評論
    收藏
    分享

    手機(jī)掃一掃分享

    分享
    舉報

    <kbd id="5sdj3"></kbd>
    <th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>
    椎名空一区二区三区无码观看 | 精品人妻一区二区三区香蕉 | 高潮喷水视频在线 | 国产V在线最新观看视频 | 久久性爱视频 |