Go語言內存管理

本章節,就來學習一下go語言的內存模型,看一下內存的分配,存儲都是如何實現的,與此同時,在正式開始今天的主題之前,首先先來學習操作系統基于這一方面的內容,來看看是如何管理內存的吧

本章及節內容參考小徐先生和劉丹冰老師的內容,加上一些個人注解,go語言版本是1.24.1,由于文章的內容是在語雀,這里就附上我的語雀鏈接,方便大家更好的查看https://www.yuque.com/chenxiangyang-n12yg/pli7v5/eadkhxswioy3w746?singleDoc# 《內存管理》

一.內存管理機制

1.1 操作系統的存儲模型

在學習go語言的內存模型之前,先來熟悉一下操作系統金典的多級存儲模型,如上圖所示,差不多大家都清楚這一些東西。

1.2 虛擬內存和物理內存

虛擬內存:是一種內存管理技術,它為每一個進程提供了一個非常大的,一致的和獨立且連續的地址空間。虛擬內存通過地址翻譯硬件和頁表,提供了內存保護和簡化內存管理的能力,將物理內存和磁盤空間結合管理

它的作用如下:

  • 在用戶與硬件間添加中間代理層(沒有什么是加一個中間層解決不了的)
  • 優化用戶體驗(進程感知到獲得的內存空間是“連續”的)
  • “放大”可用內存(虛擬內存可以由物理內存+磁盤補足,并根據冷熱動態置換,用戶無感知)

物理內存:是指計算機硬件實際的內存,即RAM(隨機存取存儲器)直接和CPU交互,用于臨時存儲運行中的程序和數據。

特點:

  • 有限性:容量受硬件限制(如8GB、16GB等)
  • 高速訪問:讀寫速度遠超磁盤,但斷電后數據丟失。
  • 直接尋址:CPU通過物理地址直接訪問內存單元。

1.3 分頁管理

操作系統中通常會將虛擬內存和物理內存切割成固定的尺寸,于虛擬內存而言叫作“”,于物理內存而言叫作“”,原因及要點如下:

? 提高內存空間利用(以頁為粒度后,消滅了不穩定的外部碎片,取而代之的是相對可控的內部碎片)

? 提高內外存交換效率(更細的粒度帶來了更高的靈活度)

? 與虛擬內存機制呼應,便于建立虛擬地址->物理地址的映射關系(聚合映射關系的數據結構,稱為頁表)

? linux 頁/幀的大小固定,為 4KB(這實際是由實踐推動的經驗值,太粗會增加碎片率,太細會增加分配頻率影響效率)

二.Golang的內存管理機制

前面的一小節對操作系統的內存模型,做了一個簡單的介紹,如果想要了解更多這一方面的知識可以上網查詢一下,下面將邁入正題,來看看golang世界中內存模型的設計

2.1 golang的內存管理的模型圖

首先先來看一下go語言的內存模型長什么樣,然后我們在進一步去了解

看完它的大致樣子之后,我們來介紹介紹這些東西都是什么:

  • mheap:全局的內存起源,訪問要加全局鎖
  • mcentral:每種對象大小規格(全局共劃分為 68 種)對應的緩存,鎖的粒度也僅限于同一種規格以內
  • mcache:每個 P(正是 GMP 中的 P)持有一份的內存緩存,訪問時無鎖

這些內容,我們會在后續詳細展開說明

Golang內存模型設計的幾個核心要點

  1. 以時間換空間,一次緩存,多次使用

首先我們要做到,每次向操作系統申請空間的操作都是很重的,那不妨一次性咱多要一點,以備后續使用。

Golang中的mheap正是基于這種思想,產生的數據結構。接下來我們從兩個不同的角度去看這個堆:

  • 對于操作系統而言:這個堆就是用戶進程中緩存的內存。
  • 對于Go進程內部:堆則是所有對象的起源。

  1. 多級緩存,以實現無/細鎖化

為什么要設置多級緩存?

實際上,堆是Go運行時中最大的臨界內存資源,這意味著每次存取都要加鎖,在性能層面上看是一件非常可怕的事情.

