當前位置:首頁 » 操作系統 » 根搜索演算法

根搜索演算法

發布時間: 2023-02-26 08:46:50

❶ 搜索演算法的運算原理

搜索演算法實際上是根據初始條件和擴展規則構造一棵「解答樹」並尋找符合目標狀態的節點的過程。所有的搜索演算法從最終的演算法實現上來看,都可以劃分成兩個部分——控制結構(擴展節點的方式)和產生系統(擴展節點),而所有的演算法優化和改進主要都是通過修改其控制結構來完成的。其實,在這樣的思考過程中,我們已經不知不覺地將一個具體的問題抽象成了一個圖論的模型——樹,即搜索演算法的使用第一步在於搜索樹的建立。
由圖一可以知道,這樣形成的一棵樹叫搜索樹。初始狀態對應著根結點,目標狀態對應著目標結點。排在前的結點叫父結點,其後的結點叫子結點,同一層中的結點是兄弟結點,由父結點產生子結點叫擴展。完成搜索的過程就是找到一條從根結點到目標結點的路徑,找出一個最優的解。這種搜索演算法的實現類似於圖或樹的遍歷,通常可以有兩種不同的實現方法,即深度優先搜索(DFS——Depth First search)和廣度優先搜索(BFS——Breadth First Search)。

java垃圾回收:GC在什麼時候對什麼做了什麼

GC在什麼時候對什麼做了什麼?
要回答這個問題,先了解下GC的發展史、jvm運行時數據區的劃分、jvm內存分配策略、jvm垃圾收集演算法等知識。
先說下jvm運行時數據的劃分,粗暴的分可以分為堆區(Heap)和棧區(Stack),但jvm的分法實際上比這復雜得多,大概分為下面幾塊:
1、程序計數器(Program Conuter Register)
程序計數器是一塊較小的內存空間,它是當前線程執行位元組碼的行號指示器,位元組碼解釋工作器就是通過改變這個計數器的值來選取下一條需要執行的指令。它是線程私有的內存,也是唯一一個沒有OOM異常的區域。
2、Java虛擬機棧區(Java Virtual Machine Stacks)
也就是通常所說的棧區,它描述的是Java方法執行的內存模型,每個方法被執行的時候都創建一個棧幀(Stack Frame),用於存儲局部變數表、操作數棧、動態鏈接、方法出口等。每個方法被調用到完成,相當於一個棧幀在虛擬機棧中從入棧到出棧的過程。此區域也是線程私有的內存,可能拋出兩種異常:如果線程請求的棧深度大於虛擬機允許的深度將拋出StackOverflowError;如果虛擬機棧可以動態的擴展,擴展到無法動態的申請到足夠的內存時會拋出OOM異常。
3、本地方法棧(Native Method Stacks)
本地方法棧與虛擬機棧發揮的作用非常相似,區別就是虛擬機棧為虛擬機執行Java方法,本地方法棧則是為虛擬機使用到的Native方法服務。
4、堆區(Heap)
所有對象實例和數組都在堆區上分配,堆區是GC主要管理的區域。堆區還可以細分為新生代、老年代,新生代還分為一個Eden區和兩個Survivor區。此塊內存為所有線程共享區域,當堆中沒有足夠內存完成實例分配時會拋出OOM異常。
5、方法區(Method Area)
方法區也是所有線程共享區,用於存儲已被虛擬機載入的類信息、常量、靜態變數、即時編譯後的代碼等數據。GC在這個區域很少出現,這個區域內存回收的目標主要是對常量池的回收和類型的卸載,回收的內存比較少,所以也有稱這個區域為永久代(Permanent Generation)的。當方法區無法滿足內存分配時拋出OOM異常。
6、運行時常量池(Runtime Constant Pool)
運行時常量池是方法區的一部分,用於存放編譯期生成的各種字面量和符號引用。

垃圾收集(Garbage Collection)並不是Java獨有的,最早是出現在Lisp語言中,它做的事就是自動管理內存,也就是下面三個問題:
1、什麼時候回收
2、哪些內存需要回收
3、如何回收

1、什麼時候回收?
上面說到GC經常發生的區域是堆區,堆區還可以細分為新生代、老年代,新生代還分為一個Eden區和兩個Survivor區。
1.1 對象優先在Eden中分配,當Eden中沒有足夠空間時,虛擬機將發生一次Minor GC,因為Java大多數對象都是朝生夕滅,所以Minor GC非常頻繁,而且速度也很快;
1.2 Full GC,發生在老年代的GC,當老年代沒有足夠的空間時即發生Full GC,發生Full GC一般都會有一次Minor GC。大對象直接進入老年代,如很長的字元串數組,虛擬機提供一個-XX:PretenureSizeThreadhold參數,令大於這個參數值的對象直接在老年代中分配,避免在Eden區和兩個Survivor區發生大量的內存拷貝;
1.3 發生Minor GC時,虛擬機會檢測之前每次晉升到老年代的平均大小是否大於老年代的剩餘空間大小,如果大於,則進行一次Full GC,如果小於,則查看HandlePromotionFailure設置是否允許擔保失敗,如果允許,那隻會進行一次Minor GC,如果不允許,則改為進行一次Full GC。

