gc java root_C#技術漫談之垃圾回收機制(GC)

GC的前世與今生

  雖然本文是以.NET作為目標來講述GC,但是GC的概念并非才誕生不久。早在1958年,由鼎鼎大名的圖林獎得主John McCarthy所實現的Lisp語言就已經提供了GC的功能,這是GC的第一次出現。Lisp的程序員認為內存管理太重要了,所以不能由程序員自己來管理。

  但后來的日子里Lisp卻沒有成氣候,采用內存手動管理的語言占據了上風,以C為代表。出于同樣的理由,不同的人卻又不同的看法,C程序員認為內存管理太重要了,所以不能由系統來管理,并且譏笑Lisp程序慢如烏龜的運行速度。的確,在那個對每一個Byte都要精心計算的年代GC的速度和對系統資源的大量占用使很多人的無法接受。而后,1984年由Dave Ungar開發的Smalltalk語言第一次采用了Generational garbage collection的技術(這個技術在下文中會談到),但是Smalltalk也沒有得到十分廣泛的應用。

  直到20世紀90年代中期GC才以主角的身份登上了歷史的舞臺,這不得不歸功于Java的進步,今日的GC已非吳下阿蒙。Java采用VM(Virtual Machine)機制,由VM來管理程序的運行當然也包括對GC管理。90年代末期.NET出現了,.NET采用了和Java類似的方法由CLR(Common Language Runtime)來管理。這兩大陣營的出現將人們引入了以虛擬平臺為基礎的開發時代,GC也在這個時候越來越得到大眾的關注。

  為什么要使用GC呢?也可以說是為什么要使用內存自動管理?有下面的幾個原因:

  1、提高了軟件開發的抽象度;

  2、程序員可以將精力集中在實際的問題上而不用分心來管理內存的問題;

  3、可以使模塊的接口更加的清晰,減小模塊間的偶合;

  4、大大減少了內存人為管理不當所帶來的Bug;

  5、使內存管理更加高效。

  總的說來就是GC可以使程序員可以從復雜的內存問題中擺脫出來,從而提高了軟件開發的速度、質量和安全性。

什么是GC?

  GC如其名,就是垃圾收集,當然這里僅就內存而言。Garbage Collector(垃圾收集器,在不至于混淆的情況下也成為GC)以應用程序的root為基礎,遍歷應用程序在Heap上動態分配的所有對象[2],通過識別它們是否被引用來確定哪些對象是已經死亡的、哪些仍需要被使用。已經不再被應用程序的root或者別的對象所引用的對象就是已經死亡的對象,即所謂的垃圾,需要被回收。這就是GC工作的原理。為了實現這個原理,GC有多種算法。比較常見的算法有Reference Counting,Mark Sweep,Copy Collection等等。目前主流的虛擬系統.NET CLR,Java VM和Rotor都是采用的Mark Sweep算法。

  一、Mark-Compact 標記壓縮算法

  簡單地把.NET的GC算法看作Mark-Compact算法。階段1: Mark-Sweep 標記清除階段,先假設heap中所有對象都可以回收,然后找出不能回收的對象,給這些對象打上標記,最后heap中沒有打標記的對象都是可以被回收的;階段2: Compact 壓縮階段,對象回收之后heap內存空間變得不連續,在heap中移動這些對象,使他們重新從heap基地址開始連續排列,類似于磁盤空間的碎片整理。

636ec137fe9f3d54fae7e046f6793e41.png

Heap內存經過回收、壓縮之后,可以繼續采用前面的heap內存分配方法,即僅用一個指針記錄heap分配的起始地址就可以。主要處理步驟:將線程掛起→確定roots→創建reachable objects graph→對象回收→heap壓縮→指針修復。可以這樣理解roots:heap中對象的引用關系錯綜復雜(交叉引用、循環引用),形成復雜的graph,roots是CLR在heap之外可以找到的各種入口點。

  GC搜索roots的地方包括全局對象、靜態變量、局部對象、函數調用參數、當前CPU寄存器中的對象指針(還有finalization queue)等。主要可以歸為2種類型:已經初始化了的靜態變量、線程仍在使用的對象(stack+CPU register) 。Reachable objects:指根據對象引用關系,從roots出發可以到達的對象。例如當前執行函數的局部變量對象A是一個root object,他的成員變量引用了對象B,則B是一個reachable object。從roots出發可以創建reachable objects graph,剩余對象即為unreachable,可以被回收 。

