C++智能指針學習——小談引用計數

来源:https://www.cnblogs.com/paw5zx/p/18120139
-Advertisement-
Play Games

本文結合源碼討論std::shared_ptr和std::weak_ptr的部分底層實現,然後討論引用計數,弱引用計數的創建和增減。 ...


目錄

前言

本文結合源碼討論std::shared_ptr和std::weak_ptr的部分底層實現,然後討論引用計數,弱引用計數的創建和增減。
文章中儘可能的先闡述原理,然後再貼上代碼。如果有不想看代碼的,直接略過代碼即可。
本文涉及的源碼均出自gcc 9.4.0版本

控制塊簡介

控制塊是shared_ptrweak_ptr中的重要組成,主要用於管理資源的引用計數和生命周期。這個機制允許智能指針安全地共用和管理同一個對象,同時自動釋放不再需要的資源。

控制塊包含以下部分:

  • 引用計數
  • 弱引用計數
  • 分配器
  • 刪除器

本文討論的引用計數和弱引用計數的創建、加減、銷毀,與控制塊密切相關。

共用控制塊

首先我們要知道,當創建一個std::shared_ptr指向某個對象時,會生成一個控制塊來存儲該對象的引用計數和其他管理信息。如果基於這個std::shared_ptr再創建一個或多個std::weak_ptr,那麼這些std::weak_ptr將也指向這個控制塊。

示意圖大概長這樣:

引用計數與弱引用計數創建過程

在談引用計數和弱引用計數的創建時,其實就是討論控制塊的創建。

我們知道std::weak_ptr是被設計用來解決std::shared_ptr智能指針可能導致的迴圈引用問題。一個有效的std::weak_ptr對象一般是通過std::shared_ptr構造的或者是通過拷貝(移動)其他std::weak_ptr對象得到的,std::weak_ptr對象的構造不涉及控制塊的創建。

因此在討論引用計數、弱引用計數的創建時,我們是去分析std::shared_ptr的源碼

__shared_ptr

__shared_ptrstd::shared_ptr的核心實現,它位於shared_ptr_base.h中。

__shared_ptr在構造實例時都會構造一個_M_refcount,它的類型為__shared_count<_Lp>

//file: shared_ptr_base.h
template<typename _Tp, _Lock_policy _Lp>
class __shared_ptr : public __shared_ptr_access<_Tp, _Lp>
{
public:
	using element_type = typename remove_extent<_Tp>::type;
	//預設構造
	constexpr __shared_ptr() noexcept
      : _M_ptr(0), _M_refcount()
      { }
	...	
	//有刪除器和分配器的構造
	template<typename _Yp, typename _Deleter, typename _Alloc,
	       typename = _SafeConv<_Yp>>
	__shared_ptr(_Yp* __p, _Deleter __d, _Alloc __a)
	: _M_ptr(__p), _M_refcount(__p, std::move(__d), std::move(__a))
	{
	  static_assert(__is_invocable<_Deleter&, _Yp*&>::value,
	      "deleter expression d(p) is well-formed");
	  _M_enable_shared_from_this_with(__p);
	}
private:
	...
	element_type*        _M_ptr;         // Contained pointer.
    __shared_count<_Lp>  _M_refcount;    // Reference counter.	
};

__shared_count

在創建__shared_count對象時,也會創建一個指向控制塊的指針(_Sp_counted_base類型的指針)。控制塊用來管理引用計數。

代碼中的_Sp_counted_ptr_Sp_counted_deleter就是_Sp_counted_base的派生類。

//file: shared_ptr_base.h
template<_Lock_policy _Lp>
class __shared_count
{
public:
	//預設構造
	__shared_count(_Ptr __p) : _M_pi(0)
	{
	    __try
	    {
	        _M_pi = new _Sp_counted_ptr<_Ptr, _Lp>(__p);
	    }
	    __catch(...)
	    {
	        delete __p;
	        __throw_exception_again;
	    }
	}
	//帶分配器和刪除器的構造
	template<typename _Ptr, typename _Deleter, typename _Alloc,
	       typename = typename __not_alloc_shared_tag<_Deleter>::type>
	__shared_count(_Ptr __p, _Deleter __d, _Alloc __a) : _M_pi(0)
	{
	    typedef _Sp_counted_deleter<_Ptr, _Deleter, _Alloc, _Lp> _Sp_cd_type;
	    __try
	    {
	        typename _Sp_cd_type::__allocator_type __a2(__a);
	        auto __guard = std::__allocate_guarded(__a2);
	        _Sp_cd_type* __mem = __guard.get();
	        ::new (__mem) _Sp_cd_type(__p, std::move(__d), std::move(__a));
	        _M_pi = __mem;
	        __guard = nullptr;
	    }
	    __catch(...)
	    {
	        __d(__p); // Call _Deleter on __p.
	        __throw_exception_again;
	    }
	}
private:
	friend class __weak_count<_Lp>;
	_Sp_counted_base<_Lp>*  _M_pi;
};

