【iOS】cache_t分析

前言

之前分析類的結構的時候,有遇到一個cache_t,當時說是用來保存方法緩存的結構,這篇文章來從源碼詳細介紹一下cache_t

概覽cache_t

cache_t結構

類在底層的結構如之前所述,存在著cache_t屬性,而cache_t的結構如下:

struct cache_t {
private:// explicit_atomic 顯示原子性,目的是為了能夠 保證 增刪改查時 線程的安全性explicit_atomic<uintptr_t> _bucketsAndMaybeMask; //8union {struct {//一共 8explicit_atomic<mask_t>    _maybeMask; //4
#if __LP64__uint16_t                   _flags;//2
#endifuint16_t                   _occupied;//2};explicit_atomic<preopt_cache_t *> _originalPreoptCache;};#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED//macOS、模擬器 -- 主要是架構區分// _bucketsAndMaybeMask is a buckets_t pointer// _maybeMask is the buckets maskstatic constexpr uintptr_t bucketsMask = ~0ul;static_assert(!CONFIG_USE_PREOPT_CACHES, "preoptimized caches not supported");
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16_BIG_ADDRSstatic constexpr uintptr_t maskShift = 48;static constexpr uintptr_t maxMask = ((uintptr_t)1 << (64 - maskShift)) - 1;static constexpr uintptr_t bucketsMask = ((uintptr_t)1 << maskShift) - 1;static_assert(bucketsMask >= MACH_VM_MAX_ADDRESS, "Bucket field doesn't have enough bits for arbitrary pointers.");
#if CONFIG_USE_PREOPT_CACHESstatic constexpr uintptr_t preoptBucketsMarker = 1ul;static constexpr uintptr_t preoptBucketsMask = bucketsMask & ~preoptBucketsMarker;
#endif
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16 //64位真機// _bucketsAndMaybeMask is a buckets_t pointer in the low 48 bits// _maybeMask is unused, the mask is stored in the top 16 bits.// How much the mask is shifted by.static constexpr uintptr_t maskShift = 48;// Additional bits after the mask which must be zero. msgSend// takes advantage of these additional bits to construct the value// `mask << 4` from `_maskAndBuckets` in a single instruction.static constexpr uintptr_t maskZeroBits = 4;// The largest mask value we can store.static constexpr uintptr_t maxMask = ((uintptr_t)1 << (64 - maskShift)) - 1;// The mask applied to `_maskAndBuckets` to retrieve the buckets pointer.static constexpr uintptr_t bucketsMask = ((uintptr_t)1 << (maskShift - maskZeroBits)) - 1;// Ensure we have enough bits for the buckets pointer.static_assert(bucketsMask >= MACH_VM_MAX_ADDRESS,"Bucket field doesn't have enough bits for arbitrary pointers.");#if CONFIG_USE_PREOPT_CACHESstatic constexpr uintptr_t preoptBucketsMarker = 1ul;
#if __has_feature(ptrauth_calls)// 63..60: hash_mask_shift// 59..55: hash_shift// 54.. 1: buckets ptr + auth//      0: always 1static constexpr uintptr_t preoptBucketsMask = 0x007ffffffffffffe;static inline uintptr_t preoptBucketsHashParams(const preopt_cache_t *cache) {uintptr_t value = (uintptr_t)cache->shift << 55;// masks have 11 bits but can be 0, so we compute// the right shift for 0x7fff rather than 0xffffreturn value | ((objc::mask16ShiftBits(cache->mask) - 1) << 60);}
#else// 63..53: hash_mask// 52..48: hash_shift// 47.. 1: buckets ptr//      0: always 1static constexpr uintptr_t preoptBucketsMask = 0x0000fffffffffffe;static inline uintptr_t preoptBucketsHashParams(const preopt_cache_t *cache) {return (uintptr_t)cache->hash_params << 48;}
#endif
#endif // CONFIG_USE_PREOPT_CACHES
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4//非64位 真機// _bucketsAndMaybeMask is a buckets_t pointer in the top 28 bits// _maybeMask is unused, the mask length is stored in the low 4 bitsstatic constexpr uintptr_t maskBits = 4;static constexpr uintptr_t maskMask = (1 << maskBits) - 1;static constexpr uintptr_t bucketsMask = ~maskMask;static_assert(!CONFIG_USE_PREOPT_CACHES, "preoptimized caches not supported");
#else
#error Unknown cache mask storage type.
#endifbool isConstantEmptyCache() const;bool canBeFreed() const;mask_t mask() const;#if CONFIG_USE_PREOPT_CACHESvoid initializeToPreoptCacheInDisguise(const preopt_cache_t *cache);const preopt_cache_t *disguised_preopt_cache() const;
#endifvoid incrementOccupied();void setBucketsAndMask(struct bucket_t *newBuckets, mask_t newMask);void reallocate(mask_t oldCapacity, mask_t newCapacity, bool freeOld);void collect_free(bucket_t *oldBuckets, mask_t oldCapacity);static bucket_t *emptyBuckets();static bucket_t *allocateBuckets(mask_t newCapacity);static bucket_t *emptyBucketsForCapacity(mask_t capacity, bool allocate = true);static struct bucket_t * endMarker(struct bucket_t *b, uint32_t cap);void bad_cache(id receiver, SEL sel) __attribute__((noreturn, cold));public:// The following four fields are public for objcdt's use only.// objcdt reaches into fields while the process is suspended// hence doesn't care for locks and pesky little details like this// and can safely use these.unsigned capacity() const;struct bucket_t *buckets() const;Class cls() const;#if CONFIG_USE_PREOPT_CACHESconst preopt_cache_t *preopt_cache() const;
#endifmask_t occupied() const;void initializeToEmpty();#if CONFIG_USE_PREOPT_CACHESbool isConstantOptimizedCache(bool strict = false, uintptr_t empty_addr = (uintptr_t)&_objc_empty_cache) const;bool shouldFlush(SEL sel, IMP imp) const;bool isConstantOptimizedCacheWithInlinedSels() const;Class preoptFallbackClass() const;void maybeConvertToPreoptimized();void initializeToEmptyOrPreoptimizedInDisguise();
#elseinline bool isConstantOptimizedCache(bool strict = false, uintptr_t empty_addr = 0) const { return false; }inline bool shouldFlush(SEL sel, IMP imp) const {return cache_getImp(cls(), sel) == imp;}inline bool isConstantOptimizedCacheWithInlinedSels() const { return false; }inline void initializeToEmptyOrPreoptimizedInDisguise() { initializeToEmpty(); }
#endifvoid insert(SEL sel, IMP imp, id receiver);void copyCacheNolock(objc_imp_cache_entry *buffer, int len);void destroy();void eraseNolock(const char *func);static void init();static void collectNolock(bool collectALot);static size_t bytesForCapacity(uint32_t cap);#if __LP64__bool getBit(uint16_t flags) const {return _flags & flags;}void setBit(uint16_t set) {__c11_atomic_fetch_or((_Atomic(uint16_t) *)&_flags, set, __ATOMIC_RELAXED);}void clearBit(uint16_t clear) {__c11_atomic_fetch_and((_Atomic(uint16_t) *)&_flags, ~clear, __ATOMIC_RELAXED);}
#endif#if FAST_CACHE_ALLOC_MASKbool hasFastInstanceSize(size_t extra) const{if (__builtin_constant_p(extra) && extra == 0) {return _flags & FAST_CACHE_ALLOC_MASK16;}return _flags & FAST_CACHE_ALLOC_MASK;}size_t fastInstanceSize(size_t extra) const{ASSERT(hasFastInstanceSize(extra));//Gcc的內建函數 __builtin_constant_p 用于判斷一個值是否為編譯時常數,如果參數EXP 的值是常數,函數返回 1,否則返回 0if (__builtin_constant_p(extra) && extra == 0) {return _flags & FAST_CACHE_ALLOC_MASK16;} else {size_t size = _flags & FAST_CACHE_ALLOC_MASK;// remove the FAST_CACHE_ALLOC_DELTA16 that was added// by setFastInstanceSize//刪除由setFastInstanceSize添加的FAST_CACHE_ALLOC_DELTA16 8個字節return align16(size + extra - FAST_CACHE_ALLOC_DELTA16);}}void setFastInstanceSize(size_t newSize){// Set during realization or construction only. No locking needed.uint16_t newBits = _flags & ~FAST_CACHE_ALLOC_MASK;uint16_t sizeBits;// Adding FAST_CACHE_ALLOC_DELTA16 allows for FAST_CACHE_ALLOC_MASK16// to yield the proper 16byte aligned allocation size with a single masksizeBits = word_align(newSize) + FAST_CACHE_ALLOC_DELTA16;sizeBits &= FAST_CACHE_ALLOC_MASK;if (newSize <= sizeBits) {newBits |= sizeBits;}_flags = newBits;}
#elsebool hasFastInstanceSize(size_t extra) const {return false;}size_t fastInstanceSize(size_t extra) const {abort();}void setFastInstanceSize(size_t extra) {// nothing}
#endif
};