2、哪些內存需要回收
jvm對不可用的對象進行回收,哪些對象是可用的,哪些是不可用的?Java並不是採用引用計數演算法來判定對象是否可用,而是採用根搜索演算法(GC Root Tracing),當一個對象到GC Roots沒有任何引用相連接,用圖論的來說就是從GC Roots到這個對象不可達,則證明此對象是不可用的,說明此對象可以被GC。對於這些不可達對象,也不是一下子就被GC,而是至少要經歷兩次標記過程:如果對象在進行根搜索演算法後發現沒有與GC Roots相連接的引用鏈,那它將會第一次標記並且進行一次篩選,篩選條件是此對象有沒有必要執行finalize()方法,當對象沒有覆蓋finalize()方法或者finalize()方法已經被虛擬機調用執行過一次,這兩種情況都被視為沒有必要執行finalize()方法,對於沒有必要執行finalize()方法的將會被GC,對於有必要有必要執行的,對象在finalize()方法中可能會自救,也就是重新與引用鏈上的任何一個對象建立關聯即可。

3、如何回收
選擇不同的垃圾收集器,所使用的收集演算法也不同。
在新生代中,每次垃圾收集都發現有大批對象死去,只有少量存活,則使用復制演算法,新生代內存被分為一個較大的Eden區和兩個較小的Survivor區,每次只使用Eden區和一個Survivor區,當回收時將Eden區和Survivor還存活著的對象一次性的拷貝到另一個Survivor區上,最後清理掉Eden區和剛才使用過的Survivor區,Eden和Survivor的默認比例是8:1,可以使用-XX:SurvivorRatio來設置該比例。
而老年代中對象存活率高,沒有額外的空間對它進行分配擔保,必須使用「標記-清理」或「標記-整理」演算法。

❸ bfs演算法是什麼

廣度優先搜索演算法(英語:Breadth-First Search,縮寫為BFS),又譯作寬度優先搜索,或橫向優先搜索,是一種圖形搜索演算法。

簡單的說,BFS是從根節點開始,沿著樹的寬度遍歷樹的節點。如果所有節點均被訪問,則演算法中止。廣度優先搜索的實現一般採用open-closed表。

作法

BFS是一種暴力搜索演算法,目的是系統地展開並檢查圖中的所有節點,以找尋結果。換句話說,它並不考慮結果的可能地址,徹底地搜索整張圖,直到找到結果為止。BFS並不使用經驗法則演算法。

從演算法的觀點,所有因為展開節點而得到的子節點都會被加進一個先進先出的隊列中。

一般的實現里,其鄰居節點尚未被檢驗過的節點會被放置在一個被稱為open的容器中(例如隊列或是鏈表),而被檢驗過的節點則被放置在被稱為closed的容器中。


(3)根搜索演算法擴展閱讀:

廣度優先搜索演算法的應用

廣度優先搜索演算法能用來解決圖論中的許多問題,例如:

1、查找圖中所有連接組件(ConnectedComponent)。一個連接組件是圖中的最大相連子圖。

2、查找連接組件中的所有節點。

3、查找非加權圖中任兩點的最短路徑。

4、測試一圖是否為二分圖。

5、(Reverse)Cuthill–McKee演算法

❹ 基本演算法——深度優先搜索(DFS)和廣度優先搜索(BFS)

        深度優先搜索和廣度優先搜索,都是圖形搜索演算法,它兩相似,又卻不同,在應用上也被用到不同的地方。這里拿一起討論,方便比較。

一、深度優先搜索

        深度優先搜索屬於圖演算法的一種,是一個針對圖和樹的遍歷演算法,英文縮寫為DFS即Depth First Search。深度優先搜索是圖論中的經典演算法,利用深度優先搜索演算法可以產生目標圖的相應拓撲排序表,利用拓撲排序表可以方便的解決很多相關的圖論問題,如最大路徑問題等等。一般用堆數據結構來輔助實現DFS演算法。其過程簡要來說是對每一個可能的分支路徑深入到不能再深入為止,而且每個節點只能訪問一次。

基本步奏

(1)對於下面的樹而言,DFS方法首先從根節點1開始,其搜索節點順序是1,2,3,4,5,6,7,8(假定左分枝和右分枝中優先選擇左分枝)。

(2)從stack中訪問棧頂的點;

(3)找出與此點鄰接的且尚未遍歷的點,進行標記,然後放入stack中,依次進行;

(4)如果此點沒有尚未遍歷的鄰接點,則將此點從stack中彈出,再按照(3)依次進行;

