通俗理解JVM細節-面試篇

文章目錄

  • 前言
  • JVM概述
    • JVM是什么?解決了什么問題?
    • JVM運行流程
    • JVM 與 JRE,JDK的關系
  • JVM內存結構
    • JVM區域劃分
      • 程序計數器
      • 方法區
  • 類加載機制
    • 五個階段
    • 加載
    • 驗證
    • 準備
    • 解析
    • 初始化
    • 總結
    • 雙親委派模型
  • 垃圾回收內存管理
    • 什么是GC?如何判定誰是垃圾?
      • 1.引用計數 判定
      • 2.可達性分析
    • 內存回收算法
      • 1.標記-清除
      • 2.復制算法
      • 3.標記-整理
    • JVM中的回收算法
    • Minor GC 與 Full GC 的區別與觸發條件
  • JVM線程與鎖機制
    • 程序計數器與線程的關系
    • 偏向鎖、輕量級鎖、重量級鎖的演化過程
  • 例子:對象的創建全過程 從new到內存分配(類加載->內存分配)
  • 參考文章

前言

本來要開始學習Spring的,但聽說最好學框架之前先把JVM,HTTP協議全都捋一遍,除了有助于Spring學習外,更重要的是這倆面試也經常會被問到,所以我必須本著“喜歡探索知識的個性”來學習了。

JVM概述

JVM是什么?解決了什么問題?

JVM(Java Virtual Machine,Java虛擬機)通俗一點說是一個假裝是計算機的程序,專門運行.java程序。擬人化說就是它像一個翻譯+保姆+管家的綜合體,它可以解決什么問題呢?
翻譯:把你寫的.java程序翻譯成別人能看懂的.class字節碼
保姆:管理內存,幫你清理垃圾(垃圾回收)
管家:跨平臺運行(可以在不同的OS上運行)
專業點來說,JVM 是運行 .class 字節碼文件的虛擬計算機,它屏蔽了底層操作系統和硬件的差異,實現“一次編寫,到處運行”。

有句話說得好,凡是都需要對比一下友商,讓我們看看C++的相關替代品是啥。
答:C++不需要這些,因為C++是編譯型語言,編譯之后變成了機器碼,直接可以運行在操作系統上。編譯型語言執行效率更高,時間消耗更少。 這么一看是不是感覺踢到鋼板了? 其實不然,友商雖然不需要這個就可以執行,這是它高性能的優點但同時也是一個很大的缺點。因為沒有管家幫它接觸各類操作系統,也沒有保姆幫他回收垃圾。這些都是C++的痛點。具體來說,對比表格如下

特性JavaC++
編譯結果字節碼(.class)機器碼(.exe / .out)
是否依賴虛擬機是,JVM 才能運行否,編譯后直接運行
是否跨平臺是(因為 JVM 屏蔽平臺差異)否(不同平臺要重新編譯)
內存管理自動垃圾回收(GC)手動管理(new/delete)
類加載運行時加載(類加載器)編譯時靜態鏈接,沒“加載器”

JVM運行流程

  1. 編譯階段(helloworld.java -> helloworld.class) 開發時
  2. 類加載階段(helloworld.class 被類加載器加載進內存 JVM進行驗證->準備->解析->初始化) 看看你合法嗎,合法給你分配住的地方,分配完了之后準備給我干活。
  3. 執行階段(將字節碼翻譯為機器碼)穿上工裝開始干活
  4. 內存管理,干著干著發現你的空間怎么越戰越大,得給你釋放一下了(垃圾回收)

JVM 與 JRE,JDK的關系

這就像一家三口 JDK最大包含 JRE JRE包含JVM

名稱作用舉例說明
JVMJava虛擬機,運行字節碼就是你寫的程序在哪執行
JREJava運行環境,包含 JVM + 核心類庫就像一個 Java 程序能跑的最小環境
JDKJava開發工具包,包含 JRE + 編譯器(javac)等工具你寫程序、編譯、運行都靠它

