Java虛擬機 - JVM

JVM的內存區域劃分

JVM它其實也是一個進程,進程運行的過程中,會從操作系統中申請一些資源.內存就是其中的一種.這些內存就支撐了java程序的運行.JVM從系統中申請的一大塊內存,會根據實際情況和使用用途來劃分出不同的空間,這個就是區域劃分.它一般分為 堆區, 棧區, 程序計數器, 元數據區4個區域.

堆區

我們代碼中new出來的對象就是在堆當中,對象持有的非靜態成員變量,也是在堆當中.

棧區

棧區分為本地方法棧和虛擬機棧. 本地方法棧是jvm通過c++寫的代碼的調用關系和局部變量.一般我們只關心虛擬機棧. 虛擬機棧是記錄了java代碼的調用關系和java代碼的局部變量.

程序計數器

這是一塊小區域,專門用來存儲下一條要執行的java指令的地址,和x86cpu中的eip寄存器差不多.

元數據區

在之前也叫做 "方法區". 它里面記錄的是類的信息,方法的信息, 一個程序有哪些類,每個類有哪些方法,方法里面都會包含哪些指令,都會記錄在元數據區中.想我們的代碼中的各種邏輯都會轉化為java字節碼,這些字節碼就會在程序運行的時候被jvm加載到內存中,放到方法區當中.程序怎么執行就會按照元數據區中的java字節碼來依次執行.

注意: 這里堆和元數據區只有一份,但是棧區和程序計數器由N份,和線程數目相關.

JVM的類加載機制

這里的類加載就是java進程來運行的時候,需要把.class文件從硬盤讀取到內存,且進行一系列的校驗解析的過程. 這里加載的過程可以分為5個步驟. 加載 - 驗證 - 準備 - 解析 - 初始化.

加載

大概就是把硬盤中的.class文件找到,打開文件且讀取到文件內容.

驗證

把當前讀到的文件內容需要確保是合法的.class文件格式.

準備

給類對象申請內存空間.這里的申請到的內存空間,里面的默認值都是0.類對象中的靜態成員變量的值也就是0)

解析

這里就是對類中的字符串常量進行處理.將常量池中的符號引用轉換為直接引用的過程.(符號引用可以理解為字符串和它的引用在文件中有偏移量,直接引用則就是地址).

初始化

針對類對象完成后續的初始化過程(這里還會執行靜態代碼塊的邏輯,父類的加載)

雙親委派模型(加載環節)

這里描述了怎么樣來查找.class文件的策略.在我們的JVM中進行類加載的操作,會有一個專門的模塊 - "類加載器(ClassLoader)". JVM中有三個ClassLoader.這里類加載器的作用就是給它一個帶有包名的類名,來找到對應的.class文件.

三個庫的作用:

BootstrapClassLoader: 負責查找標準庫中的目錄.

ExtensionClassLoader: 負責查找擴展庫中的目錄.

ApplicationClassLoader: 負責查找當前項目的代碼目錄和第三方庫.? ?? ?

這三個加載器的關系類似于父子關系.

工作過程

1. 先以ApplicationClassLoader為入口,開始工作.,但是它不會立刻開始查找自己負責的目錄,而是將任務交給它的父親.

2. 任務到ExtensionClassLoader,它也不會立刻開展工作,而是交給它的父親.

3. 任務到BootsstrapClassLoader,它才會開始真正的搜索負責的目錄.通過全限定類名,來嘗試在標準庫中找到符合的.class文件.找到了就進入打開文件,讀文件的流程.沒找到則交給它的孩子來處理.

4. ExtensionClassLoader拿到父親給它的任務后,也會通過全限定類名,在擴展庫中查找符合的.class文件.找到了就進入讀文件的流程.沒找到再交給它的孩子處理.

5. ApplicationClassLoader拿到夫妻給它的任務后,也會通過全限定類名,在項目中的目錄和第三方庫的目錄中查找.找到了就進入讀文件流程.沒找到說明加載失敗.則會拋出ClassNotFoundException異常.

這樣設定的目的其實最主要的就是確保這幾個類加載器的優先級. 按照這樣的方式就算你自己寫的類不小心和標準庫中的類名字重復了,也可以避免導致標準庫中的類失效.

JVM的垃圾回收機制(GC)