5ea91f9f18b3eb83517a7188cecb9019.png

指針修復是因為compact過程移動了heap對象,對象地址發生變化,需要修復所有引用指針,包括stack、CPU register中的指針以及heap中其他對象的引用指針。Debug和release執行模式之間稍有區別,release模式下后續代碼沒有引用的對象是unreachable的,而debug模式下需要等到當前函數執行完畢,這些對象才會成為unreachable,目的是為了調試時跟蹤局部對象的內容。傳給了COM+的托管對象也會成為root,并且具有一個引用計數器以兼容COM+的內存管理機制,引用計數器為0時,這些對象才可能成為被回收對象。Pinned objects指分配之后不能移動位置的對象,例如傳遞給非托管代碼的對象(或者使用了fixed關鍵字),GC在指針修復時無法修改非托管代碼中的引用指針,因此將這些對象移動將發生異常。pinned objects會導致heap出現碎片,但大部分情況來說傳給非托管代碼的對象應當在GC時能夠被回收掉。

二、 Generational 分代算法

  程序可能使用幾百M、幾G的內存,對這樣的內存區域進行GC操作成本很高,分代算法具備一定統計學基礎,對GC的性能改善效果比較明顯。將對象按照生命周期分成新的、老的,根據統計分布規律所反映的結果,可以對新、老區域采用不同的回收策略和算法,加強對新區域的回收處理力度,爭取在較短時間間隔、較小的內存區域內,以較低成本將執行路徑上大量新近拋棄不再使用的局部對象及時回收掉。分代算法的假設前提條件:

  1、大量新創建的對象生命周期都比較短,而較老的對象生命周期會更長;

  2、對部分內存進行回收比基于全部內存的回收操作要快;

  3、新創建的對象之間關聯程度通常較強。heap分配的對象是連續的,關聯度較強有利于提高CPU cache的命中率,.NET將heap分成3個代齡區域: Gen 0、Gen 1、Gen 2;

57e6746a9e3f109847fd486915c22d8b.png

Heap分為3個代齡區域,相應的GC有3種方式: # Gen 0 collections, # Gen 1 collections, #Gen 2 collections。如果Gen 0 heap內存達到閥值,則觸發0代GC,0代GC后Gen 0中幸存的對象進入Gen1。如果Gen 1的內存達到閥值,則進行1代GC,1代GC將Gen 0 heap和Gen 1 heap一起進行回收,幸存的對象進入Gen2。

2代GC將Gen 0 heap、Gen 1 heap和Gen 2 heap一起回收,Gen 0和Gen 1比較小,這兩個代齡加起來總是保持在16M左右;Gen2的大小由應用程序確定,可能達到幾G,因此0代和1代GC的成本非常低,2代GC稱為full GC,通常成本很高。粗略的計算0代和1代GC應當能在幾毫秒到幾十毫秒之間完成,Gen 2 heap比較大時,full GC可能需要花費幾秒時間。大致上來講.NET應用運行期間,2代、1代和0代GC的頻率應當大致為1:10:100。

 三、Finalization Queue和Freachable Queue

  這兩個隊列和.NET對象所提供的Finalize方法有關。這兩個隊列并不用于存儲真正的對象,而是存儲一組指向對象的指針。當程序中使用了new操作符在Managed Heap上分配空間時,GC會對其進行分析,如果該對象含有Finalize方法則在Finalization Queue中添加一個指向該對象的指針。

  在GC被啟動以后,經過Mark階段分辨出哪些是垃圾。再在垃圾中搜索,如果發現垃圾中有被Finalization Queue中的指針所指向的對象,則將這個對象從垃圾中分離出來,并將指向它的指針移動到Freachable Queue中。這個過程被稱為是對象的復生(Resurrection),本來死去的對象就這樣被救活了。為什么要救活它呢?因為這個對象的Finalize方法還沒有被執行,所以不能讓它死去。Freachable Queue平時不做什么事,但是一旦里面被添加了指針之后,它就會去觸發所指對象的Finalize方法執行,之后將這個指針從隊列中剔除,這是對象就可以安靜的死去了。

  .NET Framework的System.GC類提供了控制Finalize的兩個方法,ReRegisterForFinalize和SuppressFinalize。前者是請求系統完成對象的Finalize方法,后者是請求系統不要完成對象的Finalize方法。ReRegisterForFinalize方法其實就是將指向對象的指針重新添加到Finalization Queue中。這就出現了一個很有趣的現象,因為在Finalization Queue中的對象可以復生,如果在對象的Finalize方法中調用ReRegisterForFinalize方法,這樣就形成了一個在堆上永遠不會死去的對象,像鳳凰涅槃一樣每次死的時候都可以復生。

  托管資源:

  .NET中的所有類型都是(直接或間接)從System.Object類型派生的。

  CTS中的類型被分成兩大類——引用類型(reference type,又叫托管類型[managed type]),分配在內存堆上;值類型(value type),分配在堆棧上。如圖:

610c3723134d69c8b9b99d67d4bfe279.png

值類型在棧里,先進后出,值類型變量的生命有先后順序,這個確保了值類型變量在退出作用域以前會釋放資源。比引用類型更簡單和高效。堆棧是從高地址往低地址分配內存。

  引用類型分配在托管堆(Managed Heap)上,聲明一個變量在棧上保存,當使用new創建對象時,會把對象的地址存儲在這個變量里。托管堆相反,從低地址往高地址分配內存,如圖:

0b523d9461e60a135555fea10a9cf874.png

.NET中超過80%的資源都是托管資源。

  非托管資源:

  ApplicationContext, Brush, Component, ComponentDesigner, Container, Context, Cursor, FileStream, Font, Icon, Image, Matrix, Object, OdbcDataReader, OleDBDataReader, Pen, Regex, Socket, StreamWriter, Timer, Tooltip, 文件句柄, GDI資源, 數據庫連接等等資源。可能在使用的時候很多都沒有注意到!

  .NET的GC機制有這樣兩個問題:

  首先,GC并不是能釋放所有的資源。它不能自動釋放非托管資源。

  第二,GC并不是實時性的,這將會造成系統性能上的瓶頸和不確定性。

  GC并不是實時性的,這會造成系統性能上的瓶頸和不確定性。所以有了IDisposable接口,IDisposable接口定義了Dispose方法,這個方法用來供程序員顯式調用以釋放非托管資源。使用using語句可以簡化資源管理。

 示例:

///summary/// 執行SQL語句,返回影響的記錄數summary///param name="SQLString"SQL語句/param///returns影響的記錄數/returnspublicstaticint ExecuteSql(string SQLString)
{
using (SqlConnection connection =new SqlConnection(connectionString))
{
using (SqlCommand cmd =new SqlCommand(SQLString, connection))
{
try
{
connection.Open();
int rows = cmd.ExecuteNonQuery();
return rows;
}
catch (System.Data.SqlClient.SqlException e)
{
connection.Close();
throw e;
}
finally
{
cmd.Dispose();
connection.Close();
}
}
}
}

當你用Dispose方法釋放未托管對象的時候,應該調用GC.SuppressFinalize。如果對象正在終結隊列(finalization queue), GC.SuppressFinalize會阻止GC調用Finalize方法。因為Finalize方法的調用會犧牲部分性能。如果你的Dispose方法已經對委托管資源作了清理,就沒必要讓GC再調用對象的Finalize方法(MSDN)。附上MSDN的代碼,大家可以參考。