在cache_t的機構當中,有一個極為重要的屬性,就是_bucketsAndMaybeMask,它是一個bucket_t類型的結構體指針。

查看bucket_t的結構可以發現它大致存放了imp,實際上bucket作為一個桶,就是用來存放imp方法實現以及它的key,所以結合以上兩個結構體可知,cache中緩存的正是sel-imp

在cache_t結構體中提供了獲取_buckets屬性的方法buckets(),獲取了_buckets屬性,就可以獲取sel-imp了,這兩個的獲取在bucket_t結構體中也提供了相應的獲取方法sel() 以及imp(UNUSED_WITHOUT_PTRAUTH bucket_t *base, Class cls)。

在沒有執行方法調用時,cache中沒有緩存,執行了一次方法調用,cache中就有了一個緩存,即調用一次方法就會緩存一次方法

深入cache_t

cache_t中有一個函數叫incrementOccupied(),具體實現為:

全局搜索這個函數,發現它只在cache_t的insert方法有調用

insert方法,顧名思義其實就是往cache中插入sel-imp的方法。全局搜索cache_t::insert方法,發現在寫入之前,還有一步操作就是查找sel-imp,即cache的讀取

分析insert方法

insert方法的源碼如下:

void cache_t::insert(SEL sel, IMP imp, id receiver)
{runtimeLock.assertLocked();// Never cache before +initialize is doneif (slowpath(!cls()->isInitialized())) {return;}if (isConstantOptimizedCache()) {_objc_fatal("cache_t::insert() called with a preoptimized cache for %s",cls()->nameForLogging());}#if DEBUG_TASK_THREADSreturn _collecting_in_critical();
#else
#if CONFIG_USE_CACHE_LOCKmutex_locker_t lock(cacheUpdateLock);
#endifASSERT(sel != 0 && cls()->isInitialized());// Use the cache as-is if until we exceed our expected fill ratio.mask_t newOccupied = occupied() + 1;//沒有屬性賦值的情況下 occupied() = 0, newOccupied = 1unsigned oldCapacity = capacity(), capacity = oldCapacity;if (slowpath(isConstantEmptyCache())) {//小概率發生的,即當 occupied() = 0時,即創建緩存,創建屬于小概率事件// Cache is read-only. Replace it.if (!capacity) capacity = INIT_CACHE_SIZE;//初始化時,capacity = 4 (1<<2)reallocate(oldCapacity, capacity, /* freeOld */false);//開辟空間//到目前為止,if的流程的操作都是初始化創建}else if (fastpath(newOccupied + CACHE_END_MARKER <= cache_fill_ratio(capacity))) {// Cache is less than 3/4 or 7/8 full. Use it as-is.//如果小于等于占用內存的3/4,什么都不用做/*第一次時,申請開辟的內存是4個,如果此時已經有3個從bucket插入到cache里面,再插入一個,就是4個.當大于4即當前下標是4,就數組越界了,這是肯定不行的,所以需要在原理的容量上進行兩倍擴容*/}
#if CACHE_ALLOW_FULL_UTILIZATIONelse if (capacity <= FULL_UTILIZATION_CACHE_SIZE && newOccupied + CACHE_END_MARKER <= capacity) {// Allow 100% cache utilization for small buckets. Use it as-is.}
#endifelse {//如果超出了3/4,則需要擴容(兩倍擴容) -- 即 occupied 為2時,就沒有進去了//擴容算法: 有capacity時,擴容兩倍,沒有capacity就初始化為4capacity = capacity ? capacity * 2 : INIT_CACHE_SIZE;if (capacity > MAX_CACHE_SIZE) {capacity = MAX_CACHE_SIZE;}//走到這里表示:曾經有,但是已經滿了,需要重新梳理reallocate(oldCapacity, capacity, true);// 內存 擴容完畢}bucket_t *b = buckets();mask_t m = capacity - 1; //mask = capacity - 1mask_t begin = cache_hash(sel, m);//求cache哈希,即哈希下標---通過哈希算法函數計算 sel存儲的下標mask_t i = begin;// Scan for the first unused slot and insert there.// There is guaranteed to be an empty slot.//如果存在了哈希沖突,則從沖突的下標開始遍歷 即進行do-while循環do {//如果當前遍歷的下標拿不到sel,即表示當前下標沒有存儲selif (fastpath(b[i].sel() == 0)) {//則將sel存儲進去,并將對應的 occupied標記++,即從0變為1incrementOccupied();b[i].set<Atomic, Encoded>(b, sel, imp, cls());return;}//如果當前哈希下標的sel等于準備插入的sel,則直接返回if (b[i].sel() == sel) {// The entry was added to the cache by some other thread// before we grabbed the cacheUpdateLock.return;}//如果當前計算的哈希下標已經存儲了sel,且兩個sel不相等,需要重新進行哈希計算 得到新的下標} while (fastpath((i = cache_next(i, m)) != begin));bad_cache(receiver, (SEL)sel);
#endif // !DEBUG_TASK_THREADS
}