(5)直到遍歷完整個樹,stack里的元素都將彈出,最後棧為空,DFS遍歷完成。

二、廣度優先搜索

        廣度優先搜索(也稱寬度優先搜索,縮寫BFS,以下採用廣度來描述)是連通圖的一種遍歷演算法這一演算法也是很多重要的圖的演算法的原型。Dijkstra單源最短路徑演算法和Prim最小生成樹演算法都採用了和寬度優先搜索類似的思想。其別名又叫BFS,屬於一種盲目搜尋法,目的是系統地展開並檢查圖中的所有節點,以找尋結果。換句話說,它並不考慮結果的可能位置,徹底地搜索整張圖,直到找到結果為止。基本過程,BFS是從根節點開始,沿著樹(圖)的寬度遍歷樹(圖)的節點。如果所有節點均被訪問,則演算法中止。一般用隊列數據結構來輔助實現BFS演算法。

基本步奏

(1)給出一連通圖,如圖,初始化全是白色(未訪問);

(2)搜索起點V1(灰色);

(3)已搜索V1(黑色),即將搜索V2,V3,V4(標灰);

(4)對V2,V3,V4重復以上操作;

(5)直到終點V7被染灰,終止;

(6)最短路徑為V1,V4,V7.

❺ JVM G1參數

採用根搜索演算法,通過一系列名為」GC Roots」的對象作為起始點,從這些節點開始向下搜索,搜索所走過的路徑稱為引用鏈(Reference Chain),當一個對象到GC Roots沒有任何引用鏈相連時,則證明此對象是不可用的。

1. 棧中引⽤的對象

2. 靜態變數、常量引⽤的對象

3. 本地⽅法棧native⽅法引⽤的對象

1.標記-復制

2.標記-清理

3.標記-整理

G1採取了不同的策略來解決並行、串列和CMS收集器的碎片、暫停時間不可控制等問題

G1會優先回收垃圾對象特別多的分區,這樣可以花費較少的時間來回收這些分區的垃圾

在年輕代回收期間,G1 GC 會調整其年輕代空間(eden 和存活空間大小)以滿足目標。

在混合回收期間,G1 GC 會根據混合垃圾回收的目標次數調整所回收的年老代區域數量,並調整堆的每個區域中存活對象的百分比,以及總體可接受的堆廢物百分比。

G1演算法將堆劃分為若干個區域(Region), 每個region可以是edon, survior, old區域,每個region大小相同為1M, 2M, 4M 2的冪次方大小,整個堆中默認有2048個region,每個Region默認按照512Kb劃分成多個Card。

如果一個對象佔用的空間大於一個region尺寸的一半,就會專門放入到humongous區域。G1劃分了一個Humongous區,它用來專門存放巨型對象。如果一個H區裝不下一個巨型對象,那麼G1會尋找連續的H分區來存儲。為了能找到連續的H區,有時候不得不啟動Full GC。這種情況可以調整整個堆的大小,或者調整G1HeapRegionSize大小。

Remembered Set ,對應於一個region,採用point-in策略,記錄該region中某card被其它region 的引用情況。RSet其實是一個Hash Table,Key是別的Region的起始地址,Value是一個集合,裡面的元素是Card Table的Index。 進行垃圾回收時,如果Region1有根對象A引用了Region2的對象B,顯然對象B是活的,如果沒有Rset,就需要掃描整個Region1或者其它Region,才能確定對象B是活躍的,有了Rset可以避免對整個堆進行掃描。

cms中年老代也有rset,採用point-out策略,記錄年老代中引用年輕代的對象,這樣在ygc時就不用掃描整個年老代,只掃描年老代的rset。

G1MaxNewSizePercent 新生代最大值,默認值60%

G1MaxPauseTime 設置G1收集過程目標時間,默認值200ms

G1ReservePercent  預留百分之多少內存,防止晉升失敗的情況,默認值是10

-XX:=45 – 整個堆棧使用達到百分之多少的時候,啟動GC周期. 基於整個堆,不僅僅是其中的某個代的佔用情況,G1根據這個值來判斷是否要觸發GC周期, 0表示一直都在GC,默認值是45(即45%滿了,或者說佔用了),啟動mix gc

MaxRAMPercentage、InitialRAMPercentage、MinRAMPercentage 應用於docker容器中,根據docker容器內存大小指定堆的初始,最大,最小比例

ParallelRefProcEnabled 默認為false,並行的處理Reference對象,如WeakReference,除非在GC log里出現Reference處理時間較長的日誌,否則效果不會很明顯

顯式的使用-Xmn設置年輕代的大小,會干預G1的默認行為。

G1就不會再考慮設定的暫停時間目標,所以本質上說,設定了年輕代大小就相當於禁用了目標暫停時間。

