天天看點

Java虛拟機知識總結Java虛拟機

Java虛拟機

第一部分 JVM基礎

第一章 Java記憶體區域與記憶體溢出異常

運作時資料區域

1.程式計數器(線程私有)

目前線程執行的位元組碼的行号訓示器。

Java虛拟機的多線程通過線程輪流切換并配置設定處理器執行時間的方式來實作,一個處理器或者多核處理器的一個核心隻會執行一條線程,每條線程需要一個獨立的程式計數器

  1. 如果線程在執行一個普通的Java方法,計數器記錄正在執行的虛拟機位元組碼指令的位址;
  2. 如果線程正在執行一個Native方法,計數器的值為空;
  3. 此記憶體區域是唯一一個在Java虛拟機規範中沒有規定任何

    OutOfMemoryError

    情況的區域。

2.Java虛拟機棧(線程私有)

虛拟機棧描述的是Java方法執行的記憶體模型:每個方法在執行的同時會建立一個棧幀用于存儲局部變量表,操作數棧,動态連結、方法出口等資訊。每個方法的執行過程,就對應一個棧幀在虛拟機棧中的入棧和出棧。

1)局部變量表
  1. 基本資料類型(

    boolean

    byte

    char

    short

    int

    long

    double

  2. 對象引用(reference類型)對象本身、對象起始位址的引用指針、指向一個對象的句柄、其他與此對象相關的位置
  3. returnAddress

    類型 指向一條位元組碼指令的位址
2)兩種異常狀态

StackOverflowError

異常:線程請求的棧深度大于虛拟機所允許的深度

OutOfMemoryError

異常:虛拟機棧可以動态擴充,如果擴充的時無法申請到足夠的記憶體

3.本地方法棧

作用與Java虛拟機棧相似:Java虛拟機棧為Java方法服務,抛出異常同虛拟機棧。

被動方法棧為Native方法服務

4.Java堆

被所有的線程共享的一塊記憶體區域,算是虛拟機所管理的記憶體中最大的一塊,帶區域主要存儲對象執行個體,是主要垃圾收集器管理的區域。

如果堆中沒有記憶體完成執行個體配置設定,就會抛出OutOfMemoryError異常。

5.方法區

各個線程共享區域,用于存儲已被虛拟機加載的類資訊、常量、靜态常量、即時編譯器編譯後的代碼等資料。

6.運作時常量池

運作時常量池方法區的一部分

  1. Class檔案存放類的版本、字段、方法、接口等描述資訊外,還有常量池,常量池存放編譯器生成的各種字面量和符号引用,這部分内容在類加載後進入方法區的運作時常量池存放。
  2. Java虛拟機對Class檔案的每一部分的格式要求都有嚴格的規定。
  • 運作常量池Java虛拟機規範沒有任何細節的要求。
  • 運作常量池中儲存Class檔案中描述的符号引用,翻譯出來的直接引用也會存儲在運作時常量池。
  • 運作時常量池具有動态性,不要求常量一定要在編譯器産生,并非置入Class檔案中常量池的内容才能進入方法區運作時常量區,運作期間也可以将常量放在運作常量池中
程式的執行方式有:
  1. 靜态編譯執行:事前(編譯時)編譯,編譯成機器碼,直接由CPU執行
  2. 動态編譯執行:運作時編譯,JIT編譯

    動态編譯通常指運作時将所有代碼都編譯

    JIT編譯将部分代碼進行編譯(熱點代碼)

  3. 動态解釋執行:JVM有解析器,按照位元組碼指令逐行解析逐行執行,每次執行都需要解析。

JIT編譯比解釋器快,說的是執行編譯後的代碼比解釋器解釋執行要快,而不是編譯這個動作比解釋快。對于隻執行一次的代碼而言,解釋執行可能比編譯執行要快。

HotSpot

虛拟機對象

