九色国产,午夜在线视频,新黄色网址,九九色综合,天天做夜夜做久久做狠狠,天天躁夜夜躁狠狠躁2021a,久久不卡一区二区三区

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
JVM: JVM 內(nèi)存劃分

轉(zhuǎn)載自:

https://www.cnblogs.com/paulwang92115/p/12251476.html

概述

如果在大學(xué)里學(xué)過或者在工作中使用過 C 或者 C++ 的讀者一定會發(fā)現(xiàn)這兩門語言的內(nèi)存管理機制與 Java 的不同。在使用 C 或者 C++ 編程時,程序員需要手動的去管理和維護內(nèi)存,就是說需要手動的清除那些不需要的對象,否則就會出現(xiàn)內(nèi)存泄漏與內(nèi)存溢出的問題。

如果你使用 Java 語言去開發(fā),你就會發(fā)現(xiàn)大多數(shù)情況下你不用去關(guān)心無用對象的回收與內(nèi)存的管理,因為這一切 JVM 虛擬機已經(jīng)幫我們做好了。了解 JVM 內(nèi)存的各個區(qū)域?qū)⒂兄谖覀兩钊肓私馑墓芾頇C制,避免出現(xiàn)內(nèi)存相關(guān)的問題和高效的解決問題。

引出問題

在 Java 編程時我們會用到許多不同類型的數(shù)據(jù),比如臨時變量、靜態(tài)變量、對象、方法、類等等。那么他們的存儲方式有什么不同嗎?或者說他們存在哪?

運行時數(shù)據(jù)區(qū)域

Java 虛擬機在執(zhí)行 Java 程序過程中會把它所管理的內(nèi)存分為若干個不同的數(shù)據(jù)區(qū)域,各自有各自的用途。

這其中堆和方法區(qū)是線程之間共享的,而棧和程序計數(shù)器是線程私有的。

  • 程序計數(shù)器

    線程私有的,可以看作是當前線程所執(zhí)行字節(jié)碼的行號指示器。字節(jié)碼解釋器工作時就是通過改變這個計數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令。分支、循環(huán)、異常處理、線程恢復(fù)等基礎(chǔ)功能都需要依賴這個計數(shù)器來完成。

    這時唯一一個沒有規(guī)定任何 OOM 異常的區(qū)域。

  • 虛擬機棧

    虛擬機棧也是線程私有的,生命周期與線程相同。棧里面存儲的是方法的局部變量對象的引用等等。

    在這片區(qū)域中,規(guī)定了兩種異常情況,當線程請求的棧深度大于虛擬機所允許的深度,將拋出 StackOverflowError 異常。當虛擬機棧動態(tài)擴展無法申請到足夠的內(nèi)存時會拋出 OOM 異常。

  • 本地方法棧

    和虛擬機棧的作用相同,只不過它是為 Native 方法服務(wù)。HotSpot 虛擬機直接將虛擬機棧和本地方法棧合二為一了。

  • 堆是 Java 虛擬機所管理內(nèi)存中最大的一塊。是所有線程共享的一塊內(nèi)存區(qū)域,在虛擬機啟動時創(chuàng)建。這個區(qū)域唯一的作用就是存放對象實例,也就是 NEW 出來的對象。這個區(qū)域也是 Java 垃圾收集器的主要作用區(qū)域。

    當堆的大小再也無法擴展時,將會拋出 OOM 異常。

    可以說,此內(nèi)存區(qū)域唯一的作用就是存放對象實例,幾乎所有的對象實例和數(shù)組都在這里分配內(nèi)存。

    Java 堆是垃圾收集管理的主要區(qū)域,因此也被稱為 GC 堆。垃圾收集都采用分代垃圾回收算法,所以 Java 堆還可以細分:新聲代(再細致一點分為 Eden,F(xiàn)rom Survivor,To Survivor)和老年代。進一步劃分的目的是跟好地回收內(nèi)存,或者更快地分配內(nèi)存。

  • 方法區(qū)

    方法區(qū)也是線程共享的內(nèi)存區(qū)域,用于存儲已經(jīng)被虛擬機加載的類信息、常量、靜態(tài)變量等等。當方法區(qū)無法滿足內(nèi)存分配需求時,會拋出 OOM 異常。這個區(qū)域也被稱為永久代。

補充