G1就無法根據需要增大或者縮小年輕代的小心。既然大小固定了,就無法在大小上做任何改變了。

為了避免堆內存被耗盡,虛擬機會觸發一個混合的垃圾收集器,即mixed gc

除了回收整個young region,還會回收一部分的old region

主要分為以下幾個步驟:

1. initial mark: 初始標記過程,整個過程STW,標記了從GC Root可達的對象

2. concurrent marking: 並發標記過程,整個過程gc collector線程與應用線程可以並行執行,標記出GC Root可達對象衍生出去的存活對象,並收集各個Region的存活對象信息

3. remark: 最終標記過程,整個過程STW,標記出那些在並發標記過程中遺漏的,或者內部引用發生變化的對象

4. clean up: 垃圾清除過程,如果發現一個Region中沒有存活對象,則把該Region加入到空閑列表中

使用范圍不一樣

CMS收集器是老年代的收集器,可以配合新生代的Serial和ParNew收集器一起使用

G1收集器收集范圍是老年代和新生代

STW的時間

CMS收集器以最小的停頓時間為目標的收集器。

G1收集器可預測垃圾回收的停頓時間(建立可預測的停頓時間模型)

垃圾碎片

CMS收集器是使用「標記-清除」演算法進行的垃圾回收,容易產生內存碎片

G1收集器使用的是「標記-整理」演算法,進行了空間整合,降低了內存空間碎片

參考: https://www.jianshu.com/p/a3e6a9de7a5d

https://blog.csdn.net/u013380694/article/details/83341913

https://www.jianshu.com/p/ab54489f5d71?u_atoken=ca2d26ce-15a4-462b-9ee2-1d3dfca2d647&u_asession=-kRD0-eOdne8XcfWhUbbJUSLGdkUER_tKV6ZX0KNBwm7Lovlpxjd_P_q4JsKWYrT3W_NKPr8w6oU7K9iRp8G_&u_asig=_7vZwmCUEmowKET9soS-B3_YnquxJK1II_ufphdjR9EF5W4qBzbaQxa_DPpZ9KH_-QE6N5IgXkZa79JS7q8ZD7Xtz2Ly--WWPRPQyB_SKrj-61LB_f61u3h9VXwMyh6PgyDIVSG1W__la6lRJ-&u_aref=npKvxxWi1kDXXuw5mG2TYBN3CXA%3D

❻ 圖遍歷演算法之DFS/BFS

在計算機科學, 圖遍歷(Tree Traversal,也稱圖搜索)是一系列圖搜索的演算法, 是單次訪問樹結構類型數據(tree data structure)中每個節點以便檢查或更新的一系列機制。圖遍歷演算法可以按照節點訪問順序進行分類,根據訪問目的或使用場景的不同,演算法大致可分為28種:

圖遍歷即以特定方式訪問圖中所有節點,給定節點下有多種可能的搜索路徑。假定以順序方式進行(非並行),還未訪問的節點就需通過堆棧(LIFO)或隊列(FIFO)規則來確定訪問先後。由於樹結構是一種遞歸的數據結構,在清晰的定義下,未訪問節點可存儲在調用堆棧中。本文介紹了圖遍歷領域最流行的廣度優先搜索演算法BFS和深度優先搜索演算法DFS,對其原理、應用及實現進行了闡述。通常意義上而言,深度優先搜索(DFS)通過遞歸調用堆棧比較容易實現,廣義優先搜索通過隊列實現。

深度優先搜索(DFS)是用於遍歷或搜索圖數據結構的演算法,該演算法從根節點開始(圖搜索時可選擇任意節點作為根節點)沿著每個分支進行搜索,分支搜索結束後在進行回溯。在進入下一節點之前,樹的搜索盡可能的加深。
DFS的搜索演算法如下(以二叉樹為例):假定根節點(圖的任意節點可作為根節點)標記為 ,
(L) : 遞歸遍歷左子樹,並在節點 結束。
(R): 遞歸遍歷右子樹,並在節點 結束。
(N): 訪問節點 。
這些步驟可以以任意次序排列。如果(L)在(R)之前,則該過程稱為從左到右的遍歷;反之,則稱為從右到左的遍歷。根據訪問次序的不同,深度優先搜索可分為 pre-order、in-order、out-order以及post-order遍歷方式。

(a)檢查當前節點是否為空;
(b)展示根節點或當前節點數據;
(c)遞歸調用pre-order函數遍歷左子樹;
(d)遞歸調用pre-order函數遍歷右子樹。
pre-order遍歷屬於拓撲排序後的遍歷,父節點總是在任何子節點之前被訪問。該遍歷方式的圖示如下:

遍歷次序依次為:F -B -A-D- C-E-G- I-H.