JVM內存結構

JVM區域劃分

為什么要有區域劃分?答:不同區域的內存有著不同的功能便于高效管理。比如你買了房子,你得給安排洗手間,廚房,客廳,臥室一樣。不同的空間有不同的職責。

程序計數器

程序計數器的作用是保存指令地址的地方
什么是指令?指令就是字節碼,也就是我們的寫的代碼,代碼都是有作用的所以轉化為字節碼后也就是指令了,用來命令CPU下一步該干啥。什么是指令地址?就是存儲指令的地方,我們執行指令的時候需要先將指令一個個從內存中取出來。

前面的文章中說過,線程調度,主要也靠程序計數器恢復上下文。這里的程序計數器也是存儲當前線程執行的指令地址,那么JVM的程序計數器和線程的是一個嘛?顯然不是,可以理解JVM是總的程序計數器,是當前程序執行到哪了,而線程中存的則是當前線程執行到什么位置了。如果線程發生阻塞,在調度回來執行的時候方便恢復現場。

棧是存儲局部變量和函數調用信息的地方。什么是局部變量,顧名思義是指只在一個范圍內生效的變量(比如一個函數內定義的變量,for循環內部定義的變量if語句內部定義的變量) 函數調用信息:傳入函數的實參,函數內部的局部變量,調用函數的位置等。棧的基本屬性是先進后出,比如在遞歸或函數內部調用函數中最內層調用函數最先出來,最外層調用函數最后出來。舉個例子,判斷樹高度的例子
假設樹長這樣
在這里插入圖片描述

    public  int deep(TreeNode root,int x){if(root==null){return x;}int x1=deep(root.left,x+1);int x2=deep(root.right,x+1);return Math.max(x1,x2);}int h = deep(root,0)+1;

棧的最底部 會存儲實參(3,0),局部變量(x1,x2)這里說的是節點值,了解存儲的那些節點就行,實際上是存儲的一個個引用。依次類推往上分別存儲(9,1) ,但調用此節點時發現已經沒有孩子節點了 直接返回高度1,因此底部會變為(3,0),(1,x2) 。隨后右孩子入棧 分別往上存儲(20,1),(15,2),(7,2).最終逐個出棧(這里是(15,2)先出棧)。

同樣的有多個線程調用函數的話,每個線程內部也是都有棧的。

堆是占用內存較大的數據結構了,其內部主要存儲 new出來的對象實例,以及對象內部的成員變量。注意堆在線程可就沒有了,因為線程太小了。

public void f(){
HashMap<Integer,Integer> hsm = new HashMap<>();
}

那這個例子中hsm 存儲在哪呢? hsm 是局部引用 所以會存儲在棧中,這里分清引用和對象實例,引用指的是對象在堆的存儲位置,對象實例則是對象本身。 這也順便解釋 對象傳參時引用調用為什么會改變原始對象本身,因為傳參時傳的是對象的地址的引用,而修改時會修改對象在堆中的本身,所以引用調用會改變其本身。而值傳遞,是將值傳參了,不會改變原有的值(我理解的,不知道對不對,但有一種引用情況除外,那就是String類型,傳參傳入String的引用時不會改變原始值,這是因為一旦操作String類型都會創建一個副本,所以不會改變原始值)。

理解不了 沒關系 這里就記住
堆存儲new的對象實例 和 內部的成員變量
局部變量或引用都存儲在棧中

方法區

方法區存儲的是類對象,但我喜歡叫類屬性。因為更好理解,類對象不是指前面的對象的實例。而是一個類中結構是啥樣的,比如里邊的變量的類型,方法的類型,有哪些方法,名字叫什么等。

類加載機制

