Dynamic Code Evolution for Java dcevm 原理

來源:互聯網
上載者:User

在hostswap dcevm中我們對Dynamic Code Evolution VM有了一個簡單的瞭解,這篇文章將介紹Dynamic Code Evolution VM的實現原理。

有兩個概念需要區別下:

  • Dynamic Code Evolution (下文簡稱DCE):泛指java在運行時修改程式的技術.例如aop等.
  • Dynamic Code Evolution VM(下文簡稱DCE VM) 是在java hotspot的一個patch,修改後的vm,支援在運行期不受限制的代碼修改(DCE),可以認為DCE VM是實現DCE的一種方式.

英文水平有限,希望不要給讀者造成勿擾(有些地方感覺說的好蹩腳),如果英文ok,還是盡量直接閱讀原文.

概述:

       DCE是一種java在運行時修改程式(class)的技術(有點兒動態語言的感覺)。在java(物件導向語言)中,可以通過使用新版本class替換一組class的方式來實現。DCE VM通過修改JAVA HOSTSPOT虛擬機器,允許對已載入的class進行任意的修改:增加或刪除變數、方法,介面的繼承關係等等, 這有效提高了開發效率。在evaluation section中顯示了對虛擬機器的修改沒有效能上的影響,fast in-place instant update演算法保證了在效能上與full gc相當。DCE VM可以在標準的開發環境中使用,不需要額外的工具或依賴。

1.Introduction

DCE VM通過修改jvm,在執行程式與硬體(應該確切的說是code編譯後的機械碼)中增加了一層,來實現代碼在運行期動態修改(DCE)。DCE在現有的產品級的vm中是受限的,但在某些領域是非常有用的。我們討論下DCE的4種主要應用:

  • Debugging

    開發人員頻繁的去修改程式,在修改編譯後,開發人員不需要重新啟動程式(每次重啟都需要不少時間),繼續運行即可.
    任何時間、任意的修改不需要任何其他工作

  • Server applications

    關鍵性服務可以通過DCE進行不停機升級.這依賴於升級的安全性與正確性。我們相信這可以做到,需要在設計應用時考慮到代碼的變化,並對某些預定義的點限制更新。代碼升級前或升級後服務速度不會降低。

  • dynamic languages

    在動態語言中,DCE是最普通的特性,但為了在靜態類型虛擬機器上運行動態語言,需要做多方面努力[see for example 33]。把DCE作為VM的一個機制會簡化動態語言的實現。The requirement here is that small incremental changes, e.g., adding a field or method, can be carried out fast.

  • dynamic aop

    DCE也是與面向切面編程(AOP)相關的一個特性。已經有一些動態aop的工具(eg:gluonj)運行在java hotspot vm上。這些工具能使修改後的代碼立即生效。(關於此部分內容,在後續的classworking文章中將專門介紹。)

        DCE VM專註於提高開發人員的效率.如同開發人員可以在任意位置設定斷點一樣,DCE VM能夠在程式的任何位置完成修改,同時,它能夠要求所有的線程在next safe point停止,使得java程式進入暫停狀態.這些點(points),常被用來在GC執行前暫停所有的線程。換句話來講,DCE VM使用類似GC暫停方式來暫停應用,並替換新的class。Java VM保證了程式在運行期間,在有限的時間範圍內,所有的線程到達next safepoint。當VM處於暫停狀態,代碼的替換被執行。

        增強Java HostSpot VM當前對code evolution的支援(只允許方法體修改)的需求是很強烈的,這個需求是JVM 5大改進需求之一。另外,代碼的快速修改帶來的開發效率的提升是動態語言相比靜態類型語言(比如JAVA)的優勢之一。開啟JAVA虛擬機器的DCE功能會給java程式設計語言帶來這個優勢。

        The main contributions of this paper are:

  • DCE VM是在產品級的VM上的修改,以支援DCE.
  • DCE VM致力於代碼的任意修改,包括子類別關係的修改。然而,它不會產生任何間接的問題,也不會有效能損耗。
  • DCE VM允許不同版本的代碼共存。所以代碼的更新可以發生在任何時候。
  • DCE VM可以在任何符合標準java debug Wire Protocol(JDWP)的IDE下使用。例如NetBeans或Eclipse