(a)檢查當前節點是否為空;
(b)遞歸調用in-order函數遍歷左子樹;
(c)展示根節點或當前節點數據;
(d)遞歸調用in-order函數遍歷右子樹。
在二叉樹搜索中,in-order遍歷以排序順序訪問節點數據。該遍歷方式的圖示如下:

遍歷次序依次為:A -B - C - D - E - F - G -H-I

(a)檢查當前節點是否為空;
(b)遞歸調用out-order函數遍歷右子樹;
(c)展示根節點或當前節點數據;
(d)遞歸調用out-order函數遍歷左子樹。
該遍歷方式與LNR類似,但先遍歷右子樹後遍歷左子樹。仍然以圖2為例,遍歷次序依次為:H- I-G- F- B- E- D- C- A.

(a)檢查當前節點是否為空;
(b)遞歸調用post-order函數遍歷左子樹;
(c)遞歸調用post-order函數遍歷右子樹;
(d)展示根節點或當前節點數據。
post-order遍歷圖示如下:

遍歷次序依次為:A-C-E-D-B-H-I-G-F.

pre-order遍歷方式使用場景:用於創建樹或圖的副本;
in-order遍歷使用場景:二叉樹遍歷;
post-order遍歷使用場景:刪除樹

遍歷追蹤也稱樹的序列化,是所訪問根節點列表。無論是pre-order,in-order或是post-order都無法完整的描述樹特性。給定含有不同元素的樹結構,pre-order或post-order與in-order遍歷方式結合起來使用才可以描述樹的獨特性。

樹或圖形的訪問也可以按照節點所處的級別進行遍歷。在每次訪問下一層級節點之前,遍歷所在高層級的所有節點。BFS從根節點(圖的任意節點可作為根節點)出發,在移動到下一節點之前訪問所有相同深度水平的相鄰節點。

BFS的遍歷方法圖示如下:

遍歷次序依次為: F-B-G-A-D-I-C-E-H.

圖演算法相關的R包為igraph,主要包括圖的生成、圖計算等一系列演算法的實現。

使用方法:

參數說明:

示例:

結果展示:

DFS R輸出節點排序:

使用方法:

參數含義同dfs
示例:

結果展示:

BFS R輸出節點排序:

以尋找兩點之間的路徑為例,分別展示BFS及DFS的實現。圖示例如下:

示例:

輸出結果:

示例:

輸出結果:

[1] 維基網路: https://en.wikipedia.org/wiki/Tree_traversal
[2] GeeksforGeeks: https://www.geeksforgeeks.org/tree-traversals-inorder-preorder-and-postorder/
[3] http://webdocs.cs.ualberta.ca/~holte/T26/tree-traversal.html
[4]Martin Broadhurst, Graph Algorithm: http://www.martinbroadhurst.com/Graph-algorithms.html#section_1_1
[5]igraph: https://igraph.org/r/doc/dfs.html
[6]igraph: https://igraph.org/r/doc/bfs.html
[7] Depth-First Search and Breadth-First Search in python: https://eddmann.com/posts/depth-first-search-and-breadth-first-search-in-python/

❼ java有哪些垃圾回收演算法

