JVM 知識(shí)梳理

JVM 體系

  1. 類的加載機(jī)制
  2. JVM 內(nèi)存結(jié)構(gòu)
  3. GC算法 垃圾回收
  4. GC分析 命令調(diào)優(yōu)

1. 類的加載機(jī)制

主要關(guān)注點(diǎn):

  • 類的加載
  • 類的生命周期
  • 類加載器
  • 雙親委派模型

類的加載

類的加載指的是將類的.class文件中的二進(jìn)制數(shù)據(jù)讀入到內(nèi)存中拯勉,將其放在運(yùn)行時(shí)數(shù)據(jù)區(qū)的方法區(qū)內(nèi)竟趾,然后在堆區(qū)創(chuàng)建一個(gè)java.lang.Class對(duì)象,用來封裝類在方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu)宫峦。類的加載的最終產(chǎn)品是位于堆區(qū)中的java.lang.Class對(duì)象岔帽,Class對(duì)象封裝了類在方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu),并且向 Java 程序員提供了訪問方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu)的接口斗遏。

類的生命周期

類的生命周期包括加載山卦、連接初始化诵次、使用卸載幾個(gè)部分账蓉,其中前三步是類的加載過程連接又包括驗(yàn)證逾一、準(zhǔn)備解析铸本,如下圖:

類的生命周期
  • 加載:查找并加載類的二進(jìn)制數(shù)據(jù),在 Java 堆中也創(chuàng)建一個(gè)java.lang.Class類的對(duì)象遵堵。
  • 連接:連接又包含三塊內(nèi)容:驗(yàn)證箱玷、準(zhǔn)備和解析:
    • 驗(yàn)證:文件格式怨规、元數(shù)據(jù)、字節(jié)碼锡足、符號(hào)引用驗(yàn)證波丰。
    • 準(zhǔn)備:為類的靜態(tài)變量分配內(nèi)存,并將其初始化為默認(rèn)值舶得。
    • 解析:把類中的符號(hào)引用轉(zhuǎn)換為直接引用掰烟。
  • 初始化:為類的靜態(tài)變量賦予正確的初始值。
  • 使用:new 出對(duì)象在程序中使用沐批。
  • 卸載:執(zhí)行垃圾回收纫骑。

類加載器

類加載器
  • 啟動(dòng)類加載器:Bootstrap ClassLoader,負(fù)責(zé)加載存放在JDK\jre\lib(JDK 代表 JDK 的安裝目錄九孩,下同)下先馆,或被-Xbootclasspath參數(shù)指定的路徑中的,并且能被虛擬機(jī)識(shí)別的類庫躺彬。
  • 擴(kuò)展類加載器:Extension ClassLoader煤墙,該加載器由sun.misc.Launcher$ExtClassLoader實(shí)現(xiàn),它負(fù)責(zé)加載JDK\jre\lib\ext目錄中顾患,或者由java.ext.dirs系統(tǒng)變量指定的路徑中的所有類庫(如javax.*開頭的類)番捂,開發(fā)者可以直接使用擴(kuò)展類加載器。
  • 應(yīng)用程序類加載器:Application ClassLoader江解,該類加載器由sun.misc.Launcher$AppClassLoader來實(shí)現(xiàn)设预,它負(fù)責(zé)加載用戶類路徑(ClassPath)所指定的類,開發(fā)者可以直接使用該類加載器犁河。

類加載機(jī)制

  • 全盤負(fù)責(zé):當(dāng)一個(gè)類加載器負(fù)責(zé)加載某個(gè)Class時(shí)鳖枕,該Class所依賴的和引用的其他Class也將由該類加載器負(fù)責(zé)載入,除非顯示使用另外一個(gè)類加載器來加載桨螺。
  • 父類委托:先讓父類加載器試圖加載該類宾符,只有在父類加載器無法加載該類時(shí)才嘗試從自己的類路徑中加載該類。
  • 緩存機(jī)制:緩存機(jī)制將會(huì)保證所有加載過的Class都會(huì)被緩存灭翔,當(dāng)程序中需要使用某個(gè)Class時(shí)魏烫,類加載器先從緩存區(qū)尋找該Class,只有緩存區(qū)不存在肝箱,系統(tǒng)才會(huì)讀取該類對(duì)應(yīng)的二進(jìn)制數(shù)據(jù)哄褒,并將其轉(zhuǎn)換成Class對(duì)象,存入緩存區(qū)煌张。這就是為什么修改了Class后呐赡,必須重啟 JVM,程序的修改才會(huì)生效骏融。