2.Levels of Code Evolution

        DCE是分多個層次的.從實現複雜度的層面和Java語義的影響上,本文試圖將DCE分成4個層級,:

 

Swapping Method Bodies:替換java方法體的位元組碼可能是最簡單的修改.方法的實現不依賴其他的位元組碼或類型資訊等資料,因此方法體修改可以獨立完成.Java HostSpot VM支援這種類型的修改.

Adding or Removing Methods:當修改class的方法集時,虛擬方法表(用來做動態指派)需要同步修改。同時,class的修改可能會導致子類的虛擬方法表也要同步修改(詳見section3.2)。虛擬方法表中方法的索引可能會改變並導致機器碼(包含固定編碼)不可用(詳見Section3.6)。機器碼也可能包含指向已有方法的靜態連結,這些方法可能已不可用。

Adding or Removing Fields:直到這一層級,之前的修改只是影響到VM的元資訊.現在對象執行個體需要根據類或父類的修改做相應處理.VM需要把對象從一箇舊的版本轉換成一個新的版本(該版本具有不同的Fields和不同的size),我們使用修改後的mark-and-compact gc來改變object layout(詳見Section 3.5).與虛擬方法表類似,field offsets在解析器(interpreter)和編譯後的機器碼中有多處用到.這些地方需要被正確的調整或使其不可用.

Adding or Removing Supertypes:在java中修改類的父類集合是DCE中最複雜的情況.Supertypes的修改意味著修改類的方法和屬性。同時,類的元資訊也需要被修改,來反映出新的supertype的關係.

       方法簽名或者field類型、名稱的修改,VM會用兩個操作來實現:添加一個成員和刪除另一個成員。介面(initerfaces)的修改可以被當做是類(classes)的修改.添加或刪除一個介面方法會影響到子介面(subinterfaces)及類(實現了介面的類)的介面表(interface tables),但不會影響執行個體。superinterfaces的修改是類似的.

       修改Java class的另外一種情況是靜態fields或靜態方法集合的修改。這種修改不會影響子類或執行個體,但可能會讓當前的代碼不可用,比如類中存在static filed offsets。同時,code evolution algorighm需要決定如何初始化靜態fields:要麼運行the new class的static initializer(類的初始化方法),要麼從舊版本的lcass中複製靜態fields到新新版的class中.

      

      Java程式的修改還可以根據程式在不同的版本是否二進位相容來分類:相容(圖Figure1中淺灰色)和不相容(圖Figure1中深灰色).對於二進位相容的修改,old code的有效性是不受影響的.我們在新的class中把被刪除或替換方法的位元組碼定義成不同的方法,稱為old code。當更新發生在任意point(想對與vm執行來說)時,java線程有可能還在方法中執行,這時old code能夠在代碼修改後繼續執行。

      二進位不相容的修改may break old code。在old code中的正確語義,在new class中會不可用.這在java 語言說明和JAVA 虛擬機器說明都是不明確的.考慮到這種情況,我們講二進位不相容修改進行如下分類:

Removing Fields or Methods:已刪除或替換的方法的二進位代碼可能仍然被某個正在執行的程式引用,但在新的class中已經不存在這些方法了.在old code的執行中(既然有引用,就有可能被執行),VM可能會訪問到這些old code,並需要決定當調用到被刪除的方法或訪問到被刪除的fields時如何處理.

Removing Suppertypes:當去除類的父類時(when narrwing the type of a class),違反了啟動並執行java程式一個重要的不變規則:靜態或非靜態變數不在保持子類別關係。同時,調用者與被調用者已不在相容.

       Section4中會介紹下我們是如何處理二進位不相容的情況。

3.Implementation 

       DCE VM的實現(implementation)是對Java HostSpot VM的修改。JavaSpot VM是一個高效能的VM內建一個解析器和兩個即時的編譯器(client compiler and server compiler)。DCE VM基於已有的允許修改方法體的規則,擴充成支援對已載入類型任意的修改。我們把焦點集中於在現有VM上實現代碼升級(code evolution),對VM做盡量少但必須的修改,包括記憶體回收行程 ,system dictionary 和類的元數。特別要說的是,我們不會對解析器和Just-In-Time 編譯器做任何修改,並且不會對VM造成影響。

       圖Figure2是對VM修改的一個總攬。Code evolution由Java Debug Wire Protocol(JDWP)命令觸發.首先,演算法收集所有影響的class,並根據子類別關係進行排序,然後new classes會被載入並添加到VM中,並與old classes進行區。修改後的full garbage collection做版本替換,也就是code evolution.當對code狀態處理完成後,VM繼續執行程式。

