GC 垃圾回收

垃圾回收機制是由垃圾收集器Garbage Collection
GC來實現的,GC是后臺的守護進程。它的特別之處是它是一個低優先級進程,但是可以根據內存的使用情況動態的調整他的優先級。因此,它是在內存中低到一定限度時才會自動運行,從而實現對內存的回收。這也是垃圾回收的時間不確定的原因

為何要這樣設計:因為GC也是進程,也要消耗CPU等資源,如果GC執行過于頻繁會對java的程序的執行產生較大的影響(java解釋器本來就不快),因此JVM的設計者們選著了不定期的gc。

一、為什么要進行垃圾回收

我們知道Java是一門面向對象的語言,在一個系統運行中,會伴隨著很多對象的創建,而這些對象一旦創建了就占據了一定的內存,在上一篇中,我們介紹過創建的對象是保存在堆中的,當對象使用完畢之后,不對其進行清理,那么會一直占據內存空間,很明顯內存空間是有限的,如果不回收這些無用的對象占據的內存,那么新創建的對象申請不了內存空間,系統就會拋出異常而無法運行,所以必須要經常進行內存的回收,也就是垃圾收集。

二、回收

在堆里面存放著Java世界中幾乎所有的對象實例,垃圾收集器在對堆進行回收前,第一 件事情就是要確定這些對象之中哪些還“存活”著,哪些已經“死去”(即不可能再被任何途徑 使用的對象)。

1、引用計數算法

引用計數是垃圾收集器中的早期策略。在這種方法中,堆中每個對象實例都有一個引用計數。當一個對象被創建時,就將該對象實例分配給一個變量,該變量計數設置為1。當任何其它變量被賦值為這個對象的引用時,計數加1(a = b,則b引用的對象實例的計數器+1),但當一個對象實例的某個引用超過了生命周期或者被設置為一個新值時,對象實例的引用計數器減1。任何引用計數器為0的對象實例可以被當作垃圾收集。當一個對象實例被垃圾收集時,它引用的任何對象實例的引用計數器減1

優缺點

優點:引用計數收集器可以很快的執行,交織在程序運行中。對程序需要不被長時間打斷的實時環境比較有利。

缺點:無法檢測出循環引用。如父對象有一個對子對象的引用,子對象反過來引用父對象。這樣,他們的引用計數永遠不可能為0。

2、可達性分析算法

可達性分析算法是從離散數學中的圖論引入的,程序把所有的引用關系看作一張圖,從一個節點GC ROOT開始,尋找對應的引用節點,找到這個節點以后,繼續尋找這個節點的引用節點,當所有的引用節點尋找完畢之后,剩余的節點則被認為是沒有被引用到的節點,即無用的節點,無用的節點將會被判定為是可回收的對象。

img

在Java語言中,可作為GC Roots的對象包括下面幾種:

a) 虛擬機棧中引用的對象(棧幀中的本地變量表);

b) 方法區中類靜態屬性引用的對象;

c) 方法區中常量引用的對象;

d) 本地方法棧中JNI(Native方法)引用的對象。

這個算法的基本思路就是通過一系列的稱為“GC Roots”的對象作為起始點,從這些節點開始向下搜索,搜索所走過的路徑稱為引用鏈(Reference Chain),當一個對象到GC Roots沒有任何引用鏈相連(用圖論的話來說,就是從GC Roots到這個對象不可達)時,則證明此對象是不可用的。如圖所示,對象object 5、object 6、object 7雖然互相有關聯,但是它們到GC Roots是不可達的,所以它們將會被判定為是可回收的對象。

二、如何判斷對象為垃圾對象

