OutOfMemoryError系列(1): Java heap space

http://blog.csdn.net/renfufei/article/details/76350794

每個(gè)Java程序都只能使用一定量的內(nèi)存, 這種限制是由JVM的啟動(dòng)參數(shù)決定的畏梆。而更復(fù)雜的情況在于, Java程序的內(nèi)存分為兩部分: 堆內(nèi)存(Heap space)和 永久代(Permanent Generation, 簡稱 Permgen):

這兩個(gè)區(qū)域的最大內(nèi)存大小, 由JVM啟動(dòng)參數(shù)?-Xmx?和?-XX:MaxPermSize?指定. 如果沒有明確指定, 則根據(jù)平臺(tái)類型(OS版本+ JVM版本)和物理內(nèi)存的大小來確定左冬。

假如在創(chuàng)建新的對(duì)象時(shí), 堆內(nèi)存中的空間不足以存放新創(chuàng)建的對(duì)象, 就會(huì)引發(fā)java.lang.OutOfMemoryError: Java heap space?錯(cuò)誤寥袭。

不管機(jī)器上還沒有空閑的物理內(nèi)存, 只要堆內(nèi)存使用量達(dá)到最大內(nèi)存限制,就會(huì)拋出?java.lang.OutOfMemoryError: Java heap space?錯(cuò)誤。

原因分析

產(chǎn)生?java.lang.OutOfMemoryError: Java heap space?錯(cuò)誤的原因, 很多時(shí)候, 就類似于將 XXL 號(hào)的對(duì)象,往 S 號(hào)的 Java heap space 里面塞础锐。其實(shí)清楚了原因, 就很容易解決對(duì)不對(duì)? 只要增加堆內(nèi)存的大小, 程序就能正常運(yùn)行. 另外還有一些比較復(fù)雜的情況, 主要是由代碼問題導(dǎo)致的:

超出預(yù)期的訪問量/數(shù)據(jù)量。 應(yīng)用系統(tǒng)設(shè)計(jì)時(shí),一般是有 “容量” 定義的, 部署這么多機(jī)器, 用來處理一定量的數(shù)據(jù)/業(yè)務(wù)漾根。 如果訪問量突然飆升, 超過預(yù)期的閾值, 類似于時(shí)間坐標(biāo)系中針尖形狀的圖譜, 那么在峰值所在的時(shí)間段, 程序很可能就會(huì)卡死、并觸發(fā)?java.lang.OutOfMemoryError: Java heap space?錯(cuò)誤到踏。

內(nèi)存泄露(Memory leak). 這也是一種經(jīng)常出現(xiàn)的情形。由于代碼中的某些錯(cuò)誤, 導(dǎo)致系統(tǒng)占用的內(nèi)存越來越多. 如果某個(gè)方法/某段代碼存在內(nèi)存泄漏的, 每執(zhí)行一次, 就會(huì)(有更多的垃圾對(duì)象)占用更多的內(nèi)存. 隨著運(yùn)行時(shí)間的推移, 泄漏的對(duì)象耗光了堆中的所有內(nèi)存, 那么?java.lang.OutOfMemoryError: Java heap space?錯(cuò)誤就爆發(fā)了尚猿。

具體示例

一個(gè)非常簡單的示例

以下代碼非常簡單, 程序試圖分配容量為 2M 的 int 數(shù)組. 如果指定啟動(dòng)參數(shù)?-Xmx12m, 那么就會(huì)發(fā)生?java.lang.OutOfMemoryError: Java heap space?錯(cuò)誤窝稿。而只要將參數(shù)稍微修改一下, 變成?-Xmx13m, 錯(cuò)誤就不再發(fā)生。

publicclassOOM {staticfinalintSIZE=2*1024*1024;publicstaticvoidmain(String[] a) {int[] i =newint[SIZE];? ? }}

1

2

3

4

5

6

內(nèi)存泄漏示例

這個(gè)示例更真實(shí)一些凿掂。在Java中, 創(chuàng)建一個(gè)新對(duì)象時(shí), 例如?Integer num = new Integer(5);?, 并不需要手動(dòng)分配內(nèi)存伴榔。因?yàn)?JVM 自動(dòng)封裝并處理了內(nèi)存分配. 在程序執(zhí)行過程中, JVM 會(huì)在必要時(shí)檢查內(nèi)存中還有哪些對(duì)象仍在使用, 而不再使用的那些對(duì)象則會(huì)被丟棄, 并將其占用的內(nèi)存回收和重用。這個(gè)過程稱為?垃圾收集. JVM中負(fù)責(zé)垃圾回收的模塊叫做?垃圾收集器(GC)庄萎。