正是為了解決這個問題,Golang在堆mheap上,做了更加細微的處理,建立了 mcentral、mcache。

通過設置不同規格的mcentral,從而實現對不同大小的對象進行管理,分開加鎖,避免了直接對mheap直接加鎖導致的性能問題。

  1. 多級規格,提高利用率

首先先來了解一下page和mspan這兩個概念:

  • page:最小的存儲單元

Golang 借鑒操作系統分頁管理的思想,每個最小的存儲單元也稱之為頁 page,但大小為 8 KB

  • mspan:最小的管理單元

mspan 大小為 page 的整數倍,且從 8B 到 80 KB 被劃分為 67 種不同的規格,分配對象時,會根據大小映射到不同規格的 mspan,從中獲取空間,實際上有一個更大的規格,所以說是68種,后續會在涉及

為什么要將mspan劃分為多個規格呢?

  1. 有了規格化,便產生了等級制度,有了等級,才支持mcentral實現細瑣化
  2. 消除了外部碎片,但是不能避免內部碎片,宏觀上提高了整體空間的利用率

可以看一下整體的架構圖,我們后續會不斷對細節進行一個補充的。

2.2 內存單元 mspan

mspan,其實就是類似雙向鏈表中的一個結點,我們來看下mspan有那些特點:

  • mspan 是 Golang 內存管理的最小單元
  • mspan 大小是 page 的整數倍(Go 中的 page 大小為 8KB),且內部的頁是連續的(至少在虛擬內存的視角中是這樣)
  • 每個 mspan 根據空間大小以及面向分配對象的大小,會被劃分為不同的等級(2.2小節展開)
  • 同等級的 mspan 會從屬同一個 mcentral,最終會被組織成鏈表,因此帶有前后指針(prev、next)
  • 由于同等級的 mspan 內聚于同一個 mcentral,所以會基于同一把互斥鎖管理
  • mspan 會基于 bitMap 輔助快速找到空閑內存塊(塊大小為對應等級下的 object 大小),此時需要使用到 Ctz64 算法.

type mspan struct {// 標識前后節點的指針 next *mspan     prev *mspan    // ...// 起始地址startAddr uintptr // 包含幾頁,頁是連續的npages    uintptr // 標識此前的位置都已被占用 freeindex uintptr// 最多可以存放多少個 objectnelems uintptr // number of object in the span.// bitmap 每個 bit 對應一個 object 塊,標識該塊是否已被占用allocCache uint64// ...// 標識 mspan 等級,包含 class 和 noscan 兩部分信息spanclass             spanClass    // ...
}

2.3 內存單元等級 spanClass

mspan根據空間大小和面向分配對象的大小,被劃分為67種不同的等級(1-67,實際上還有一種隱藏的0級,用于處理更大的對象,上不封頂)

實際上對于不同規格的等級,都會產生對應的浪費,簡單說一下這個浪費是怎么造成的,比如:

此時我們創建了一個對象,它的大小是1b,但是最小的mspan是8b,他不會分配1b大小的mspan,所以就會導致7b被浪費掉,比如這樣:

除了上面談及的根據大小確定的 mspan 等級外,每個 object 還有一個重要的屬性叫做 noscan,標識了 object 是否包含指針,在 gc 時是否需要展開標記.

(這里做一個簡單的拓展:Go的垃圾回收器GC需要找到存活的數據,為此GC會從根對象比如棧,全局變量,出發,遍歷所有可以到達的對象,過程是標記所有存活對象,檢查每個對象的內部字段,遞歸標記其引用的對象,如果對象不包含指針,掃描它就是多余,反而浪費CPU時間,所有noscan作用就是告訴GC,這家伙沒有指針,跳過掃描。)

在 Golang 中,會將 span class + noscan 兩部分信息組裝成一個 uint8,形成完整的 spanClass 標識. 8 個 bit 中,高 7 位表示了上表的 span 等級(總共 67 + 1 個等級,8 個 bit 足夠用了),最低位表示 nocan 信息.

2.4 線程緩存 mcache

要點:

(1)mcache 是每個 P 獨有的緩存,因此交互無鎖

(2)mcache 將每種 spanClass 等級的 mspan 各緩存了一個,總數為 2(nocan 維度) * 68(大小維度)= 136