insert的代碼很長,但是其實主要可以分為三個部分:

  • 第一步,計算當前緩存占用量

  • 第二部,根據緩存占用量判斷執行的操作

  • 針對需要存儲的bucket進行內部的sel-imp賦值

下面我們來詳細解釋每個部分各自是如何實現的

計算當前緩存占用量

剛剛incrementOccupied的實現里有一步occupied的自增操作,這個occupied其實就代表著當前緩存中保存的sel-imp對的數量。

insert方法就是根據occupied的值計算出當前的緩存占用量,當屬性未賦值及無方法調用時,此時的occupied()為0,而newOccupied為1

這里有幾個需要注意的點:

  • alloc申請空間時,此時的對象已經創建,如果再調用init方法,occupied也會+1

  • 當有屬性賦值時,會隱式調用set方法,occupied也會增加,即有幾個屬性賦值,occupied就會在原有的基礎上加幾個

  • 有方法調用時,occupied也會增加,即有幾次調用,occupied會在原有的基礎上加幾個

根據緩存占用量判斷執行的操作

如果是第一次創建,那么磨人開辟四個sel-imp的空間

如果緩存占用量小于等于3/4,就不做任何處理

而如果緩存占用量超過了3/4,需要進行兩倍擴容以及重新開辟空間

reallocate方法

