【Java ee初階】jvm(3)

一、雙親委派機制(類加載機制中,最經常考到的問題)

類加載的第一個環節中,根據類的全限定類名(包名+類名)找到對應的.class文件的過程。

JVM中進行類加載的操作,需要以來內部的模塊“類加載器”(class loader)

JVM自帶了三種類加載器

Bootstrap ClassLoader 負責在Java的標準庫中進行查找

ExtensionClassLoader? 負責在Java的擴展庫中進行查找

ApplicationClassLoader 負責在Java的第三方庫/當前項目中進行查找

其中,Java的擴展庫是JDK自帶的,但是不是標準約定的庫,是JDK的廠商自行擴展的功能。現在很少涉及到,一般都是使用第三方庫。

Java的官方(Oracle)推出Java的標準文檔,其他的廠商就會依據官方的文檔,開發對應的JDK(官方確實也開發了JDK,還有一些第三方的,比如知名的OpenJDK,比如知名大廠也會有自己版本的JDK)

不同廠商,都能保證,標準約定的功能都是包含的,并且表現一致。但是這些廠商也會根據需要,擴展出一些功能出來。

這三類加載器之間,存在“父子關系”(不是父類子類,繼承關系),每個類加載器中有一個parent這樣的屬性,保存了自己的父親是誰。這是在JVM的源碼中已經寫死的。

雙親委派模型的目的,是為了確保三個類加載的優先級:標準庫優先加載,第三方庫/當前項目類最后加載。比如自己寫一個類,和標準庫恰好重復了。java.lang.String。此時JVM保證加載的仍然是標準庫的String,而不是你自己寫的。

雙親委派模型也是可以打破的。程序員在特定場景下,也可以實現自己的類加載器(實現庫/框架 可能涉及到),自己實現的類加載器可以讓他遵守,也可以不遵守。

二、JVM的垃圾回收機制 GC

C/C++ 這樣的編程語言中,申請內存的時候,是需要用完了手動進行釋放的

C 申請內存
1) 局部變量
2) 全局變量 不需要手動釋放
3) 動態申請 malloc 通過 free 進行釋放的

C++ 申請內存
1) 局部變量
2) 全局變量 / 靜態變量
3) 動態申請 new 通過 delete 進行釋放

這樣的釋放操作,容易遺忘(執行不到)就會導致 “內存泄露”

malloc
free

邏輯代碼
1) 條件判定 觸發 return
2) 拋出異常

很多編程語言,引入了 垃圾回收 機制
不需要程序員寫代碼手動釋放內存,會有專門的邏輯,幫助自動進行釋放
垃圾回收,大大的解放了程序員,提高了開發效率

Java, Python, Go, PHP, JS.... 大多數主流語言都包含 GC 功能

為啥 C/C++ 沒有引入 GC 呢?
C++ 的設計的核心理念,有兩個(C++ 的紅線)
1) 和 C 兼容 (C 語言寫的代碼,用 C++ 編譯器可以正常編譯運行的)
2) 把性能發揮到極致
隔壁會有很多的技巧?提高 "性能"
++i 代替 i++
通過返回 右值引用 代替返回值對象
通過引用傳參代替值傳參
通過 constexpr 增加編譯期做的工作,減少運行時開銷
...................