(3)mcache 中還有一個為對象分配器 tiny allocator,用于處理小于 16B 對象的內存分配

const numSpanClasses = 136
type mcache struct {// 微對象分配器相關tiny       uintptrtinyoffset uintptrtinyAllocs uintptr// mcache 中緩存的 mspan,每種 spanClass 各一個alloc [numSpanClasses]*mspan // ...
}

這里可能大家大家可能會疑惑為什么是136個大小,而不是68個,從圖中不難看出它分為了scan和noscan兩種,他表示是否含有指針,在spanclass里說過,這個指針就是告訴GC要不要掃描,如果我直接在他的上層直接將其分開,也就是有scan在一塊,沒有的在另外一塊,這樣就可以提高GC的效率,不用每次都去掃描mspan,判斷有無指針了。

mcache具體的申請形式

通過alloc區分出了136個指針,指向對應的mspan,用完時,會重新向mcentral申請,并更新alloc中的指針哦。

2.5 中心緩存 mcentral

要點:

(1)每個 mcentral 對應一種 spanClass

(2)每個 mcentral 下聚合了該 spanClass 下的 mspan

(3)mcentral 下的 mspan 分為兩個鏈表,分別為有空間 mspan 鏈表 partial 和滿空間 mspan 鏈表 full

(4)每個 mcentral 都有一把屬于自己的鎖

type mcentral struct {_         sys.NotInHeap// 對應的 spanClassspanclass spanClass// 有空位的 mspan 集合,數組長度為 2 是用于抗一輪 GCpartial [2]spanSet // 無空位的 mspan 集合full    [2]spanSet 
}

簡單的說一下這兩個鏈表的作用:

內存分配的時候,mcache從partical鏈表獲取mspan,當其mcache需要新的mspan時(例如mspan已滿),會向mcentral請求。mcentral會優先從partial鏈表中取出一個mspan,返回給mcache

對于這個已滿的mspan會歸還到mcache的full鏈表。

當 GC 掃描并釋放某些對象時,原本在 full 鏈表中的 mspan 可能重新獲得空閑對象。此時,這些 mspan 會從 full 鏈表移回 partial 鏈表

2.6 全局堆緩存 mheap

  • 對于 Golang 上層應用而言,堆是操作系統虛擬內存的抽象
  • 以頁(8KB)為單位,作為最小內存存儲單元
  • 負責將連續頁組裝成 mspan
  • 全局內存基于 bitMap 標識頁使用情況,每個 bit 對應一頁,為 0 則自由,為 1 則已被 mspan 組裝(也就是說全部的頁是否使用,都會在一個巨大的bitmap里面標記)
  • 通過 heapArena 聚合頁,記錄了頁到 mspan 的映射信息(后續2.8小節展開)
  • 建立空閑頁基數樹索引 radix tree index,輔助快速尋找空閑頁(后續2.7小節展開)
  • 是 mcentral 的持有者,持有所有 spanClass 下的 mcentral,作為自身的緩存
  • 內存不夠時,向操作系統申請,申請單位為 heapArena(64M)

內存的包含形式

這里做一個簡簡單單的說明哈,可能之前的圖會誤導大家,就是mcache和mcentral都是mheap的一部分哦,可不是分開的。

type mheap struct {// 堆的全局鎖lock mutex// 空閑頁分配器,底層是多棵基數樹組成的索引,每棵樹對應 16 GB 內存空間pages pageAlloc // 記錄了所有的 mspan. 需要知道,所有 mspan 都是經由 mheap,使用連續空閑頁組裝生成的allspans []*mspan// heapAreana 數組,64 位系統下,二維數組容量為 [1][2^22]// 每個 heapArena 大小 64M,因此理論上,Golang 堆上限為 2^22*64M = 256T// 通過這個來間接管理bitmaparenas [1 << arenaL1Bits]*[1 << arenaL2Bits]*heapArena// ...// 多個 mcentral,總個數為 spanClass 的個數central [numSpanClasses]struct {mcentral mcentral// 用于內存地址對齊pad      [cpu.CacheLinePadSize - unsafe.Sizeof(mcentral{})%cpu.CacheLinePadSize]byte}// ...
}

