【Xamain 跨平臺機制原理剖析】
來自: http://www.cnblogs.com/micro-chen/p/5173344.html
【看了請推薦,推薦滿100后,將發補丁地址】
Xamarin項目從喊口號到現在,好幾個年頭了,在內地沒有火起來,原因無非有三,1.授權費貴 2.貴 3.原生態Java開發Android的越來越多,人工費用成本降低。
上面說的3條,都跟錢相關,不占技術邊,看起來跟本文的標題嚴重不符。但是,細細看來,如果這個產品的圈子打不開,再牛的技術,也是枉然。因為技術是在不斷推進的,
性能問題,技術問題,實現問題,等等都可以隨著時間的推動去解決,但是,Xamarin公司貌似沒打算把價格降低。越發的穩定。死鬼死鬼.......。所以,我們需要破解,我們需要破解,我們需要破解(重要的事情說3遍),但是破解并非容易,這個平臺軟件,幾乎是我見過的CLI語言最難破解的東東,因為里面釘子太多,剃不干凈。(我曾經嘗試去破解,發現需要耗費太多的時間,而且不穩定)。。但是,嗯,道高一尺魔高一丈。找補丁,到谷歌..........現在可以明確的告訴大家,最新版的Xamarin有了破解補丁。谷歌去吧。
上面說的是題外話,希望這個生態可以在這片土壤繼續壯大。我們開始進入我們的標題,不要說我是標題黨。呵呵。
我們就討論Xamarin的Android開發機制吧,IOS的,本身就是在MAC下面編譯的,是AOT,不是JIT.........AOT機制,本身把代碼轉換成了Native代碼應用。JIT是基于虛擬機語言運行的中間代碼。Java和.Net 都是JIT 。。。。都有類似運行的虛擬機。Java->JVM .....C#->CLR->JIT->Native Code......
首先,這個跨平臺的思路是基于很久以前的的Mono項目,關于Mono項目,自己百度腦補。Mono是對微乳.Net的跨平臺的實現。基于C語言的實現,可以實現CLR在不同的平臺系統下的運行。當然,針對的就是Linux系統。Mono的誕生,讓.net可以在Linux下跑了。
好,上面僅僅是開端,我們再來看下Android。這個項目是Google收購的一家Droid公司的產品。就是用開源的Linux系統,做的二次開發。本質依然是Linux內核。
谷歌收購這個項目后,加大了對這個項目的投入。最終得以讓這個項目在移動設備上運行起來。。(資本+科技=進步)
看Android項目的結構圖:
- Applications 應用程序層(由Java編寫并且在Dalvk虛擬機來運行)
- Framework 應用框架層 (由Java編寫)
- Libraries And Android Runtime 各種庫和Android 運行環境
- Linux Kernel 操作系統層
從最上層的應用層,如 短信 電話 視頻 微信 QQ等應用 到先的支持庫,都是用Java實現的。
再往下就是一些對硬件功能包裝的庫lib層,大多是一些開源的Linux應用庫,跟這個層平級的Android的虛擬機Dalvik。
最終是Linux的內核,系統的內核的作用,就是操作系統跟硬件進行交互。(當然,硬件需要對應的驅動被系統所能識別)
以上是安卓的結構和運行概述。當你打開微信的時候,就是從應用層-》框架層-》虛擬機到內核的一個來回調用交互。
----------------------------------Xamarin是怎么運行的?----------------------------------
我們上面說過,Mono項目,實現.net語言在Linux下運行,Android是Linux項目,那只要Android中有Mono,那么.net 就能在安卓里跑!!!!
于是,從Mono開發過的一些人,過度到了Xamarin公司。Xamarin公司就是基于此。
那么Xamarin打包的安卓應用是怎么跑的? 看下面的圖:
這是啥?
哦,老伯伯的皮影。
額,被老外學會了。。。。。
然后, 無語了...................
沒錯,這個Xamarin其實就是皮影戲(高科技的皮影)!!!!!!!!!!!!!!!!!!!!!
(寫到這里,不僅為國人悲哀,自己家的東西都被當垃圾丟棄,外國佬卻總能從里面找出點什么。。。比如:炸藥 司南 羅盤 等等,我們自己拋棄了自己,我們的眼里腦子里只有錢錢房房.................拿出點理想吧 my guys!!!)
說它是高科技的皮影,并非空穴來風。我們看看官方給出的結構解釋:
原文:
大概意思是:Xamarin安卓項目是基于Mono運行時的,Android原生應用基于安卓運行時,也就是那個Dalvik虛擬機。。。當運行Xamarin創建的安卓應用的時候,
對應的安卓虛擬機上也會有對應包裝過的對象的實例被分配。那個MCW 代表的是對原生Android的Lib的綁定,ACW是基于調用映射的API后返回響應到Mono。
怎么綁定,又怎么反饋呢?看官方怎么說的。。。
下文:
地址:http://developer.xamarin.com/guides/android/under_the_hood/architecture/
英文不好的同學,自己打開網址找翻譯工具吧。
大概意思是:MCW 的綁定機制還有ACW的核心就是 JNI 。
JNI是什么鬼?科普一下:JNI是Java Native Interface的縮寫,它提供了若干的API實現了Java和其他語言的通信(主要是C&C++)。
好,既然提供交互入口JNI,那么我們的代碼在經過monodroid處理后,就可以與Android這個java項目交互了。
那么C#語言是不是被處理轉換成了JNI的應用了?是的。看圖:
看完上圖,你應該知道了,它在編譯的時候,將C#代碼文件,轉化成了基于對Java原生文件的映射。注意:不是直接轉化成了Java代碼!是調用注冊。
生產的java文件中并不提供運行實現,而是提供了在Android運行時的匹配映射!!!!!!!!!!!!!!!
真正的執行代碼依然在classes.dex文件中。但并不是我們的C#編譯的業務代碼哦~~~業務代碼一會兒會說的哦(這個文件是二進制的可被安卓虛擬機執行的文件,跟語言無關)。
Java原生的開發會把業務代碼壓縮進此文件。這個文件也是安卓虛擬機執行對應的應用時候,能找到入口,起始點。但是,Mono項目的業務代碼沒在這個里面,記住,它是皮影戲。這個是皮影。真正的操作者,幕后兇手,在后臺!!!
上法寶:
Xamarin生成的apk包中,也是這樣。
lib目錄提供了運行時需要的lib 庫,如Mono運行時。
注意下面的那倆文件,就是皮影戲的桿兒,映射工具。
真正的執行代碼在????
在 lib 中或者在assemblies中!!
為啥是或者?因為企業版的Xamarin,支持使用NDK將應用打包成Native應用!!!
有的人糾結那個calsses.dex文件,作為入口,說Android的應用不是非線性耦合的模塊應用嗎?是的,但是,這非線性針對的是系統,或者是程序塊內部,而不是系統跟程序應用之間的描述。應用插入到系統上,那個自描述文件 AndroidManifest.xml 提供了程序的入口描述。
而calsses.dex被安卓虛擬機 Dalvik 執行后,會給這個程序分配對應的程序運行域,
(摘自:騰飛(Jesse))關于Dalvk虛擬機與Java運行環境的區別
- Dalvik主要是完成對象生命周期管理,堆棧管理,線程管理,安全和異常管理,以及垃圾回收等等重要功能。
- Dalvik負責進程隔離和線程管理,每一個Android應用在底層都會對應一個獨立的Dalvik虛擬機實例,其代碼在虛擬機的解釋下得以執行。
- 不同于Java虛擬機運行java字節碼,Dalvik虛擬機運行的是其專有的文件格式
- Dex文件格式可以減少整體文件尺寸,提高I/o操作的類查找速度。
- 是為了在運行過程中進一步提高性能,對dex文件的進一步優化。
- 所有的Android應用的線程都對應一個Linux線程,虛擬機因而可以更多的依賴操作系統的線程調度和管理機制
- 有一個特殊的虛擬機進程Zygote,他是虛擬機實例的孵化器。它在系統啟動的時候就會產生,它會完成虛擬機的初始化,庫的加載,預制類庫和初始化的操作。如果系統需要一個新的虛擬機實例,它會迅速復制自身,以最快的數據提供給系統。對于一些只讀的系統庫,所有虛擬機實例都和Zygote共享一塊內存區域。
大家注意第2點和第7點有助于我們理解Xamarin.Android的工作機制
好,到此,你就知道了吧,那個classes.dex 就是給Zygote提供孵化器應用的............Zygote(受精卵。很形象。應用像是受精卵,通過他暖化出來了個小雞應用)
KO!!!!!!!到此,你也就基本知道,這玩意兒是怎么運行的了吧。。。。沒錯,記住,皮影戲!!!!!!!!!!!!!!!
-----------------------------垃圾回收----------------------------------------
關于這個話題,其實沒必要過分討論,因為既然是皮影戲,那么肯定兩邊交互的時候 肯定不如原生,如果你糾結這個,那么請學好Java去吧。
然后就是硬件的問題,從09年到現在,內存啊 CPU什么的再移動終端上換了好幾代了。從單核到雙核到四核到八核。。。。。硬件的提升簡直是要親命的。
花好幾個月優化的項目,可能新一代的的硬件直接藐視了以前的項目性能問題。。。(當然,不能不關注性能。在一些游戲 視頻等對硬件渲染要求較高的時候,就能體現出性能的致命性!!)
好了看到這里,你也沒必要繼續往下看了。下面的是基于Xamarin的內存管理。
因為是皮影戲,兩邊其實是互相持有對方的信息的。耍皮影的根據皮影反饋的信息,進行下一步如何操作,皮影接到命令,就會在屏幕上顯示對應的動作。。。
摘抄:http://www.cnblogs.com/wintersun/archive/2013/02/28/2937270.html
1 . 性能問題。 例如垃圾回收,Mono for Android 聲稱支持垃圾回收,但也有需要注意的一些嚴重限制。"GC不完整視圖的進程,可能無法運行在內存不足時,由于GC不知道內存不足。"因為這通常需要手動,每當創建一個activity運行垃圾回收或銷毀,以釋放未使用的內存。否則,可能導致內存不足的異常。 我自己也不止一次碰到了這個問題,不得不使用替代方法來解決問題。
關于內存管理:
許多Mono for Android被分配對象為包裝Java對象做為它們的代表。 這時會發生什么:每次你分配一個包裝過類型相對應Java類型,就創建兩個對象:
1). Java對象在Java堆中
2). Mono代理對象在Mono堆中
Mono for Android不能確保這兩個對象相互引用后長時間存活。那就是,Mono的垃圾回收引用一個對象,Java端的對象將一直活著,反之亦然。這個代理對象的創建mandroid.exe是工具編譯時完成。 然而,GC是懶惰的,按需運行的集合,而不是簡單地對象超出范圍時候。 那么這意味著跨虛擬機的垃圾至少比一般更多,這是不可避免的。所以,為了臨時使用時分配一個大數字對象,顯示釋放那些對象所需的資源是寶貴的。約定的方法使用using關鍵字來new一個object,使用using子句來隱式釋放目標的new object是有必要的。釋放Mono端的包裝的Java-VM收集的對象,從而來防止太多的臨時對象關聯在一起很長時間。
去官方網站了解更多 關于Mono for Android的垃圾回收 。
其實,Mono已經對垃圾回收機制,一直在優化。
針對iOS的MonoTouch目前支持了分代式垃圾回收器(generational garbage collector)SGen。直到不久之前,Sgen還只是完整版本Mono中的一個實驗性部分。伴隨著垃圾回收器一同到來的,還有一個為iOS準備的 內存分析器(Memory Profiler),它可以從MonoDevelop集成開發環境中訪問到。
分代式垃圾回收器Sgen取代了Mono中傳統的 Boehm垃圾回收器 。雖然Sgen預計會有更好的性能,但其保守式掃描仍然會給它帶來一些阻礙。預計將來Sgen會切換到精確的堆棧標記系統,這樣應當可以大幅度減少產生的內存碎片。
iOS分析器支持兩種模式,默認模式是堆分析模式,在該模式中內存快照可以根據需要或者觸發器進行采集,而該觸發器可以設置成一定數量的垃圾收集器周期或者基于時間的時間間隔。此外,該分析器還包含了一些標準工具,如對比快照和查找內存中的對象位置。
性能分析支持統計抽樣,以及精確性雖佳但速度不足的進入/離開事件記錄。統計抽樣采集應用程序快照,并根據每個方法被采集器抓取的頻度對它們的速度 進行評估。從字面上看,進入/離開事件記錄就是在函數每次開始或完成時記錄一條日志。它雖然提供了時間花費的完整記錄,但會帶來嚴重的性能開銷。這種模式 同樣能夠捕獲與對象分配相關的堆棧跟蹤。
MonoDevelop 2.8.5和MonoTouch 5.1.1都規定使用該工具。而對于個別項目,必須激活調試、分析和SGen垃圾回收器選項。
Sgen 項目一直在推進著。。。
關于怎么個機制,自己看官方文章吧。太長,不翻譯了。
http://www.mono-project.com/docs/advanced/garbage-collector/sgen/
https://developer.xamarin.com/guides/android/advanced_topics/garbage_collection/
院子里倒是有點小資料,了解下就是了。
來自:http://www.cnblogs.com/shanyou/archive/2012/12/09/2810468.html
Mono 3現在是默認 GC是SGen 垃圾回收器,垃圾回收器幾個性能和擴展性方面的改進,以更好地利用多核處理器硬件。SGen 已移植到 Windows 和 MIPS。
mono 最開始使用的是 Boehm-Demers-Wiser Conservative Garbage Collector ,mono 3.0之前的版本作為默認的垃圾收集器也是這個,Boehm垃圾收集器的主要問題在于無法精確讀取寄存器與棧幀。因為無法確定給定值到底是指針還是標量,因此它總是假設給定值是指針,并且將相關聯的對象標記為存活狀態。這么做不僅會錯誤導致大塊內存無法分配,同時還使得壓縮可用空間這項工作變得異常艱難。
后來mono有了自己的 Simple Generational GC , 就是分代式垃圾回收器Sgen,取代了Mono中傳統的 Boehm垃圾回收器 。文檔地址: http://www.mono-project.com/Compacting_GC ,它使用精確的分代式(generational)垃圾收集器,類似于.NET版本的CLR。SGen垃圾收集器使用兩生代而非.NET中的三個,但像.NET一樣對于大對象使用獨立的堆。
- 分為兩代,之前使用 conservative gc..可見其文檔的描述。是一種較為落后的實現,沒有分代,.NET 的CLR是三代的
- 大對象特殊處理,默認大于64KB作為大對象,.NET的大對象是20KB以上,被分配到一個特殊的大對象堆中。該堆中的對象像其它小對象一樣可以被finalized和釋放。
- 小對象采用get_internal_mem/free_internal_mem 進行內存分配處理,大對象使用OS的malloc/free
- major collection 的時候采用 mark/sweep
- 收集進行時是 “stop the world”
- 保守掃描對象
- 老一代指向新一代的情況只有下面兩種,所以都進行了跟蹤:
- 程序執行中,一個字段進行了賦值
- 在復制(代移動)過程中,這個對象指向了一個新一代中的對象
Mono 3.2進一步提升了SGen GC,特別是針對下面的場景——
- 流行對象負載,老一代對象非常歡迎的一些固定新生對象會導致創建很多記憶的集合。為了避免這種情況的發生,流行對象會被作為永恒對象直到下一次主收集。
- 大規模線程棧負載,這種情況下需要合理地掃描大量大堆棧線程。這在以前通常會把大量壓力放到規劃階段,因為它會產生非常大的固定隊列。為了避免這種情況,SGen現在會在固定隊列上使用哈希過濾,這大大降低了它們的平均大小,并且它會固定主要塊而不是個別對象。
Mono 3.0添加了異步支持、改進的SGen垃圾收集器及其他特性
SGen – Concurrency and Evacuation