在判斷完操作之后,如果是第一次創建或者兩倍擴容,會用到一個方法來開辟空間,這個方法就是reallocate。

這個方法主要有以下幾步:

allocateBuckets方法:向系統申請開辟內存,即開辟bucket,此時的bucket只是一個臨時變量。 setBucketsAndMask方法:將臨時的bucket存入緩存中,此時的存儲分為兩種情況:

1.如果是真機,根據bucket和mask的位置存儲,并將occupied占用設置為0

2.如果不是真機,正常存儲bucket和mask,并將occupied占用設置為0

如果有舊的buckets,需要清理之前的緩存,即調用collect_free方法

可以看到collect_free方法就是創建垃圾回收的空間,然后把需要回收的sel-imp放進去,最后進行回收。

其中_garbage_make_room方法創建垃圾回收空間的邏輯如下圖:

如果是第一次,需要分配回收空間 如果不是第一次,則將內存段加大,即原有內存*2

針對需要存儲的bucket進行內部imp和sel賦值

這個部分就是通過哈希算法來計算sel-imp的哈希下標:

  • 如果哈希下標的位置未存儲sel,即該下標位置獲取sel等于0,此時將sel-imp存儲進去,并將occupied占用大小加1

  • 如果當前哈希下標存儲的sel 等于 即將插入的sel,則直接返回

  • 如果當前哈希下標存儲的sel 不等于 即將插入的sel,則重新經過cache_next方法 即哈希沖突算法,重新進行哈希計算,得到新的下標,再去對比進行存儲

哈希算法和解決哈希沖突的源碼如下:

補充

何為_mask?

_mask是指掩碼數據,用于在哈希算法或者哈希沖突算法中計算哈希下標,其中mask等于capacity - 1

何為_occupied?

這個其實前面已經解釋過了,這里再匯總講一遍:

_occupied表示哈希表中 sel-imp 的占用大小 (即可以理解為分配的內存中已經存儲了sel-imp的的個數)

為什么是在 3/4 時進行擴容

在哈希這種數據結構里面,有一個概念用來表示空位的多少叫做裝載因子——裝載因子越大,說明空閑位置越少,沖突越多,散列表的性能會下降

負載因子是3/4的時候,空間利用率比較高,而且避免了相當多的Hash沖突,提升了空間效率

bucket數據為什么會有丟失的情況?

原因是在擴容時,是將原有的內存全部清除了,再重新申請了內存導致的

方法緩存是否有序?