Java的自動(dòng)內(nèi)存管理依賴?GC, GC會(huì)一遍又一遍地掃描內(nèi)存區(qū)域, 將不使用的對(duì)象刪除. 簡單來說,?Java中的內(nèi)存泄漏, 就是那些邏輯上不再使用的對(duì)象, 卻沒有被?垃圾收集程序?給干掉. 從而導(dǎo)致垃圾對(duì)象繼續(xù)占用堆內(nèi)存中, 逐漸堆積, 最后造成?java.lang.OutOfMemoryError: Java heap space?錯(cuò)誤踪少。

很容易寫個(gè)BUG程序, 來模擬內(nèi)存泄漏:

import java.util.*;publicclassKeylessEntry {staticclass Key {? ? ? ? Integer id;? ? ? ? Key(Integer id) {this.id = id;? ? ? ? }? ? ? ? @OverridepublicinthashCode() {returnid.hashCode();? ? ? ? }? ? }publicstaticvoidmain(String[] args) {? ? ? ? Map m =newHashMap();while(true){for(inti =0; i <10000; i++){if(!m.containsKey(newKey(i))){? ? ? ? ? ? ? m.put(newKey(i),"Number:"+ i);? ? ? ? ? }? ? ? ? }? ? ? ? System.out.println("m.size()="+ m.size());? ? ? ? }? ? }}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

粗略一看, 可能覺得沒什么問題, 因?yàn)檫@最多緩存 10000 個(gè)元素嘛! 但仔細(xì)審查就會(huì)發(fā)現(xiàn),?Key?這個(gè)類只重寫了?hashCode()?方法, 卻沒有重寫?equals()?方法, 于是就會(huì)一直往 HashMap 中添加更多的 Key。

請(qǐng)參考:?Java中hashCode與equals方法的約定及重寫原則

隨著時(shí)間推移, “cached” 的對(duì)象會(huì)越來越多. 當(dāng)泄漏的對(duì)象占滿了所有的堆內(nèi)存,?GC?又清理不了, 就會(huì)拋出?java.lang.OutOfMemoryError:Java heap space?錯(cuò)誤糠涛。

解決辦法很簡單, 在?Key?類中恰當(dāng)?shù)貙?shí)現(xiàn)?equals()?方法即可:

@Overridepublicbooleanequals(Object o) {booleanresponse =false;if(oinstanceofKey) {? ? ? response = (((Key)o).id).equals(this.id);? ? }returnresponse;}

1

2

3

4

5

6

7

8

說實(shí)話, 在尋找真正的內(nèi)存泄漏原因時(shí), 你可能會(huì)死掉很多很多的腦細(xì)胞援奢。

一個(gè)SpringMVC中的場景

譯者曾經(jīng)碰到過這樣一種場景:

為了輕易地兼容從 Struts2 遷移到 SpringMVC 的代碼, 在 Controller 中直接獲取 request.

所以在?ControllerBase?類中通過?ThreadLocal?緩存了當(dāng)前線程所持有的 request 對(duì)象:

publicabstractclassControllerBase {privatestaticThreadLocal requestThreadLocal =newThreadLocal();publicstaticHttpServletRequestgetRequest(){returnrequestThreadLocal.get();? ? }publicstaticvoidsetRequest(HttpServletRequest request){if(null== request){? ? ? ? requestThreadLocal.remove();return;? ? ? ? }? ? ? ? requestThreadLocal.set(request);? ? }}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

然后在 SpringMVC的攔截器(Interceptor)實(shí)現(xiàn)類中, 在?preHandle?方法里, 將 request 對(duì)象保存到 ThreadLocal 中:

/**

* 登錄攔截器

*/publicclassLoginCheckInterceptorimplementsHandlerInterceptor{privateList excludeList =newArrayList();publicvoidsetExcludeList(List excludeList) {this.excludeList = excludeList;? ? }privatebooleanvalidURI(HttpServletRequest request){// 如果在排除列表中String uri = request.getRequestURI();? ? ? ? Iterator iterator = excludeList.iterator();while(iterator.hasNext()) {? ? ? ? String exURI = iterator.next();if(null!= exURI && uri.contains(exURI)){returntrue;? ? ? ? }? ? ? ? }// 可以進(jìn)行登錄和權(quán)限之類的判斷LoginUser user = ControllerBase.getLoginUser(request);if(null!= user){returntrue;? ? ? ? }// 未登錄,不允許returnfalse;? ? }privatevoidinitRequestThreadLocal(HttpServletRequest request){? ? ? ? ControllerBase.setRequest(request);? ? ? ? request.setAttribute("basePath", ControllerBase.basePathLessSlash(request));? ? }privatevoidremoveRequestThreadLocal(){? ? ? ? ControllerBase.setRequest(null);? ? }@OverridepublicbooleanpreHandle(HttpServletRequest request,? ? ? ? HttpServletResponse response, Object handler)throwsException {? ? ? ? initRequestThreadLocal(request);// 如果不允許操作,則返回false即可if(false== validURI(request)) {// 此處拋出異常,允許進(jìn)行異常統(tǒng)一處理thrownewNeedLoginException();? ? ? ? }returntrue;? ? }@OverridepublicvoidpostHandle(HttpServletRequest request,? ? ? ? HttpServletResponse response, Object handler, ModelAndView modelAndView)throwsException {? ? ? ? removeRequestThreadLocal();? ? }@OverridepublicvoidafterCompletion(HttpServletRequest request,? ? ? ? HttpServletResponse response, Object handler, Exception ex)throwsException {? ? ? ? removeRequestThreadLocal();? ? }}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

在?postHandle?和?afterCompletion?方法中, 清理 ThreadLocal 中的 request 對(duì)象。

但在實(shí)際使用過程中, 業(yè)務(wù)開發(fā)人員將一個(gè)很大的對(duì)象(如占用內(nèi)存200MB左右的List)設(shè)置為 request 的 Attributes忍捡, 傳遞到 JSP 中集漾。

JSP代碼中可能發(fā)生了異常, 則SpringMVC的postHandle?和?afterCompletion?方法不會(huì)被執(zhí)行。

Tomcat 中的線程調(diào)度, 可能會(huì)一直調(diào)度不到那個(gè)拋出了異常的線程, 于是 ThreadLocal 一直 hold 住 request砸脊。 隨著運(yùn)行時(shí)間的推移,把可用內(nèi)存占滿, 一直在執(zhí)行 Full GC, 系統(tǒng)直接卡死具篇。

后續(xù)的修正: 通過 Filter, 在 finally 語句塊中清理 ThreadLocal。

@WebFilter(value="/*", asyncSupported=true)publicclassClearRequestCacheFilterimplementsFilter{@OverridepublicvoiddoFilter(ServletRequest request, ServletResponse response, FilterChain chain)throwsIOException,? ? ? ? ? ? ServletException {? ? ? ? clearControllerBaseThreadLocal();try{? ? ? ? ? ? chain.doFilter(request, response);? ? ? ? }finally{? ? ? ? ? ? clearControllerBaseThreadLocal();? ? ? ? }? ? }privatevoidclearControllerBaseThreadLocal() {? ? ? ? ControllerBase.setRequest(null);? ? }@Overridepublicvoidinit(FilterConfig filterConfig)throwsServletException {}@Overridepublicvoiddestroy() {}}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

教訓(xùn)是:可以使用 ThreadLocal, 但必須有受控制的釋放措施脓规、一般就是?try-finally?的代碼形式栽连。

說明:?SpringMVC 的 Controller 中, 其實(shí)可以通過?@Autowired?注入 request, 實(shí)際注入的是一個(gè)?HttpServletRequestWrapper對(duì)象, 執(zhí)行時(shí)也是通過 ThreadLocal 機(jī)制調(diào)用當(dāng)前的 request。

常規(guī)方式: 直接在controller方法中接收 request 參數(shù)即可侨舆。

解決方案

如果設(shè)置的最大內(nèi)存不滿足程序的正常運(yùn)行, 只需要增大堆內(nèi)存即可, 配置參數(shù)可以參考下文秒紧。

但很多情況下, 增加堆內(nèi)存空間并不能解決問題。比如存在內(nèi)存泄漏, 增加堆內(nèi)存只會(huì)推遲?java.lang.OutOfMemoryError: Java heap space?錯(cuò)誤的觸發(fā)時(shí)間挨下。

當(dāng)然, 增大堆內(nèi)存, 可能會(huì)增加?GC pauses?的時(shí)間, 從而影響程序的?吞吐量或延遲熔恢。

要從根本上解決問題, 則需要排查分配內(nèi)存的代碼. 簡單來說, 需要解決這些問題:

哪類對(duì)象占用了最多內(nèi)存?

這些對(duì)象是在哪部分代碼中分配的臭笆。

要搞清這一點(diǎn), 可能需要好幾天時(shí)間叙淌。下面是大致的流程:

獲得在生產(chǎn)服務(wù)器上執(zhí)行堆轉(zhuǎn)儲(chǔ)(heap dump)的權(quán)限〕钇蹋“轉(zhuǎn)儲(chǔ)”(Dump)是堆內(nèi)存的快照, 稍后可以用于內(nèi)存分析. 這些快照中可能含有機(jī)密信息, 例如密碼鹰霍、信用卡賬號(hào)等, 所以有時(shí)候, 由于企業(yè)的安全限制, 要獲得生產(chǎn)環(huán)境的堆轉(zhuǎn)儲(chǔ)并不容易。

在適當(dāng)?shù)臅r(shí)間執(zhí)行堆轉(zhuǎn)儲(chǔ)茵乱。一般來說,內(nèi)存分析需要比對(duì)多個(gè)堆轉(zhuǎn)儲(chǔ)文件, 假如獲取的時(shí)機(jī)不對(duì), 那就可能是一個(gè)“廢”的快照. 另外, 每次執(zhí)行堆轉(zhuǎn)儲(chǔ), 都會(huì)對(duì)JVM進(jìn)行“凍結(jié)”, 所以生產(chǎn)環(huán)境中,也不能執(zhí)行太多的Dump操作,否則系統(tǒng)緩慢或者卡死,你的麻煩就大了茂洒。

用另一臺(tái)機(jī)器來加載Dump文件。一般來說, 如果出問題的JVM內(nèi)存是8GB, 那么分析 Heap Dump 的機(jī)器內(nèi)存需要大于 8GB. 打開轉(zhuǎn)儲(chǔ)分析軟件(我們推薦Eclipse MAT?, 當(dāng)然你也可以使用其他工具)瓶竭。

檢測快照中占用內(nèi)存最大的 GC roots督勺。詳情請(qǐng)參考:?Solving OutOfMemoryError (part 6) – Dump is not a waste渠羞。 這對(duì)新手來說可能有點(diǎn)困難, 但這也會(huì)加深你對(duì)堆內(nèi)存結(jié)構(gòu)以及navigation機(jī)制的理解。

接下來, 找出可能會(huì)分配大量對(duì)象的代碼. 如果對(duì)整個(gè)系統(tǒng)非常熟悉, 可能很快就能定位了智哀。

打個(gè)廣告, 我們推薦?Plumbr, the only Java monitoring solution with automatic root cause detection次询。 Plumbr 能捕獲所有的java.lang.OutOfMemoryError?, 并找出其他的性能問題, 例如最消耗內(nèi)存的數(shù)據(jù)結(jié)構(gòu)等等。

Plumbr 在后臺(tái)負(fù)責(zé)收集數(shù)據(jù) —— 包括堆內(nèi)存使用情況(只統(tǒng)計(jì)對(duì)象分布圖, 不涉及實(shí)際數(shù)據(jù)),以及在堆轉(zhuǎn)儲(chǔ)中不容易發(fā)現(xiàn)的各種問題瓷叫。 如果發(fā)生?java.lang.OutOfMemoryError?, 還能在不停機(jī)的情況下, 做必要的數(shù)據(jù)處理. 下面是Plumbr 對(duì)一個(gè)?java.lang.OutOfMemoryError?的提醒:

強(qiáng)大吧, 不需要其他工具和分析, 就能直接看到:

哪類對(duì)象占用了最多的內(nèi)存(此處是 271 個(gè)?com.example.map.impl.PartitionContainer?實(shí)例, 消耗了 173MB 內(nèi)存, 而堆內(nèi)存只有 248MB)

這些對(duì)象在何處創(chuàng)建(大部分是在?MetricManagerImpl?類中,第304行處)

當(dāng)前是誰在引用這些對(duì)象(從 GC root 開始的完整引用鏈)

得知這些信息, 就可以定位到問題的根源, 例如是當(dāng)?shù)鼐啍?shù)據(jù)結(jié)構(gòu)/模型, 只占用必要的內(nèi)存即可屯吊。

