亚洲欧洲国产欧美一区精品,激情五月亚洲色五月,最新精品国偷自产在线婷婷,欧美婷婷丁香五月天社区

      考試首頁 | 考試用書 | 培訓(xùn)課程 | 模擬考場 | 考試論壇  
      全國  |             |          |          |          |          |         
        當(dāng)前位置:計算機(jī)等級 > 二級考試 > Java語言程序設(shè)計 > 考試輔導(dǎo) > 文章內(nèi)容
        

      全國計算機(jī)二級考試Java語言程序設(shè)計知識點(25)

      中華IT學(xué)院   【 】  [ 2016年5月5日 ]

      JVM 數(shù)據(jù)存儲介紹及性能優(yōu)化

        本節(jié)解說:本小節(jié)首先按照數(shù)據(jù)存儲方式對程序計數(shù)器、虛擬機(jī)棧、本地方法棧、堆和方法區(qū)等做了一定程度的介紹,接著結(jié)合 JVM 各種參數(shù)配置對 Java 應(yīng)用程序進(jìn)行優(yōu)化,并通過具體實例幫助讀者找到優(yōu)化 Java 代碼的方法。

        JVM 內(nèi)存模式介紹

        Java 虛擬機(jī)內(nèi)存模型是 Java 程序運行的基礎(chǔ)。為了能使 Java 應(yīng)用程序正常運行,JVM 虛擬機(jī)將其內(nèi)存數(shù)據(jù)分為程序計數(shù)器、虛擬機(jī)棧、本地方法棧、Java 堆和方法區(qū)等部分。

        程序計數(shù)器 (Program Counter Register)

        程序計數(shù)器 (Program Counter Register) 是一塊很小內(nèi)存空間,由于 Java 是支持線程的語言,當(dāng)線程數(shù)量超過 CPU 數(shù)量時,線程之間根據(jù)時間片輪詢搶奪 CPU 資源。對于單核 CPU 而言,每一時刻只能有一個線程在運行,而其他線程必須被切換出去。為此,每一個線程都必須用一個獨立的程序計數(shù)器,用于記錄下一條要運行的指令。各個線程之間的計數(shù)器互不影響,獨立工作,是一塊線程獨有的內(nèi)存空間。如果當(dāng)前線程正在執(zhí)行一個 Java 方法,則程序計數(shù)器記錄正在執(zhí)行的 Java 字節(jié)碼地址,如果當(dāng)前線程正在執(zhí)行一個 Native 方法,則程序計數(shù)器為空。

        虛擬機(jī)棧

        虛擬機(jī)棧用于存放函數(shù)調(diào)用堆棧信息。Java 虛擬機(jī)棧也是線程私有的內(nèi)存空間,它和 Java 線程在同一時間創(chuàng)建,它保存方法的局部變量、部分結(jié)果,并參與方法的調(diào)用和返回。

        Java 虛擬機(jī)規(guī)范允許 Java 棧的大小是動態(tài)的或者是固定的。在 Java 虛擬機(jī)規(guī)范中定義了兩種異常與棧空間有關(guān):StackOverflowError 和 OutOfMemoryError。如果線程在計算過程中,請求的棧深度大于最大可用的棧深度,則拋出 StackOverflowError;如果 Java ?梢詣討B(tài)擴(kuò)展,而在擴(kuò)展棧的過程中沒有足夠的內(nèi)存空間來支持棧的發(fā)展,則拋出 OutOfMemeoryError?梢允褂-Xss 參數(shù)來設(shè)置棧的大小,棧的大小直接決定了函數(shù)調(diào)用的可達(dá)深度。

        下面的例子展示了一個遞歸調(diào)用的應(yīng)用。計數(shù)器 count 記錄了遞歸的層次,這個沒有出口的遞歸函數(shù)一定會導(dǎo)致棧溢出。程序則在棧溢出時,打印出棧的當(dāng)前深度。

        清單 1. 遞歸調(diào)用顯示棧的最大深度

      public class TestStack {

      private int count = 0;

      //沒有出口的遞歸函數(shù)

      public void recursion(){

      count++;//每次調(diào)用深度加 1

      recursion();//遞歸

      }

      public void testStack(){

      try{

      recursion();

      }catch(Throwable e){

      System.out.println("deep of stack is "+count);//打印棧溢出的深度

      e.printStackTrace();

      }

      }

      public static void main(String[] args){

      TestStack ts = new TestStack();

      ts.testStack();

      }

      }

        清單 2. 清單 1 運行結(jié)果

      java.lang.StackOverflowError

      at TestStack.recursion(TestStack.java:7)

      at TestStack.recursion(TestStack.java:7)

      at TestStack.recursion(TestStack.java:7)

      at TestStack.recursion(TestStack.java:7)

      at TestStack.recursion(TestStack.java:7)

      at TestStack.recursion(TestStack.java:7)

      at TestStack.recursion(TestStack.java:7)deep of stack is 9013

        虛擬機(jī)棧在運行時使用一種叫做棧幀的數(shù)據(jù)結(jié)構(gòu)保存上下文數(shù)據(jù)。在棧幀中,存放了方法的局部變量表、操作數(shù)棧、動態(tài)連接方法和返回地址等信息。每一個方法的調(diào)用都伴隨著棧幀的入棧操作。相應(yīng)地,方法的返回則表示棧幀的出棧操作。如果方法調(diào)用時,方法的參數(shù)和局部變量相對較多,那么棧幀中的局部變量表就會比較大,棧幀會膨脹以滿足方法調(diào)用所需傳遞的信息。因此,單個方法調(diào)用所需的?臻g大小也會比較多。

        函數(shù)嵌套調(diào)用的次數(shù)由棧的大小決定。棧越大,函數(shù)嵌套調(diào)用次數(shù)越多。對一個函數(shù)而言,它的參數(shù)越多,內(nèi)部局部變量越多,它的棧幀就越大,其嵌套調(diào)用次數(shù)就會減少。

        本地方法棧

        本地方法棧和 Java 虛擬機(jī)棧的功能很相似,本地方法棧用于存放函數(shù)調(diào)用堆棧信息。Java 虛擬機(jī)棧用于管理 Java 函數(shù)的調(diào)用,而本地方法棧用于管理本地方法的調(diào)用。本地方法并不是用 Java 實現(xiàn)的,而是使用 C 實現(xiàn)的。在 SUN 的 HotSpot 虛擬機(jī)中,不區(qū)分本地方法棧和虛擬機(jī)棧。因此,和虛擬機(jī)棧一樣,它也會拋出 StackOverflowError 和 OutofMemoryError。

        Java 堆

        堆用于存放 Java 程序運行時所需的對象等數(shù)據(jù)。幾乎所有的對象和數(shù)組都是在堆中分配空間的。Java 堆分為新生代和老生代兩個部分,新生代用于存放剛剛產(chǎn)生的對象和年輕的對象,如果對象一直沒有被回收,生存得足夠長,老年對象就被移入老年代。新生代又可進(jìn)一步細(xì)分為 eden、survivor space0 和 survivor space1。eden 即對象的出生地,大部分對象剛剛建立時都會被存放在這里。survivor 空間是存放其中的對象至少經(jīng)歷了一次垃圾回收,并得以幸存下來的。如果在幸存區(qū)的對象到了指定年齡仍未被回收,則有機(jī)會進(jìn)入老年代 (tenured)。下面例子演示了對象在內(nèi)存中的分配方式。

        清單 3. 進(jìn)行一次新生代 GC

      public class TestHeapGC {

      public static void main(String[] args){

      byte[] b1 = new byte[1024*1024/2];

      byte[] b2 = new byte[1024*1024*8];

      b2 = null;

      b2 = new byte[1024*1024*8];//進(jìn)行一次新生代 GC

      System.gc();

      }

      }

        清單 4. 清單 3 的配置

        -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 -Xms40M 

      -Xmx40M -Xmn20M

        清單 5. 清單 3 的輸出

        [GC [DefNew: 9031K->661K(18432K), 0.0022784 secs] 

      9031K->661K(38912K),

        0.0023178 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]

        Heap

        def new generation total 18432K, used 9508K [0x348100000x35c10000

      0x35c10000)

        eden space 16384K, 54% used [0x348100000x350b3e580x35810000)

        from space 2048K, 32% used [0x35a100000x35ab54900x35c10000)

        to space 2048K, 0% used [0x358100000x358100000x35a10000)

        tenured generation total 20480K, used 0K [0x35c100000x37010000

      0x37010000)

        the space 20480K, 0% used [0x35c100000x35c100000x35c10200

      0x37010000)

        compacting perm gen total 12288K, used 374K [0x370100000x37c10000

      0x3b010000)

        the space 12288K, 3% used [0x370100000x3706db100x3706dc00

      0x37c10000)

        ro space 10240K, 51% used [0x3b0100000x3b5430000x3b543000

      0x3ba10000)

        rw space 12288K, 55% used [0x3ba100000x3c0ae4f80x3c0ae600

      0x3c610000)

        上述輸出顯示 JVM 在進(jìn)行多次內(nèi)存分配的過程中,觸發(fā)了一次新生代 GC。在這次 GC 中,原本分配在 eden 段的變量 b1 被移動到 from 空間段 (s0)。最后分配的 8MB 內(nèi)存被分配在 eden 新生代。

        方法區(qū)

        方法區(qū)用于存放程序的類元數(shù)據(jù)信息。方法區(qū)與堆空間類似,它也是被 JVM 中所有的線程共享的。方法區(qū)主要保存的信息是類的元數(shù)據(jù)。方法區(qū)中最為重要的是類的類型信息、常量池、域信息、方法信息。類型信息包括類的完整名稱、父類的完整名稱、類型修飾符和類型的直接接口類表;常量池包括這個類方法、域等信息所引用的常量信息;域信息包括域名稱、域類型和域修飾符;方法信息包括方法名稱、返回類型、方法參數(shù)、方法修飾符、方法字節(jié)碼、操作數(shù)棧和方法棧幀的局部變量區(qū)大小以及異常表?傊,方法區(qū)內(nèi)保持的信息大部分來自于 class 文件,是 Java 應(yīng)用程序運行必不可少的重要數(shù)據(jù)。

        在 Hot Spot 虛擬機(jī)中,方法區(qū)也稱為永久區(qū),是一塊獨立于 Java 堆的內(nèi)存空間。雖然叫做永久區(qū),但是在永久區(qū)中的對象同樣也可以被 GC 回收的。只是對于 GC 的表現(xiàn)也和 Java 堆空間略有不同。對永久區(qū) GC 的回收,通常主要從兩個方面分析:一是 GC 對永久區(qū)常量池的回收;二是永久區(qū)對類元數(shù)據(jù)的回收。Hot Spot 虛擬機(jī)對常量池的回收策略是很明確的,只要常量池中的常量沒有被任何地方引用,就可以被回收。

        清單 6 所示代碼會生成大量 String 對象,并將其加入常量池中。String.intern() 方法的含義是如果常量池中已經(jīng)存在當(dāng)前 String,則返回池中的對象,如果常量池中不存在當(dāng)前 String 對象,則先將 String 加入常量池,并返回池中的對象引用。因此,不停地將 String 對象加入常量池會導(dǎo)致永久區(qū)飽和。如果 GC 不能回收永久區(qū)的這些常量數(shù)據(jù),那么就會拋出 OutofMemoryError 錯誤。

        清單.6 GC 收集永久區(qū)

      public class permGenGC {

      public static void main(String[] args){

      for(int i=0;i

      String t = String.valueOf(i).intern();//加入常量池

      }

      }

      }

        清單 7. 清單 6 的配置

      -XX:PermSize=2M -XX:MaxPermSize=4M -XX:+PrintGCDetails

        清單 8. 清單 6 的輸出

        [Full GC [Tenured: 0K->149K(10944K), 0.0177107 secs] 

      3990K->149K(15872K),

        [Perm : 4096K->374K(4096K)], 0.0181540 secs] [Times: user=0.02 sys=0.02

      real=0.03 secs]

        [Full GC [Tenured: 149K->149K(10944K), 0.0165517 secs] 

      3994K->149K(15936K),

        [Perm : 4096K->374K(4096K)], 0.0169260 secs] [Times: user=0.01 sys=0.00

      real=0.02 secs]

        [Full GC [Tenured: 149K->149K(10944K), 0.0166528 secs] 

      3876K->149K(15936K),

        [Perm : 4096K->374K(4096K)], 0.0170333 secs] [Times: user=0.02 sys=0.00

      real=0.01 secs]

        每當(dāng)常量池飽和時,FULL GC 總能順利回收常量池數(shù)據(jù),確保程序穩(wěn)定持續(xù)進(jìn)行。

      分享到:
      本文糾錯】【告訴好友】【打印此文】【返回頂部
      將考試網(wǎng)添加到收藏夾 | 每次上網(wǎng)自動訪問考試網(wǎng) | 復(fù)制本頁地址,傳給QQ/MSN上的好友 | 申請鏈接 | 意見留言 TOP
      關(guān)于本站  網(wǎng)站聲明  廣告服務(wù)  聯(lián)系方式  站內(nèi)導(dǎo)航  考試論壇
      Copyright © 2007-2013 中華考試網(wǎng)(Examw.com) All Rights Reserved