_Sp_counted_base

_Sp_counted_base負責管理引用計數和弱引用計數,其中

  • _M_use_countshared_ptr的計數,就是引用計數,表示有多少個shared_ptr對象共用同一個記憶體資源。
  • _M_weak_countweak_ptr的計數,也就是弱引用計數,表示有多少個weak_ptr對象引用同一個資源。

我們可以看到在_Sp_counted_base的初始化列表中,初始化了_M_use_count_M_weak_count為1,完成了引用計數和弱引用計數的創建和初始化。

//file: shared_ptr_base.h
template<_Lock_policy _Lp = __default_lock_policy>
class _Sp_counted_base : public _Mutex_base<_Lp>
{
public:
    _Sp_counted_base() noexcept : _M_use_count(1), _M_weak_count(1) { }
	...	
private:
	_Atomic_word  _M_use_count;     // #shared
	_Atomic_word  _M_weak_count;    // #weak + (#shared != 0)
};

這裡再簡單提一下_Sp_counted_base_Sp_counted_ptr_Sp_counted_deleter的關係與各自的功能。

  • _Sp_counted_base是一個抽象基類,定義並管理了引用計數與弱引用記數。
  • _Sp_counted_ptr繼承自_Sp_counted_base,主要是使用預設的分配策略和刪除策略管理資源對象。
  • _Sp_counted_deleter繼承自_Sp_counted_base,主要是使用用戶提供的分配器和刪除器管理資源對象。

因為_Sp_counted_base是抽象基類無法被實例化,所以使用的是其派生類_Sp_counted_ptr_Sp_counted_deleter對象來管理引用計數、弱引用計數、分配器、刪除器。這個對象就是我們常說的控制塊。

_Sp_counted_base還有一個派生類_Sp_counted_ptr_inplace,適合使用std::make_shared的場景,此處不過多討論)

弱引用計數增加過程

再談共用控制塊

在上面的引用計數與弱引用計數創建過程中,我們提到:

一個有效的std::weak_ptr對象一般是通過std::shared_ptr構造的或者是通過拷貝(移動)其他std::weak_ptr對象得到的

對應的__weak_count__shared_count對象也具有上述關係。

查看源碼,我們可以發現,__weak_count__shared_count都有一個指向控制塊的多態指針。

	_Sp_counted_base<_Lp>*  _M_pi;

__weak_count中並沒有使用new或者類似操作讓_M_pi指向一塊新的記憶體(控制塊)。追根溯源,__weak_count中多態指針指向的控制塊的來源就是__shared_count。代碼中是通過在__weak_count構造函數和重載的賦值運算符中給多態指針_M_pi初始化和賦值實現的。以此實現了weak_ptrshared_ptr共用控制塊的功能。

__weak_count

弱引用計數的增加可以分為下麵幾種情況:

  • 通過std::shared_ptr構造std::weak_ptr
  • 通過std::weak_ptr構造std::weak_ptr
  • 通過std::shared_ptrstd::weak_ptr賦值
  • 通過std::weak_ptrstd::weak_ptr賦值

其實本質是靠調用_M_weak_add_ref()增加的弱引用計數,詳情見__weak_count的源碼:

//file: shared_ptr_base.h
template<_Lock_policy _Lp>
class __weak_count
{
public:
	...
	//通過__shared_count構造
    //和一個已存在的__shared_count對象共用控制塊,並更新控制塊的弱引用計數
    __weak_count(const __shared_count<_Lp>& __r) noexcept
     : _M_pi(__r._M_pi)
    {
    	//若入參的多態指針不為空
        //弱引用計數++(增加_Sp_counted_base對象的_M_weak_count)
		if (_M_pi != nullptr)
			_M_pi->_M_weak_add_ref();
	}
	