類加載機制是指 JVM 在運行期間,將 .class 字節碼文件加載到內存中,并轉換成 Class 對象的整個過程。也就是說,當你在代碼里用 new User() 或 User.class 的時候,JVM 要確保這個類的字節碼已經在內存中了,這個“確保 + 加載 + 準備 + 連接 + 初始化”的過程,就是類加載機制。

五個階段

加載-驗證-準備-解析-初始化

加載

干了什么? 讀取字節碼內,創建一個class對象,存進方法區,此class對象是后面你反射的基礎
目的:將類的字節碼讀取到JVM內存

舉例子來說,你在運行Java對象前需要先把說明書(字節碼) 從硬盤搬進內存,才能參考它干活

驗證

干了什么?檢查.class文件是否符合規范,是否非法越界,引用了不存在的類。
目的:保證虛擬機安全的運行,防止惡意或錯誤字節碼破壞

就像你拿到了這個說明書,你得檢查是不是騙人的。

準備

給類的靜態變量分配內存,并初始化默認值。注意是初始化默認值,不是初始化。默認值指的是(0/null/false) 這種
比如 int x = 3 不會初始化為3,而是初始化為0.

就像你租一間房,房東先給你空房子和床,等你入住時再布置和擺東西(下一步才賦值)

解析

把常量池中的符號引用轉化直接引用
例如:User u = new User(); 在常量池里是個字符串 “User”,解析后才變成真正的方法地址、內存地址等。

提前把類中引用的類/字段/方法都找好,變成可執行的“具體地址”
你看到說明書說“點這里”(符號引用),你得先知道“這里”是哪里(真正的函數地址)

初始化

這是真正的初始化,給類中的靜態變量初始化提前設定的值
房間準備好了,現在你把桌子椅子搬進來、墻上掛個畫,開始“入住

總結

階段干了什么目的是否常考
加載讀取 .class → 生成 Class 對象把類搬進 JVM?
驗證校驗字節碼合法性防止非法破壞 JVM?
準備給 static 字段分配內存 & 默認值建立“類模板”?
解析符號引用 → 真實引用做好準備執行??(次要)
初始化執行 <clinit> 初始化代碼正式讓類“準備就緒”?

雙親委派模型

這個屬于加載階段的部分,為什么單獨要拿出來說。因為這部分理解對于你學習java有著重要意義,劣勢基礎,最終成為大牛。。。好了不說廢話,因為這部分面試喜歡問。它的目的是找.class文件。 因為.class的文件可能存放在多個地方。比如在JDK中,比如自定義的在項目目錄中。 類加載器有三個,每一個負責不同的區域。就像外賣小哥,每個區域可能會安排一個外賣小哥。
1、BootStrapClassLoader【模擬線路類加載器】 負責標準庫常用的類
2、ExtensionClassLoader【擴展類加載器】加載JDK擴展的類,很少用
3、ApplicationClassLoader【應用類加載器】負責我們在項目中自定義的類

工作流程:首先進入應用類加載器,此時他會檢查擴展類加載器是否加載過了。沒有則進入擴展類加載器,進入之后,也會判斷是模擬線路類加載器加載過了,沒有則進入模擬線路類加載器。 就是一句話,如果沒被加載過會先到自己的父類加載器去加載

為什么這樣設計?因為這保證加載類時的一致性,不會出現自定義的類和標準庫的類重名了不知道加載哪個。這里會優先加載標準庫的類。

垃圾回收內存管理

垃圾回收這些一直是由JVM自動判定并回收,所以我們接觸的很少。但對比友商的程序員(C),這就是不得不接觸的了,因為C語言追求高性能,這種垃圾回收一類的東西它不care,所以只能辛苦程序員來完成了。看到這里是不是心里寬慰了一些

什么是GC?如何判定誰是垃圾?

什么是垃圾回收?我們寫程序new的對象 創建的變量這些只要不用了就是垃圾,但垃圾依然占用著內存,我們需要清理它并回收內存。但不能亂回收,比如一個對象明明你還有用你卻給它扔了這樣會有大問題。因此如何判定誰是垃圾成為了重中之重。