常用的垃圾回收演算法有:
(1).引用計數演算法:
給對象中添加一個引用計數器,每當有一個地方引用它時,計數器值就加1;當引用失效時,計數器值就減1;任何時刻計數器都為0的對象就是不再被使用的,垃圾收集器將回收該對象使用的內存。
引用計數演算法實現簡單,效率很高,微軟的COM技術、ActionScript、Python等都使用了引用計數演算法進行內存管理,但是引用計數演算法對於對象之間相互循環引用問題難以解決,因此java並沒有使用引用計數演算法。
(2).根搜索演算法:
通過一系列的名為「GC Root」的對象作為起點,從這些節點向下搜索,搜索所走過的路徑稱為引用鏈(Reference Chain),當一個對象到GC Root沒有任何引用鏈相連時,則該對象不可達,該對象是不可使用的,垃圾收集器將回收其所佔的內存。
主流的商用程序語言C#、java和Lisp都使用根搜素演算法進行內存管理。
在java語言中,可作為GC Root的對象包括以下幾種對象:
a. java虛擬機棧(棧幀中的本地變數表)中的引用的對象。
b.方法區中的類靜態屬性引用的對象。
c.方法區中的常量引用的對象。
d.本地方法棧中JNI本地方法的引用對象。
java方法區在Sun HotSpot虛擬機中被稱為永久代,很多人認為該部分的內存是不用回收的,java虛擬機規范也沒有對該部分內存的垃圾收集做規定,但是方法區中的廢棄常量和無用的類還是需要回收以保證永久代不會發生內存溢出。
判斷廢棄常量的方法:如果常量池中的某個常量沒有被任何引用所引用,則該常量是廢棄常量。
判斷無用的類:
(1).該類的所有實例都已經被回收,即java堆中不存在該類的實例對象。
(2).載入該類的類載入器已經被回收。
(3).該類所對應的java.lang.Class對象沒有任何地方被引用,無法在任何地方通過反射機制訪問該類的方法。
Java中常用的垃圾收集演算法:
(1).標記-清除演算法:
最基礎的垃圾收集演算法,演算法分為「標記」和「清除」兩個階段:首先標記出所有需要回收的對象,在標記完成之後統一回收掉所有被標記的對象。
標記-清除演算法的缺點有兩個:首先,效率問題,標記和清除效率都不高。其次,標記清除之後會產生大量的不連續的內存碎片,空間碎片太多會導致當程序需要為較大對象分配內存時無法找到足夠的連續內存而不得不提前觸發另一次垃圾收集動作。
(2).復制演算法:
將可用內存按容量分成大小相等的兩塊,每次只使用其中一塊,當這塊內存使用完了,就將還存活的對象復制到另一塊內存上去,然後把使用過的內存空間一次清理掉。這樣使得每次都是對其中一塊內存進行回收,內存分配時不用考慮內存碎片等復雜情況,只需要移動堆頂指針,按順序分配內存即可,實現簡單,運行高效。
復制演算法的缺點顯而易見,可使用的內存降為原來一半。
(3).標記-整理演算法:
標記-整理演算法在標記-清除演算法基礎上做了改進,標記階段是相同的標記出所有需要回收的對象,在標記完成之後不是直接對可回收對象進行清理,而是讓所有存活的對象都向一端移動,在移動過程中清理掉可回收的對象,這個過程叫做整理。
標記-整理演算法相比標記-清除演算法的優點是內存被整理以後不會產生大量不連續內存碎片問題。
復制演算法在對象存活率高的情況下就要執行較多的復制操作,效率將會變低,而在對象存活率高的情況下使用標記-整理演算法效率會大大提高。
(4).分代收集演算法:
根據內存中對象的存活周期不同,將內存劃分為幾塊,java的虛擬機中一般把內存劃分為新生代和年老代,當新創建對象時一般在新生代中分配內存空間,當新生代垃圾收集器回收幾次之後仍然存活的對象會被移動到年老代內存中,當大對象在新生代中無法找到足夠的連續內存時也直接在年老代中創建。

❽ 什麼是 Java 虛擬機

您好,提問者:

Java虛擬機簡稱JVM,它的作用如下:

1、其實Java不可跨平台,真正實現跨平台的是JVM虛擬機。

2、JVM其實就是一個編譯java、運行class的一個跟操作系統的一個軟體。

3、JVM的作用只針對於Java,而系統中的東西與它無關。

4、其實說白了就是一個軟體,就像VMware一樣。

Java虛擬機


一、什麼是Java虛擬機


Java虛擬機是一個想像中的機器,在實際的計算機上通過軟體模擬來實現。Java虛擬機有自己想像中的硬體,如處理器、堆棧、寄存器等,還具有相應的指令系統。


  1. 為什麼要使用Java虛擬機

Java語言的一個非常重要的特點就是與平台的無關性。而使用Java虛擬機是實現這一特點的關鍵。一般的高級語言如果要在不同的平台上運行,至少需要編譯成不同的目標代碼。而引入Java語言虛擬機後,Java語言在不同平台上運行時不需要重新編譯。Java語言使用模式Java虛擬機屏蔽了與具體平台相關的信息,使得Java語言編譯程序只需生成在Java虛擬機上運行的目標代碼(位元組碼),就可以在多種平台上不加修改地運行。Java虛擬機在執行位元組碼時,把位元組碼解釋成具體平台上的機器指令執行。


2.誰需要了解Java虛擬機


Java虛擬機是Java語言底層實現的基礎,對Java語言感興趣的人都應對Java虛擬機有個大概的了解。這有助於理解Java語言的一些性質,也有助於使用Java語言。對於要在特定平台上實現Java虛擬機的軟體人員,Java語言的編譯器作者以及要用硬體晶元實現Java虛擬機的人來說,則必須深刻理解Java虛擬機的規范。另外,如果你想擴展Java語言,或是把其它語言編譯成Java語言的位元組碼,你也需要深入地了解Java虛擬機。


3.Java虛擬機支持的數據類型


Java虛擬機支持Java語言的基本數據類型如下:


byte://1位元組有符號整數的補碼

short://2位元組有符號整數的補碼

int://4位元組有符號整數的補碼

long://8位元組有符號整數的補碼

float://4位元組IEEE754單精度浮點數

double://8位元組IEEE754雙精度浮點數

char://2位元組無符號Unicode字元