2. JVM 內(nèi)存結(jié)構(gòu)

主要關(guān)注點(diǎn):

  • JVM 內(nèi)存結(jié)構(gòu)
  • 對(duì)象分配規(guī)則

JVM 內(nèi)存結(jié)構(gòu)

JVM 內(nèi)存結(jié)構(gòu)

方法區(qū)(Method Area)和堆(Heap)是所有線程共享的內(nèi)存區(qū)域链嘀;而java棧(JVM Stacks)萌狂、本地方法棧(Native Method Stacks)和程序計(jì)數(shù)器(Program Counter Register)是線程私有的內(nèi)存區(qū)域。

  • Java堆:Java虛擬機(jī)所管理的內(nèi)存中最大的一塊怀泊。Java堆是被所有線程共享的一塊內(nèi)存區(qū)域茫藏,在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建。此內(nèi)存區(qū)域的唯一目的就是存放對(duì)象實(shí)例包个,幾乎所有的對(duì)象實(shí)例都在這里分配內(nèi)存刷允。堆大小由-Xmx-Xms來調(diào)節(jié)。
  • 方法區(qū):所有線程共享的內(nèi)存區(qū)域碧囊,它用于存儲(chǔ)已被虛擬機(jī)加載的類信息、常量纤怒、靜態(tài)變量糯而、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)。調(diào)節(jié)參數(shù):-XX:MetaspaceSize-XX:MaxMetaspaceSize泊窘。
  • 程序計(jì)數(shù)器:是一塊較小的內(nèi)存空間熄驼,它的作用可以看做是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)指示器
  • Java 虛擬機(jī)棧:線程私有的區(qū)域烘豹,它的生命周期與線程相同。虛擬機(jī)棧描述的是Java方法執(zhí)行的內(nèi)存模型:每個(gè)方法被執(zhí)行的時(shí)候都會(huì)同時(shí)創(chuàng)建一個(gè)棧幀(Stack Frame)用于存儲(chǔ)局部變量表、操作棧揣钦、動(dòng)態(tài)鏈接移层、方法出口等信息。每一個(gè)方法被調(diào)用直至執(zhí)行完成的過程憔鬼,就對(duì)應(yīng)著一個(gè)棧幀在虛擬機(jī)棧中從入棧到出棧的過程龟劲。調(diào)節(jié)參數(shù):-Xss128k
  • 本地方法棧:與虛擬機(jī)棧所發(fā)揮的作用是非常相似的轴或,其區(qū)別不過是虛擬機(jī)棧為虛擬機(jī)執(zhí)行Java方法(也就是字節(jié)碼)服務(wù)昌跌,而本地方法棧則是為虛擬機(jī)使用到的Native方法服務(wù)。調(diào)節(jié)參數(shù):-Xss128k照雁。

對(duì)象分配規(guī)則

  • 對(duì)象優(yōu)先分配在Eden區(qū)蚕愤。如果Eden區(qū)沒有足夠的空間時(shí),虛擬機(jī)執(zhí)行一次Minor GC饺蚊。
  • 大對(duì)象直接進(jìn)入老年代萍诱,大對(duì)象指的是需要大量連續(xù)內(nèi)存空間的對(duì)象。這樣做的目的是避免在Eden區(qū)和兩個(gè)Survivor區(qū)之間發(fā)生大量的內(nèi)存拷貝(新生代采用復(fù)制算法收集內(nèi)存)卸勺。
  • 長(zhǎng)期存活的對(duì)象進(jìn)入老年代砂沛。虛擬機(jī)為每個(gè)對(duì)象定義了一個(gè)年齡計(jì)數(shù)器,如果對(duì)象經(jīng)過了1次Minor GC曙求,那么對(duì)象會(huì)進(jìn)入Survivor區(qū)碍庵,之后每經(jīng)過一次Minor GC那么對(duì)象的年齡加1映企,直到達(dá)到閥值對(duì)象進(jìn)入老年區(qū)
  • 動(dòng)態(tài)判斷對(duì)象的年齡静浴。如果Survivor區(qū)中相同年齡的所有對(duì)象大小的總和大于Survivor區(qū)空間的一半堰氓,年齡大于或等于該年齡的對(duì)象可以直接進(jìn)入老年代
  • 空間分配擔(dān)保苹享。每次進(jìn)行Minor GC時(shí)双絮,JVM會(huì)計(jì)算Survivor區(qū)移至老年區(qū)的對(duì)象的平均大小,如果這個(gè)值大于老年區(qū)的剩余值大小則進(jìn)行一次Full GC得问,如果小于檢查HandlePromotionFailure設(shè)置囤攀,如果為true則只進(jìn)行Monitor GC,如果為false則進(jìn)行Full GC