對象建立

  1. 遇到new現在常量池定位這個類的符号引用,沒有進行類加載過程。
  2. 對象配置設定記憶體
    1. 指針碰撞

      java堆中記憶體絕對規整,所有用過的記憶體放在一邊,空閑的記憶體放在另一邊,中間放着一個指針作為分界點訓示器。配置設定記憶體就是把指針向空閑部分挪動一段與對象大小相同的距離。

    2. 空閑清單

      Java堆中記憶體不規整,虛拟機維護一個清單記錄可以記憶體,配置設定之時從清單中查找足夠大的記憶體即可。

    多線程下對象記憶體配置設定
    1. 對配置設定記憶體空間動作進行同步處理,采用CAS配上失敗重方式保證更新操作的原子性。
    2. 把記憶體配置設定動作按照線程劃分為不同的空間之上運作,每個線程預先分得一塊記憶體,本地線程配置設定緩沖(TLAB).那個線程的TLAB用完,配置設定新的TLAB才進行同步鎖定。
  3. 對象記憶體布局

    3個區域:對象頭,實際資料,對齊填充

    hashcode,GC分代年齡,等等

    相同寬度的字段配置設定在一起,短的字段可以配置設定到之前字段的空缺位置,子類中的變量也可以配置設定在父類的字段部分

    確定對象起始位址為8位元組的整數倍。

  4. 對象通路定位

    1.通過句柄方式通路,

    在Java堆中分出一塊記憶體進行存儲句柄池,這樣的話,在棧中存儲的是句柄的位址

    Java虛拟機知識總結Java虛拟機

    優點:

    當對象移動的時候(垃圾回收的時候移動很普遍),這樣值需要改變句柄中的指針,但是棧中的指針不需要變化,因為棧中存儲的是句柄的位址

    缺點:

    需要進行二次定位,尋找兩次指針,開銷相對于更大一些

    2.直接指針通路方式

    Java棧直接與對象進行通路,在Java堆中對象帆布中必須考慮存儲通路類型的資料的相關資訊,因為沒有了句柄了

    Java虛拟機知識總結Java虛拟機

    優點:

    速度快,不需要和句柄一樣指針定位的開銷

第二章垃圾收集器與記憶體配置設定政策

哪些區域的記憶體是垃圾收集器關注的?

Java 記憶體中程式計數器,虛拟機棧和本地方法棧3個區域都是線程私有的,其中記憶體都會随着方法結束,線程結束自動回收。而堆記憶體和方法區的記憶體配置設定回收時垃圾收集器關注的部分。

對象死亡判斷

1.引用計數算法

對象添加一個計數器,每一次引用它就在計數器加一,每次引用失效,就在計數器減一。計數器為0的對象不能被使用,是垃圾回收的對象。

缺點:很難解決對象之間互相引用問題

public class Test{
    public Object instance = null;
    public static void testGC(){
        Test a = new Test();
        Test b = new Test();
        a.instance = b;
        b.instance = a;
        a = null;
        b = null;
        System.gc();
    }
}
           
2.可直達分析算法

通過一系列的

GC Roots

的對象作為起始點,從這些結點開始向下搜尋,搜尋走過的路徑成為引用鍊,當一個對象到

GC Roots

沒有任何引用鍊,也就是從

GC Roots

到這個對象不可達,證明對象不可用。不可用對象将是可回收對象。

主流程式語言都是使用可直達算法作為判定對象是否存活的。

Java語言中可作為GC Roots的對象包括

  1. 虛拟機棧中引用的對象
  2. 方法區中類靜态屬性引用的對象
  3. 方法區中常量引用的對象
  4. 本地方法棧中JNI(即一般說的Native方法)引用的對象

引用

強引用

代碼之中普遍存在類似

Object a = new Object();

的引用,隻要強引用還存在,垃圾收集器就不會回收被引用的對象

軟引用

描述一些有用但是并非必須的對象,對于軟引用對象,在系統發生記憶體溢出異常之前,把軟引用對象列入回收範圍之内進行第二次回收。如果第二次回收之後還是沒有足夠的記憶體才會抛出記憶體溢出的異常。

弱引用

描述非必要對象,強度比軟引用弱,被弱引用引用的對象隻能存活到下一次垃圾回收之前。當垃圾收集器工作時,無論記憶體是否足夠,都會回收隻被弱引用引用的對象。

虛引用

幽靈引用或者幻影引用,最弱的引用。該引用的存在不會影響垃圾回收,也無法通過虛引用來擷取對象執行個體,唯一目的:能在垃圾回收時收到一個系統通知。

jdk

中直接記憶體的回收就用到虛引用,由于

jvm

自動記憶體管理的範圍是堆記憶體,而直接記憶體是在堆記憶體之外(其實是記憶體映射檔案,自行去了解虛拟記憶體空間的相關概念),是以直接記憶體的配置設定和回收都是有