publicclass BaseResource : IDisposable
{
// 指向外部非托管資源private IntPtr handle;
// 此類使用的其它托管資源.private Component Components;
// 跟蹤是否調用.Dispose方法,標識位,控制垃圾收集器的行為 privatebool disposed =false;
// 構造函數public BaseResource()
{
// Insert appropriate constructor code here. }
// 實現接口IDisposable. // 不能聲明為虛方法virtual. // 子類不能重寫這個方法. publicvoid Dispose()
{
Dispose(true);
// 離開終結隊列Finalization queue // 設置對象的阻止終結器代碼 // GC.SuppressFinalize(this);
}
// Dispose(bool disposing) 執行分兩種不同的情況. // 如果disposing 等于 true, 方法已經被調用 // 或者間接被用戶代碼調用. 托管和非托管的代碼都能被釋放 // 如果disposing 等于false, 方法已經被終結器 finalizer 從內部調用過, //你就不能在引用其他對象,只有非托管資源可以被釋放。 protectedvirtualvoid Dispose(bool disposing)
{
// 檢查Dispose 是否被調用過.if (!this.disposed)
{
// 如果等于true, 釋放所有托管和非托管資源if (disposing)
{
// 釋放托管資源. Components.Dispose();
}
// 釋放非托管資源,如果disposing為 false, // 只會執行下面的代碼. CloseHandle(handle);
handle = IntPtr.Zero;
// 注意這里是非線程安全的. // 在托管資源釋放以后可以啟動其它線程銷毀對象, // 但是在disposed標記設置為true前 // 如果線程安全是必須的,客戶端必須實現。 }
disposed =true;
}
// 使用interop 調用方法 // 清除非托管資源. [System.Runtime.InteropServices.DllImport("Kernel32")]
privateexternstatic Boolean CloseHandle(IntPtr handle);
// 使用C# 析構函數來實現終結器代碼 // 這個只在Dispose方法沒被調用的前提下,才能調用執行。 // 如果你給基類終結的機會. // 不要給子類提供析構函數.~BaseResource()
{
// 不要重復創建清理的代碼. // 基于可靠性和可維護性考慮,調用Dispose(false) 是最佳的方式 Dispose(false);
}
// 允許你多次調用Dispose方法, // 但是會拋出異常如果對象已經釋放。 // 不論你什么時間處理對象都會核查對象的是否釋放, // check to see if it has been disposed.publicvoid DoSomething()
{
if (this.disposed)
{
thrownew ObjectDisposedException();
}
}
// 不要設置方法為virtual. // 繼承類不允許重寫這個方法 publicvoid Close()
{
// 無參數調用Dispose參數. Dispose();
}
publicstaticvoid Main()
{
// Insert code here to create // and use a BaseResource object. }
}

GC.Collect() 方法

  作用:強制進行垃圾回收。

  GC的方法:

204599a9830f69f4b76755f0072324c9.png

GC注意事項:

  1、只管理內存,非托管資源,如文件句柄,GDI資源,數據庫連接等還需要用戶去管理。

  2、循環引用,網狀結構等的實現會變得簡單。GC的標志-壓縮算法能有效的檢測這些關系,并將不再被引用的網狀結構整體刪除。

  3、GC通過從程序的根對象開始遍歷來檢測一個對象是否可被其他對象訪問,而不是用類似于COM中的引用計數方法。

  4、GC在一個獨立的線程中運行來刪除不再被引用的內存。

  5、GC每次運行時會壓縮托管堆。

  6、你必須對非托管資源的釋放負責。可以通過在類型中定義Finalizer來保證資源得到釋放。

  7、對象的Finalizer被執行的時間是在對象不再被引用后的某個不確定的時間。注意并非和C++中一樣在對象超出聲明周期時立即執行析構函數

  8、Finalizer的使用有性能上的代價。需要Finalization的對象不會立即被清除,而需要先執行Finalizer.Finalizer,不是在GC執行的線程被調用。GC把每一個需要執行Finalizer的對象放到一個隊列中去,然后啟動另一個線程來執行所有這些Finalizer,而GC線程繼續去刪除其他待回收的對象。在下一個GC周期,這些執行完Finalizer的對象的內存才會被回收。

  9、.NET GC使用"代"(generations)的概念來優化性能。代幫助GC更迅速的識別那些最可能成為垃圾的對象。在上次執行完垃圾回收后新創建的對象為第0代對象。經歷了一次GC周期的對象為第1代對象。經歷了兩次或更多的GC周期的對象為第2代對象。代的作用是為了區分局部變量和需要在應用程序生存周期中一直存活的對象。大部分第0代對象是局部變量。成員變量和全局變量很快變成第1代對象并最終成為第2代對象。

  10、GC對不同代的對象執行不同的檢查策略以優化性能。每個GC周期都會檢查第0代對象。大約1/10的GC周期檢查第0代和第1代對象。大約1/100的GC周期檢查所有的對象。重新思考Finalization的代價:需要Finalization的對象可能比不需要Finalization在內存中停留額外9個GC周期。如果此時它還沒有被Finalize,就變成第2代對象,從而在內存中停留更長時間。

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

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

相關文章

中信銀行c語言筆試題庫,中信銀行筆試題型及題目

中信銀行筆試題型及題目考試題型一:專業知識(2個小時)1.專業知識單選,60題,每題1分。2.專業知識多選,20題,每題1分,未選全即少選得0.5分,錯選、漏選得0分。3.英語閱讀,20分&#xff…

北航軟件工程碩士_21考研擇校 | 北京航空航天大學軟件工程怎么樣?

??? 軟件工程考研獨家平臺撰稿 | 康康哥編輯 | 麗麗姐本文由懂計算機、軟件工程的博士師哥原創01學校名氣北京航空航天大學,簡稱“北航”,是一所把“985”、“211”、“雙一流”寫進骨子里的學校。為什么這么說呢?這得從源頭說起&#xff…

kettle同步數據到hive 巨慢_超詳細教程,kettle ETL mysql到hadoop hive數據抽取,值得收藏...

那么如何將mysql數據導入到hive里呢,kettle里有自帶方法,可以通過表輸入和表輸出來實現,不過自帶的方法載入數據極其緩慢,不推薦使用這種方法。1、hive建表注意hive表使用“^”作為分隔符,這個可以更具實際情況來。2、…

linux 查看path文件,linux入門之環境變量與文件查找

環境變量分類當前 Shell 進程私有用戶自定義變量,如上面我們創建的 temp 變量,只在當前 Shell 中有效。Shell 本身內建的變量。從自定義變量導出的環境變量。declare tmp;tmp"shiyanlou i love y";echo $tmp ; **不要忘記$**實驗樓…

python手機編譯器可以干什么_世界上最好的Python編輯器是什么?

在這種情況下,你需要使用一個 IDE(集成開發環境)或專用的代碼編輯器。由于 Python 是最流行的編程語言之一,IDE 的選擇也非常多。那么問題來了:「究竟什么樣的 IDE 最適合 Python?」 很明顯,沒有…

c是過程化語言嗎數據庫,A.數據庫語言B.過程化語言C.宿主語言D.數據庫管理系統...

A.數據庫語言B.過程化語言C.宿主語言D.數據庫管理系統更多相關問題[填空題] 為了安全起見,起動發動機前要檢查()有無行人。---叉車操作和保養手冊第2-77.在起動叉車前()以警告周圍的人。[單選] 制造廠規定叉車走合期一…

python讀取xlsx文件pandas_用Python的pandas框架操作Excel文件中的數據教程

引言 本文的目的,是向您展示如何使用pandas來執行一些常見的Excel任務。有些例子比較瑣碎,但我覺得展示這些簡單的東西與那些你可以在其他地方找到的復雜功能同等重要。作為額外的福利,我將會進行一些模糊字符串匹配,以此來展示一…

delphi 生成 超大量xml_用OpenCV4實現圖像的超分別率

用OpenCV4實現圖像的超分別率本實驗原文鏈接: f"https://arxiv.org/pdf/1807.06779.pdf">https://arxiv.org/pdf/1807.06779.pdf原文摘要單圖像超分辨率(SISR)的主要挑戰是如何恢復微小紋理等高頻細節。然而,大多數最…

dubbo全局異常處理_基于spring aop的dubbo異常統一處理

dubbo統一異常處理,調用方只顯示封裝后的異常。1、返回封裝后的Exception2、返回封裝后的統一返回信息import org.aspectj.lang.annotation.AfterThrowing;import org.aspectj.lang.annotation.Aspect;import org.slf4j.Logger;import org.slf4j.LoggerFactory;impo…

python123平臺作業答案第十一周_馬哥2016全新Linux+Python高端運維班第十次作業

系統的INPUT和OUTPUT默認策略為DROP,請完成以下關于iptables的題目;iptables -A INPUT -d 10.18.11.13 -p tcp --dport 22 -j ACCEPT #允許ssh端口 iptables -A OUTPUT -s 10.18.11.13 -p tcp --sport 22 -j ACCEPT iptables -P OUTPUT DROP #設置OUTPUT…

義教志愿服務系統c語言,[志愿服務]以己為師 東科學子義教傳愛心

為積極響應國家教育計劃,豐富孩子們的暑期生活,進一步落實“春泥”課堂先進義教理念,7月12日,浙江海洋大學東海科學技術學院大學生“海島征程十五載,紅色基因永傳承”赴螞蟻島實踐團23名隊員走進螞蟻島文化禮堂&#x…

主動斷開socket鏈接_TCP連接與斷開詳解(socket通信)

http://blog.csdn.net/Ctrl_qun/article/details/52518479一、TCP數據報結構以及三次握手TCP(Transmission Control Protocol,傳輸控制協議)是一種面向連接的、可靠的、基于字節流的通信協議,數據在傳輸前要建立連接,傳輸完畢后還要斷開連接。…

大整數算術求值 c語言 棧,用C語言實現 多位整數的四則運算,用棧,例如56*(12+20)-102/2...

該樓層疑似違規已被系統折疊 隱藏此樓查看此樓gets(szExpression);// 中綴表達式轉后綴表達式&#xff0c;結果保存在expression中for (int i 0; i < strlen(szExpression); i){if (isspace(szExpression[i])) // 空白字符{if (bFindBegin){expression[num].type 1;expres…

java可視化壓縮_WEB可視化技術發展

EverCraft一直在關注Web可視化技術的發展&#xff0c;本文對國外一篇感覺很不錯的綜述性文章進行翻譯&#xff0c;供這一領域的愛好者相互學習。這篇paper的信息為&#xff1a;“Mwalongo, F., et al., State-of-the-Art Report in Web-based Visualization. COMPUTER GRAPHICS…

thumbdata4刪除后果_安卓手機上巨大的.thumbdata4圖片預讀緩存清理方法

以下是目前找到比較有效的幫 .thumbdata4-文件瘦身的好方法.下面的一大堆廢話的核心就是進手機設置>應用程序>顯示系統程序>媒體存儲器>存儲>清除數據/緩存.重啟后會發現thumbdata還會出現但是沒有數GB那么大了.How do I Reset the AndroidMedia Scan Database?…

android自定義alertdialog不現實輸入法,自定義的dialog中的EditText無法彈出輸入法解決方案...

1.解決無法彈出輸入法&#xff1a;在show()方法調用之前&#xff0c;用dialog.setView(new EditText(context))添加一個空的EditText&#xff0c;由于是自定義的AlertDialog&#xff0c;有我們指定的布局&#xff0c;所以設置這個不會影響我們的功能&#xff0c;這樣就可以彈出…

python爬取微博內容_請問該如何通過python調用新浪微博的API來爬取數據?

1&#xff1a;安裝python(這個不多說啦) 2&#xff1a;下載新浪微博SDK的python包&#xff0c;解壓為weibopy目錄 3&#xff1a;申請AppKey&#xff0c; 流程&#xff1a; 1&#xff1a;通過oAuth認證 按我的理解簡化如下&#xff1a; 用戶在新浪微博給的頁面輸入賬號密碼&…

android 保活方案_Android 后臺保活手段總結 (上篇)

Android 后臺保活手段總結 (上篇)由于眾所周知的限制&#xff0c;在國內無法使用GCM推送服務&#xff0c;想要自己搭建推送服務的話&#xff0c;有兩個繞不開的技術點&#xff0c;一個是TCP長連的保活&#xff0c;另一個就是后臺進程的保活。雖然看起來是老生常談的問題&#x…

linux修改棧指針x86,為什么x86-64 Linux系統調用會修改RCX,這個值意味著什么?

我正在嘗試使用sys_brk syscall在linux中分配一些內存.這是我嘗試過的&#xff1a;BYTES_TO_ALLOCATE equ 0x08section .textglobal _start_start:mov rax, 12mov rdi, BYTES_TO_ALLOCATEsyscallmov rax, 60syscall根據linux調用約定,我希望返回值在rax寄存器中(指向已分配內存…

wordpress phpmyadmin_西部數碼使用指南:虛擬主機WordPress部署SSL注意事項

版權歸西部數碼所有&#xff0c;原文鏈接&#xff1a;https://www.west.cn/faq/list.asp?unid2068注意事項&#xff1a;1.如果您的主題/插件使用絕對地址調用了http請求可能會導致網站打開會亂碼&#xff0c;或不能有綠鎖標識&#xff0c;需要聯系程序提供商將所有http請求修改…