如何判定誰是垃圾?
在判定之前,我們要明確目標,內存區域中只有堆中占用空間最大且是最需要回收垃圾釋放內存的地方。所以我們默認垃圾回收針對的是堆中的數據。
如何判定?

1.引用計數 判定

此方法不是用在java中,了解如何運作的就可。引用計數對每個對象實例都會增加一份額外的空間用來計數,注意是對象實例,不是引用。例如:

Result re = new Result()  \\這里re 是引用,Result是對象實例我們針對的是對象實例,那有人會問引用如何回收\\引用的生命周期更短,只要超過作用域或者被定為null就回收了。
Result re1 =re; \\Result()的計數是2.因為有兩個指向它

當引用計數變為0時,就回收這部分內存。也就是說取消一個引用時,其計數器減一。
這種方法存在什么問題呢? 具體看代碼注釋

public class RefCountDemo {public Object instance = null; // 引用另一個對象public static void main(String[] args) {//此時第一個RefCountDemo();計數器為1RefCountDemo objA = new RefCountDemo();此時第二個RefCountDemo();計數器為1RefCountDemo objB = new RefCountDemo();// A 引用了 B 此時第二個RefCountDemo();計數器為2objA.instance = objB;// B 又引用了 A 此時A的計數器為1 此時第1個RefCountDemo();計數器為2objB.instance = objA;// 斷開外部引用  objA = null;objB = null;// 此時 objA 和 objB 相互引用,引用計數 ≠ 0// 但它們已經無用了,GC 無法識別(引用計數法失效)}
}

此外 空間利用率低,因為每次new 一個對象你都要分配額外的空間去計數。

2.可達性分析

于是為了解決以上兩個問題,Java決定另辟蹊徑。
可達性分析(Reachability Analysis) 是一種判斷對象是否“存活”的算法:
從一組稱為 GC Roots 的根對象出發,沿著引用鏈向下搜索,能被訪問到的對象就是“可達的”,不可達的對象則認為“已經死亡”,可以被垃圾回收。

工作流程:
從 GC Roots 出發
沿著所有引用向下搜索(廣度或深度優先)
標記所有能訪問到的對象為“活著”
未被訪問到的對象則是“死對象”,可以被回收

GCRoots有那些來源?

GC Roots 來源說明
棧幀中的本地變量表方法調用中的局部變量,如 new 出來的對象引用
方法區中靜態變量如 static 字段的引用
方法區中常量引用final 常量等
JNI 引用(本地方法)通過 C/C++ 引用的 Java 對象
活躍線程對象每個正在運行的線程本身

public class ReachabilityDemo {
public Object ref = null;

public static void main(String[] args) {ReachabilityDemo a = new ReachabilityDemo();ReachabilityDemo b = new ReachabilityDemo();a.ref = b;b.ref = a;// 外部斷開引用a = null;b = null;// 現在 JVM 會觸發 GC,能正確識別 a 和 b 都不可達(雖然互相引用)
}

}
以上例子中 首先從棧幀中的本地變量表 a,b開始掃描 ,發現 a 和b 都為null了,所以第一個和第二個ReachabilityDemo(); 都無法訪問到,于是回收這部分空間。同時將引用也回收

內存回收算法

找完垃圾后,就需要清理,如何清理呢?(釋放內存)

1.標記-清除

通過可達性分析找到要回收的對象后,我們直接對垃圾占用內存釋放。面試遇到的話,理解性記憶,標記了就清除嘛不就是,誰是垃圾就清楚誰其他的我不管,這就是標記清除。這種存在什么問題呢? 猛地一看貌似沒毛病啊不就是誰是垃圾,誰就要扔啊,難不成還不扔垃圾?道理是這樣地,但問題是扔了垃圾你不收拾一下房間嘛? 也就是說清理了垃圾,但會變成碎片化內存,就是隔一段有一小快是空閑,這些小塊加起來是很大地,但分配這樣大地空間 我們卻無法分配(因為空間分配必須是連續地)。所以清理垃圾必須也得收拾房間,要不然雜亂無章,每地方都有東西放,但卻又放不下大點地東西。