Unsafe

類去操作,

java

在申請一塊直接記憶體之後,會在堆記憶體配置設定一個對象儲存這個堆外記憶體的引用,這個對象被垃圾收集器管理,一旦這個對象被回收,相應的使用者線程會收到通知并對直接記憶體進行清理工作。

finalize()方法

如果對象在可直達分析後發現沒有與GC Roots相連接配接的引用鍊,它會進行第一次标記,進行進一步的篩選。

進一步的篩選條件是對象是否有必要執行finalize()方法,對象沒有覆寫該方法或者該方法已經被虛拟機調用過,這兩種情況都是沒有必要執行。

如果這個對象有必要執行finalize()方法,這個對象就會放置在一個F-Queue隊列中,稍後虛拟機建立Finalizer線程去執行。但是不等待它結束。

finalize方法是對象逃脫死亡的最後機會,隻要在finalize中重新與引用鍊上任何對象建立關聯,這個對象就會免死亡。沒有逃脫,該對象就被回收了。

注意:finalize方法在面臨第二次回收的時候不會在執行。

垃圾收集算法

1.标記清除算法

标記所有需要回收的對象,标記完成後統一回收被标記對象。

不足:

  1. 效率問題,标記和清除過程的效率都不高
  2. 空間問題,标記清除後産生大量不連續記憶體碎片,空間碎片太多導緻配置設定較大對象時找不到足夠的連續空間而不得不提前出發再一次垃圾收集。
2.複制算法

将記憶體劃分為大小相同的兩塊,每次隻使用一塊,一塊用完,就将存活的對象複制在另一塊,并把原來的塊的記憶體清空,對整個半區進行垃圾回收。

缺點:将記憶體區域縮小為原來的一半。

優化不按照1:1的比例劃分而是劃分為Eden和兩個Survivor空間。每次使用Eden和其中一個Survivor,将存活的對象複制到兩一個Survivor清理之前用過的Survivor和Eden空間。預設Eden空間和Survivor比例是8:1,由于新生代垃圾回收有98%對象需要回收。當回收的對象大于10%的時候借助其他記憶體如老年代。

3.标記整理算法

複制算法在存活率較高的情況下,進行較多的複制操作,效率降低。

針對老年代對象存活率較高,提出标記整理算法。

标記整理算法的标記過程與标記清除相同,清除過程将對象向一端移動然後清除邊界以外的記憶體。

4.分代收集算法

把記憶體分成新生代和老年代,根據各個年代的特點采用适當的收集算法

HotSpot算法實作

1.枚舉根結點

由于GC Roots的結點主要在全局性引用與執行上下文(棧幀中的本地變量表)中,光方法區就有數百兆,逐個檢查這裡的引用會消耗大量時間。

可達性分析對于執行時間的敏感性還展現在GC停頓上。分析性工作必須在一個確定一緻的快照中進行。不可以出現對象引用關系的改變。需要停止Java執行線程(Stop The World)

主流Java使用準确式GC,在執行系統停頓下來,不需要一個不漏的檢查所有執行上下文和全局的引用位置,虛拟機有辦法知道哪些地方存放對象引用。在HotSpot的實作中,用OopMap資料結構達到這個目的,在類加載完成的時候,HotSpot就把對象内什麼偏移上什麼類型的資料計算出來,在JIT編譯的過程中,會在特定的位置記錄棧和寄存器中哪些地方是引用。

2.安全點

可能導緻引用關系變化或者說OopMap的内容變化的指令很多,如果為每條指令都生成對應的OopMap,會消耗大量的額外空間。

HotSpot沒有為每條可能改變引用關系的指令生成OopMap,而是在特定的位置記錄,這些位置稱為安全點。

到達安全點後,執行系統停頓開始GC,隻有到達安全點之後才能停止。

在方法調用,循環跳轉,異常跳轉等功能指令下會産生Safepoint。

如何讓所有的線程都跑到最近的安全點并停頓下來?

  1. 搶占式中斷

    不需要線程主動配合,GC發生後所有線程中斷,如果有的線程中斷的地方不在安全點,就恢複線程讓它執行到安全點。(幾乎沒有虛拟機使用搶占式中斷)

  2. 主動式中斷

    當GC需要中斷線程時,不直接對線程操作,而是設定一個标志,各個線程執行時主動去輪詢這個标志,發現中斷标志為真,就自己中斷線挂起,輪詢标志的地方和安全點重合,再加上建立對象配置設定記憶體的地方。