因為sel-imp的存儲是通過哈希算法計算下標的,其計算的下標有可能已經存儲了sel,所以又需要通過哈希沖突算法重新計算哈希下標,所以導致下標是隨機的,并不是固定的

bucket與mask、capacity、sel、imp的關系

  • 類cls擁有屬性cache_t,cache_t中的buckets有多個bucket——存儲著方法實現imp和方法編號sel強轉成的key值cache_key_t

  • mask對于bucket來說,主要是用來在緩存查找時的哈希算法

  • capacity則可以獲取到cache_t中bucket的數量

capacity與occupied有何區別?

區別在于一個表示總容量,一個表示實際已使用數量

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

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

相關文章

java面試題:List如何排序?內存溢出/OOM怎么回事?如何排查和解決?

List如何排序 List排序可以通過實現Comparable接口并且實現compareTo方法&#xff0c;或者傳入comparator去實現排序。 內存溢出/OOM是怎么回事&#xff1f; 內存溢出就是程序在運行的過程中&#xff0c;申請的內存超過了最大內存限制&#xff0c;導致JVM拋出OOM異常&#x…

Python cryptography【密碼庫】庫功能與使用指南

邊寫代碼零食不停口 盼盼麥香雞味塊 、卡樂比&#xff08;Calbee&#xff09;薯條三兄弟 獨立小包、好時kisses多口味巧克力糖、老金磨方【黑金系列】黑芝麻丸 邊寫代碼邊貼面膜 事業美麗兩不誤 DR. YS 野森博士【AOUFSE/澳芙雪特證】377專研美白淡斑面膜組合 優惠劵 別光顧寫…

第二十四章 流程控制_ if分支

第二十四章 流程控制: if分支和輸入 正如許多編程語言一樣Shell也有自己的條件分支語句。有時需要根據情況進行相應的處理&#xff0c;因此可以通過條件分支語句實現&#xff0c;本章主要介紹的是if分支語句。 if語句 在Shell中if語句語法格式如下&#xff1a; if commands…

電腦網絡重置,找不到原先自家的WIFI,手機還能正常連接并上網

問題排查&#xff1a;1、電腦感覺網絡太慢&#xff0c;因此打算點擊了網絡重置 2、點擊提示會刪除網絡&#xff0c;在五分鐘后關機重啟 3、從設備管理器設備的無線wifi屬性-事件中發現刪除記錄 4、選擇更新驅動程序 5、從列表中選取 6、更改回老驅動版本 備選方案&#…

C語言_預處理詳解

1. 預定義符號 C語言設置了一些預定義符號&#xff0c;可以直接使用&#xff0c;預定義符號也是在預處理期間處理的 1 __FILE__ //進行編譯的源文件 2 __LINE__//文件當前的行號 3 __DATE__ //文件被編譯的日期 4 __TIME__//文件被編譯的時間 5 __STDC__//如果編譯器遵循ANSI…

【QT】使用QT幫助手冊找控件樣式

選擇幫助—》輸入stylesheet(小寫)—》選擇stylesheet—》右側選擇Qt Style Sheets Reference 2.使用CtrlF—》輸入要搜索的控件—》點擊Customizing QScrollBar 3.顯示參考樣式表–》即可放入QT-designer的樣式表中

SQL知識合集(二):函數篇

TRIM函數 作用&#xff1a;去掉字符串前后的空格 SELECT * FROM your_table_name WHERE TRIM(column_name) ; COALESCE函數 作用&#xff1a;返回其參數中的第一個非 NULL 值。它可以接受多個參數&#xff0c;并從左到右依次評估這些參數&#xff0c;直到找到第一個非 NUL…

Cursor 工具項目構建指南: Uniapp Miniprogram 環境下的 Prompt Rules 約束

簡簡單單 Online zuozuo: 簡簡單單 Online zuozuo 簡簡單單 Online zuozuo 簡簡單單 Online zuozuo 簡簡單單 Online zuozuo :本心、輸入輸出、結果 簡簡單單 Online zuozuo : 文章目錄 Cursor 工具項目構建指南: Uniapp Miniprogram 環境下的 Prompt Rules 約束前言項目簡…

Java轉Go日記(六十):gin其他常用知識