2.7 空閑頁索引 pageAlloc

這一小節的內容主要講解空閑頁尋址分配的計數樹索引有關的內容,可能比較苦澀難懂,就做一個簡單的介紹吧,gogogo出發咯。

pageAlloc的底層是基數樹組成的一個索引,它是Go語言高效管理大內存空間的核心機制。尤其是在空閑頁的快速尋址和分配中。這一設計在 Google 的提案文檔(Scaling the Page Allocator)中有詳細描述。

接下來我會做一個簡單的介紹

為什么要使用基數樹(radix tree),而不采用傳統的bitmap呢?

  • 主要就是現代程序的內存都是TB級別,傳統的bitmap掃描算法時間復雜度為O(N),無法滿足需求
  • 通過基數樹可以降低時間復雜度為O(1)或O(logN)

該數據結構的一些必備知識點

  • 一顆基數樹的大小為16GB,聚合了內存中各頁的使用情況,幫助mheap快速找到指定長度的連續的空閑頁所在的位置
  • mheap內存的上限為256TB,所以它不是單單由一顆基數樹組成,而是森林,一共有2的14次方棵。
  • mheap會對被使用過的頁進行標記,1表示已使用,0表示空閑

基數樹的節點設置

基數樹中,每個節點稱之為 PallocSum,是一個 uint64 類型,體現了索引的聚合信息,包含以下四部分:

  • start:最右側 21 個 bit,標識了當前節點映射的 bitMap 范圍中首端有多少個連續的 0 bit(空閑頁),稱之為 start;
  • max:中間 21 個 bit,標識了當前節點映射的 bitMap 范圍中最多有多少個連續的 0 bit(空閑頁),稱之為 max;
  • end:左側 21 個 bit,標識了當前節點映射的 bitMap 范圍中最末端有多少個連續的 0 bit(空閑頁),稱之為 end.
  • 最左側一個 bit,棄置不用

(這樣做的目的就是為了避免內存碎片化,這里的start和end只關心首端和尾端是不是0,如果不是0,那么他們的結果就是0,只有當首端和尾端是0的時候才會開始計算)

start和end主要用于為父節點提供合并之后的max是否發生改變,每個PallocSum下都有8個,相鄰的兩個節點的strat和end可能會合成比原本更大的一個max,所以需要記錄,后續會講到。

父子節點的關系

  • 每個父 pallocSum 有 8 個子 pallocSum
  • 根 pallocSum 總覽全局,映射的 bitMap 范圍為全局的 16 GB 空間(其 max 最大值為 2^21,因此總空間大小為 2^21*8KB=16GB);
  • 從首層向下是一個依次八等分的過程,每一個 pallocSum 映射其父節點 bitMap 范圍的八分之一,因此第二層 pallocSum 的 bitMap 范圍為 16GB/8 = 2GB,以此類推,第五層節點的范圍為 16GB / (8^4) = 4 MB,已經很小
  • 聚合信息時,自底向上. 每個父 pallocSum 聚合 8 個子 pallocSum 的 start、max、end 信息,形成自己的信息,直到根 pallocSum,坐擁全局 16 GB 的 start、max、end 信息
  • mheap 尋頁時,自頂向下. 對于遍歷到的每個 pallocSum,先看起 start 是否符合,是則尋頁成功;再看 max 是否符合,是則進入其下層孩子 pallocSum 中進一步尋訪;最后看 end 和下一個同輩 pallocSum 的 start 聚合后是否滿足,是則尋頁成功.

2.8 heapArena

  • 每個 heapArena 包含 8192 個頁,大小為 8192 * 8KB = 64 MB
  • heapArena 記錄了頁到 mspan 的映射. 因為 GC 時,通過地址偏移找到頁很方便,但找到其所屬的 mspan 不容易. 因此需要通過這個映射信息進行輔助.
  • heapArena 是 mheap 向操作系統申請內存的單位(64MB)

三.分配流程流程

下面來串聯 Golang 中分配對象的流程,不論是以下哪種方式,最終都會殊途同歸步入 mallocgc 方法中,并且根據 3.1 小節中的策略執行分配流程:

  • new(T)
  • &T{}
  • make(xxxx)