幾乎所有的Java類型檢查都是在編譯時完成的。上面列出的原始數據類型的數據在Java執行時不需要用硬體標記。操作這些原始數據類型數據的位元組碼(指令)本身就已經指出了操作數的數據類型,例如iadd、ladd、fadd和dadd指令都是把兩個數相加,其操作數類型別是int、long、float和double。虛擬機沒有給boolean(布爾)類型設置單獨的指令。boolean型的數據是由integer指令,包括integer返回來處理的。boolean型的數組則是用byte數組來處理的。虛擬機使用IEEE754格式的浮點數。不支持IEEE格式的較舊的計算機,在運行Java數值計算程序時,可能會非常慢。


虛擬機支持的其它數據類型包括:

object//對一個Javaobject(對象)的4位元組引用

returnAddress//4位元組,用於jsr/ret/jsr-w/ret-w指令

注:Java數組被當作object處理。


虛擬機的規范對於object內部的結構沒有任何特殊的要求。在Sun公司的實現中,對object的引用是一個句柄,其中包含一對指針:一個指針指向該object的方法表,另一個指向該object的數據。用Java虛擬機的位元組碼表示的程序應該遵守類型規定。Java虛擬機的實現應拒絕執行違反了類型規定的位元組碼程序。Java虛擬機由於位元組碼定義的限制似乎只能運行於32位地址空間的機器上。但是可以創建一個Java虛擬機,它自動地把位元組碼轉換成64位的形式。從Java虛擬機支持的數據類型可以看出,Java對數據類型的內部格式進行了嚴格規定,這樣使得各種Java虛擬機的實現對數據的解釋是相同的,從而保證了Java的與平台無關性和可

移植性。


二、Java虛擬機體系結構


Java虛擬機由五個部分組成:一組指令集、一組寄存器、一個棧、一個無用單元收集堆(Garbage-collected-heap)、一個方法區域。這五部分是Java虛擬機的邏輯成份,不依賴任何實現技術或組織方式,但它們的功能必須在真實機器上以某種方式實現。


  1. Java指令集

Java虛擬機支持大約248個位元組碼。每個位元組碼執行一種基本的CPU運算,例如,把一個整數加到寄存器,子程序轉移等。Java指令集相當於Java程序的匯編語言。

Java指令集中的指令包含一個單位元組的操作符,用於指定要執行的操作,還有0個或多個操作數,提供操作所需的參數或數據。許多指令沒有操作數,僅由一個單位元組的操作符構成。


虛擬機的內層循環的執行過程如下:


do{

取一個操作符位元組;

根據操作符的值執行一個動作;

}while(程序未結束)


由於指令系統的簡單性,使得虛擬機執行的過程十分簡單,從而有利於提高執行的效率。指令中操作數的數量和大小是由操作符決定的。如果操作數比一個位元組大,那麼它存儲的順序是高位位元組優先。例如,一個16位的參數存放時佔用兩個位元組,其值為:


第一個位元組*256+第二個位元組位元組碼指令流一般只是位元組對齊的。指令tabltch和lookup是例外,在這兩條指令內部要求強制的4位元組邊界對齊。


2.寄存器


Java虛擬機的寄存器用於保存機器的運行狀態,與微處理器中的某些專用寄存器類似。


Java虛擬機的寄存器有四種:

pc:Java程序計數器。

optop:指向操作數棧頂端的指針。

frame:指向當前執行方法的執行環境的指針。

vars:指向當前執行方法的局部變數區第一個變數的指針。


Java虛擬機


Java虛擬機是棧式的,它不定義或使用寄存器來傳遞或接受參數,其目的是為了保證指令集的簡潔性和實現時的高效性(特別是對於寄存器數目不多的處理器)。

所有寄存器都是32位的。


3.棧


Java虛擬機的棧有三個區域:局部變數區、運行環境區、操作數區。


(1)局部變數區 每個Java方法使用一個固定大小的局部變數集。它們按照與vars寄存器的字偏移量來定址。局部變數都是32位的。長整數和雙精度浮點數占據了兩個局部變數的空間,卻按照第一個局部變數的索引來定址。(例如,一個具有索引n的局部變數,如果是一個雙精度浮點數,那麼它實際占據了索引n和n+1所代表的存儲空間。)虛擬機規范並不要求在局部變數中的64位的值是64位對齊的。虛擬機提供了把局部變數中的值裝載到操作數棧的指令,也提供了把操作數棧中的值寫入局部變數的指令。


(2)運行環境區 在運行環境中包含的信息用於動態鏈接,正常的方法返回以及異常傳播。


·動態鏈接

運行環境包括對指向當前類和當前方法的解釋器符號表的指針,用於支持方法代碼的動態鏈接。方法的class文件代碼在引用要調用的方法和要訪問的變數時使用符號。動態鏈接把符號形式的方法調用翻譯成實際方法調用,裝載必要的類以解釋還沒有定義的符號,並把變數訪問翻譯成與這些變數運行時的存儲結構相應的偏移地址。動態鏈接方法和變數使得方法中使用的其它類的變化不會影響到本程序的代碼。


·正常的方法返回