3.安全區域

程式不執行,在下次sleep和blocked狀态下,這些線程無法響應JVM中斷将線程執行到安全點的地方挂起。

安全區域是指在一段代碼片段中引用關系不會發生變化。線上程執行到安全區域,就不用管表示自己在安全區域狀态的線程。線上程離開安全區域時,檢查系統是否完成根結點枚舉,完成後線程繼續執行,否則必須等到收到可以安全離開安全區域的信号為止。

垃圾收集器

[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-prfvEa7X-1576995119051)(D:\面試\springbootimages\20160505170035450)]

兩個收集器之間存在連線說明可以搭配使用,所在區域表示它屬于新生代收集器還是老年代收集器。

(紅色部分為新生代)

1.Serial收集器

單線程收集器,使用一個CPU一條收集線程完成垃圾收集工作,在垃圾收集時必須停掉其他的所有工作線程,直到收集結束。Stop The World

在垃圾收集器不斷改進中,停頓時間不短縮小,但是還是沒有辦法消除。

Serial收集器是虛拟機Client模式下預設的新生代收集器,簡單高效,在限定單CPU的情況下,沒有線程互動開銷。在使用者桌面應用場景下,配置設定給虛拟機管理的記憶體不大,停頓時間完全在可控範圍内。

2.ParNew收集器

Serial的多線程版本,運作在Server模式下虛拟機新生代首選的新生代收集器。

  • 并發:多個線程同時執行(但是在微觀上是交替進行的)
  • 并行:多個線程同一時刻都在運作
3.Parallel Scavenge收集器

新生代收集器,使用複制算法,并行的多線程收集器。

  • 特别之處:

Parallel Scavenge收集器關注目的是達到一個可以控制的吞吐量。

​ 吞吐量=運作使用者代碼時間/(運作使用者代碼時間+垃圾收集時間)

高吞吐量可以高效利用CPU時間,盡快完成程式的運算任務。

相比之下:

CMS等收集器關注盡可能地縮短垃圾收集使用者線程停頓時間

停頓時間越短越适合需要與使用者互動的程式,良好的相應速度提升使用者體驗。

  • Parallel Scavenge收集器兩個參數用于精确控制吞吐量。

-XX:MaxGCPauseMillis控制最大的停頓時間

-XX:GCTimeRatio直接設定吞吐量大小

自适應調節政策

Parallel Scavenge收集器被稱為吞吐量優先的收集器

-XX:UseAdaptiveSizePolicy參數是一個開關參數,該參數打開,不需要手動指定新生代的大小,Eden與Survivor區的比例,晉升老年代對象的大小等細節參數了,虛拟機會根據目前系統運作情況收集性能監控資訊,動态調整這些參數以提供最合适的停頓時間和最大的吞吐量。這種調節方式叫做GC自适應的調節政策。

自适應調節是Parallel Scavenge收集器與ParNew收集器的一個重要差別。

4.Serial Old收集器

使用标記整理算法,單線程老年代收集器,Client模式下虛拟機使用。如果在Server模式下兩個用途:

  1. 在JDK1.5之前的版本中與Parallel Scavenge收集器搭配使用
  2. 作為CMS收集器的後備預案,在并發收集中發生Concurrent Mode Failure時使用
5.Parallel Old收集器

Parallel Old收集器是Parallel Scavenge收集器的老年版本,多線程,标記整理算法。

由于Parallel Scavenge收集器在Parallel Old收集器出現之前,Parallel Scavenge收集器隻能與Serial Old配合,Serial在伺服器端應用性能拖累,Parallel Scavenge收集器的最大吞吐量不能獲得最大化效果。

6.CMS收集器

以擷取最短回收停頓時間為目标的收集器,目前大部分Java應用集中在網際網路網站或者B/S系統的伺服器端上,這類應用重視伺服器響應速度,希望停頓時間最短,帶來較好的使用者體驗。

标記清除算法