3.1 Class Redefinition Command

       我們使用JDWP 類重定義命令來觸發DCE.JDWP是VM與java調試器之間的規範介面.因此修改後的vm立即可以通過JDWP協議進行調試.也就是說我們可以使用常用的使用JDWP協議的調試工具例如NetBeans或eclipse的調試功能進行代碼調試,並觸發類的重定義.

       類重定義命令需要的所有重定義類已經被VM載入,如果一個類沒有被載入過,也就不需要進行類的重定義,新版本的class可以直接當成初始版本載入.每個類都有一個數字來進行唯一標識,並且有一個類位元組碼數組.修改後的VM對類重定義指令的實現完全基於JDWP規範,不需要額外的資訊來進行code evolution.

       類重定義的前幾步驟(接下來要介紹的3步)是可以與程式運行並存執行的.只有接下來的GC,在更新執行個體時才需要暫停所有java線程.我們使用與gc相同的安全點(safepoint)規則來暫停所有活動的線程.

       可以理解為redefination是個類似gc的過程,只是redifination只對對象的參考關聯性進行處理.

3.2 Finding Affected Types

       當對class的修改不只是方法體時,會間接的影響到與其有關係的類,比如子類。類中添加一個field,會隱士的在所有的子類中添加。添加一個方法,可能會改變子類的虛擬方法表。

       因此,演算法需要對redifined class的集合擴充,將受影響的子類也放入其中。在Figure3中給出了一個列子。

       ClassA 和C 被重定義了,B是A的子類,所以Class B也需要加入到重定義類的集合中,並修改為B’,B’與B類是相同的,但可能因為A的修改而具有不同的屬性。因此B的metadata(包含虛擬方法表)需要重新初始化,即重新載入B。

       同樣的規則也適用於介面的重定義。

3.3 Ordering the Types

       類重定義命令並不對需要重定義的類進行排序。對使用者來講,類的修改應該是原子的。我們的演算法是對類及子類的關係進行拓撲排序。類或介面應該在子類重定義之前被重定義。新版本的class可以與舊版本class的父類是不相容的。因此,父類先被替換成新的版本子類才能正常的載入。

       為了確保class的繼承關係,我們的排序應該是作用在改進後代碼的關係而不是當前代碼的關係。類被VM載入後其關係資訊才能擷取,所以我們需要在class載入前就來分析類檔案,從中擷取新的類別關係。在例子Figure3中,需要先重定義C為C’,然後重定義A到A’,因為在新的類中,A是C的子類.

3.4 Building a Side Universe

       在系統中會共存新老版本的class,這對正在執行的代碼(基於old calss)來說是必要的.不同版本的類執行個體對象在記憶體中也可能是共存的。這也是解決code evolution時循環相依性的惟一解決辦法。例如:B在重定義前依賴A,但同時A在重定義前又依賴B。當增加一個新的類時,我們為新的類構造了一個單獨的空間(side universe),使得類空間(type universe)始終保持一致性狀態.因此老版本的class不會影響其他新版本class的載入和驗證.

       Java HotSpot VM 維護了一個class的系統詞典,該詞典根據class名稱和classloader進行查詢.載入一個新的class後,我們會立即用new class替換old class.提前排好序的重定義classes保證了side universe能夠正常的建立.在執行個體Figure3中,當我們載入class A時,對Class C的檢索將返回Class C’,因為Class C在class A之前已經被重定義過.如果靜態類Field名及簽名匹配的情況下,VM將複製舊的Class的Static fields值倒新的Class的static fields上(並不是通過初始化類的方式).Figure4 顯示了建立了size universe後類空間(universe)的狀態.

       我們在記憶體中保持了相同class的不同版本,不同版本的class通過雙向鏈表互聯.這有助於在gc時在不同版本跳轉(This helps navigating through the versions during garbage collection).系統詞典中維護指向最新版本Class的引用.