2.復制算法

此算法就是解決以上問題,整理內存。首先將內存一分為二,一半用,一半備用。當清理垃圾時,會先將不是垃圾地內存地值復制到另一把中去。然后在講這一半地所有空間全部清除,這樣就釋放了這一半地所有空間。 復制算法,面試遇到就要想到加了復制倆字,就說明他會整理內存,變聰明了。但此時又會有什么問題呢? 這我好像只能用一半空間啊,內存這么金貴好不容易申請到,卻只能用一半(空間利用率低)。此外,每次都要從這個房間把東西搬過去,萬一這個房間垃圾不多有用地很多,全搬過去好累地(復制開銷較大)。

3.標記-整理

對于復制出現地問題,標記整理解決了一部分(空間利用率低) 如何做呢?它將不是垃圾的內存的值覆蓋到前邊時垃圾的內存哪里,注意后者的內存一定滿足大于等于前者才可直接覆蓋。隨后對后面的元素直接釋放。此方法問題還是沒有解決需要復制的問題

JVM中的回收算法

上面每一種算法單拎出來發現都不夠完美,所以JVM采用了三者的結合分代回收! 顧名思義,將不同的對象根據存在時間的長短分為輩分大的和輩分小的。然后將堆內存一分為二, 其一存放輩分大的,其二存放輩分小的。 其中第二部分,又分為兩部分,一部分伊甸區,一部分時幸存區。話不多說直接偷張圖(這篇圖的作者講的特別好,文章最后我會表明引用他文章的)
在這里插入圖片描述

1.剛new出來的對象直接放到伊甸區。
2.如果伊甸區對象熬過了可達性分析,則就放入幸村區。
3.幸存區繼續開熬,來回復制
4.經過多輪后,幸存區已經有資格放到老年區,此時放到老年區。

Minor GC 與 Full GC 的區別與觸發條件

Minor GC 針對年輕代判定并回收垃圾
Full GC 指 JVM 對整個堆空間進行回收

項目Minor GCFull GC
作用范圍僅年輕代(Eden + Survivor)整個堆(新生代 + 老年代 + 方法區)
觸發條件年輕 區滿了,需要分配新對象老年代滿、System.gc()、元空間不足、CMS失敗等
耗時較短(幾十 ms)較長(幾百 ms 或秒級)
是否會 Stop-The-World? 是? 是
是否影響用戶體驗輕微明顯卡頓,尤其是 Full GC 頻繁時
回收頻率高頻盡量少

JVM線程與鎖機制

程序計數器與線程的關系

  1. 程序計數器(Program Counter Register)
    每條線程都有獨立的程序計數器,是線程私有的內存空間。
    是 JVM 中唯一一個線程私有的內存區域。
    用來記錄當前線程正在執行的字節碼指令地址。
    如果當前正在執行的是 native 方法,則該計數器值為 undefined。

  2. 為什么線程私有?
    Java 是多線程語言,而 JVM 采用線程隔離的方式來執行多線程代碼。每個線程需要知道自己執行到哪一行代碼了,所以每個線程必須有自己的 PC 寄存器,否則線程切換后無法恢復上下文。

偏向鎖、輕量級鎖、重量級鎖的演化過程

無鎖
↓(第一個線程獲取)
偏向鎖
↓(出現競爭)
輕量級鎖
↓(競爭激烈,阻塞)
重量級鎖

1 偏向鎖(Biased Lock)
特點:偏向于第一個獲取鎖的線程
無需 CAS(Compare-And-Swap)操作,無鎖競爭
將線程 ID 記錄在對象頭中