運作步驟:

  1. 初始标記
  2. 并發标記
  3. 重新标記
  4. 并發清除

    解釋:

  • 其中初始标記和重新标記需要Stop The World。
  • 初始标記标記一下GC Roots能直接關聯到的對象,速度快
  • 并發标記階段進行GC Roots Tracing 的過程
  • 重新标記為了修正并發标記期間因使用者程式繼續運作而導緻的标記産生變化的那一部分對象的标記,這階段的停頓時間大于初始标記停頓時間。
  • 并發标記和并發清除都可以和使用者線程一起運作,從整體上可以看做CMS收集器的記憶體回收過程與使用者線程一起并發執行。

缺點:

  1. CPU資源敏感
  2. 無法處理浮動垃圾:在CMS進行并發垃圾清除階段,使用者線程還在執行過程中産生的垃圾沒有标記,此次GC無法處理這個階段産生的垃圾,這些垃圾叫做浮動垃圾,隻能遺留至下一次GC過程進行垃圾回收。

    參數

    -XX:CMSInitiatingOccupancyFraction

    設定老年代使用空間達到多少時激活CMS收集器,一般設定為68%,設定門檻值偏高會導緻,Concurrent Mode Failure,虛拟機啟用Serial Old收集器,提高停頓時間性能降低。
  3. 由于吃用标記清除算法導緻産生大量的記憶體垃圾
7.G1收集器

記憶體配置設定政策

對象優先在Eden配置設定

大多數情況下,對象會在新生代Eden區中配置設定,當Eden沒有足夠的空間,虛拟機發起一次Minor GC。

​ Minor GC是新生代GC發生在新生代垃圾回收動作,新生代對象朝生夕死,是以Minor GC 頻率高,速度快

​ Major GC/Full GC發生在老年代的垃圾收集動作,速度比Minor GC 慢十倍以上。

大對象直接進入老年代
長期存活的對象直接進入老年代

虛拟機給對象定義年齡計時器,沒熬過一次Minor GC,年齡計時器就加1,年齡增長到一定程度就直接晉升老年代。

動态對象年齡判斷

在Survivor空間中相同年齡所有對象大小總和大于Survivor空間的一半,年齡大于等于代年齡的對象直接進入老年代。無須達到進入老年代的年齡。

空間配置設定擔保

在發生Minor GC 之前虛拟機先檢查老年代最大可用的連續空間是否大于新生代所有對象的總空間,這個條件成立,Minor GC可以確定是安全的。如果不成立,虛拟機檢視

HandlePromotionFailure

設定的值是否允許擔保失敗,如果允許,那麼虛拟機繼續檢查老年代最大可用連續空間是否大于曆次晉升到老年代對象的平均大小,如果大于就嘗試進行Minor GC,這次Minor GC 是有風險的。如果小于,或者

HandlePromotionFailure

設定不允許冒險,這時就要進行一次Full GC。

為何HotSpot虛拟機要實作兩個不同的即時編譯器?

第二部分 虛拟機執行子系統

類檔案結構

class檔案的頭四個位元組為魔數,确定這個檔案是否為一個被虛拟機接收的class檔案,魔數用做身份識别。

接下來的四個位元組為class版本号:第五第六個位元組為次版本号,第七第八位元組為主版本号。高版本JDK可以相容低版本,但是低版本不能運作高版本class檔案。

緊接着為常量池入口,class檔案資源倉庫,最大資料項目之一,常量池數量不固定需要一個u2類型的資料代表常量池計數值。計數值從1開始。

​ 常量池兩大類常量:字面量和符号引用

​ 符号引用包括:類和接口的全限定名、字段的名稱和描述、方法的名稱和描述

通路标志:public final super interface abstract synthetic annotation enum

類索引 父類索引 接口索引集合

字段表集合描述接口或者類中聲明的變量

方法表集合

屬性表集合

虛拟機類加載機制

類的生命周期:加載、連接配接(驗證、準備、解析)、初始化、使用、解除安裝。

詳細見java高并發詳解

第三部分 程式編譯代碼優化

Javac編譯器

HotSpot虛拟機使用c++語言實作,Javac編譯器由Java語言編寫。

編譯的過程:

解析與填充符号表過程:詞法分析和文法分析,填充符号表

插入式注解器的注解過程

語義分析位元組碼生成過程

HotSpot的即時編譯器

HotSpot虛拟機是解釋器和編譯器并存架構,當程式需要迅速啟動和執行使用解釋器,程式運作後随之時間推移編譯器之間發揮作用。