在JVM中主要的垃圾收集算法有:標記-清除、標記-清除-壓縮(簡稱**“標記-整理”)、標記-復制-清除(簡稱“復制”、分代收集算法**。這幾種收集算法互相配合,針對不同的內存區域采取對應的收集算法實現(這里具體是由相應的垃圾收集器實現)

垃圾回收涉及到大量的程序細節,而且各個平臺的虛擬機操作內存的方式也不一樣,但是他們進行垃圾回收的算法是通用的,所以這里我們也只介紹幾種通用算法。

①、標記-清除算法

算法實現:分為標記-清除兩個階段,首先根據上面的根搜索算法標記出所有需要回收的對象,在標記完成后,然后在統一回收掉所有被標記的對象。

缺點

1、效率低:標記和清除這兩個過程的效率都不高。

2、容易產生內存碎片:因為內存的申請通常不是連續的,那么清除一些對象后,那么就會產生大量不連續的內存碎片,而碎片太多時,當有個大對象需要分配內存時,便會造成沒有足夠的連續內存分配而提前觸發垃圾回收,甚至直接拋出OutOfMemoryExecption。

img

②、復制算法

為了解決標記-清除算法的兩個缺點,復制算法誕生了。

算法實現:將可用內存按容量劃分為大小相等的兩塊區域,每次只使用其中一塊,當這一塊的內存用完了,就將還活著的對象復制到另一塊區域上,然后再把已使用過的內存空間一次性清理掉。

優點:每次都是只對其中一塊內存進行回收,不用考慮內存碎片的問題,而且分配內存時,只需要移動堆頂指針,按順序進行分配即可,簡單高效。

缺點:將內存分為兩塊,但是每次只能使用一塊,也就是說,機器的一半內存是閑置的,這資源浪費有點嚴重。并且如果對象存活率較高,每次都需要復制大量的對象,效率也會變得很低。

img

③、標記-整理算法

上面我們說過復制算法會浪費一半的內存,并且對象存活率較高時,會有過多的復制操作,效率低下。

如果對象存活率很高,基本上不會進行垃圾回收時,標記-整理算法誕生了。

算法實現:首先標記出所有存活的對象,然后讓所有存活對象向一端進行移動,最后直接清理到端邊界以外的內存。

局限性:只有對象存活率很高的情況下,使用該算法才會效率較高。

img

④、分代收集算法

當前商業虛擬機都是采用此算法,但是其實這不是什么新的算法,而是上面幾種算法的合集。

算法實現:根據對象的存活周期不同將內存分為幾塊,然后不同的區域采用不同的回收算法。

對于 HotSpot 虛擬機,它將堆空間分為老年代和新生代兩塊區域

1、對于存活周期較短,每次都有大批對象死亡,只有少量存活的區域,采用復制算法,因為只需要付出少量存活對象的復制成本即可完成收集;

2、對于存活周期較長,沒有額外空間進行分配擔保的區域,采用標記-整理算法,或者標記-清除算法。

堆有新生代和老年代兩塊區域組成,而新生代區域又分為三個部分,分別是 Eden,From Surivor,To Survivor ,比例是8:1:1。

新生代采用復制算法,每次使用一塊Eden區和一塊Survivor區,當進行垃圾回收時,將Eden和一塊Survivor區域的所有存活對象復制到另一塊Survivor區域,然后清理到剛存放對象的區域,依次循環。

老年代采用標記-清除或者標記-整理算法,根據使用的垃圾回收器來進行判斷。

至于為什么要這樣,這是由于內存分配的機制導致的,新生代存的基本上都是朝生夕死的對象,而老年代存放的都是存活率很高的對象。關于內存分配下篇博客我們會詳細進行介紹。

四、何時進行垃圾回收

理清了什么是垃圾,怎么回收垃圾,最后一點就是Java虛擬機何時進行垃圾回收呢?

程序員可以調用 System.gc()方法,手動回收,但是調用此方法表示希望進行一次垃圾回收。但是它不能保證垃圾回收一定會進行,而且具體什么時候進行是取決于具體的虛擬機的,不同的虛擬機有不同的對策。

其次虛擬機會自行根據當前內存大小,判斷何時進行垃圾回收,比如前面所說的,新生代滿了,新產生的對象無法分配內存時,便會觸發垃圾回收機制。

這里需要說明的是宣告一個對象死亡,至少要經歷兩次標記,前面我們說過,如果對象與GC Roots 不可達,那么此對象會被第一次標記并進行一次篩選,篩選的條件是此對象是否有必要執行 finalize() 方法,當對象沒有覆蓋 finalize()方法,或者該方法已經執行了一次,那么虛擬機都將視為沒有必要執行finalize()方法。

如果這個對象有必要執行 finalize() 方法,那么該對象將會被放置在一個有虛擬機自動建立、低優先級,名為 F-Queue 隊列中,GC會對F-Queue進行第二次標記,如果對象在finalize() 方法中成功拯救了自己(比如重新與GC Roots建立連接),那么第二次標記時,就會將該對象移除即將回收的集合,否則就會被回收。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/535625.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/535625.shtml
英文地址,請注明出處:http://en.pswp.cn/news/535625.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

如何讓你變得魅力十足

我們每個人都希望自己在某些方面對他人來說是有用的。我們渴望那種被人需要的感覺,覺得自己是有能力的,就像我們在某方面很與眾不同,很獨特一樣。 有些人非常有吸引力。他們是那些每當需要幫助便會被想起的人。他們是那些另你覺得非常有幫助…

日志linux

syslog日志系統: syslogd 系統,非內核產生的信息 man 2 syslog klogd 內核,專門負責內核產生的信息 man 3 syslog /var/log/messages 系統標準錯誤日志信息,非內核 syslogd /var/log/dmesg klogd 共同配置文件etc/…

sysbench的安裝和做性能測試

sysbench是一個模塊化的、跨平臺、多線程基準測試工具,主要用于評估測試各種不同系統參數下的數據庫負載情況。關于這個項目的詳細介紹請看:http://sysbench.sourceforge.net。它主要包括以下幾種方式的測試:1、cpu性能2、磁盤io性能3、調度程…

加密解密

PKI public key Infrastructure 公鑰基礎設施 CRl 證書吊銷列表 CA證書頒發機構 Certificate Authority x509 證書 包括公鑰、過期時間、證書的合法擁有者、證書如何被使用 CA的信息 CA的校驗碼等等 Pki實現方式 TLS/ssl:x509 opengpg ssl安全的套接字層…

高性能MySQL(1)——MYSQL架構

MySQL最重要、最與眾不同的特性是它的存儲引擎架構,這種架構將查詢處理與數據的存儲/提取相分離,使得可以在使用時根據不同的需求來選擇數據存儲的方式。 一、Mysql邏輯架構 如果能在頭腦中構建出一幅MySQL各組件之間如何協同工作的架構圖,就…

數據庫設計中的14個關鍵技巧

1. 原始單據與實體之間的關系  可以是一對一、一對多、多對多的關系。在一般情況下,它們是一對一的關系:即一張原始單據對應且只對應一個實體。在特殊情況下,它們可能是一對多或多對一的關系,即一張原始單證對應多個實體&#xf…

高性能MySQL(2)——Schema與數據類型的優化

良好的邏輯設計和物理設計是高性能的基石,應該根據系統將要執行的查詢語句來設計 schema,這往往需要權衡各種因素。 一、選擇優化的數據類型 MySQL支持的數據類型非常多,選擇正確的數據類型對于獲得高性能至關重要。不管 存儲哪種類型的數據&#xff0c…

用戶權限sudo、suid、sgid以及facl等

su 切換用戶或以指定用戶運行命令。 使用su可以指定運行命令的身份(user/group/uid/gid)。 為了向后兼容,su默認不會改變當前目錄,且僅設置HOME和SHELL這兩個環境變量(若目標用戶非root,則還設置USER和LOGNAME環境變量)。推薦使用--login選項…

MySQL 服務器調優

關于 MySQL 調優 有 3 種方法可以加快 MySQL 服務器的運行速度,效率從低到高依次為: 替換有問題的硬件。 對 MySQL 進程的設置進行調優。 對查詢進行優化。 替換有問題的硬件通常是我們的第一考慮,主要原因是數據庫會占用大量資源。不過這…

通過腳本啟動批量服務

/app/all_start_script/wwyt/此目錄服務如下:apigateway.sh auth.sh config.sh register.sh zipkin.sh /app/all_start_script/other/此目錄服務如下: tomcat.sh wwyt_base.sh wwyt_cache.sh wwyt_flow.sh wwyt_risk_login.sh ww…

高性能MySQL(3)——創建高性能索引

索引對于良好的性能非常關鍵。尤其是當表中的數據量越來越大時,索引對性能的影響愈發重要。 一、索引的類型 在MySQL中,索引是在存儲引擎層而不是服務器層實現的。所以沒用統一的索引標準,不同存儲引擎的索引工作方式并不相同。 1.1、B-Tre…

linux 調優系列

Linux系統內核:修改TCP/IP調優參數 所有的TCP/IP調優參數都位于/proc/sys/net/目錄。例如, 下面是最重要的一些調優參數, 后面是它們的含義: 1. /proc/sys/net/core/rmem_max — 最大的TCP數據接收緩沖。 2. /proc/sys/net/core/wmem_max — 最大的TCP數據發送緩沖。 3.…

java中的構造方法與代碼塊

一、構造方法 1.1、java中的構造方法跟普通方法有很大的區別: 構造方法的方法名跟類名相同構造方法沒有返回值類型,連void也沒有,也不能用return返回值每次創建一個對象,都會調用構造方法,如果沒有寫構造方法,系統會默認加上一個空參的構造,如果已經寫了構造方法,…

bash shell是如何識別特殊符號的

一 、 shell命令解析以及識別通配符 Shell是系統的用戶界面,提供了用戶與內核進行交互操作的一種接口。它接收用戶輸入的命令并把它送入內核去執行 。 實際上Shell是一個命令解釋器,它解釋由用戶輸入的命令并且把它們送到內核。不僅如此,Shell有自己的編程語言用于對命令的編…

linux 調優系列(續)

linux 的各大發行版,都有些不必要的服務被默認開啟了,針對ubuntu,我們 可以采用選擇性關閉的方法加速起動,提高系統性能。 這里我們安裝一個軟件: sudo apt-get install sysv-rc-conf 然后這樣起動: 在這個…

配置文件bashrc與profile的區別

1、當登入系統時候獲得-個shell進程時,其讀取環境設定檔有三步 首先讀入的是全局環境變量設定檔/ete/profile,然后根據其內容讀取額外的設定的文檔,如/etc/profile. d和/ etc/ inputre 然后根據不同使用者帳號,去其家目錄讀取, bash, pr…

高性能MySQL(4)——查詢性能優化

査詢優化、索引優化、庫表結構優化需要齊頭并進,一個不落。 一、為什么查詢速度為變慢 在嘗試編寫快速的查詢之前,需要清楚一點,真正重要是響應時間。如果把查詢看作是一個任務,那么他由一系列子任務組成,每個子任務都會消耗一定的時間。如果…

GooglePerformanceTools--tcmalloc

TCmalloc全稱是Thread-Caching malloc,作者宣稱tcmalloc相對于glibc2.3 malloc(aka ptmalloc2)有6倍的性能提高,tcmalloc的常用場景是用于加速MySQL,不過據Wikipedia的hacker Domas Mituzas說,tcmalloc不僅僅對MySQL起作用&#x…

linux基本命令以及命令常用選項

linux基本命令以及命令常用選項touch 創建文件,改變恩建時間戳,如果直接跟上一個文件,該文件不存在則創建文件-c文件不存在不創建文件,存在則改變文件的時間戳-a只改變文件的訪問時間-m改變文件的修改時間-t時間格式CCYYMMDDhhmm…

Java獲取上一周、上一個月、上一年的時間

SimpleDateFormat format new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”); Calendar c Calendar.getInstance(); 1.過去七天 c.setTime(new Date()); c.add(Calendar.DATE, - 7); Date d c.getTime(); String day format.format(d); System.out.println(“過去七天&#…