雖然上面的圖里沒有運行時常量池和直接內(nèi)存,但是這兩部分也是我們開發(fā)時經(jīng)常接觸的。所以給大家補充出來。

  • 運行時常量池

    運行時常量池是方法區(qū)的一部分,Class 文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項信息是常量池,用于存放編譯期生成的各種字面量符號引用,這部分內(nèi)容將在類加載后存放到方法區(qū)的運行時常量池中。也會拋出 OOM 異常。

  • 直接內(nèi)存

    直接內(nèi)存并不是虛擬機運行時數(shù)據(jù)區(qū)的一部分,也不是 Java 虛擬機規(guī)范中定義的內(nèi)存區(qū)域,但是卻是NIO 操作時會直接使用的一塊內(nèi)存,雖然不受虛擬機參數(shù)限制,但是還是會受到本機總內(nèi)存的限制,會拋出 OOM 異常。

這里有一個概念希望大家能夠清除,堆中使用分代垃圾回收算法時的永久代表方法區(qū),它并不在堆內(nèi)存中,上面的圖片將其放在一起是為了說明分代垃圾回收算法會作用在這幾個區(qū)域。

JDK 1.8 的改變

對于方法區(qū),它是線程共享的,主要用于存儲類的信息,常量池,方法數(shù)據(jù),方法代碼等。我們稱這個區(qū)域為永久代。它也是 JVM 垃圾回收作用的區(qū)域。

大部分程序員應(yīng)該都見過 java.lang.OutOfMemoryError:PermGen space 異常,這里的 PermGen space 其實指的就是方法區(qū)。由于方法區(qū)主要存儲類的相關(guān)信息,所以對于動態(tài)生成類的情況比較容易出現(xiàn)永久代的內(nèi)存溢出,典型的場景是在 JSP 頁面比較多的情況,容易出現(xiàn)永久代內(nèi)存溢出。

在 JDK 1.8 中,HotSpot 虛擬機已經(jīng)沒有 PermGen space 方法區(qū)這個地方了,取而代之的是一個叫 Metaspace(元空間)的東西。

元空間與方法區(qū)最大的區(qū)別是:元空間不再虛擬機中,而是使用本地內(nèi)存。默認情況下,元空間的大小僅受本地內(nèi)存限制。

常量區(qū)原本在方法區(qū)中,現(xiàn)在方法區(qū)被移除了,所以常量池被放倒了堆中。

這樣做的好處是:

這樣更改的好處:

  • 字符串常量存在方法區(qū)中,容易出現(xiàn)性能問題和內(nèi)存溢出。

  • 類和方法的信息等比較難確定大小,因此對于方法區(qū)大小的指定比較困難,太小容易出現(xiàn)方法區(qū)溢出,太大容易導(dǎo)致堆的空間不足。

  • 方法區(qū)的垃圾回收會帶來不必要的復(fù)雜度,并且回收效率偏低(垃圾回收會在下一章給大家介紹)。

虛擬機對象揭秘

對象的創(chuàng)建過程,最好是能記住,并且能知道每一步在做什么。

  1. 類加載檢查:虛擬機遇到一條 new 指令的時候,首先去檢查這個指令的參數(shù)能否在常量池中定位這個類的符號飲用,檢查這個類的符號引用所代表的類是否已被加載,解析,初始化過。如果沒有,那必須先執(zhí)行響應(yīng)的類加載過程。簡單來說,就是要看對象的類是否已經(jīng)被加載過了。

  2. 分配內(nèi)存:在類加載檢查通過后,接下來虛擬機將會為新生對象分配內(nèi)存。對象所需的內(nèi)存大小在類加載完畢后便可以確定了,為對象分配空間的任務(wù)相當于把一塊確定大小的內(nèi)存從 Java 堆中劃分出來。

    分配方式有指針碰撞和空閑列表兩種。選擇那種方式由 Java 堆是否規(guī)整決定,而 Java 堆是否規(guī)整由垃圾收集器是否帶有壓縮功能決定(復(fù)制算法和標記整理算法是規(guī)整的,標記清除算法是不規(guī)整的)。

    內(nèi)存分配并發(fā)問題

    • CAS 失敗重試,CAS 是客觀鎖的一種實現(xiàn)方式。

    • TLAB:為每一個線程預(yù)先在 Eden 分配一塊內(nèi)存,JVM 在給線程中的對象分配內(nèi)存時,首先在 TLAB 分配,如果不夠,使用 CAS 進行分配。

  3. 初始化零值:內(nèi)存分配完畢后,虛擬機將要分配的內(nèi)存空間都初始化為零值(不包括對象頭)。這一步保證了對象實例在 Java 中不賦初值就可以直接使用。

  4. 設(shè)置對象頭:初始化零值完成之后,虛擬機要對對象進行必要的設(shè)置。比如對象的哈希碼,對象的 GC 分代年齡信息,偏向鎖,這些信息放在對象頭中。

  5. 執(zhí)行 init 方法:上面工作完成后,從虛擬機視角看,一個新的對象已經(jīng)產(chǎn)生了。然后執(zhí)行 init 方法,按照程序員的意愿將對象進行初始化。