3.1 分配對象

golang中,會依據object的大小,分為三類對象,從而對其進行內存的分配,接下來就來看看吧:

不同類型的對象,會有不同的分配策略,這些內容在mallocgc方法中都有體現:

核心流程類似于讀多級緩存的過程,自上而下,每一步只要成功就立即返回,若失敗,則由下層方法兜底。

對于微對象的分配流程:

(1)從 P 專屬 mcache 的 tiny 分配器取內存(無鎖)

(2)根據所屬的 spanClass,從 P 專屬 mcache 緩存的 mspan 中取內存(無鎖)

(3)根據所屬的 spanClass 從對應的 mcentral 中取 mspan 填充到 mcache,然后從 mspan 中取內存(spanClass 粒度鎖)

(4)根據所屬的 spanClass,從 mheap 的頁分配器 pageAlloc 取得足夠數量空閑頁組裝成 mspan 填充到 mcache,然后從 mspan 中取內存(全局鎖)

(5)mheap 向操作系統申請內存,更新頁分配器的索引信息,然后重復(4)

對于小對象的分配流程是跳過(1)步,執行上述流程的(2)-(5)步;

對于大對象的分配流程是跳過(1)-(3)步,執行上述流程的(4)-(5)步.

3.2 分配流程

(1)微小對象分配(Tiny Allocator)

  • 條件:對象大小 < 16 B 不包含指針(避免影響垃圾回收掃描)。
  • 流程
    1. 嘗試合并:若當前 mcache.tiny 塊剩余空間足夠,直接在當前 tiny 塊分配(無鎖)。
    2. 申請新塊:若空間不足,向 mheap 申請一個新的 16 B 內存塊(可能觸發垃圾回收)。
  • 優化:通過合并多個微小對象減少內存碎片。

(2)小對象分配(mcache -> mcentral)

  • 步驟
    1. 確定 Size Class:根據對象大小匹配預定義的 Size Class(Go 有 67 個預定義規格,如 8 B、16 B、32 B ... 32 KB)。
    2. 從 mcache 獲取 Span
      • mcache 為每個 P(處理器)維護本地緩存 Span,包含對應 Size Class 的空閑對象鏈表。
      • 若當前 Span 有空閑對象,直接分配(無鎖)。
    1. mcache 向 mcentral 申請新 Span
      • mcache 的 Span 耗盡,向對應的 mcentral 請求新的 Span。
      • mcentral 維護全局 Span 列表(nonemptyempty 鏈表),通過鎖保證并發安全。
    1. mcentral 向 mheap 擴容
      • mcentral 無可用 Span,向 mheap 申請新的 Span(通常為 8 KB 的倍數)。
      • mheap 通過基數樹查找連續空閑頁,切割為所需大小的 Span 返回給 mcentral

(3)大對象分配(直接通過 mheap)

  • 觸發條件:對象大小 > 32 KB。
  • 流程
    1. 計算所需頁數:將對象大小轉換為頁數(1 頁 = 8 KB)。
    2. 基數樹查找空閑頁
      • mheap 使用基數樹快速定位滿足需求的連續空閑頁。
      • 基數樹的 max 字段幫助跳過無法滿足需求的子樹,start/end 處理跨子樹合并。
    1. 分配并更新元數據
      • 標記位圖中對應頁為已占用。
      • 更新基數樹節點的 start/max/end 摘要信息。
    1. 直接返回內存地址:無需切割 Span,直接將連續頁映射給大對象。

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

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

相關文章

【docker】啟動臨時MongoDB容器、掛載數據卷運行數據庫服務,并通過備份文件恢復MongoDB數據庫備份數據

?啟動臨時 MongoDB 容器、掛載數據卷運行數據庫服務&#xff0c;并通過備份文件恢復數據 1.命令分解與功能說明1.1.啟動一個臨時 MongoDB 容器?&#xff0c;并進入交互式終端&#xff08;1&#xff09;執行命令&#xff08;2&#xff09;實現功能?&#xff08;3&#xff09;…