1. 日志文件 package mainimport ("io""os""github.com/gin-gonic/gin" )func main() {gin.DisableConsoleColor()// Logging to a file.f, _ : os.Create("gin.log")gin.DefaultWriter io.MultiWriter(f)// 如果需要同時將日志寫入…

cocos單例工廠和自動裝配

cocos單例工廠和自動裝配 1 單例工廠 1.1 分析 實例字典 原理很簡單&#xff0c;只是一個map&#xff0c;確保每個類只保留一個實例&#xff1b; private static _instances new Map<string, any>();獲取與存儲實例 這邊使用的方式是生成一個唯一的id存儲在類上&…

django paramiko 跳轉登錄

在使用Django框架結合Paramiko進行SSH遠程操作時&#xff0c;通常涉及到自動化腳本的執行&#xff0c;比如遠程服務器上的命令執行、文件傳輸等。如果你的需求是“跳轉登錄”&#xff0c;即在登錄遠程服務器后&#xff0c;再通過該服務器的SSH連接跳轉到另一臺服務器&#xff0…

《C++初階之類和對象》【命名空間 + 輸入輸出 + 缺省參數 + 函數重載】

【命名空間 輸入&輸出 缺省參數 函數重載】目錄 前言&#xff1a;---------------hello world---------------比較C語言和C的第一個程序&#xff1a;hello word ---------------命名空間---------------什么是命名空間&#xff1f;怎么使用命名空間&#xff1f;怎么定義…

[USACO1.5] 八皇后 Checker Challenge Java

import java.util.*;public class Main {// 標記 對角線1&#xff0c;對角線2&#xff0c;所在x軸 是否存在棋子static boolean[] d1 new boolean[100], d2 new boolean[100], d new boolean[100]; static int n, ans 0;static int[] arr new int[14]; // 記錄一輪棋子位置…

云服務器Xshell登錄拒絕訪問排查

根據你的描述&#xff0c;使用Xshell 8登錄云服務器時顯示“拒絕訪問”&#xff0c;可能涉及多個原因。以下結合搜索結果整理出排查和解決方法&#xff0c;按優先級排序&#xff1a; 一、檢查基礎網絡與端口連通性 本地網絡與服務器IP是否可達 在本地電腦的CMD中執行 ping 服務…

Python爬蟲實戰:研究urlunparse函數相關技術

1. 引言 1.1 研究背景與意義 在當今信息爆炸的時代,互聯網上的數據量呈現出指數級增長。如何從海量的網頁數據中高效地獲取有價值的信息,成為了學術界和工業界共同關注的問題。網絡爬蟲作為一種自動獲取網頁內容的技術,能夠按照預定的規則遍歷互聯網上的網頁,并提取出所需…

Spring AI學習一

隨著Chatpt的火爆&#xff0c;現在Spring官方也開始支持AI了并推出了Spring AI框架&#xff0c;目前還沒發布正式版本&#xff0c;這里可以先看一下官方依賴的版本。 Spring官網地址可以看這里&#xff1a;Spring | Home 目前官網上是有這兩個版本&#xff1a;1.0.0和1.1.0-SN…

reverse筆記

一&#xff0c;strcat的使用方法&#xff08;在攻防世界中刷題時遇到的&#xff09; 二&#xff0c;殼&#xff08;做題遇到過但是一直不是很理解&#xff0c;今天查了一下&#xff09; 殼是一種軟件保護技術&#xff0c;能夠防止程序被輕易地分析和修改。 總而言之&#xff0…

spring4第7-8課-AOP的5種通知類型+切點定義詳解+執行順序

繼續學習&#xff0c;方便自己復查記錄 ①AOP簡介&#xff1a; 面向切面編程(也叫面向方面編程)&#xff1a;Aspect Oriented Programming(AOP)。 Spring框架中的一個重要內容。。 通過預編譯方式和運行期間動態代理實現在不修改源代碼的情況下給程序動態統一添加功能…

EscapeX:去中心化游戲,開啟極限娛樂新體驗

VEX 平臺推出全新去中心化游戲 EscapeX&#xff08;數字逃脫&#xff09;&#xff0c;創新性地將大逃殺玩法與區塊鏈技術相融合。用戶不僅能暢享緊張刺激的解謎過程&#xff0c;更能在去中心化、公正透明的環境中參與游戲。EscapeX 的上線&#xff0c;為 VEX 生態注入全新活力&…

Multi Agents Collaboration OS:Web DeepSearch System

背景&#xff1a;多智能體協作驅動網絡信息處理的范式革新 隨著大型語言模型&#xff08;LLM&#xff09;能力的突破性進展&#xff0c;人工智能正從“單點賦能”向“系統協同”演進。傳統單一智能體在復雜業務場景中逐漸顯露局限&#xff1a;面對需多維度知識整合、動態任務拆…