關(guān)于如何通過參數(shù)來控制個(gè)各個(gè)內(nèi)存區(qū)域請(qǐng)參考 JVM系列(二):JVM內(nèi)存結(jié)構(gòu)

3. GC算法 垃圾回收

主要關(guān)注點(diǎn):

  • 對(duì)象存活判斷
  • GC算法
  • 垃圾回收器

對(duì)象存活判斷

判斷對(duì)象是否存活一般有兩種方式:

  • 引用計(jì)數(shù):每個(gè)對(duì)象有一個(gè)引用計(jì)數(shù)屬性宫纬,新增一個(gè)引用時(shí)計(jì)數(shù)加1焚挠,引用釋放時(shí)計(jì)數(shù)減1,計(jì)數(shù)為0時(shí)可以回收漓骚。此方法簡(jiǎn)單蝌衔,無法解決對(duì)象相互循環(huán)引用的問題。
  • 可達(dá)性分析(Reachability Analysis):從GC Roots開始向下搜索蝌蹂,搜索所走過的路徑稱為引用鏈噩斟。當(dāng)一個(gè)對(duì)象到GC Roots沒有任何引用鏈相連時(shí),則證明此對(duì)象是不可用的孤个,不可達(dá)對(duì)象剃允。

GC算法

GC最基礎(chǔ)的算法有三種:標(biāo)記 -清除算法、復(fù)制算法硼身、標(biāo)記-壓縮算法硅急,我們常用的垃圾回收器一般都采用分代收集(Generational Collection)算法。

  • 標(biāo)記 -清除算法:標(biāo)記-清除(Mark-Sweep)算法佳遂,如它的名字一樣营袜,算法分為“標(biāo)記”和“清除”兩個(gè)階段:首先標(biāo)記出所有需要回收的對(duì)象,在標(biāo)記完成后統(tǒng)一回收掉所有被標(biāo)記的對(duì)象丑罪。
  • 復(fù)制算法:復(fù)制(Copying)的收集算法荚板,它將可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中的一塊吩屹。當(dāng)這一塊的內(nèi)存用完了跪另,就將還存活著的對(duì)象復(fù)制到另外一塊上面,然后再把已使用過的內(nèi)存空間一次清理掉煤搜。
  • 標(biāo)記-壓縮算法:標(biāo)記過程仍然與標(biāo)記-清除算法一樣免绿,但后續(xù)步驟不是直接對(duì)可回收對(duì)象進(jìn)行清理,而是讓所有存活的對(duì)象都向一端移動(dòng)擦盾,然后直接清理掉端邊界以外的內(nèi)存嘲驾。
  • 分代收集算法:分代收集算法淌哟,把Java堆分為新生代和老年代,這樣就可以根據(jù)各個(gè)年代的特點(diǎn)采用最適當(dāng)?shù)氖占惴ā?/li>

垃圾回收器

  • Serial收集器辽故,串行收集器是最古老徒仓,最穩(wěn)定以及效率高的收集器,可能會(huì)產(chǎn)生較長(zhǎng)的停頓誊垢,只使用一個(gè)線程去回收掉弛。
  • ParNew收集器,ParNew收集器其實(shí)就是Serial收集器的多線程版本喂走。
  • Parallel收集器殃饿,Parallel Scavenge收集器類似ParNew收集器,Parallel收集器更關(guān)注系統(tǒng)的吞吐量芋肠。
  • Parallel Old 收集器壁晒,Parallel Old是Parallel Scavenge收集器的老年代版本,使用多線程和“標(biāo)記-整理”算法业栅。
  • CMS收集器,CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時(shí)間為目標(biāo)的收集器谬晕。
  • G1收集器碘裕,G1 (Garbage-First)是一款面向服務(wù)器的垃圾收集器,主要針對(duì)配備多顆處理器及大容量?jī)?nèi)存的機(jī)器. 以極高概率滿足GC停頓時(shí)間要求的同時(shí),還具備高吞吐量性能特征。