如果當前方法正常地結束了,在執行了一條具有正確類型的返回指令時,調用的方法會得到一個返回值。執行環境在正常返回的情況下用於恢復調用者的寄存器,並把調用者的程序計數器增加一個恰當的數值,以跳過已執行過的方法調用指令,然後在調用者的執行環境中繼續執行下去。


·異常和錯誤傳播

異常情況在Java中被稱作Error(錯誤)或Exception(異常),是Throwable類的子類,在程序中的原因是:①動態鏈接錯,如無法找到所需的class文件。②運行時錯,如對一個空指針的引用


·程序使用了throw語句。

當異常發生時,Java虛擬機採取如下措施:

·檢查與當前方法相聯系的catch子句表。每個catch子句包含其有效指令范圍,能夠處理的異常類型,以及處理異常的代碼塊地址。

·與異常相匹配的catch子句應該符合下面的條件:造成異常的指令在其指令范圍之內,發生的異常類型是其能處理的異常類型的子類型。如果找到了匹配的catch子句,那麼系統轉移到指定的異常處理塊處執行;如果沒有找到異常處理塊,重復尋找匹配的catch子句的過程,直到當前方法的所有嵌套的catch子句都被檢查過。

·由於虛擬機從第一個匹配的catch子句處繼續執行,所以catch子句表中的順序是很重要的。因為Java代碼是結構化的,因此總可以把某個方法的所有的異常處理器都按序排列到一個表中,對任意可能的程序計數器的值,都可以用線性的順序找到合適的異常處理塊,以處理在該程序計數器值下發生的異常情況。

·如果找不到匹配的catch子句,那麼當前方法得到一個"未截獲異常"的結果並返回到當前方法的調用者,好像異常剛剛在其調用者中發生一樣。如果在調用者中仍然沒有找到相應的異常處理塊,那麼這種錯誤傳播將被繼續下去。如果錯誤被傳播到最頂層,那麼系統將調用一個預設的異常處理塊。

(3)操作數棧區 機器指令只從操作數棧中取操作數,對它們進行操作,並把結果返回到棧中。選擇棧結構的原因是:在只有少量寄存器或非通用寄存器的機器(如Intel486)上,也能夠高效地模擬虛擬機的行為。操作數棧是32位的。它用於給方法傳遞參數,並從方法接收結果,也用於支持操作的參數,並保存操作的結果。例如,iadd指令將兩個整數相加。相加的兩個整數應該是操作數棧頂的兩個字。這兩個字是由先前的指令壓進堆棧的。這兩個整數將從堆棧彈出、相加,並把結果壓回到操作數棧中。


每個原始數據類型都有專門的指令對它們進行必須的操作。每個操作數在棧中需要一個存儲位置,除了long和double型,它們需要兩個位置。操作數只能被適用於其類型的操作符所操作。例如,壓入兩個int類型的數,如果把它們當作是一個long類型的數則是非法的。在Sun的虛擬機實現中,這個限制由位元組碼驗證器強制實行。但是,有少數操作(操作符pe和swap),用於對運行時數據區進行操作時是不考慮類型的。


4.無用單元收集堆


Java的堆是一個運行時數據區,類的實例(對象)從中分配空間。Java語言具有無用單元收集能力:它不給程序員顯式釋放對象的能力。Java不規定具體使用的無用單元收集演算法,可以根據系統的需求使用各種各樣的演算法。


5.方法區


方法區與傳統語言中的編譯後代碼或是Unix進程中的正文段類似。它保存方法代碼(編譯後的java代碼)和符號表。在當前的Java實現中,方法代碼不包括在無用單元收集堆中,但計劃在將來的版本中實現。每個類文件包含了一個Java類或一個Java界面的編譯後的代碼。可以說類文件是Java語言的執行代碼文件。為了保證類文件的平台無關性,Java虛擬機規范中對類文件的格式也作了詳細的說明。其具體細節請參考Sun公司的Java虛擬機規范。

熱點內容
linux查看arp 發布:2025-08-13 07:53:30 瀏覽:474
為什麼刷機還要弄以前的解鎖密碼 發布:2025-08-13 07:47:14 瀏覽:273
acfun如何上傳 發布:2025-08-13 07:35:10 瀏覽:270
ftp共享伺服器需要什麼配置 發布:2025-08-13 07:33:00 瀏覽:543
主要資料庫 發布:2025-08-13 07:15:27 瀏覽:178
壓縮包漫畫 發布:2025-08-13 07:15:25 瀏覽:131
伺服器空島原版如何獲得礦物 發布:2025-08-13 07:08:22 瀏覽:437
購車時哪些是必備的配置 發布:2025-08-13 06:42:33 瀏覽:693
寶塔添加腳本 發布:2025-08-13 06:41:56 瀏覽:502
ios資料庫存儲 發布:2025-08-13 06:28:10 瀏覽:975