JVM的垃圾回收,說的就是將內存回收.在JVM的多個區中. 程序計數器不需要GC,棧不需要,因為它的局部變量在代碼塊執行結束后就會進行自動銷毀. 元數據區也不需要,它一般都是涉及到類加載,很少涉及到類卸載. 這里最主要使用GC的就是堆區.因為它里面記錄的是對象,而代碼中可能會new出許許多多的對象,有的對象可能不使用了,就需要銷毀.所以這里的內存回收,也可以看成是對象回收.

垃圾回收分為兩步:

1. 識別出垃圾,哪些對象是垃圾,哪些對象不是垃圾.

2. 把標記為垃圾的對象的內存空間進行釋放.

識別垃圾

這里識別一個對象是不是需要繼續使用,就是判斷還有沒有引用在指向它.如果沒有,它就可以視為垃圾.這里有兩種方法: 1. 引用計數 2. 可達性分析

引用計數

這個方法在JVM中并沒有使用,但是在其他的主流語言中還是在廣泛應用中.

它的處理方式就是:

給每一個對象安排一個額外的空間,這個空間里就保存當前有幾個引用. 而另一邊會有專門的掃描線程,去獲取到當前每個對象的引用計數的情況,一但發現這個對象的引用計數為0,就代表可以釋放了.

但是它會存在一些問題:

1. 會消耗額外的內存空間.尤其是當對象比較小時,你的計數器消耗的空間可能就達到對象的一半了.

2.引用計數可能會產生循環引用的問題.

可達性分析(JVM使用的方法)

它的本質就是用時間來換空間. 它就不會產生額外的空間和循環引用的問題.

在代碼中,會定義出很多變量,棧上的局部變量,堆上的成員變量,方法區的靜態變量,常量池中引用的對象... 它就可以從這些變量作為起點,去進行遍歷.這個遍歷就是沿著這些變量持有的引用類型的成員,再進行下一步的訪問. 可以遍歷到的對象就不是垃圾,反之.

這里的遍歷我們可以想象為二叉樹的遍歷.一但有一個節點為null,那這個結點后面的也都為null,也就是垃圾.

JVM中會存在掃描線程,對代碼不斷進行遍歷.

清理垃圾

清理垃圾就是將被標記的內存空間進行釋放.這里釋放有三種方法.

標記 - 清除

這里就是直接將標記為垃圾的對象直接釋放掉.但是一般不會使用這個方案.因為它會有內存碎片問題.直接釋放,就會產生很多小的,離散的空閑內存空間,這就可能導致后續的內存申請失敗.因為內存申請一般都是申請一塊連續,大范圍的空間.

復制

它的核心就不是直接釋放內存,也是將不是垃圾的對象,復制到另一半的空間中(它會將可用內存空間分為兩半).?

這樣子就規避了內存碎片化的問題,但是也會有一些缺點.

1. 總用的內存空間變少了.

2. 如果每次需要復制的對象比較多,復制開銷就會很大了(它適合大部分對象都釋放,少部分對象存活,這個時候使用復制)

標記 - 整理

這個方式就類似于順序表中的刪除中間元素,需要進行搬運.通過這個過程,也就解決了內存碎片的問題,但是它這里的搬運內存開銷會很大.

JVM的分代回收

?因為上面方法的優缺點,JVM就結合上面的思想,搞出來了一個綜合方案.

JVM會給對象進行年齡記錄,被JVM的掃描線程掃描過一次后就加一歲,起始歲數為0. 而還給堆內存劃分了兩個區域:分為為新生代和老年代.且新生代中還分了三個區:伊甸區和兩個生存區.

?處理流程:

1. 當代碼中new出一個新的對象時,就是創建在伊甸區中的,伊甸區中會有很多對象.(放在里面是有一個經驗規矩: 伊甸區的對象大多都是活不過第一輪GC的,"朝生夕死")

2. 當第一輪GC后,少部分存活下來的對象,就會通過復制算法拷貝到生存區中(GC后存活下來的對象不會很多,復制開銷也不會很大).后續還會有GC掃描,不僅會掃描伊甸區,還會掃描生存區.生存區的大多數對象也會在掃描中被標記為垃圾.少部分存活的,又會繼續復制算法拷貝到另一個生存區匯中.這樣不斷往復,且每過一次GC,生存下來的對象年齡就會+1.

3. 當一個對象在生存區中經歷了若干次GC還沒有消亡,則JVM就會認為它的生命周期特別長,就會將它從生存區拷貝到老年代.