【最新 MCP 戰神手冊 08】工具使用詳解:實現 AI 行動

文章目錄 1. 開始啦!2. 第一部分:設計高效且安全的工具3. 第二部分:定義工具藍圖——參數、輸出與約束條件4. 第三部分:彌合差距:LLM 兼容性(函數調用)5. 第四部分:實施與測試的最佳實踐1. 開始啦! 在前幾章中,我們將工具介紹為 AI 模型在 MCP 客戶端引導下向 MCP 服…

介紹 IntelliJ IDEA 快捷鍵操作

IntelliJ IDEA 快捷鍵操作 1. 編輯與導航2. 查找與替換3. 調試與運行4. 導航與視圖5. 重構與生成6. 高級快捷鍵&#xff08;提高效率&#xff09;注意事項 IntelliJ IDEA 是一款功能強大的集成開發環境&#xff0c;掌握其常用快捷鍵可以顯著提升開發效率。但是有些小伙伴并不清…

Javascript 中作用域的理解?

一、作用域的類型 1. 全局作用域&#xff08;公司大門外&#xff09; 范圍&#xff1a;整個 JavaScript 文件變量&#xff1a;像貼在公告欄上的信息&#xff0c;所有人可見例子&#xff1a;const companyName "阿里"; // 全局變量&#xff0c;任何地方都能訪問 fu…

Leetcode刷題記錄22——滑動窗口最大值

題源&#xff1a;https://leetcode.cn/problems/sliding-window-maximum/description/?envTypestudy-plan-v2&envIdtop-100-liked 題目描述&#xff1a; 思路一&#xff1a; 暴力遍歷法&#xff0c;通過一個長度為k的滑動窗口遍歷nums&#xff0c;將其中最大的數依次記…

Apache Flink的架構設計與運行流程說明

在大數據領域&#xff0c;實時計算的重要性隨著業務需求的爆發式增長愈發凸顯。從電商的實時銷量監控到金融的高頻交易風控&#xff0c;從物聯網設備的實時告警到社交平臺的熱點追蹤&#xff0c;企業對“秒級甚至毫秒級”數據處理能力的需求已成為剛需。在眾多實時計算框架中&a…

經典算法 最長單調遞增子序列

最長單調遞增子序列 問題描述 找出由n個數組成的序列的最長單調遞增子序列。 示例輸入 9 2 1 5 3 6 4 8 9 7示例輸出 5示例輸入 6 5 6 7 1 2 8示例輸出 4c代碼(動態規劃 O(n^2)) #include<bits/stdc.h>using namespace std;int main() {int n, ans 0;cin >&g…

【語法】C++繼承中遇到的問題及解決方法

目錄 1.子類構造函數中初始化父類成員 2.子類顯式調用父類的析構函數 第一種說法&#xff1a;重定義 反駁&#xff1a; 第二種說法&#xff1a;operator~ 3.因編譯器版本過低而出現錯誤 貼主在學習C的繼承時&#xff0c;遇到了很多問題&#xff0c;覺得很變態&#xff0c…

前綴和 后綴和 --- 尋找數組的中心下標

題目鏈接 尋找數組的中心下標 給你一個整數數組 nums &#xff0c;請計算數組的 中心下標 。 數組 中心下標 是數組的一個下標&#xff0c;其左側所有元素相加的和等于右側所有元素相加的和。 如果中心下標位于數組最左端&#xff0c;那么左側數之和視為 0 &#xff0c;因為…

NVIDIA --- 端到端自動駕駛

前言 參加了NVIDIA 高級輔助駕駛開發者實驗室的活動&#xff0c;本次活動基于 NVIDIA 汽車行業的端到端解決方案——DRIVE AGX? 平臺&#xff0c;實現高級別智能和安全性的軟硬件開發工具和 AV 基礎設施。并且NVIDIA自動駕駛實驗室推出了一系列自動駕駛算法最新的前沿研究視頻…

SQL實戰:03之SQL中的遞歸查詢