3.5 Garbage Collector Adjestments

類重定義演算法的核心部分是mark-and-compact gc演算法修改:

  1. Forward Pointers:該演算法計算出每個存活對象的forward pointer,forward pointer指向了記憶體壓縮後對象的記憶體位址.
  2. Adjust Pointers:接下來遍曆記憶體,所有的引用指標(指向old class)將被修改到新的地址(new class).
  3. Comaction phase:在最後的壓縮階段,對象將被移動到新的地址.

在Pointer adjustment 階段,指向old class的引用被修改到new class的forward pointer的地址,執行個體引用的修改類似於compact 階段.因此,對gc進行修改後變實現了code evolution,不僅重用了代碼,並且在執行個體更新時保持了高效能.這更加確定了我們不需要間接的或添加的資料介面,就可以實現code evolution.下面的兩個小節將主要介紹2個主要修改.

3.5.1 Swapping Pointers

       當更新Class C到Class C’後,我們需要確保所有的Class C的執行個體也同步更新成Class C’的執行個體。對象執行個體中儲存了指向對象的引用.但是在Java HotSpot VM中,沒有記錄一個對象被哪些執行個體所引用,因此需要對記憶體進行遍曆來找到所有的執行個體對象.同時,系統的其他引用例如native code也需要同步更新指向old class的引用.

       Figure5顯示了gc及我們所做的修改對指標的處理過程。假定在初始記憶體中存在Class A的一個執行個體對象x和新版本的ClassA,記作A’。第一步,收集器計算所有live對象在compaction後的地址,並在每個對象中加入(install)了一個指向新地址的指標:forward pointer。在pointer adjustment階段,對象的引用指標及類的指標被修改到新的地址上,我們攔截了這個階段的操作,以確保在compaction之後所有指向old class A的指標指向新的地址(x指向A’).