	//通過__weak_count拷貝構造
    //和傳入的__weak_count對象就共用同一個控制塊,並更新控制塊的弱引用計數
    __weak_count(const __weak_count& __r) noexcept
     : _M_pi(__r._M_pi)
    {
		if (_M_pi != nullptr)
			_M_pi->_M_weak_add_ref();
    }
    
	//通過__shared_count給__weak_count賦值
	__weak_count& operator=(const __shared_count<_Lp>& __r) noexcept
    {
    	_Sp_counted_base<_Lp>* __tmp = __r._M_pi;
    	//新對象弱引用計數++
		if (__tmp != nullptr)
	  		__tmp->_M_weak_add_ref();
	  	//原對象弱引用計數--
		if (_M_pi != nullptr)
	  		_M_pi->_M_weak_release();
	  	//指向新對象的控制塊
		_M_pi = __tmp;
		return *this;
	}

	//通過__weak_count給__weak_count賦值
	__weak_count& operator=(const __weak_count& __r) noexcept
    {
		_Sp_counted_base<_Lp>* __tmp = __r._M_pi;
		if (__tmp != nullptr)
	  		__tmp->_M_weak_add_ref();
		if (_M_pi != nullptr)
	  		_M_pi->_M_weak_release();
		_M_pi = __tmp;
		return *this;
    }
    ...
private:
	friend class __shared_count<_Lp>;
	_Sp_counted_base<_Lp>*  _M_pi;
};

引用計數增加過程

引用計數的增加可以分為下麵幾種情況:

  • 通過std::shared_ptr構造std::shared_ptr
  • 通過std::shared_ptrstd::shared_ptr賦值
  • std::weak_ptr升級為std::shared_ptr

本質是靠調用_M_add_ref_copy()_M_add_ref_lock增加的引用計數,詳情見__shared_count的源碼:

//file: shared_ptr_base.h
template<_Lock_policy _Lp>
class __shared_count
{
public:
	//拷貝構造
    __shared_count(const __shared_count& __r) noexcept
     : _M_pi(__r._M_pi)
    {
		if (_M_pi != 0)
			_M_pi->_M_add_ref_copy();
    }

	//拷貝賦值
	__shared_count& operator=(const __shared_count& __r) noexcept
    {
		_Sp_counted_base<_Lp>* __tmp = __r._M_pi;
		if (__tmp != _M_pi)
	  	{
	    	if (__tmp != 0)
	      		__tmp->_M_add_ref_copy();
	    	if (_M_pi != 0)
	      		_M_pi->_M_release();
	    	_M_pi = __tmp;
	    }
		return *this;
	}
	
	//轉換構造
	//weak_ptr使用lock()時會調用此構造函數
	explicit __shared_count(const __weak_count<_Lp>& __r) 
	 : _M_pi(__r._M_pi)
    {
    	if (_M_pi != nullptr)
			_M_pi->_M_add_ref_lock();//引用計數++,具體實現依賴於鎖策略
      	else
			__throw_bad_weak_ptr();
    }
private:
	friend class __weak_count<_Lp>;
	_Sp_counted_base<_Lp>*  _M_pi;
};

弱引用計數的減少過程