GC算法和垃圾回收器算法圖解以及更詳細(xì)內(nèi)容參考: jvm系列(三):GC算法 垃圾收集器

4. GC分析 命令調(diào)優(yōu)

主要關(guān)注點(diǎn):

  • GC日志分析
  • 調(diào)優(yōu)命令
  • 調(diào)優(yōu)工具

GC日志分析

摘錄GC日志一部分

Young GC回收日志:

  1. 2016-07-05T10:43:18.093+0800: 25.395: [GC [PSYoungGen: 274931K->10738K(274944K)] 371093K->147186K(450048K), 0.0668480 secs] [Times: user=0.17 sys=0.08, real=0.07 secs]

Full GC回收日志:

  1. 2016-07-05T10:43:18.160+0800: 25.462: [Full GC [PSYoungGen: 10738K->0K(274944K)] [ParOldGen: 136447K->140379K(302592K)] 147186K->140379K(577536K) [PSPermGen: 85411K->85376K(171008K)], 0.6763541 secs] [Times: user=1.75 sys=0.02, real=0.68 secs]

通過上面日志分析得出攒钳,PSYoungGen帮孔、ParOldGenPSPermGen屬于Parallel收集器不撑。其中PSYoungGen表示 GC 回收前后年輕代的內(nèi)存變化文兢;ParOldGen表示 GC 回收前后老年代的內(nèi)存變化;PSPermGen表示 GC 回收前后永久區(qū)的內(nèi)存變化焕檬。Young GC 主要是針對(duì)年輕代進(jìn)行內(nèi)存回收比較頻繁姆坚,耗時(shí)短;Full GC 會(huì)對(duì)整個(gè)堆內(nèi)存進(jìn)行回城实愚,耗時(shí)長(zhǎng)兼呵,因此一般盡量減少 Full GC 的次數(shù)。

Young GC日志:


Young GC日志

Full GC日志:


Full GC日志

調(diào)優(yōu)命令

Sun JDK監(jiān)控和故障處理命令有jps(JVM Process Status Tool)腊敲、jstat(JVM statistics Monitoring)击喂、jmapjhat(JVM Heap Analysis Tool)碰辅、jstackjinfo(JVM Configuration info)

  • jps:顯示指定系統(tǒng)內(nèi)所有的HotSpot虛擬機(jī)進(jìn)程懂昂。
  • jstat:是用于監(jiān)視虛擬機(jī)運(yùn)行時(shí)狀態(tài)信息的命令,它可以顯示出虛擬機(jī)進(jìn)程中的類裝載没宾、內(nèi)存凌彬、垃圾收集沸柔、JIT編譯等運(yùn)行數(shù)據(jù)。
  • jmap饿序,JVM Memory Map命令用于生成heap dump文件勉失。
  • jhat:命令是與jmap搭配使用,用來分析jmap生成的dump原探,jhat內(nèi)置了一個(gè)微型的HTTP/HTML服務(wù)器乱凿,生成dump的分析結(jié)果后,可以在瀏覽器中查看咽弦。
  • jstack:用于生成java虛擬機(jī)當(dāng)前時(shí)刻的線程快照徒蟆。
  • jinfo:這個(gè)命令作用是實(shí)時(shí)查看和調(diào)整虛擬機(jī)運(yùn)行參數(shù)。

詳細(xì)的命令使用參考這里:jvm系列(四):jvm調(diào)優(yōu)-命令篇

調(diào)優(yōu)工具

常用調(diào)優(yōu)工具分為兩類型型,JDK自帶監(jiān)控工具:jconsolejvisualvm段审,第三方有:MAT(Memory Analyzer Tool)、GChisto闹蒜。

  • jconsole:Java Monitoring and Management Console是從 Java 5 開始寺枉,在 JDK 中自帶的 Java 監(jiān)控和管理控制臺(tái),用于對(duì) JVM 中內(nèi)存绷落,線程和類等的監(jiān)控姥闪。
  • jvisualvm:jdk自帶全能工具,可以分析內(nèi)存快照砌烁、線程快照筐喳;監(jiān)控內(nèi)存變化、GC變化等函喉。
  • MAT:一個(gè)基于Eclipse的內(nèi)存分析工具避归,是一個(gè)快速、功能豐富的Java heap分析工具管呵,它可以幫助我們查找內(nèi)存泄漏和減少內(nèi)存消耗梳毙。
  • GChisto:一款專業(yè)分析gc日志的工具。