3.5.2 Updating Instances

       我們需要一個初始化新對象執行個體fields的策略來更新執行個體對象.對於名稱和類型相同的field,我們採用了一個簡單的演算法:從舊的執行個體中複製屬性值到新的執行個體中,其他所有的fileds初始化為0、null或false。

        使用這種方式,我們能夠通過高效的記憶體操作完成執行個體的更新(拷貝或填充為預設值)。更新資訊對於每個class只需要計算一次,並臨時將該資訊存入class的元資訊中。修改後的gc讀取該資訊,對每個執行個體進行記憶體拷貝或清除操作。這種演算法相比其他自訂的轉換方法要快。我們相信開發人員在調試過程中權衡易用性後,更願意採取這種缺少了靈活性但不需要額外輸入的方式。

        執行個體的更新是在gc的compaction階段完成的,所以不需要額外的記憶體來保持新舊執行個體的共存,新對象執行個體建立並對field複製後,老的樣本會立即被銷毀。(更新class後沒有發生gc啊?奇怪,難道是修改後的gc與普通的gc是分別啟動並執行?

        考慮到新對象執行個體的大小可能發生變化(比如class欄位增加),我們調整了forward pointer的演算法,並在記憶體回收行程中修改.這種情況下,在Mark-and-compact gc的compaction階段,執行個體在某些必要條件下不一定是被分配到記憶體的低位空間.而是會被分配到稱為side buffer的記憶體高位地址上(be rescued)。否則回收器會覆蓋掉沒有複製過的對象,並破壞其fields值(見下例)。在所有的執行個體全都被copied或rescued後,sife buffer中記錄的資料被用來初始被rescued的新執行個體。為了減少需要被複製到side buffer中對象的個數,forward pointer 演算法會自動將需要放到side buffer中的對象放到記憶體的最高位,以便為其他對象能夠正常的被複製到記憶體地位空間。(有點兒不太好理解,見例子吧)

        在Figure6中,x的size變大了,因此x在其目標地址中覆蓋了尚未被複製的其他對象,比如y,並且覆蓋了y的fields值(可以這麼說麼??).被修改的forward pointer 演算法檢測到x是個需要被reduced的執行個體,然後將其放到了side buffer中(記憶體的最高位地址).這為y和z省出了空間.把需要reduce的對象放到side buffer中顯著的減少了需要被reduce的對象個數(y,z不需要放到side buffer中了),也因此減少了side buffer需要的空間.在Compaction階段,x被複製到side buffer中,y和z被正常處理,然後.依據在side buffer中x的資料構造x的新的執行個體。

3.6 State Invalidation

        由code evolution引起的修改違反了VM中多個規則。Java HotSpot VM在設計中並不考慮code evolution,而且做了很多不變的假定,例如field 位移量(offset)不會改變。由於打破了這些假定,VM中有些子模組需要修改,來避免出現錯誤.本節將做概要性的介紹.

3.6.1 Compiled Code

        在code evolution之前由Just-In-Time 編譯器產生的機器碼需要驗證其有效性.大多數比較明顯潛在的無效的資訊是虛擬方法表索引和fields offset。另外,關於類的繼承(例如一個類是否是葉子節點)和調用(例如一個調用是否可以被statically bound)的假設也變得失效。

        Java HotSpot VM有一個內建的叫做deoptimization的機制,它可以廢除經最佳化後的方法機器碼。在stack中如果有一個方法啟用(there is an activation of the method on the stack)  。stack frame將被轉換為interpreter frame,代碼將在interpreter中繼續執行。另外,vm將通過在entry point 控制的方式使機器碼將不在被執行,而是進入interpreter 。我們可以通過這種方式deoptimaze所有編譯過的方法,以確保沒有由在錯誤的假定上產生的機器碼在運行。

3.6.2 Constant Pool Cache

        Java HotSpot VM維護了一個類的constant pool的緩衝.這種方式相比於每次讀取java class 檔案中constant pool擷取常量的方式,明顯的提高了interpreter的執行速度。original entries僅包含到fields 方法 及類的符號引用,cached entries中包含了對象元資訊的直接引用。與code evolution相關的entries包含fields entries(the offset of a field is cached)、method entries(for a statically bound call a pointer to the method meta object, for a dynamically bound call the virtual method table index is cached)。我們遍曆constant pool cache entries,清除與code evolution相關的entries(例如那些重定義類的成員)。當interpreter訪問一個已被清除的entry時,將被重新擷取。從system dictionary 尋找類時,會自動返回新版本的類,因此,entry也會被重新初始化,保持了正確的field offset 或者方法的相關資訊。

3.6.3 Class Pointer Values

        在java HotSpot Vm中有些資料結構依賴於類的元資訊對象的真真實位址,例如一個映射了類與JDWP對象的hash table(這個例子什麼意思啊?)。我們需要確保在code evolution後,要重新初始化這些資料結構。在gc運行中類對象也可能被移動了,pointer swapping也可能會改變兩個類對象的順序。The just-in-time compiler uses a binary search array for compiler interface objects that depends on the order of the class objects and therefore must be resorted after a code evolution step.

4. Binary InCompatible Changes

        如果old code被破壞了,那麼修改就是二進位不相容的。本節就在Section2中提到過的兩種二進位不相容的修改給出我們的處理方法。在Section7中,將討論我們在未來使用的其他解決方案。

4.1 Deleted Fields and Methods

        類方法體的修改或者是添加field或方法,old code是可以被繼續執行的,不需要調用新的方法或訪問新的fields。由於在系統stack中的old code可以繼續執行,所以當刪除了一個方法或field時,可能會導致被刪除了的方法被調用或被刪除了的field被訪問的情況,old code處於不可用狀態。Figure7中顯示了這種情況的一個樣本:

 

        程式在調用bar前被暫停(modified gc),foo方法在重定義成為foo’方法,同時刪除了bar方法.接下來的foo方法的調用理解指向了new code,但正在執行的foo方法在old code中,此時被刪除的bar方法被調用了。(就上例而言,在開發環境不是每次都能遇到,因為我們很難控制當class被redifine的時候程式正好暫停在bar的調用之前或是foo方法的調用之前.)

        新的foo‘是正確的,因為它不在調用bar方法。有沒有可能狀態的轉換更智能呢?比如,根據擷取stack中的值和二進位碼的執行位置轉換成新的stack值和二進位代碼位置。這是有可能實現的。但就一般情況而言,這種轉換不符合使用者的直覺。

        我們當前的做法是,old method可以繼續在interpreter中執行,當運行到bar的調用時,bar的引用需要重新resolve(之前我們在redefinition階段已經清楚了constant pool cache Section3.6).Resolution會找不到這個方法,然後拋出一個NoSuchMethodException的異常。(這很容易可以理解,就如同依賴的類做了修改一樣,不同的只是這是運行時刪除依賴類的方法)。

4.2  Type Narrowing

        class的介面或父類集合增加,old code可以正常運行。It does not use instances of the class as instances of their added interfaces or supertypes, but executes as before。相反的,如果class的介面或父類集合減小了,old code可能不在可用。Figure8顯示了一個這種情況的樣本。

 

        ClassB被重定義後不在繼承A.現在B的執行個體不在可以當成A的執行個體來處理.如上面代碼中顯示,可能存在A的執行個體變數,該變數是B的執行個體變數的引用。在code evolution後,這些變數變得不可用,因為B不再是A的子類.a.foo()不再有任何意義。

        當前的的dcevm能夠正確的進行code evolution,但是foo的調用會導致VM運行終止。我們認為這在debug環境是個可以被接受的方案,我們會在Section7中討論其他可行的解決方案。

5. Evalution

        本節將對我們的實現方案從以下3方面來進行評估:

  1. 討論我們對不同層級的code evolution的支援。
  2. 修改後的VM與修改前的VM 效能處於同等水平。
  3. 通過micro benchmarks 來討論修改後的gc的效能特性。

5.1 Functional Evaluation

        我們的方案支援了類的任何修改。當修改是二進位相容的,code可以按照預期執行。二進位不相容的修改,依據程式的運行狀態,可能會導致異常(方法或field不存在)或VM停止(類型不符)等問題。然而這些問題是不太容易出現的,因為一般情況下增加方法或屬性會比刪除要常用些,即使是被刪除了,這個方法在類重定義後處於active狀態的可能性也比較小。除了刪除父類(super type,也可能是介面),java程式(主要是old code)繼續運行時,在語義上是相容的。Table1給出了在Section2中討論的支援修改類型:

 

        在程式debug時,更新代碼可能帶來的上述問題,比重啟服務要好多了。最壞的情況是開發人員重啟服務,而這是沒有code evolution時必須的。由於Java標準在設計時不考慮code evolution,當出現問題時,是不太容易描述清楚的,因此,我們相信相比於隱藏問題,拋出一個異常或終止VM是可以接受的,並且可以避免產生困惑。

        Java1.4以後,JPDA定義了類重定義的指令。VM定義了三個標識來告知調試器其code evolution的能力:canRedefineClasses,可以從定義類;canAddMethod,可以向類中添加方法;canUnrestrictedlyRedefineClasses,可以任意的修改一個類。據我們所知,dce vm 是第一個在三種標識下都能返回true的VM。允許添加一個方法到可以做任意修改的跨度太大了。基於在Section2中關於實現的複雜度,我們計劃對code evolution的層級做更細緻的劃分。

        由於應用領域或開發習慣的不同,很難來度量code evolution的使用方式(不同類型的修改的比例等)。Gustavsson[20]給出了一個例子來研究一個web server項目在不同版本中的修改情況。結果方法體的修改佔37%,方法的增刪佔16%,代碼的任意修改佔33%,其他(比如code不能變為inactive狀態或需要vm外部的修改)14%。在這個例子中我們可以將可不需要重啟服務的修改從37%提高到86%。

5.2 Effects on Normal Execution

5.3 Micro Benchmarks

6 Related Work

6.1 General Discussions

6.2 Procedural Languages

6.3 Object-Oriented Languages

6.4 Java

7 Future Work

        dce vm當前的實現是在debug的基礎上,我們計劃將其擴充到更新服務上.相比於debug,更新服務的安全性更高,除此之外,需要更合適的更新點。我們想將類重定義指令擴充到安全更新要求時發生。。。。todo

8 Conclutions

 

譯文參考文章:

Dynamic Code Evolution for the Java HotSpot TM Virtual Machine(version1)

Dynamic Code Evolution for Java(current version)原文

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.