弱引用計數的減少可以分為下麵幾種情況:

  • std::weak_ptr析構
  • std::weak_ptr對象被覆蓋(賦值操作覆蓋原std::weak_ptr

本質是靠調用_M_weak_release()減少弱引用計數:

//file: shared_ptr_base.h
template<_Lock_policy _Lp>
class __weak_count
{
public:
	//析構
	~__weak_count() noexcept
    {
      if (_M_pi != nullptr)
        _M_pi->_M_weak_release();
    }
	//轉換賦值
	__weak_count& operator=(const __shared_count<_Lp>& __r) noexcept
	{
    	_Sp_counted_base<_Lp>* __tmp = __r._M_pi;
        if (__tmp != nullptr)
          	__tmp->_M_weak_add_ref();
        if (_M_pi != nullptr)
        	_M_pi->_M_weak_release();
        _M_pi = __tmp;
	    return *this;
	}
	//拷貝賦值
	__weak_count& operator=(const __weak_count& __r) noexcept
	{
      	_Sp_counted_base<_Lp>* __tmp = __r._M_pi;
        if (__tmp != nullptr)
          	__tmp->_M_weak_add_ref();
        if (_M_pi != nullptr)
          	_M_pi->_M_weak_release();
        _M_pi = __tmp;
    	return *this;
    }
    //移動賦值
    __weak_count& operator=(__weak_count&& __r) noexcept
    {
    	if (_M_pi != nullptr)
        	_M_pi->_M_weak_release();
      	_M_pi = __r._M_pi;
            __r._M_pi = nullptr;
      	return *this;
    }

private:
	friend class __shared_count<_Lp>;
	_Sp_counted_base<_Lp>*  _M_pi;
};

然後在這裡對std::weak_ptr::reset()說明一下:它是用來重置 std::weak_ptr 的。調用 reset() 會使std::weak_ptr不再指向它原本觀察的對象。

它也會減少原對象的弱引用計數(本質是通過調用的析構函數使得弱引用計數減少)

//file: shared_ptr_base.h
void reset() noexcept
{ 
	__weak_ptr().swap(*this); 
}

弱引用計數減為0

在上面提到:弱引用計數的減少是通過調用_M_weak_release()實現的。通過分析_M_weak_release()的代碼我們可以知道,_M_weak_release()中主要做了:

  • 對弱引用計數做減1操作並
  • 判斷弱引用計數減1後是否為0,若為0則調用_M_destroy()刪除控制塊。
//file: shared_ptr_base.h
template<_Lock_policy _Lp = __default_lock_policy>
class _Sp_counted_base : public _Mutex_base<_Lp>
{
    //控制塊的弱引用計數為0時,銷毀自身
    virtual void _M_destroy() noexcept
    { delete this; }
    
    //弱引用計數--
    //當弱引用計數變為0,銷毀控制塊
	void _M_weak_release() noexcept
    {
    	// Be race-detector-friendly. For more info see bits/c++config.
        _GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_M_weak_count);
        //減少弱引用計數,並返回-1之前的值
	    if (__gnu_cxx::__exchange_and_add_dispatch(&_M_weak_count, -1) == 1)
	    {
        	_GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_M_weak_count);
	        if (_Mutex_base<_Lp>::_S_need_barriers)
	        {
	        // See _M_release(),
	        // destroy() must observe results of dispose()
		    	__atomic_thread_fence (__ATOMIC_ACQ_REL);
	        }
	        _M_destroy();
	    }
    }
};

引用計數的減少過程

引用計數的減少可以分為下麵幾種情況:

  • std::shared_ptr析構
  • std::shared_ptr對象被覆蓋(賦值操作覆蓋原std::shared_ptr

本質是靠調用_M_release()減少弱引用計數

//file: shared_ptr_base.h
template<_Lock_policy _Lp>
class __shared_count
{
public:
	//析構
	~__shared_count() noexcept
	{
        if (_M_pi != nullptr)
        	_M_pi->_M_release();
    }
    //拷貝賦值
    __shared_count& operator=(const __shared_count& __r) noexcept
    {
		_Sp_counted_base<_Lp>* __tmp = __r._M_pi;
        if (__tmp != _M_pi)
        {
          	if (__tmp != 0)
            	__tmp->_M_add_ref_copy();
          	if (_M_pi != 0)
            	_M_pi->_M_release();
          	_M_pi = __tmp;
        }
	    return *this;
	}
private:
	friend class __weak_count<_Lp>;
	_Sp_counted_base<_Lp>*  _M_pi;
};

引用計數減為0

上面提到:引用計數的減少是通過調用_M_release()實現的。通過分析_M_release()的代碼我們可以知道,_M_release()中主要做了

  • 對引用計數做減1操作並
  • 判斷引用計數減1後是否為0,若為0則調用_M_dispose()釋放其所管理的記憶體資源
  • 若引用計數減1後為0,則還會對弱引用計數做一次減1操作並
  • 判斷弱引用計數減1後是否為0,若為0則調用_M_destroy()刪除控制塊。
//file: shared_ptr_base.h
template<_Lock_policy _Lp = __default_lock_policy>
class _Sp_counted_base : public _Mutex_base<_Lp>
{
	//當前對象的引用計數為0時,釋放管理的資源
    //純虛函數,取決於釋放策略,由派生類實現
    virtual void _M_dispose() noexcept = 0;

    //當前對象的弱引用計數為0時,銷毀自身
    virtual void _M_destroy() noexcept
    { delete this; }
	
	void _M_release() noexcept
    {
    	// Be race-detector-friendly.  For more info see bits/c++config.
        _GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_M_use_count);
        //減少引用計數,並返回-1之前的值
        //如果引用計數為0,則釋放管理的資源
	    if (__gnu_cxx::__exchange_and_add_dispatch(&_M_use_count, -1) == 1)
	    {
        	_GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_M_use_count);
	        _M_dispose();
          	// There must be a memory barrier between dispose() and destroy()
          	// to ensure that the effects of dispose() are observed in the
          	// thread that runs destroy().
          	// See http://gcc.gnu.org/ml/libstdc++/2005-11/msg00136.html
          	if (_Mutex_base<_Lp>::_S_need_barriers)
          	{
            	__atomic_thread_fence (__ATOMIC_ACQ_REL);
          	}

          	// Be race-detector-friendly.  For more info see bits/c++config.
          	_GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_M_weak_count);
          	//減少弱引用計數,並返回-1之前的值
          	//如果弱引用計數為0,則銷毀控制塊自身
	        if (__gnu_cxx::__exchange_and_add_dispatch(&_M_weak_count, -1) == 1)
          	{
            	_GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_M_weak_count);
	          	_M_destroy();
          	}
        }
	}
};