參考 純潔的微笑 JVM系列

JVM 參數(shù)

-XX:+HeapDumpOnOutOfMemoryError
//當(dāng)JVM發(fā)生OOM時(shí)撇寞,自動(dòng)生成DUMP文件
-XX:HeapDumpPath=./heapdump.hprof
生成DUMP文件的路徑顿天,也可以指定文件名稱,如果不指定文件名蔑担,默認(rèn)為:java_<pid>_<date>_<time>_heapDump.hprof
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末牌废,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子啤握,更是在濱河造成了極大的恐慌鸟缕,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異懂从,居然都是意外死亡授段,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門番甩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來侵贵,“玉大人,你說我怎么就攤上這事缘薛∏嫌” “怎么了?”我有些...
    開封第一講書人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵宴胧,是天一觀的道長(zhǎng)漱抓。 經(jīng)常有香客問我,道長(zhǎng)恕齐,這世上最難降的妖魔是什么乞娄? 我笑而不...
    開封第一講書人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮显歧,結(jié)果婚禮上仪或,老公的妹妹穿的比我還像新娘。我一直安慰自己士骤,他們只是感情好溶其,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著敦间,像睡著了一般。 火紅的嫁衣襯著肌膚如雪束铭。 梳的紋絲不亂的頭發(fā)上廓块,一...
    開封第一講書人閱讀 51,462評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音契沫,去河邊找鬼带猴。 笑死,一個(gè)胖子當(dāng)著我的面吹牛懈万,可吹牛的內(nèi)容都是我干的拴清。 我是一名探鬼主播,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼会通,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼口予!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起涕侈,我...
    開封第一講書人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤沪停,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體木张,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡众辨,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了舷礼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鹃彻。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖妻献,靈堂內(nèi)的尸體忽然破棺而出蛛株,到底是詐尸還是另有隱情,我是刑警寧澤旋奢,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布泳挥,位于F島的核電站,受9級(jí)特大地震影響至朗,放射性物質(zhì)發(fā)生泄漏屉符。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一锹引、第九天 我趴在偏房一處隱蔽的房頂上張望矗钟。 院中可真熱鬧,春花似錦嫌变、人聲如沸吨艇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽东涡。三九已至,卻和暖如春倘待,著一層夾襖步出監(jiān)牢的瞬間疮跑,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工凸舵, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留祖娘,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓啊奄,卻偏偏與公主長(zhǎng)得像渐苏,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子菇夸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 這篇文章是我之前翻閱了不少的書籍以及從網(wǎng)絡(luò)上收集的一些資料的整理琼富,因此不免有一些不準(zhǔn)確的地方,同時(shí)不同JDK版本的...
    高廣超閱讀 15,601評(píng)論 3 83
  • 原文閱讀 前言 這段時(shí)間懈怠了庄新,罪過公黑! 最近看到有同事也開始用上了微信公眾號(hào)寫博客了,挺好的~給他們點(diǎn)贊,這博客我...
    碼農(nóng)戲碼閱讀 5,968評(píng)論 2 31
  • 內(nèi)存溢出和內(nèi)存泄漏的區(qū)別 內(nèi)存溢出:out of memory凡蚜,是指程序在申請(qǐng)內(nèi)存時(shí)人断,沒有足夠的內(nèi)存空間供其使用,...
    Aimerwhy閱讀 741評(píng)論 0 1
  • 1.什么是垃圾回收朝蜘? 垃圾回收(Garbage Collection)是Java虛擬機(jī)(JVM)垃圾回收器提供...
    簡(jiǎn)欲明心閱讀 89,493評(píng)論 17 311
  • 簡(jiǎn)述 sd通常用來表示群體的取值范圍.se通常用來表示(群體平均值)的取值范圍. 例子 X表示所有中國(guó)人的身高集合...
    gada閱讀 11,063評(píng)論 0 2