對象構(gòu)成

HotSpot 虛擬機中,對象在內(nèi)存中的布局可以分為三塊區(qū)域:對象頭,實例數(shù)據(jù)和對齊填充。

對象頭中包含兩部分信息,第一部分用于存儲對象自身運行時數(shù)據(jù)(哈希碼,GC 分代年齡,鎖狀態(tài)標志),另一部分是類型指針,即指向它的類元數(shù)據(jù)的指針,虛擬機通過這個指針來確定這個對象是哪個類的實例。

實例數(shù)據(jù)部分存儲的對象的有效信息。

對其填充起到的是占位的作用。

對象的訪問定位

  1. 句柄。

  2. 直接指針。

補充

String str1 = 'abcd';String str2 = new String('abcd');System.out.println(str1==str2);//false

這兩種方式創(chuàng)建的對象是有差別的,第一種方式是在常量池中,第二種方式是在堆內(nèi)存中。

直接使用雙引號聲明創(chuàng)建出來的 String 對象會直接存儲在常量池中。

如果不是使用常量池聲明的 String 對象,可以使用 String 提供的 intern 方String.intern() 是一個 Native 方法,它的作用是:如果運行時常量池中已經(jīng)包含一個等于此 String 對象內(nèi)容的字符串,則返回常量池中該字符串的引用;如果沒有,則在常量池中創(chuàng)建與此 String 內(nèi)容相同的字符串,并返回常量池中創(chuàng)建的字符串的引用。

String s1 = new String('計算機');String s2 = s1.intern();String s3 = '計算機';System.out.println(s2);//計算機System.out.println(s1 == s2);//false,因為一個是堆內(nèi)存中的String對象一個是常量池中的String對象,System.out.println(s3 == s2);//true,因為兩個都是常量池中的String對
String str1 = 'str';String str2 = 'ing';
String str3 = 'str' + 'ing';//常量池中的對象String str4 = str1 + str2; //在堆上創(chuàng)建的新的對象 String str5 = 'string';//常量池中的對象System.out.println(str3 == str4);//falseSystem.out.println(str3 == str5);//trueSystem.out.println(str4 == str5);//false

String s1 = new String('abc'); 這句話創(chuàng)建了幾個對象?

先有字符串 “abc” 放入常量池,然后 new 了一個字符串 “abc” 放入 Java 堆。棧中的引用指向堆中的對象。

Java 基本類型的包裝類的大部分都實現(xiàn)了常量池技術(shù),即 Byte,Short,Integer,Long,Character,Boolean。除了 Boolean 之外的 5 種包裝類都默認創(chuàng)建了 【-128 127】的緩存數(shù)據(jù),超出此范圍仍然去創(chuàng)建新的對象。Float 和 Double 并沒有實現(xiàn)常量池技術(shù)。

Integer i1 = 33;Integer i2 = 33;System.out.println(i1 == i2);// 輸出trueInteger i11 = 333;Integer i22 = 333;System.out.println(i11 == i22);// 輸出falseDouble i3 = 1.2;Double i4 = 1.2;System.out.println(i3 == i4);// 輸出false
  1. Integer i1=40;Java 在編譯的時候會直接將代碼封裝成Integer i1=Integer.valueOf(40);,從而使用常量池中的對象。

  2. Integer i1 = new Integer(40);這種情況下會創(chuàng)建新的對象。

Integer i1 = 40;Integer i2 = new Integer(40);System.out.println(i1==i2);//輸出false
本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Java中的棧,堆,方法區(qū)和常量池
Java虛擬機(JVM)總結(jié)
淺談Java中數(shù)據(jù)在內(nèi)存中的狀態(tài),以及String、StringBuffer、==、equals、數(shù)組等問題
Java中的字符串常量池
Java面試基礎(chǔ):Q1. ==和equals()的區(qū)別
Java中的字符串
更多類似文章 >>
生活服務(wù)
熱點新聞
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服