當(dāng)然, 根據(jù)內(nèi)存分析的結(jié)果, 以及Plumbr生成的報(bào)告, 如果發(fā)現(xiàn)對(duì)象占用的內(nèi)存很合理, 也不需要修改源代碼的話, 那就增大堆內(nèi)存吧。在這種情況下,修改JVM啟動(dòng)參數(shù), (按比例)增加下面的值:

-Xmx1024m

這里配置Java堆內(nèi)存最大為?1024MB赞辩〈蒲浚可以使用?g/G?表示 GB,?m/M?代表 MB,?k/K?表示 KB.

下面的這些形式都是等價(jià)的, 設(shè)置Java堆的最大空間為 1GB:

# 等價(jià)形式: 最大1GB內(nèi)存

java -Xmx1073741824 com.mycompany.MyClass

java -Xmx1048576k com.mycompany.MyClass

java -Xmx1024m com.mycompany.MyClass

java -Xmx1g com.mycompany.MyClass

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市辨嗽,隨后出現(xiàn)的幾起案子世落,更是在濱河造成了極大的恐慌,老刑警劉巖糟需,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件屉佳,死亡現(xiàn)場離奇詭異,居然都是意外死亡洲押,警方通過查閱死者的電腦和手機(jī)武花,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來杈帐,“玉大人体箕,你說我怎么就攤上這事√敉” “怎么了累铅?”我有些...
    開封第一講書人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長站叼。 經(jīng)常有香客問我娃兽,道長,這世上最難降的妖魔是什么尽楔? 我笑而不...
    開封第一講書人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任投储,我火速辦了婚禮,結(jié)果婚禮上阔馋,老公的妹妹穿的比我還像新娘玛荞。我一直安慰自己,他們只是感情好呕寝,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開白布冲泥。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪凡恍。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,772評(píng)論 1 290
  • 那天怔球,我揣著相機(jī)與錄音嚼酝,去河邊找鬼。 笑死竟坛,一個(gè)胖子當(dāng)著我的面吹牛闽巩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播担汤,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼涎跨,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了崭歧?” 一聲冷哼從身側(cè)響起隅很,我...
    開封第一講書人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎率碾,沒想到半個(gè)月后叔营,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡所宰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年绒尊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片仔粥。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡婴谱,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出躯泰,到底是詐尸還是另有隱情谭羔,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布斟冕,位于F島的核電站口糕,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏磕蛇。R本人自食惡果不足惜景描,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望秀撇。 院中可真熱鬧超棺,春花似錦、人聲如沸呵燕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至氧苍,卻和暖如春夜矗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背让虐。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來泰國打工紊撕, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人赡突。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓对扶,卻偏偏與公主長得像,于是被迫代替她去往敵國和親惭缰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子浪南,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

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

  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司漱受,掛了不少络凿,但最終還是拿到小米、百度拜效、阿里喷众、京東、新浪紧憾、CVTE到千、樂視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,209評(píng)論 11 349
  • Java8張圖 11、字符串不變性 12赴穗、equals()方法憔四、hashCode()方法的區(qū)別 13、...
    Miley_MOJIE閱讀 3,696評(píng)論 0 11
  • Big Endian(大端)指最高有效字節(jié)在內(nèi)存的低地址位般眉;Little Endian(小端)指最低有效字節(jié)在內(nèi)存...
    yekai閱讀 543評(píng)論 0 0
  • 2018甸赃,你好柿汛!趁早,你好埠对! 偶然遇見络断,必然喜歡。當(dāng)內(nèi)心未泯滅的上進(jìn)心和對(duì)人生精進(jìn)的期待项玛,如魚得水般貌笨,遇上了一個(gè)組...
    林小辮兒閱讀 208評(píng)論 0 2
  • 今天早晨爸爸媽媽帶著我和妹去打預(yù)防針,我們到那兒的時(shí)候人還不多襟沮,但是我取的號(hào)是38號(hào)锥惋,哇昌腰,人們?cè)趺炊寄敲丛绲娜〉教?hào)...
    崔珂豪本人閱讀 371評(píng)論 0 2