引入 GC 會影響性能,?引入了額外的運行時開銷。
很早之前,C++ 的標準委員會討論這個事情。
C++ 引入了 "智能指針",可以一定程度的解決內存泄露的問題。(雖然可用性,遠不如 GC,總比 C 語言啥都不做,直接擺爛強

在對性能有要求的開發場景中 C++ 是無可替代的
AI
游戲引擎
搜索引擎 (現在 java 性能也趕上來不少,也有 java 實現的版本了...)
交易系統 (股票,基金,外匯,期貨...)
操作系統級的開發
...................

挑戰者,Rust,嘗試挑戰 C++ 的生態位
走高性能的路線

主打優勢,能夠很好的應對內存錯誤問題
(內存泄露,內存訪問越界...)
Rust 通過特殊的語法,在編譯期做檢查的。
假設代碼寫出內存泄露,編譯通過不了
目前,Rust 發展下來,也變的語法非常復雜了

為什么GC 會影響執行效率?因為觸發 GC 的時候,可能會涉及到 STW 問題

stop the world 世界都停止

1. GC 回收的內存區域是哪個部分呢?
IVM
程序計數器
元數據區

堆 => GC 主要回收這個區域

2. GC 的目的是為了釋放內存,是以字節為單位“釋放”嘛?
不是的,而是以“對象為單位”

正在使用的內存 不回收
不再使用(尚未回收) 不回收
沒有使用的區域 回收

按照對象為維度進行回收,更簡單方便。
如果是按照“字節維度”,就可能針對每個對象都得描述出哪部分需要回收,哪部分不需要。比較麻煩了。

堆上的內存 => new 的對象

3. 如何回收?
1) 找出垃圾,區分出哪些對象是垃圾(后續代碼不再使用)
2) 釋放這些垃圾對象的內存

如何“找出垃圾” ?

由于在 Java 中使用對象,都是通過 “引用” 來進行的,使用對象,無非是使用對象的屬性/方法,都要通過對象的引用進行。.前面的部分就是指向對象的引用。

如果一個對象已經沒有任何引用指向它了,此時這個對象就注定無法被使用了。

判斷一個對象是否是垃圾這個問題比較抽象,因此我們將其轉換成判斷是否有引用指向這個對象,這樣子問題就比較具體了。

JVM 內部是有一些辦法可以做到的以上這種解決方案的,周大佬 《深入理解 Java 虛擬機》 這本書介紹了兩種方案:

*面試的時候,區分好,看面試官是咋問的:
1) 讓你介紹下 垃圾回收 中如何判定對象是垃圾的 兩個方案都可以介紹
2) 讓你介紹 JVM 中如何判定對象是垃圾的 別說引用計數

1) 引用計數(Java 沒有使用,Python,PHP... 采用的方案)
簡單粗暴的方案。
給每個對象都分配了一個 “計數器”

1. 引用計數 [Java 沒有使用,Python,PHP... 采用的方案]
簡單粗暴的方案。
給每個對象都分配了一個 “計數器”

Test a = new Test();
Test b = a;
a = null;
b = null;
當引用計數為 0,此時對象就沒有任何引用指向了。
對象就是 垃圾了

Python / PHP 等語言
會搭配其他垃圾回收機制,識別當前的引用是否構成循環引用

兩個弊端
1) 消耗額外的內存空間較大。
如果對象本身很小(就 4 個字節)
計數器占了倆字節,相當于額外的內存空間多了 50%
2) 循環引用問題 (類似于死鎖)

class Test {
? ? Test t;
}
Test a = new Test();
Test b = new Test();
a.t = b
b.t = a

此時,這倆對象的引用計數是 1,不能釋放。
但是,這倆對象卻無法通過任何引用來訪問到。

AB 相互證明對方不是垃圾
實際上 AB 都是垃圾

以下是圖中的文字內容:

```
2. 可達性分析 [Java 使用的方案]
在 Java 代碼中,每個 “可訪問的對象” 一定是可以通過一系列的引用操作,訪問到的。

Node build() {
? ? Node a = new Node();
? ? Node b = new Node();
? ? Node c = new Node();
? ? Node d = new Node();
? ? Node e = new Node();
? ? Node f = new Node();
? ? Node g = new Node();
? ? a.left = b;
? ? a.right = c;
? ? b.left = d;
? ? b.right = e;
? ? e.left = g;
? ? c.right = f;
? ? return a;
}

Node root = build(); ?構建二叉樹
此時通過 root 這個引用,是可以訪問到這個樹上的任何一個對象的


此時通過 root 這個引用,是可以訪問到這個樹上的任何一個對象的

root => a
root.left => b
root.left.left => d
root.left.right.left => g
...........

假設,寫 root.right.right = null
這樣的代碼會使 f 無法被訪問到(f 已經沒有引用指向了)

假設,寫 root.right = null
這樣的代碼使 c 不可達。由于 f 必須依賴 c,f 也一起不可達

JVM 安排專門的線程,負責上述的 “掃描” 的過程
會從一些特殊的引用開始掃描 (GC roots)
1. 棧上的局部變量 (引用類型)
2. 常量池里指向的對象 (final 修飾的,引用類型)
3. 元數據區 (靜態成員,引用類型)

這三組里可能有很多變量
以這些變量為起點,盡可能的往里訪問所有可能被訪問到的對象
但凡被訪問到的對象,都 “標記為可達”
JVM 又能夠知道所有的對象列表,去掉 “標記為可達的”,剩下的就是垃圾了

不引入額外的內存空間
但是需要消耗較多的時間,進行上述掃描過程,這些過程中也是容易觸發STW的

(時間換了空間)
另外這里也不會涉及到 “循環引用”

如何釋放垃圾 (回收內存)
關于內存回收,涉及到幾種算法.

1. 標記 - 清除

標記,就是可達性分析,找到垃圾的過程.
清除,直接釋放這部分的內存(相當于直接調用 free / delete 釋放這個內存給操作系統)

存在內存碎片問題

總的空閑內存空間,是比較多的 (一共 4MB)
但是這些空閑空間,不連續.

在申請內存的時候,都是在申請連續的內存空間
當嘗試申請 2MB 的內存時候,就會申請失敗

2. 復制算法解 決內存碎片

把不是垃圾的對象,復制到另外一側
把整個空間都釋放掉

很好的解決了內存碎片問題。
弊端:
1. 內存浪費比較多
2. 如果存活的對象比較多/比較大,復制開銷非常明顯的

3. 標記 - 整理 類似于順序表刪除元素 - 搬運元素

4. 分代回收 (綜合方案), 把上述幾個方案,結合起來,揚長避短

整個堆空間,分成 "新生代" "老年代"
年輕對象 年老對象
年齡:一個對象經過垃圾回收掃描線程的輪次

對于年輕對象來說,是容易成為垃圾的。
年老對象,則不容易成為垃圾

可達性分析中,JVM 會不停使用線程掃描這些對象是否是垃圾。每隔一定時間,掃描一次。如果一個對象掃描一次,不是垃圾,年齡就 + 1。一般來說年齡超過 15 (可以配) 的就進入老年代。

"要死早死了"
比如 C 語言,已經存在了 50 年了,可以遇見到這個 C 語言還有很大的希望再活 50 年
> 和 C 語言同時期的 C++ 語言,都死的差不多...

剛創建的新鮮對象放到伊甸區。如果對象活過一輪 GC,進入幸存區。

新對象,大多數是生命周期非常短的 “朝生夕死”,經驗規律。這倆幸存區,同一時刻使用一個(相當于復制算法,分出兩個部分)。每次經過一輪 GC,都會淘汰掉幸存區中的一大部分對象,把存活的對象和伊甸區中新存活下來的對象,復制算法拷貝到另一個幸存區。新生代非常適合復制算法的。

如果這個對象在新生代中存活多輪之后,就會進入老年代。老年代的對象由于生命周期大概率很長,沒有必要頻繁掃描。如果這個對象非常大,不適合使用復制算法了。直接進入老年代
老年代回收內存采取的是 標記-清除 / 標記-整理(取決于垃圾回收器的具體實現了)

主流垃圾收集器詳解

1.G1垃圾收集器(GarbageFirst)

定位:自Java11起成為默認收集器,是目前最主流的垃圾收集器。

核心特點:

采用分區堆(Region)設計,將堆內存劃分為多個大小相等的塊(通常為1MB~32MB)。

通過優先回收垃圾最多的Region("GarbageFirst"策略)實現高效回收。

支持大內存(幾十GB級別),同時保持可控的STW(StopTheWorld)停頓時間。

2.ZGC垃圾收集器(ZGarbageCollector)

定位:新一代低延遲收集器,未來可能逐步取代G1。

核心特點:

突破性低延遲,STW時間可控制在1ms以內。

同樣采用分區堆設計,但通過染色指針(ColoredPointers)和讀屏障(LoadBarriers)技術實現并發標記與整理。

支持超大堆內存(TB級別),適合現代高性能應用。

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

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

相關文章

wps excel將表格輸出pdf時所有列在一張紙上

記錄:wps excel將表格輸出pdf時所有列在一張紙上 1,調整縮放比例,或選擇將所有列打印在一頁 2,將表格的所有鋪滿到這套虛線

分布式微服務系統架構第134集:筆記1運維服務器經驗,高并發,大數據量系統

加群聯系作者vx:xiaoda0423 倉庫地址:https://webvueblog.github.io/JavaPlusDoc/ https://1024bat.cn/ https://github.com/webVueBlog/fastapi_plus https://webvueblog.github.io/JavaPlusDoc/ ? 一、查看端口是否被占用的常用命令 1?? lsof 命令&…

IS-IS 中間系統到中間系統

前言: 中間系統到中間系統IS-IS(Intermediate System to Intermediate System)屬于內部網關協議IGP(Interior Gateway Protocol),用于自治系統內部 IS-IS也是一種鏈路狀態協議,使用最短路徑優先…

前端安全:XSS、CSRF 防御與最佳實踐

引言 隨著互聯網應用的普及,前端安全問題日益凸顯。作為開發者,了解并防范常見的安全威脅至關重要。本文將深入探討兩種最常見的前端安全威脅:跨站腳本攻擊(XSS)和跨站請求偽造(CSRF)&#xff…

uniapp 彈窗封裝(上、下、左、右、中五個方位)

無腦復制即可&#xff01;&#xff01;&#xff01; <template><view><viewv-if"mask"class"tui-drawer-mask":class"{ tui-drawer-mask_show: visible }":style"{ zIndex: maskZIndex }"tap"handleMaskClick&qu…

Axure制作可視化大屏動態滾動列表教程

在可視化大屏設計中&#xff0c;動態滾動列表是一種常見且實用的展示方式&#xff0c;能夠有效地展示大量信息。本文將詳細介紹如何使用Axure制作一個動態滾動的列表展示模塊。 一、準備工作 打開Axure軟件&#xff1a;確保你已經安裝并打開了Axure RP軟件。創建新項目&#x…

零基礎玩轉Apache Superset可視化部署

根據官方Quick Start Guide&#xff0c;你可以按照以下步驟進行部署&#xff1a; 1. 確認環境2. 獲取代碼3. 獲取官方最新代碼4. 啟動服務5. 訪問Superset Web界面6. 接入數據源 前提條件&#xff1a; dockerdocker compose 1. 確認環境 安裝Docker和Docker Compose 確保你…

服務器數據恢復—XFS文件系統分區消失的數據恢復案例

服務器數據恢復環境&故障&#xff1a; 服務器上有一組由raid卡組建的raid5磁盤陣列。上層安裝linux才做系統&#xff0c;采用XFS文件系統&#xff0c;劃分了3個分區。 管理員將服務器的操作系統重裝后&#xff0c;發現服務器上的分區發生了改變&#xff1a;一個分區消失&am…

2025/5/18

繼續研究一下大佬的RAG項目。開始我的碎碎念。 RAG可以分成兩部分&#xff1a;一個是問答&#xff0c;一個是數據處理。 問答是人提問&#xff0c;然后查數據庫&#xff0c;把查的東西用大模型組織成人話&#xff0c;回答人的提問。 數據處理是把當下知識庫里的東西&#xf…

在 Vue 中插入 B 站視頻

前言 在 Vue 項目中&#xff0c;有時我們需要嵌入 B 站視頻來豐富頁面內容&#xff0c;為用戶提供更直觀的信息展示。本文將詳細介紹在 Vue 中插入 B 站視頻的多種方法。 使用<iframe>標簽直接嵌入,<iframe>標簽是一種簡單直接的方式&#xff0c;可將 B 站視頻嵌…

OpenCv高階(八)——攝像頭調用、攝像頭OCR

文章目錄 前言一、攝像頭調用通用方法1、導入必要的庫2、創建攝像頭接口 二、攝像頭OCR1.引入庫2、定義函數&#xff08;1&#xff09;定義顯示opencv顯示函數&#xff08;2&#xff09;保持寬高比的縮放函數&#xff08;3&#xff09;坐標點排序函數&#xff08;4&#xff09;…

特斯拉虛擬電廠:能源互聯網時代的分布式革命

在雙碳目標與能源轉型的雙重驅動下&#xff0c;特斯拉虛擬電廠&#xff08;Virtual Power Plant, VPP&#xff09;通過數字孿生技術與能源系統的深度融合&#xff0c;重構了傳統電力系統的運行范式。本文從系統架構、工程實踐、技術挑戰三個維度&#xff0c;深度解析這一顛覆性…

【漫話機器學習系列】258.拐點(Inflection Point)

拐點&#xff08;Inflection Point&#xff09;詳解&#xff1a;定義、原理與應用 在數學分析與數據建模中&#xff0c;“拐點&#xff08;Inflection Point&#xff09;”是一個非常重要的概念。今天這篇文章&#xff0c;我們將結合圖示&#xff0c;深入理解拐點的定義、數學…

語音識別——聲紋識別

通過將說話人的聲音與數據庫中的記錄聲音進行比對&#xff0c;判斷說話人是否為數據庫白名單中的同一人&#xff0c;從而完成語音驗證。目前&#xff0c;3D-Speaker 聲紋驗證的效果較為出色。 3D-Speaker 是一個開源工具包&#xff0c;可用于單模態和多模態的說話人驗證、說話…

DeepSeek 賦能軍事:重塑現代戰爭形態的科技密碼

目錄 一、引言&#xff1a;AI 浪潮下的軍事變革與 DeepSeek 崛起二、DeepSeek 技術原理與特性剖析2.1 核心技術架構2.2 獨特優勢 三、DeepSeek 在軍事偵察中的應用3.1 海量數據快速處理3.2 精準目標識別追蹤3.3 預測潛在威脅 四、DeepSeek 在軍事指揮決策中的應用4.1 戰場態勢實…

uWSGI是什么?

uWSGI 是一個功能強大的應用服務器&#xff0c;專為部署高性能 Web 應用設計&#xff0c;尤其適合 Python 生態系統。以下是對其核心介紹及適用場景的總結&#xff1a; uWSGI 是什么&#xff1f; uWSGI 是一個實現了 WSGI&#xff08;Web Server Gateway Interface&#xff09…

Digi XBee XR 系列介紹

Digi 延續了 20 多年來亞 GHz 射頻模塊的傳統&#xff0c;推出了 Digi XBee XR 系列遠距離模塊&#xff0c;包括 Digi XBee XR 900 - 已通過多個地區的預先認證 - 以及 Digi XBee XR 868 - 已通過歐洲地區應用的預先認證。 這些先進的射頻模塊專為遠距離抗干擾無線通信而設計。…

RabbitMq C++客戶端的使用

介紹 RabbitMQ 是一個開源的消息代理和隊列服務器&#xff0c;用于在分布式系統之間傳遞消息。它實現了高級消息隊列協議(AMQP)&#xff0c;同時也支持其他協議如 STOMP、MQTT 等。 核心概念 Producer(生產者): 發送消息的應用程序 Consumer(消費者): 接收消息的應用程序 Q…

HTML 中的 input 標簽詳解

HTML 中的 input 標簽詳解 一、基礎概念 1. 定義與作用 HTML 中的 <input> 標簽是表單元素的核心組件&#xff0c;用于創建各種用戶輸入字段。作為一個空標簽&#xff08;沒有閉合標簽&#xff09;&#xff0c;它通過 type 屬性來決定呈現何種輸入控件&#xff0c;是實…

基于Piecewise Jerk Speed Optimizer的速度規劃算法(附ROS C++/Python仿真)

目錄 1 時空解耦運動規劃2 PJSO速度規劃原理2.1 優化變量2.2 代價函數2.3 約束條件2.4 二次規劃形式 3 算法仿真3.1 ROS C仿真3.2 Python仿真 1 時空解耦運動規劃 在自主移動系統的運動規劃體系中&#xff0c;時空解耦的遞進式架構因其高效性與工程可實現性被廣泛采用。這一架…