使用場景:大量線程反復進入同步塊但沒有競爭的情況
觸發升級:如果另一個線程試圖獲取這個鎖,則撤銷偏向 → 升級為輕量級鎖

2 輕量級鎖(Lightweight Lock)
特點:多線程交替進入同步塊時使用,使用 CAS 實現樂觀鎖
沒有線程阻塞,嘗試加鎖失敗的線程會 自旋 等待鎖釋放
使用場景:短時間同步,線程競爭不激烈(兩個線程交替進入)
觸發升級:如果 CAS 多次失敗,自旋多次未成功 → 升級為重量級鎖

3 重量級鎖(Heavyweight Lock)
特點:使用操作系統 Mutex 來實現(synchronized 的早期實現)會使線程阻塞掛起和喚醒
問題:線程掛起、恢復的上下文切換代價非常大

例子:對象的創建全過程 從new到內存分配(類加載->內存分配)

public class JVMExample {public static void main(String[] args) {System.out.println("程序啟動");User user = new User("Alice", 28);user.printInfo();user = null; // 使對象變為垃圾System.gc(); // 主動調用 GC(并不一定立即執行)System.out.println("程序結束");}
}class User {private String name;private int age;public User(String name, int age) {this.name = name; // 存在于堆中對象的屬性this.age = age;}public void printInfo() {String info = "用戶:" + name + ",年齡:" + age;System.out.println(info);}@Overrideprotected void finalize() throws Throwable {// finalize 可能被 GC 調用(不保證一定被調用)System.out.println("User 對象正在被 GC 回收!");}
}

1.類加載過程

階段說明
加載從 .class 文件中加載字節碼進方法區(元空間)
驗證校驗字節碼正確性(如格式、指令合法)
準備分配靜態變量的內存,并賦默認值
解析將常量池中符號引用替換為直接引用(如方法、字段地址)
初始化執行類的 <clinit> 靜態初始化塊或靜態變量賦值

2.內存分配

區域內容及示例
程序計數器每個線程一個,當前執行指令地址。比如 System.out.println(...) 執行時,PC 保存當前 JVM 指令的地址。
虛擬機棧(Java棧)每個線程一個,保存方法調用棧幀。方法中的局部變量 user 就保存在此處。
堆(Heap)所有對象實例都在堆中分配,例如 new User(...) 創建的對象。
方法區(元空間)類的結構信息(方法表、常量池、字段表)被加載到這里,如 User.class
本地方法棧調用 Native 方法時使用,例:System.gc() 最終可能調用 native 函數觸發 GC

3.垃圾回收
user = null;
System.gc();
上述代碼使得 user 不再引用堆中的對象,變成“垃圾對象”。
GC 機制會:
通過可達性分析(引用鏈)找出不可達對象
標記-清除 / 標記-整理 / 分代收集(Young、Old、Perm)等策略執行清理
如果 User 類中定義了 finalize() 方法,GC 會調用它(只調用一次,不保證執行)

參考文章

最后參考一下細節佬的文章
JVM - JavaEE初階最后一篇 - 細節狂魔

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

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

相關文章

意識邊疆保衛戰:22:47深圳AI-BioFab人機融合危機全息實錄

前言 前些天發現了一個巨牛的人工智能免費學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到網站 &#x1f9e0;《意識邊疆保衛戰&#xff1a;22&#xff1a;47深圳AI-BioFab人機融合危機全息實錄》 副標題&#xff1a;機械義…

langchain從入門到精通(三十四)——RAG優化策略(十)父文檔檢索器實現拆分和存儲平衡

1. 拆分文檔與檢索的沖突 在 RAG 應用開發中&#xff0c;文檔拆分 和 文檔檢索 通常存在相互沖突的愿望&#xff0c;例如&#xff1a; 我們可能希望擁有小型文檔&#xff0c;以便它們的嵌入可以最準確地反映它們的含義&#xff0c;如果太長&#xff0c;嵌入/向量沒法記錄太多…

Javaweb - 7 xml

XML 是EXtensible Markup Language 的縮寫&#xff0c;翻譯過來就是可擴展標記語言。即&#xff0c;XML 和 HTML 一樣&#xff0c;都是標記語言&#xff0c;也就是說&#xff0c;它們的基本語法都是標簽。 特點&#xff1a; 1. 可擴展&#xff1a;即 XML 是允許自定義格式的。但…

qml實現 裁剪進度條

我們需要實現一個垂直進度條效果&#xff0c;但使用圖片裁剪的方式。具體需求&#xff1a; 1. 圖片位置固定&#xff0c;容器對齊其左邊和下邊&#xff0c;寬度和圖片相同。 2. 容器背景透明&#xff0c;調整裁剪容器的高度&#xff0c;使其有高度進度條的感覺&#xff08;從下…

Dify 工作流全棧解析:從零構建你的 AI 應用流程引擎

AI工作流的導言&#xff1a; 工作流系統&#xff08;Workflow System&#xff09;是 Dify 的核心組成部分&#xff0c;它通過可視化編程界面支持創建復雜的 AI 應用程序。用戶可以將不同的功能塊連接起來&#xff0c;從而設計出用于處理數據、與 AI 模型交互、管理條件以及執行…

上下位機通訊規則

0&#xff1a;事由 最近開發&#xff0c;上位機Qt與下位機通訊的時候發現通訊規則有些不一樣&#xff0c;這里簡單記錄一下 。所有代碼基于元寶生成&#xff0c;屬于偽代碼不保證真實可用&#xff0c;啊但是邏輯是這么個邏輯。 1&#xff1a;底層通訊規則 以STM32向上位機通訊…

創建平衡二叉樹C++

給你一個整數數組 nums &#xff0c;其中元素已經按 升序 排列&#xff0c;請你將其轉換為一棵 平衡 二叉搜索樹。 平衡二叉樹&#xff1a;每個節點的左右子樹高度差不超過1 class Solution { public:TreeNode* dfs(vector<int>& nums, int left, int right){if(l…

海光の初體驗

背景 八張K100的風扇已經將近一年沒轉過了…早在今年4月29日&#xff0c;Qwen3正式發布并全部開源8款「混合推理模型」。作為Qwen系列中的最新一代大型語言模型&#xff0c;Qwen3在推理、指令遵循、工具調用、多語言能力等方面進行了全面增強。海光DCU&#xff08;Deep Comput…

場外交易(OTC)財富管理系統開發及解決方案報告

——跨境金融科技賦能機構客戶新增長 一、OTC市場現狀與機構業務痛點 1. 政策機遇與市場擴容 “北向互換通”期限延長&#xff1a;2025年7月1日&#xff0c;中國外匯交易中心聯合香港交易所將利率互換合約期限延長至30年&#xff0c;首日交易規模達15.3億元&#xff0c;填補超…

pytorch底層原理學習--JIT與torchscript

文章目錄 0 目的1 TorchScript1.1 語言特性的限定性1.2 設計目的&#xff1a;模型表達的專注性 2pytorch JIT&#xff08;Just-in-time compilation)2.1pytorch JIT定義2.1pytorch JIT整個過程&#xff1a;1. 前端轉換層&#xff1a;生成靜態計算圖2. 中間表示層&#xff08;IR…

Ubuntu+Nginx+php+SQLite3+typecho手動搭建個人博客

零.Ubuntu環境 一.安裝nginx 使用以下指令進行nginx web服務器安裝&#xff1a; apt-get install nginx 如果提示找不到安裝包&#xff0c;也可以更新一下系統的apt環境包&#xff1a; sudo apt update 安裝完成后&#xff0c;可以使用以下指令查看nginx是否處于激活狀態&#…

網絡協議概念與應用層

1.概念 1.1 例子 點外賣 上述這個過程,就是自定義協議 自定義協議,具體的方式也是非常靈活的 2.幾種開發中更常見的格式 2.1xml 上古時期的組織數據的格式 通過標簽來組織數據 xml的優勢:讓數據的可讀性變得更好了 劣勢:標簽寫起來繁瑣,傳輸的時候也占用更多網絡帶寬 2.2…

pytorch學習—7.處理多維特征的輸入

2. 線性模型 3.梯度下降算法 4.反向傳播(用pytorch算梯度) 5.用pytorch實現線性回歸 6.logistic回歸 7.處理多維特征的輸入_嗶哩嗶哩_bilibili 7.1代碼復現: import numpy as np import torch import matplotlib.pyplot as plt# 1. 獲取數據集 xy_data = np.lo

AI助手“智普清言”《三元》(Python)詩解

文本邏輯解清晰&#xff0c;詩意對應技法輕。 筆記模板由python腳本于2025-07-01 06:54:55創建&#xff0c;本篇筆記適合喜歡python三元語句的coder翻閱。 學習的細節是歡悅的歷程 博客的核心價值&#xff1a;在于輸出思考與經驗&#xff0c;而不僅僅是知識的簡單復述。 Pytho…

本地RAG實戰:用Spring AI+Ollama+DeepSeek+ChromaDB增強文檔問答

本文手把手教你在本地部署RAG系統&#xff1a; 用 Spring AI 整合 Ollama&#xff08;運行DeepSeek中文模型&#xff09;ChromaDB 存儲本地文檔&#xff08;PDF/TXT&#xff09;向量Java程序實現&#xff1a;文檔解析 → 語義檢索 → 增強生成 最終效果&#xff1a;模型回答更準…

Python 數據分析:DataFrame,生成,用字典創建 DataFrame ,鍵值對數量不一樣怎么辦?

目錄 1 示例代碼2 歡迎糾錯3 論文寫作/Python 學習智能體------以下關于 Markdown 編輯器新的改變功能快捷鍵合理的創建標題&#xff0c;有助于目錄的生成如何改變文本的樣式插入鏈接與圖片如何插入一段漂亮的代碼片生成一個適合你的列表創建一個表格設定內容居中、居左、居右S…

Java 并發編程的 CAS(Compare and Swap)是什么?

CAS&#xff08;Compare and Swap&#xff0c;比較并交換&#xff09; 并非 Java 語言特有的概念&#xff0c;而是現代計算機硬件提供的一條核心原子指令。在 Java 并發編程中&#xff0c;它扮演著“幕后英雄”的角色&#xff0c;是構建高性能、無鎖并發工具&#xff08;如原子…

【UnityAssetBundle】AssetBundle打包

AssetBundle生成AB包資源文件方式&#xff1a; Unity編輯器開發&#xff0c;自定義打包工具&#xff1b;官方提供好的打包工具&#xff0c;Asset Bundle Browser 打包 選擇一個資源&#xff0c;new一個壓縮包名稱或選擇一個壓縮包名稱 點擊Window->AssetBundle Browser&…

Hush Puppies大中華區鞋類業務移交品牌方繼續經營

據悉&#xff0c;隨著百麗集團運營的暇步士&#xff08;Hush Puppies&#xff09;大中華區鞋類授權的到期&#xff0c;暇步士&#xff08;Hush Puppies&#xff09;鞋類業務已開始運營權移交。其中線上渠道授權于2025年6月30日正式到期&#xff0c;線下渠道將于2025年12月31日前…

解釋LLM怎么預測下一個詞語的

解釋LLM怎么預測下一個詞語的 通過上文詞的向量進行映射 在Transformer架構的大語言模型(如GPT系列、BERT等)中,詞語會先被轉化為詞向量。在預測下一個詞時,模型會基于之前所有詞的向量表示(并非僅僅上一個詞,但上一個詞的向量是重要信息來源之一)進行計算。 以GPT-2…