HotSpot虛拟機内部有兩個即時編譯器Client Compiler和Server Compiler,簡化為c1、c2編譯器。

主流的HotSpot虛拟機預設采用解釋器和其中一個編譯器配合使用的工作方式,程式使用哪種編譯器主要取決于虛拟機的運作模式。HotSpot虛拟機會根據自身的版本于主控端器的硬體性能自動選擇運作的模式,使用者也可通過使用

-client

或者

-server

參數強制指定虛拟機運作在哪個子產品下。

什麼是熱點代碼?
  • 被多次調用的方法
  • 被多次調用的循環體

這兩種情況,編譯器是以對象為編譯對象。棧上替換,即方法棧幀還在棧上,方法就被替換。

方法計數器:統計方法調用的次數

回邊計數器:統計一個方法中循環體代碼執行的次數,在位元組碼中遇到控制流向後跳轉的指令稱為回邊。

代碼優化技術

語言無關的公共子表達式消除

一個表達式已經計算過,并且沒有發生變化,那該表達式就是公共子表達式。對于這個表達式直接使用之前的計算結果代替該表達式即可。

如果這種優化僅限于程式基本塊内就成為局部公共子表達式消除,如果優化範圍覆寫多個基本塊就稱為全局公共子表達式消除。

語言相關的數組邊界檢查消除

把運作期檢查提到編譯期完成。

其他類似優化技術:自動裝箱消除,安全點消除、消除反射等等。

方法内聯

逃逸分析

逃逸分析的基本行為:分析對象的動态作用域,一個對象被定義後,可能被外部方法引用,例如通過調用參數傳遞到其他的方法就是方法逃逸。有可能被外部線程通路,例如指派給類變量或者可以在其他線程中通路該執行個體對象,叫做線程逃逸。

别的方法或者線程無法通過任何管道通路這個對象執行個體,也就是對象不會逃逸到方法或者線程之外。對這個變量進行優化:

  • 棧上配置設定

    Java堆中的對象對于各個線程都是共享和可見的,隻要持有該對象的引用就可以通路該對象的資料。如果想要避免這個對象不會逃逸出方法之外,就将其在棧上配置設定記憶體,對象所占空間會随着方法調用傳回出棧而銷毀。一般應用中不會逃逸的局部對象所占的比例較大,如果棧上配置設定,垃圾回收系統的壓力會減小。

  • 同步消除

    對這個變量實施同步措施就可以消除其逃逸到别的線程

  • 标量替換

    标量是指一個資料已經無法再分解成更小的資料來表示了,Java中的原始資料類型(int,long等資料類型,reference類型)都是不能進一步分解的。如果一個資料可以被分解稱為聚合量。

    把一個Java對象拆散,根據程式通路情況将其使用到的成員變量恢複原始資料類型來通路叫做标量替換。

    如果逃逸分析證明一個對象不會被外部通路,并且這個對象可以被拆散,程式真正執行的時候可能不建立這個對象改為直接建立它的若幹個被這個方法使用的成員變量來替換。對象拆分後不僅可以在棧上配置設定和讀寫之外,還可以為後續優化創造條件。

Java編譯器和C++編譯器的對比

代表即時編譯器與靜态編譯器的對比

  1. 即時編譯器編譯過程占用使用者線程的運作時間,具有時間壓力,如果編譯的速度不能達到要求使用者程式将會察覺到重大的延遲。嚴重受制于編譯成本,而靜态編譯器不考慮編譯成本。
  2. Java語言是動态類型的編譯語言,虛拟機必須頻繁的動态檢查,對于程式沒有明确的檢查行為,盡管努力優化但是也會消耗運作時間
  3. Java中沒有virtual關鍵字,但虛方法使用頻率遠遠大于C++語言,是以運作時對方法接收者進行動态選擇頻率較高。
  4. Java是動态可擴充語言,運作時加載新的類可能改變程式類型的繼承關系,使得全局優化難以進行,因為編譯器無法看到程式全貌。許多的優化措施隻能以激進優化的方式進行。
  5. Java對象大多配置設定在堆記憶體,很少配置設定在棧上。垃圾回收上,C++使用代碼進行回收,Java中存在無用對象篩選,是以Java在垃圾回收上效率較C++低。

Java語言在性能上的劣勢為了換取開發上的效率,比如動态安全,動态擴充,垃圾回收機制。

繼續閱讀