日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

JVM内存管理

發布時間:2025/6/17 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JVM内存管理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

JVM將內存主要劃分為:方法區、虛擬機棧、本地方法棧、堆、程序計數器。JVM運行時數據區.

關系圖:

程序計數器

記錄當前線程鎖執行的字節碼的行號。

  • 程序計數器是一塊較小的內存空間。
  • 處于線程獨占區。
  • 執行java方法時,它記錄正在執行的虛擬機字節碼指令地址。執行native方法,它的值為undefined
  • 該區域是唯一一個沒有規定任何OutOfMemoryError的區域
  • 虛擬機棧

    存放方法運行時所需的數據,成為棧幀。其實它很簡單!它里面存放的是一個函數的上下文,具體存放的是執行的函數的一些數據。執行的函數需要的數據無非就是局部變量表(保存函數內部的變量)、操作數棧(執行引擎計算時需要),方法出口等等。

    • 棧幀:執行引擎每調用一個方法時,就為這個函數創建一個棧幀,并加入虛擬機棧。換個角度理解,每個函數從調用到執行結束,其實是對應一個棧幀的入棧和出棧。

    • 局部變量表: 存放編譯期間可知的各種基本數據類型、引用類型、return Address類型

      • 局部變量表的內存空間在編譯期間就完成分配,運行期間不會改變。

    相關報錯: StackOverflowError:當棧幀大于我們設置的棧大小,就會出現棧溢出(遞歸沒有出口等因素) OutOfMemoryError:

    本地方法棧

    本地方法棧與虛擬機棧所發揮的作用很相似,他們的區別在于虛擬機棧為執行Java代碼方法服務,而本地方法棧是為Native方法服務。與虛擬機棧一樣,本地方法棧也會拋出StackOverflowError和OutOfMemoryError異常。

    堆內存

    存儲對象實例。

    Java堆可以說是虛擬機中最大一塊內存了。它是所有線程所共享的內存區域,幾乎所有的實例對象都是在這塊區域中存放。當然,睡著JIT編譯器的發展,所有對象在堆上分配漸漸變得不那么“絕對”了。

    Java堆是垃圾收集器管理的主要區域。由于現在的收集器基本上采用的都是分代收集算法,所有Java堆可以細分為:新生代和老年代。在細致分就是把新生代分為:Eden空間、From Survivor空間、To Survivor空間。當堆無法再擴展時,會拋出OutOfMemoryError異常。

    分配堆內存指令參數:-Xms -Xmx

    方法區

    存儲運行時常量池,已被虛擬機加載的類信息,常量,靜態變量,即時編譯器編譯后的代碼等數據。(類版本、字段、方法、接口)。

    運行時常量池:占用方法區中的一塊。

    方法區是各個線程共享區域,很容易理解,我們在寫Java代碼時,每個線程度可以訪問同一個類的靜態變量對象。

    由于使用反射機制的原因,虛擬機很難推測那個類信息不再使用,因此這塊區域的回收很難。另外,對這塊區域主要是針對常量池回收,值得注意的是JDK1.7已經把常量池轉移到堆里面了。同樣,當方法區無法滿足內存分配需求時,會拋出OutOfMemoryError。 制造方法區內存溢出,注意,必須在JDK1.6及之前版本才會導致方法區溢出,原因后面解釋,執行之前,可以把虛擬機的參數-XXpermSize和-XX:MaxPermSize限制方法區大小。

    List<String> list =new ArrayList<String>(); int i =0; while(true){list.add(String.valueOf(i).intern()); } 復制代碼

    運行后會拋出java.lang.OutOfMemoryError:PermGen space異常。 解釋一下,String的intern()函數作用是如果當前的字符串在常量池中不存在,則放入到常量池中。上面的代碼不斷將字符串添加到常量池,最終肯定會導致內存不足,拋出方法區的OOM。

    下面解釋一下,為什么必須將上面的代碼在JDK1.6之前運行。我們前面提到,JDK1.7后,把常量池放入到堆空間中,這導致intern()函數的功能不同,具體怎么個不同法,且看看下面代碼:

    String str1 =new StringBuilder("fant").append("j").toString(); System.out.println(str1.intern()==str1);String str2=new StringBuilder("ja").append("va").toString(); System.out.println(str2.intern()==str2); 復制代碼

    這段代碼在JDK1.6和JDK1.7運行的結果不同。JDK1.6結果是:false,false ,JDK1.7結果是true, false。原因是:JDK1.6中,intern()方法會吧首次遇到的字符串實例復制到常量池中,返回的也是常量池中的字符串的引用,而StringBuilder創建的字符串實例是在堆上面,所以必然不是同一個引用,返回false。

    在JDK1.7中,intern不再復制實例,常量池中只保存首次出現的實例的引用,因此intern()返回的引用和由StringBuilder創建的字符串實例是同一個。為什么對str2比較返回的是false呢?這是因為,JVM中內部在加載類的時候,就已經有"java"這個字符串,不符合“首次出現”的原則,因此返回false。

    更深入的了解常量池和intern:
    /*** Created by Fant.J.*/ public class Test {public static void main(String[] args) {String a = "fantj";String b = "fantj";//a和b 會存到常量池里,常量池類似一個set集合,不允許有重復的值,所以加入第二個重復的值會返回已存在值的索引System.out.println(a == b);//new操作會實例化一個對象,會把他放到棧中。String c = new String("fantj");//所以a和c比較,a在常量池,b在堆,索引肯定不同,結果自然不同,返回falseSystem.out.println(a == c);//a和c.intern比較,intern會把c搬到常量池,所以加入第二個重復的值會返回已存在值的索引,返回trueSystem.out.println(a == c.intern());} } 復制代碼

    有注釋,仔細看注釋。

    總結

    以上是生活随笔為你收集整理的JVM内存管理的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。