文章目錄 概述SQL 中的遞歸實現題目一:分析組織層級題解題目二:樹節點求解題解步驟一&#xff1a;通過遞歸查詢出每個節點的上級節點和下級節點分布步驟二&#xff1a;分組統計 概述 最近刷題時遇到了一道需要根據組織層級來統計各個層級的一些數據&#xff0c;當時碰到時的第…

MySQL 語法與基礎完全指南

MySQL 是最流行的開源關系型數據庫管理系統之一&#xff0c;廣泛應用于 Web 應用程序開發。本文將全面介紹 MySQL 的基礎知識和完整語法結構。 一、MySQL 基礎概念 1. 數據庫基本術語 數據庫(Database): 存儲數據的集合 表(Table): 數據以表格形式組織 列(Column): 表中的一…

【Sqlalchemy Model轉換成Pydantic Model示例】

【Sqlalchemy Model轉換成Pydantic Model示例】 由于Sqlalchemy和Pydantic的模型字段類型可能有差異, 所以需要一個通用的裝換類 def sqlalchemy_to_pydantic_v2(sqlalchemy_model, pydantic_model):"""通用函數&#xff0c;將 SQLAlchemy 模型實例轉換為 Pyd…

2025年歐洲西南部大停電

2025年4月28日&#xff0c;歐洲西南部出現大規模停電&#xff0c;西班牙、葡萄牙和法國南部均受到影響。有報道指出停電可能與 歐洲電網出現問題有關&#xff0c;但最終原因尚未確定。由于停電&#xff0c;上述地區的交通和通信服務均受到嚴重影響&#xff0c;交通信號燈停止工…

Java EE初階——計算機是如何工作的

1. cpu 馮諾依曼體系&#xff08;Von Neumann Architecture&#xff09; ? CPU 中央處理器: 進?算術運算和邏輯判斷. ? 存儲器: 分為外存和內存, ?于存儲數據(使??進制?式存儲) ? 輸?設備: ??給計算機發號施令的設備. ? 輸出設備: 計算機個??匯報結果的設…

飛鳥游戲模擬器 1.0.3 | 完全免費無廣告,內置大量經典童年游戲,重溫美好回憶

飛鳥游戲模擬器是一款專為安卓用戶設計的免費游戲模擬器&#xff0c;內置了大量經典的童年游戲。該模擬器擁有豐富的游戲資源&#xff0c;目前已有約20,000款游戲&#xff0c;包括多種類型如冒險、動作、角色扮演等。用戶可以直接搜索查找想要玩的游戲進行下載并啟動。游戲庫中…

網絡爬取需謹慎:警惕迷宮陷阱

一、技術背景:網絡爬蟲與數據保護的博弈升級 1. 問題根源:AI訓練數據爬取的無序性 數據需求爆炸:GPT-4、Gemini等大模型依賴數萬億網頁數據訓練,但大量爬蟲無視網站的robots.txt協議(非法律強制),未經許可抓取內容(如新聞、學術論文、代碼),引發版權爭議(如OpenAI被…

Qwen3簡介:大型語言模型的革命

Qwen3簡介&#xff1a;大型語言模型的革命 Qwen系列語言模型的最新發布——Qwen3&#xff0c;標志著人工智能&#xff08;AI&#xff09;技術的一次重大飛躍。基于前代版本的成功&#xff0c;Qwen3在架構、推理能力和多項先進功能上都取得了顯著提升&#xff0c;正在重新定義大…

MODSIM選型指南:汽車與航空航天企業如何選擇仿真平臺

1. 引言 在競爭激烈的汽車與航空航天領域&#xff0c;仿真技術已成為產品研發不可或缺的環節。通過在設計階段驗證概念并優化性能&#xff0c;仿真平臺能有效縮短開發周期并降低物理樣機制作成本。 MODSIM&#xff08;建模與仿真&#xff09;作為達索系統3DEXPERIENCE平臺的核…

linux 內核 debugfs 使用介紹

一&#xff1a;概述 debugfs 是 Linux 內核提供的一個特殊的虛擬文件系統&#xff0c;用于 暴露內核模塊&#xff08;如驅動&#xff09;內部的調試信息或控制接口&#xff0c;供開發者、調試人員實時查看和排查問題。即 debugfs 就是一個“調試專用的 /proc 或 /sys”&#xf…