今天就跟大家聊聊有關(guān)java中的垃圾回收概念與算法是怎樣的,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。
在安岳等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強(qiáng)發(fā)展的系統(tǒng)性、市場前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務(wù)理念,為客戶提供網(wǎng)站設(shè)計(jì)、成都網(wǎng)站設(shè)計(jì) 網(wǎng)站設(shè)計(jì)制作按需求定制開發(fā),公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),成都品牌網(wǎng)站建設(shè),成都營銷網(wǎng)站建設(shè),外貿(mào)營銷網(wǎng)站建設(shè),安岳網(wǎng)站建設(shè)費(fèi)用合理。
對(duì)于一個(gè)對(duì)象A,只要有任何一個(gè)對(duì)象引用了A,則A的引用計(jì)數(shù)器就加1,當(dāng)引用失效時(shí),引用計(jì)數(shù)器就減1.只要對(duì)象A的引用計(jì)數(shù)器值為0,則對(duì)象A就不可能再被使用。
引用計(jì)數(shù)器的實(shí)現(xiàn)非常簡單,但有兩個(gè)嚴(yán)重問題:
無法處理循環(huán)引用。
引用計(jì)數(shù)器要求在每次引用產(chǎn)生和消除的時(shí)候,伴隨一個(gè)加法操作和減法操作,對(duì)系統(tǒng)性能有一定影響。
因此,java虛擬機(jī)并未選擇此算法作為垃圾回收算法。
標(biāo)記清除法是現(xiàn)在垃圾回收算法的思想基礎(chǔ)。標(biāo)記清除法將垃圾回收分為兩個(gè)階段:標(biāo)記和清除階段。
標(biāo)記階段:首先通過根節(jié)點(diǎn)標(biāo)記所有從根節(jié)點(diǎn)開始的可達(dá)對(duì)象。因此,未被標(biāo)記的對(duì)象就是未被引用的垃圾對(duì)象。
清除階段:清除所有未被標(biāo)記的對(duì)象。
標(biāo)記清除法的最大問題是可能產(chǎn)生空間碎片。
復(fù)制算法的核心思想是:將原有的內(nèi)存空間分為兩塊,每次只使用其中一塊,在進(jìn)行垃圾回收時(shí),將正在使用的內(nèi)存中的存活對(duì)象復(fù)制到未使用的內(nèi)存塊中,之后清除正在使用的內(nèi)存塊中的所有對(duì)象,交換兩個(gè)內(nèi)存的角色,完成垃圾回收。
復(fù)制算法優(yōu)點(diǎn):如果系統(tǒng)中的垃圾對(duì)象很多,復(fù)制算法需要復(fù)制的存活對(duì)象數(shù)量就會(huì)相對(duì)較少。因此,在真正需要垃圾回收的時(shí)刻,復(fù)制算法的效率是很高的。又由于對(duì)象是在垃圾回收過程中統(tǒng)一被復(fù)制到新的內(nèi)存空間的,可確?;厥蘸蟮膬?nèi)存空間是沒有碎片的。
復(fù)制算法缺點(diǎn):復(fù)制算法需要將內(nèi)存分成兩塊,每次只使用其中一塊(真正使用的內(nèi)存只有其中一半).
標(biāo)記壓縮算法
在 標(biāo)記清除算法
的基礎(chǔ)上做了一些優(yōu)化。和 標(biāo)記清除算法
一樣,票房壓縮算法也是需要從根節(jié)點(diǎn)開始,對(duì)所有可達(dá)對(duì)象做一次標(biāo)記。但之后,它并不只是簡單地清理未標(biāo)記的對(duì)象,而是將所有的存活對(duì)象壓縮到內(nèi)存的一端。然后清理邊界外所有的空間。
這種方法既避免了碎片的產(chǎn)生,又不需要兩塊相同的內(nèi)存空間。
在前面介紹的幾種算法中,沒有一種算法是可以完全替代其他算法,它們都有自己的優(yōu)勢(shì)和特點(diǎn),根據(jù)垃圾回收對(duì)象的特性,使用合適的算法,才是明智的選擇。
分代算法就是基于這種思想,它將內(nèi)存區(qū)間根據(jù)對(duì)象的特點(diǎn)分成幾塊,根據(jù)每塊內(nèi)存區(qū)間的特點(diǎn)使用不同的回收算法,以提高垃圾回收的效率。
一般來說,java虛擬機(jī)會(huì)將所有的新建對(duì)象都放入新生代的內(nèi)存區(qū)域,新生代的特點(diǎn)是朝生夕滅,大約90%的新建對(duì)象會(huì)被很快回收,因此新生代比較適合使用復(fù)制算法。
當(dāng)一個(gè)對(duì)象經(jīng)過幾次回收后依然存活,對(duì)象就會(huì)晉升到老年代內(nèi)存空間中。在老年代中,幾乎所有的對(duì)象都是經(jīng)過幾次垃圾回收依然得存活的,因此可以認(rèn)為這些對(duì)象在一段時(shí)期內(nèi),都將是常駐內(nèi)存的。如果依然使用復(fù)制算法,將需要復(fù)制大量對(duì)象,再加上老年代的回收性價(jià)比也低于新生代,因此這種做法不可取。根據(jù)分代的思想,可以對(duì)老年代使用標(biāo)記壓縮或標(biāo)記清除算法。
通常新生代回收的頻率很高,但每次回收的耗時(shí)很短,而老年代回收的頻率比較低,但耗時(shí)長。為了支持高頻率的新生代回收,虛擬機(jī)可能使用一種叫作卡表的數(shù)據(jù)結(jié)構(gòu)。
卡表為一個(gè)比特位集合,每一個(gè)比特位可以用來表示老年代的某一區(qū)域中的所有對(duì)象是否持有新生代對(duì)象的引用。這樣在新生代gc時(shí),可以不用花大量時(shí)間掃描所有的老年代對(duì)象來確定每一個(gè)對(duì)象的引用關(guān)系,可以先掃描卡表,只有當(dāng)卡表的標(biāo)記位為1時(shí),才需要掃描給定區(qū)域的老年代對(duì)象,而卡表為0的老年代對(duì)象肯定不含有新生代對(duì)象的引用。使用這種方式,可以大大加快新生代的回收速度。
分區(qū)將整個(gè)堆空間劃分成連續(xù)的不同小區(qū)間,每一個(gè)小區(qū)間都獨(dú)立使用,獨(dú)立回收。這種算法的好處是可以控制一次回收小區(qū)間的數(shù)量。
一般來說,在相同條件下,堆空間越大,一次GC所需要的時(shí)間就越長,從而產(chǎn)生的停頓也越長。為了更好地控制gc產(chǎn)生的停頓時(shí)間,將一塊大的內(nèi)存區(qū)域分割成多個(gè)小塊,根據(jù)目錄停頓時(shí)間,每次合理地回收若干個(gè)小區(qū)間,而不是回收整個(gè)堆空間,從而減少一次gc所產(chǎn)生的停頓。
對(duì)象的可觸及性包含以下三種狀態(tài):
可觸及的:從根節(jié)點(diǎn)開始,可以到達(dá)這個(gè)對(duì)象;
可復(fù)活的:對(duì)象的所有引用都被釋放,但是對(duì)象有可能在 finallize()
函數(shù)中復(fù)活;
不可觸及的:對(duì)象的 finallize()
函數(shù)被調(diào)用,并且沒有復(fù)活,那么就會(huì)進(jìn)入不可觸及狀態(tài),不可觸及狀態(tài)的對(duì)象不可能被復(fù)活,因?yàn)?finallize()
函數(shù)只會(huì)被調(diào)用一次。
以上3種狀態(tài)中,只有在對(duì)象不可觸及時(shí)才可以被回收。
對(duì)象很有可能在 finalize()
函數(shù)中使自己復(fù)活,這里給出一個(gè)例子:
public class Demo01 { public static Demo01 obj; @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("obj finalize called"); obj = this; } @Override public String toString() { return "I am can rellive obj"; } public static void main(String[] args) throws InterruptedException { obj = new Demo01(); obj = null; System.gc(); Thread.sleep(1000); if(obj == null) { System.out.println("obj 是 null"); } else { System.out.println("obj 可用"); } System.out.println("第2次gc"); obj = null; System.gc(); Thread.sleep(1000); if(obj == null) { System.out.println("obj 是 null"); } else { System.out.println("obj 可用"); } } }
結(jié)果:
can rellive obj finalize called obj 可用 第2次gc obj 是 null
可以看到,將obj設(shè)置為null后,進(jìn)行g(shù)c,結(jié)果發(fā)現(xiàn)obj對(duì)象復(fù)活了。接著,再次釋放對(duì)象引用并進(jìn)行g(shù)c,對(duì)象才真正回收。這是因?yàn)榈谝淮芜M(jìn)行g(shù)c時(shí),在 finalize()
函數(shù)調(diào)用前,雖然系統(tǒng)中的引用已經(jīng)被清除,但是作為實(shí)例方法finalize()
,對(duì)象的this引用依然會(huì)被傳入方法內(nèi)部,如果引用外泄,對(duì)象就會(huì)復(fù)活,此時(shí)對(duì)象又變?yōu)榭捎|及狀態(tài)。而finalize()
函數(shù)只會(huì)被調(diào)用一次,在第2次清除對(duì)象時(shí),對(duì)象就再無機(jī)會(huì)復(fù)活,因此就會(huì)被回收。
注意:
finalize()
函數(shù)是一個(gè)非常糟糕的模式,不推薦使用finalize()
函數(shù)釋放資源,原因如下:
finalize()
函數(shù)有可能發(fā)生引用外泄,在無意中復(fù)活對(duì)象;
finalize()
函數(shù)是被系統(tǒng)調(diào)用的,調(diào)用時(shí)間不明確,因此不是一個(gè)好的資源釋放方案,推薦在try-catch-finally
語句中進(jìn)行資源的釋放。
在java中,提供了4個(gè)級(jí)別的引用:強(qiáng)引用、軟引用、弱引用和虛引用。隊(duì)強(qiáng)引用外,其他3種引用均可以在java.lang.ref
包中找到。其中,FinalReference
為 “最終” 引用,它用以實(shí)現(xiàn)對(duì)象的 finallize()
函數(shù)。
強(qiáng)引用就是程序中一般使用的引用類型,強(qiáng)引用的對(duì)象是可觸及的,不會(huì)被回收。軟引用、弱引用和虛引用的對(duì)象是軟可觸及、弱可觸及和虛可觸及的,在一定條件下都是可以被回收的。
強(qiáng)引用示例:
//這里str就是強(qiáng)引用 String str = "aaa";
強(qiáng)引用有如下特點(diǎn):
強(qiáng)引用可以直接訪問目標(biāo)對(duì)象;
強(qiáng)引用所指向的對(duì)象在任何時(shí)候都不會(huì)被系統(tǒng)回收,虛擬機(jī)寧愿拋出OOM異常,也不會(huì)回收強(qiáng)引用所指向的對(duì)象;
強(qiáng)引用可能導(dǎo)致內(nèi)存泄露。
軟引用是指強(qiáng)引用弱一點(diǎn)的引用類型。如果一個(gè)對(duì)象只持有軟引用,那么當(dāng)空間不足時(shí),就會(huì)被回收。軟引用使用java.lang.ref.SoftReference
類實(shí)現(xiàn)。
以下示例演示了軟引用會(huì)在系統(tǒng)堆內(nèi)存不足時(shí)被回收:
public class Demo02 { public static class User { public int id; public String name; public User(int id, String name) { this.id = id; this.name = name; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + '}'; } } public static void main(String[] args) { SoftReferenceuserSoftReference = new SoftReference<>(new User(1, "geym")); System.out.println(userSoftReference.get()); //第一次垃圾回收 System.gc(); System.out.println("after gc"); System.out.println(userSoftReference.get()); //初始化數(shù)組后 byte[] b = new byte[1024 * 973 * 7]; System.gc(); System.out.println(userSoftReference.get()); } }
使用參數(shù)-Xmx10m
運(yùn)行,結(jié)果如下:
User{id=1, name='geym'} after gc User{id=1, name='geym'} null
上述代理,首先聲明了User
對(duì)象的軟引用,接著進(jìn)行了一次垃圾回收,發(fā)現(xiàn)軟引用對(duì)象依然存在;接著分配了一個(gè)大對(duì)象,系統(tǒng)此時(shí)認(rèn)為內(nèi)存緊張,于是進(jìn)行了一次gc,此時(shí)會(huì)回收軟引用。
每一個(gè)軟引用都可以附帶一個(gè)引用隊(duì)列,當(dāng)對(duì)象的可達(dá)性發(fā)生改變時(shí),軟引用對(duì)象就會(huì)進(jìn)入引用隊(duì)列,通過這個(gè)引用隊(duì)列,可以跟蹤對(duì)象的回收情況,代碼示例如下:
public class Demo03 { private static class User { public int id; public String name; public User(int id, String name) { this.id = id; this.name = name; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + '}'; } } static ReferenceQueuesoftQueue = null; public static class CheckRefQueue extends Thread { @Override public void run() { while(true) { if(softQueue != null) { UserSoftReference obj = null; try { obj = (UserSoftReference) softQueue.remove(); } catch (Exception e) { e.printStackTrace(); } if(obj == null) { System.out.println("user id " + obj.uid + "is delete"); } } } } } //自定義一個(gè)軟引用類,擴(kuò)展軟引用的目的是記錄User.uid, //后續(xù)在引用隊(duì)列中就可以通過這個(gè)uid字段知道哪個(gè)User實(shí)例被回收了。 public static class UserSoftReference extends SoftReference { int uid; public UserSoftReference(User referent, ReferenceQueue super User> q) { super(referent, q); this.uid = referent.id; } } public static void main(String[] args) throws InterruptedException { Thread t = new CheckRefQueue(); t.setDaemon(true); t.start(); //創(chuàng)建軟引用時(shí),指定了一個(gè)軟引用隊(duì)列,當(dāng)給定的對(duì)象實(shí)例被回收時(shí),就會(huì)被加入這個(gè)引用隊(duì)列, //通過訪問訪隊(duì)列可以跟蹤對(duì)象的回收情況 User u = new User(1, "geym"); softQueue = new ReferenceQueue<>(); UserSoftReference userSoftReference = new UserSoftReference(u, softQueue); u = null; System.out.println(userSoftReference.get()); System.gc(); //內(nèi)存足夠,不會(huì)被回收 System.out.println("after gc:"); System.out.println(userSoftReference.get()); System.out.println("try to create byte array and GC"); byte[] b = new byte[1024 * 973 * 7]; System.gc(); System.out.println(userSoftReference.get()); Thread.sleep(1000); } }
使用參數(shù) -Xmx10m
運(yùn)行上述代碼就可以得到:
User{id=1, name='geym'} after gc: User{id=1, name='geym'} try to create byte array and GC null
弱引用是一種比軟引用弱的引用類型。在系統(tǒng)gc時(shí),只要發(fā)現(xiàn)弱引用,不管堆空間使用情況如何,都會(huì)將對(duì)象進(jìn)行回收。但是,由于垃圾回收器的線程通常優(yōu)先級(jí)很低,并不一定能很快地發(fā)現(xiàn)持有弱引用的對(duì)象。在這種情況下,弱引用對(duì)象可以存在較長的時(shí)間。一旦一個(gè)弱引用對(duì)象被垃圾回收器回收,便會(huì)加入一個(gè)注冊(cè)的引用隊(duì)列,這一點(diǎn)和軟引用很相似。軟引用使用java.lang.ref.WeakReference
類實(shí)現(xiàn)。
以下示例顯示了弱引用的特點(diǎn)
public class Demo04 { private static class User { public int id; public String name; public User(int id, String name) { this.id = id; this.name = name; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + '}'; } } public static void main(String[] args) { WeakReferenceuserWeakReference = new WeakReference<>(new User(1, "geym")); System.out.println(userWeakReference.get()); System.gc(); // 不管當(dāng)前空間足夠與否,都會(huì)回收它的內(nèi)存 System.out.println("after gc"); System.out.println(userWeakReference.get()); } }
運(yùn)行結(jié)果:
User{id=1, name='geym'} after gc null
弱引用和軟引用一樣,在構(gòu)造弱引用時(shí),也可以指定一個(gè)引用隊(duì)列,當(dāng)弱引用對(duì)象被回收時(shí),就會(huì)加入指定的引用隊(duì)列,通過這個(gè)隊(duì)列可以跟蹤對(duì)象的回收情況。
虛引用是所有引用類型中最弱的一個(gè)。一個(gè)持有虛引用的對(duì)象,和沒有引用幾乎是一樣的,隨時(shí)都可能被垃圾回收器回收。當(dāng)試圖通過虛引用的 get()
方法取得強(qiáng)引用時(shí),總會(huì)失敗。并且,虛引用必須和引用隊(duì)列一起使用,它的作用在于跟蹤垃圾回收過程。
下面給出一個(gè)示例,使用虛引用跟蹤一個(gè)可復(fù)活對(duì)象的回收:
public class Demo05 { public static Demo05 obj = null; static ReferenceQueuephantomQueue = null; public static class CheckRefQueue extends Thread { @Override public void run() { while(true) { if(phantomQueue != null) { PhantomReference objt = null; try { objt = (PhantomReference ) phantomQueue.remove(); } catch (Exception e) { e.printStackTrace(); } if(objt != null) { System.out.println("demo05 obj is delete by gc"); } } } } } @Override protected void finalize() throws Throwable{ super.finalize(); System.out.println("demo05 obj finalize called"); obj = this; } @Override public String toString() { return "I am Demo05"; } public static void main(String[] args) throws InterruptedException { Thread t = new CheckRefQueue(); t.setDaemon(true); t.start(); phantomQueue = new ReferenceQueue<>(); obj = new Demo05(); //構(gòu)造一個(gè)虛引用 PhantomReference phantomReference = new PhantomReference<>(obj, phantomQueue); //去除強(qiáng)引用,進(jìn)行垃圾回收,由于對(duì)象可復(fù)活,gc無法回收該對(duì)象 obj = null; System.gc(); Thread.sleep(1000); if(obj == null) { System.out.println("obj 是 null"); } else { System.out.println("obj 可用"); } //第2次進(jìn)行g(shù)c,由于 finalize()函數(shù)只會(huì)被調(diào)用一次,因此第2次gc會(huì)回收對(duì)象, //同時(shí)其引用隊(duì)列應(yīng)該也會(huì)捕獲取對(duì)象的回收 System.out.println("第二次gc"); obj = null; System.gc(); Thread.sleep(1000); if(obj == null) { System.out.println("obj 是 null"); } else { System.out.println("obj 可用"); } } }
執(zhí)行代碼,結(jié)果如下:
demo05 obj finalize called obj 可用 第二次gc demo05 obj is delete by gc obj 是 null
由于虛引用可以跟蹤對(duì)象的回收時(shí)間,所以也可以將一些資源的釋放操作放在虛引用中執(zhí)行和記錄。
為了讓垃圾回收器正常且高效地執(zhí)行,在大部分情況下,會(huì)要求系統(tǒng)進(jìn)入一個(gè)停頓的狀態(tài),停頓的目的是終止所有應(yīng)用線程的執(zhí)行,只有這樣系統(tǒng)才不會(huì)有新地垃圾產(chǎn)生,同時(shí)停頓保證了系統(tǒng)狀態(tài)在某一個(gè)瞬間的一致性,也有益于垃圾回收器更好地標(biāo)記垃圾對(duì)象。因此,在垃圾回收時(shí),都會(huì)產(chǎn)生應(yīng)用程序的停頓。停頓產(chǎn)生時(shí),整個(gè)應(yīng)用程序會(huì)被卡死,沒有任何響應(yīng),因此這個(gè)停頓也叫“Stop-The-World”(STW).
下面這個(gè)示例顯示了停頓的情況:
public class Demo06 { private static class MyThread extends Thread { HashMap map = new HashMap(); @Override public void run() { try { while (true) { if(map.size() * 512 /1024 / 1024 >= 900) { map.clear(); System.out.println("clean map"); } byte[] b1; for(int i = 0; i < 100; i++) { b1 = new byte[512]; map.put(System.nanoTime(), b1); } Thread.sleep(1); } } catch (Exception e) { e.printStackTrace(); } } } private static class PrintThread extends Thread { public static final long startTime = System.currentTimeMillis(); @Override public void run() { try { while(true) { long t = System.currentTimeMillis() - startTime; System.out.println(t / 1000 + "." + t % 1000); Thread.sleep(100); } } catch (Exception e) { e.printStackTrace(); } } } public static void main(String[] args) { MyThread t = new MyThread(); PrintThread p = new PrintThread(); t.start(); p.start(); } }
以上代碼創(chuàng)建了兩個(gè)線程:一個(gè)用來分配空間,另一個(gè)用來打印時(shí)間,使用參數(shù) -Xmx1g -Xms1g -Xmn512k -XX:+UseSerialGC -Xloggc:gc.log -XX:+PrintGCDetails
運(yùn)行,部分輸出如下:
34.732 34.833 34.940 35.810 (從此處開始,程序中設(shè)置每隔0.1秒輸出,但此處時(shí)間間隔明顯大于0.1秒) 36.604 37.38 38.230 39.20 39.813 40.590 41.420
此處對(duì)應(yīng)的gc日志如下:
35.100: [GC (Allocation Failure) 35.100: [DefNew: 447K->64K(448K), 0.0015667 secs] 1047853K->1047838K(1048512K), 0.0016257 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 35.110: [GC (Allocation Failure) 35.110: [DefNew: 448K->448K(448K), 0.0000200 secs]35.110: [Tenured: 1047774K->1048063K(1048064K), 0.7820350 secs] 1048222K->1048208K(1048512K), [Metaspace: 8969K->8969K(1058816K)], 0.7821444 secs] [Times: user=0.78 sys=0.00, real=0.78 secs] 35.895: [Full GC (Allocation Failure) 35.895: [Tenured: 1048063K->1048063K(1048064K), 0.7921236 secs] 1048511K->1048417K(1048512K), [Metaspace: 8974K->8974K(1058816K)], 0.7922297 secs] [Times: user=0.78 sys=0.00, real=0.79 secs] 36.689: [Full GC (Allocation Failure) 36.689: [Tenured: 1048063K->1048063K(1048064K), 0.7799881 secs] 1048510K->1048439K(1048512K), [Metaspace: 8976K->8976K(1058816K)], 0.7800798 secs] [Times: user=0.78 sys=0.00, real=0.78 secs] 37.469: [Full GC (Allocation Failure) 37.469: [Tenured: 1048063K->1047758K(1048064K), 0.8430948 secs] 1048511K->1047758K(1048512K), [Metaspace: 8965K->8965K(1058816K)], 0.8432301 secs] [Times: user=0.84 sys=0.00, real=0.84 secs] 38.316: [GC (Allocation Failure) 38.316: [DefNew: 384K->384K(448K), 0.0000200 secs]38.316: [Tenured: 1047758K->1048010K(1048064K), 0.7867043 secs] 1048142K->1048010K(1048512K), [Metaspace: 8955K->8955K(1058816K)], 0.7868497 secs] [Times: user=0.78 sys=0.00, real=0.79 secs]
注意看gc日志中的[Times: ... real=...]
,正常情況下,gc所用時(shí)間為real=0.00 secs
,但是如果發(fā)生full gc,那么gc時(shí)間就會(huì)變長,在35.895、36.689和37.469時(shí),gc所用時(shí)間接近0.8s,分別為real=0.79 secs
、real=0.78 secs
和real=0.84 secs
。
使用jvisualvm
觀察gc過程,如圖所求,可以看到:
新生代(eden space
) GC共進(jìn)行了2828次,共耗時(shí)7.571s,平均耗時(shí) 0.002677s;
老年代(Old Gen
) GC共進(jìn)行31次,共耗時(shí)24.084s,平均耗時(shí) 0.7769s;
整個(gè)堆發(fā)生GC共2859次,共耗時(shí)31.655s.
看完上述內(nèi)容,你們對(duì)java中的垃圾回收概念與算法是怎樣的有進(jìn)一步的了解嗎?如果還想了解更多知識(shí)或者相關(guān)內(nèi)容,請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝大家的支持。