這裡再說明一下為什麼__shared_count要在引用計數減為0時還要對弱引用計數做減1操作:
__shared_count構造的同時,也會構造一個控制塊對象,其中引用計數和弱引用計數一同被初始化為1。這意味著,即使最後一個std::weak_ptr被銷毀了,但若其對應的std::shared_ptr還至少存在一個,那麼弱引用計數就不會被減少至0(代碼中的註釋也是這麼提示的)。

//file: shared_ptr_base.h
template<_Lock_policy _Lp = __default_lock_policy>
class _Sp_counted_base : public _Mutex_base<_Lp>
{
	_Atomic_word  _M_use_count;     // #shared
	_Atomic_word  _M_weak_count;    // #weak + (#shared != 0)
};

std::shared_ptr對象存在的情況下,所有相關std::weak_ptr對象被銷毀後,控制塊仍存在,且其中的弱引用計數為1,此時在銷毀最後一個std::shared_ptr對象時,除了要減少引用計數為0,釋放管理的記憶體資源,還要把最後一個弱引用計數減少為0,銷毀控制塊。

std::weak_ptr對象存在的情況下,所有相關std::shared_ptr對象都被銷毀後,①std::shared_ptr管理的記憶體資源會被釋放(因為引用計數為0,_M_dispose()被調用)②弱引用計數不為0,控制塊仍然存在(直到最後一個std::weak_ptr對象被銷毀,控制塊才會被銷毀)

參考文章