4. 老年代的對象也會被GC掃描,但是掃描頻率會大大降低.

5.當老年代的對象消亡時,JVM就會按照標記整理的方式,釋放內存(老年代的對象很少,所以搬運開銷不會很大)


到這里,上面的就是JVM中GC的核心思想.但是在一些實現細節上還是會有一些變數和優化~

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

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

相關文章

springboot240基于Spring boot的名城小區物業管理系統

基于Spring boot的名城小區物業管理系統的設計與實現 摘要 當下,正處于信息化的時代,許多行業順應時代的變化,結合使用計算機技術向數字化、信息化建設邁進。以前相關行業對于物業信息的管理和控制,采用人工登記的方式保存相關數…

InnoDB存儲引擎對MVCC的實現

MVCC MVCC的目的 在搞清楚MVCC之前,我們要搞懂一個問題,MVCC到底解決的是什么問題? 我用一句話概括,那就是為了解決讀-寫可以一起的問題! 在我們的印象里,InnoDB可以讀讀并發,不能讀寫并發,或者寫寫并發 這是很正常的想法,因為如果讀寫并發的化,會有并發問題 而對于寫寫…

帶壓縮路徑的并查集

find帶壓縮路徑的并查集 int fa[]; void init(int _size) {for(int i0;i<_size;i){fa[i] i;} } int find(int aim) {int cur aim;while (fa[aim] ! aim){aim fa[aim];}while (fa[cur] ! cur){int tmp cur;cur fa[cur];fa[tmp] aim;}return aim; } void join(int a,in…

構建安全的REST API:OAuth2和JWT實踐

引言 大家好&#xff0c;我是小黑&#xff0c;小黑在這里跟咱們聊聊&#xff0c;為什么REST API這么重要&#xff0c;同時&#xff0c;為何OAuth2和JWT在構建安全的REST API中扮演著不可或缺的角色。 想象一下&#xff0c;咱們每天都在使用的社交媒體、在線購物、銀行服務等等…

file-upload-download

方式一 情況1&#xff1a; PostMapping("/download1")public ResponseEntity<byte[]> download1() throws Exception {// 下載文件目錄位置FileInputStream fis new FileInputStream("C:\\Users\\wsd\\Pictures\\susu.jpg");// 一次讀取bytes.leng…

Sqli-labs靶場第16關詳解[Sqli-labs-less-16]自動化注入-SQLmap工具注入

Sqli-labs-Less-16 #自動化注入-SQLmap工具注入 SQLmap用戶手冊&#xff1a;文檔介紹 - sqlmap 用戶手冊 以非交互式模式運行 --batch 當你需要以批處理模式運行 sqlmap&#xff0c;避免任何用戶干預 sqlmap 的運行&#xff0c;可以強制使用 --batch 這個開關。這樣&#xff0…

【視頻編碼\VVC】多樣化視頻編碼工具了解

除了通用編碼工具&#xff0c;VVC還針對特定特性的全景視頻、屏幕視頻開發了特定的編碼工具。 全景視頻編碼 360度全包圍視角的球面視頻。為了采用傳統的視頻編碼&#xff0c;全景視頻需要轉換為平面視頻&#xff0c;經緯度等角映射&#xff08;ERF&#xff09;、立方體映射&…

PostgreSQL操作筆記

基礎操作 數據庫相關 -- 查看所有數據庫 \l-- 切換到指定數據庫 \c 庫名-- 查看庫中所有表 \d執行SQL腳本 如果有現成的SQL腳本&#xff1a; \i 腳本路徑路徑一般需要用單引號引起來。 如果需要當場編輯一次性的SQL腳本&#xff0c;可以&#xff1a; \e執行上述命令后會進…

GC機制以及Golang的GC機制詳解

要了解Golang的GC機制,就需要了解什么事GC,以及GC有哪幾種實現方式 一.什么是GC 當一個電腦上的動態內存不再需要時&#xff0c;就應該予以釋放&#xff0c;以讓出內存&#xff0c;這種內存資源管理&#xff0c;稱為垃圾回收&#xff08;Garbage Collection&#xff09;&#x…

最長上升子序列(LIS)簡介及其例題分析

一.最長上升子序列&#xff08;LIS&#xff09;的相關知識 1.最長上升子序列&#xff08;Longest Increasing Subsequence&#xff09;&#xff0c;簡稱LIS&#xff0c;也有些情況求的是最長非降序子序列&#xff0c;二者區別就是序列中是否可以有相等的數。假設我們有一個序…

【論文筆記】Initializing Models with Larger Ones

Abstract 介紹權重選擇&#xff0c;一種通過從預訓練模型的較大模型中選擇權重子集來初始化較小模型的方法。這使得知識從預訓練的權重轉移到更小的模型。 它還可以與知識蒸餾一起使用。 權重選擇提供了一種在資源受限的環境中利用預訓練模型力量的新方法&#xff0c;希望能夠…

代碼隨想錄Day67 | 695.島嶼的最大面積 1020.飛地的數量

代碼隨想錄Day67 | 695.島嶼的最大面積 1020.飛地的數量 695.島嶼的最大面積1020.飛地的數量 695.島嶼的最大面積 文檔講解&#xff1a;代碼隨想錄 視頻講解&#xff1a; 狀態 采用bfs&#xff0c;這道題相較于之前的題變為了求島嶼的最大面積。那就說明我們每遇到一個新的島嶼…

【Linux】軟件管理yum | 編輯器vim | vim插件安裝

目錄 1. Linux軟件管理yum 1.1 什么是軟件包 1.2 查看軟件包 1.3 如何安裝軟件 1.4 如何卸載軟件 2. Linux編輯器vim 2.1 vim的基本概念 2.2 vim的基本操作 2.3 vim正常模式命令集 2.4 vim末行模式命令集 2.5 簡單vim配置 2.6 插件安裝 1. Vim-Plug 3. coc.nvim …

如何自己系統的學python

學習Python是一項很好的投資&#xff0c;因為它是一種既強大又易于學習的編程語言&#xff0c;適用于多種應用&#xff0c;如數據分析、人工智能、網站開發等。下面是一個系統學習Python的步驟建議&#xff1a; 基礎準備 安裝Python&#xff1a; 訪問Python官網下載最新版本的…

微服務獲取當前登錄用戶信息

一&#xff0c;實現思路 1&#xff0c;基于JWT令牌登陸方式 JWT實現登錄的&#xff0c;登錄信息就保存在請求頭的token中。因此要獲取當前登錄用戶&#xff0c;只要獲取請求頭&#xff0c;解析其中的token。 1&#xff09;&#xff0c;Gateway網關攔截&#xff0c;解析用戶信…

微信小程序-生命周期

頁面生命周期 onLoad: 頁面加載時觸發的方法&#xff0c;在這個方法中可以進行頁面初始化的操作&#xff0c;如獲取數據、設置頁面狀態等。 onShow: 頁面顯示時觸發的方法&#xff0c;在用戶進入頁面或從其他頁面返回該頁面時會調用此方法。可以在此方法中進行頁面數據刷新、動…

Onenote軟件新建筆記本時報錯:無法在以下位置新建筆記本

報錯現象&#xff1a; 當在OneNote軟件上&#xff0c;新建筆記本時&#xff1a; 然后&#xff0c;嘗試重新登錄微軟賬戶&#xff0c;也不行&#xff0c;提示報錯&#xff1a; 解決辦法&#xff1a; 打開一個新的記事本&#xff0c;復制粘貼以下內容&#xff1a; C:\Users\Adm…

Mysql中的事務

什么是事務&#xff1a; 多條sql語句&#xff0c;要么全部成功&#xff0c;要么全部失敗。 事務的特性&#xff1a; 1&#xff1a;原子性(Atomic)&#xff1a; 組成一個事務的多個數據庫操作是一個不可分割的原子單元&#xff0c;只有所有操作都成功&#xff0c;整個事務才會…

在Unity中模擬實現手勢識別功能

在虛擬現實(VR)和增強現實(AR)的應用開發中&#xff0c;手勢識別技術扮演著至關重要的角色&#xff0c;它允許用戶以自然的方式與虛擬世界進行交云。然而&#xff0c;并非所有開發者都有條件使用真實的手勢識別硬件。本文介紹了如何在Unity中通過模擬的方式實現一個簡單的手勢識…

【LeetCode】1768_交替合并字符串_C

題目描述 給你兩個字符串 word1 和 word2 。請你從 word1 開始&#xff0c;通過交替添加字母來合并字符串。如果一個字符串比另一個字符串長&#xff0c;就將多出來的字母追加到合并后字符串的末尾。 返回 合并后的字符串 。 https://leetcode.cn/problems/merge-strings-al…