1.C++2.0 shared_ptr和weak_ptr深入刨析
2.智能指針std::weak_ptr


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 拓展閱讀 常見免費開源繪圖工具 OmniGraffle 創建精確、美觀圖形的工具 UML-架構圖入門介紹 starUML UML 繪製工具 starUML 入門介紹 PlantUML 是繪製 uml 的一個開源項目 UML 等常見圖繪製工具 繪圖工具 draw.io / diagrams.net 免 ...
  • 1. 工程搭建 前端的工程運行流程: 進入項目目錄執行cmd命令: 若是第一次啟動需要依次輸入如下命令: npm install npm run build npm run dev 之後直接執行 npm run dev 即可! 1.1 新建maven工程 新建maven工程blog作為父工程,然後在 ...
  • 新網站對接到KC的部署 kc的環境 向kc申請自己的客戶端 kc的登錄介面 通過code換token介面 刷新token介面 kc的用戶信息介面 kc的jwt token說明 1. kc的環境 測試環境:https://test-kc.xxx.com 預發佈環境:https://pre-kc.xxx ...
  • PDF 文件是共用和分發文檔的常用選擇,但提取和再利用 PDF 文件中的內容可能會非常麻煩。而利用 Python 將 PDF 文件轉換為 HTML 是解決此問題的理想方案之一,這樣做可以增強文檔可訪問性,使文檔可搜索,同時增強文檔在不同場景中的實用性。此外,HTML 格式使得搜索引擎能夠對內容進行索 ...
  • 本文介紹基於Python語言,讀取Excel表格文件數據,並基於其中某一列數據的值,將這一數據處於指定範圍的那一行加以複製,並將所得結果保存為新的Excel表格文件的方法~ ...
  • 本文提供了一份全面的Kubernetes(K8S)命令指南,旨在幫助用戶掌握和運用K8S的各種命令。 關註【TechLeadCloud】,分享互聯網架構、雲服務技術的全維度知識。作者擁有10+年互聯網服務架構、AI產品研發經驗、團隊管理經驗,同濟本復旦碩,復旦機器人智能實驗室成員,阿裡雲認證的資深架 ...
  • `synchronized`作為Java併發編程的基礎構建塊,其簡潔易用的語法形式背後蘊含著複雜的底層實現原理和技術細節。深入理解`synchronized`的運行機制,不僅有助於我們更好地利用這一特性編寫出高效且安全的併發程式。 ...
  • 用C語言並利用遞歸思想實現設計一個程式,完成斐波那契數列的函數設計,利用遞歸實現! /******************************************************************* * * file name: * author : RISE_AND_GRIN ...
一周排行
    -Advertisement-
    Play Games
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...
  • 目錄前言PostgreSql安裝測試額外Nuget安裝Person.cs模擬運行Navicate連postgresql解決方案Garnet為什麼要選擇Garnet而不是RedisRedis不再開源Windows版的Redis是由微軟維護的Windows Redis版本老舊,後續可能不再更新Garne ...
  • C#TMS系統代碼-聯表報表學習 領導被裁了之後很快就有人上任了,幾乎是無縫銜接,很難讓我不想到這早就決定好了。我的職責沒有任何變化。感受下來這個系統封裝程度很高,我只要會調用方法就行。這個系統交付之後不會有太多問題,更多應該是做小需求,有大的開發任務應該也是第二期的事,嗯?怎麼感覺我變成運維了?而 ...
  • 我在隨筆《EAV模型(實體-屬性-值)的設計和低代碼的處理方案(1)》中介紹了一些基本的EAV模型設計知識和基於Winform場景下低代碼(或者說無代碼)的一些實現思路,在本篇隨筆中,我們來分析一下這種針對通用業務,且只需定義就能構建業務模塊存儲和界面的解決方案,其中的數據查詢處理的操作。 ...
  • 對某個遠程伺服器啟用和設置NTP服務(Windows系統) 打開註冊表 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer 將 Enabled 的值設置為 1,這將啟用NTP伺服器功 ...
  • title: Django信號與擴展:深入理解與實踐 date: 2024/5/15 22:40:52 updated: 2024/5/15 22:40:52 categories: 後端開發 tags: Django 信號 松耦合 觀察者 擴展 安全 性能 第一部分:Django信號基礎 Djan ...
  • 使用xadmin2遇到的問題&解決 環境配置: 使用的模塊版本: 關聯的包 Django 3.2.15 mysqlclient 2.2.4 xadmin 2.0.1 django-crispy-forms >= 1.6.0 django-import-export >= 0.5.1 django-r ...
  • 今天我打算整點兒不一樣的內容,通過之前學習的TransformerMap和LazyMap鏈,想搞點不一樣的,所以我關註了另外一條鏈DefaultedMap鏈,主要調用鏈為: 調用鏈詳細描述: ObjectInputStream.readObject() DefaultedMap.readObject ...
  • 後端應用級開發者該如何擁抱 AI GC?就是在這樣的一個大的浪潮下,我們的傳統的應用級開發者。我們該如何選擇職業或者是如何去快速轉型,跟上這樣的一個行業的一個浪潮? 0 AI金字塔模型 越往上它的整個難度就是職業機會也好,或者說是整個的這個運作也好,它的難度會越大,然後越往下機會就會越多,所以這是一 ...
  • @Autowired是Spring框架提供的註解,@Resource是Java EE 5規範提供的註解。 @Autowired預設按照類型自動裝配,而@Resource預設按照名稱自動裝配。 @Autowired支持@Qualifier註解來指定裝配哪一個具有相同類型的bean,而@Resourc... ...