小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

【前端進(jìn)階之路】?jī)?nèi)存基本知識(shí)

 郭恩 2018-12-13

內(nèi)存管理

本文以V8為背景

對(duì)之前的文章進(jìn)行重新編輯,內(nèi)容做了很多的調(diào)整,使其具有邏輯更加緊湊,內(nèi)容更加全面。

1. 基礎(chǔ)概念

1.1 生命周期

不管什么程序語(yǔ)言,內(nèi)存生命周期基本是一致的:

  1. 分配你所需要的內(nèi)存

  2. 使用分配到的內(nèi)存(讀、寫)

  3. 不需要時(shí)將其釋放、歸還

在所有語(yǔ)言中第一和第二部分都很清晰。最后一步在低級(jí)語(yǔ)言(例如C語(yǔ)言)中很清晰,但是在像JavaScript等高級(jí)語(yǔ)言中,這一步依賴于垃圾回收機(jī)制,一般情況下不用程序員操心。

1.2 堆與棧

我們知道,內(nèi)存空間可以分為??臻g和堆空間,其中

  1. ??臻g:由操作系統(tǒng)自動(dòng)分配釋放,存放函數(shù)的參數(shù)值,局部變量的值等。其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧。

  2. 堆空間:一般由程序員分配釋放,這部分空間就要考慮垃圾回收的問題。

1.3 基本類型與引用類型

在JavaScript中

  1. 基本類型:undefined,null,boolean,number,string,在內(nèi)存中占有固定的大小,他們的值保存在棧空間中,我們通過按值來訪問。

  2. 引用類型:Object,Array,Function,則在堆內(nèi)存中為這個(gè)值分配空間,然后把它的內(nèi)存地址保存在棧內(nèi)存中。(區(qū)分變量和對(duì)象)

圖片描述

1.4 V8的變量存放

圖片描述

  • handle

    handle是指向?qū)ο蟮闹羔?,在V8中,所有對(duì)象都是通過handle來引用,handle主要用于V8的垃圾回收機(jī)制。進(jìn)一步的,handle分為兩種:

    • 持久化(Persistent handle),存放在堆上

    • 本地化(Local handle),存放在棧上

  • scope

    scope是handle的集合,可以包含若干個(gè)handle,這樣就無需將每個(gè)handle逐次釋放,而是直接釋放整個(gè)scope。

  • context

    context是一個(gè)執(zhí)行器環(huán)境,使用context可以將相互分離的JavaScript腳本在同一個(gè)V8實(shí)例中運(yùn)行,而不互相干涉。在運(yùn)行JavaScript腳本時(shí),需要顯示的指定context對(duì)象。

2. 垃圾回收

2.1 分代策略

腳本中,絕大多數(shù)對(duì)象的生存期很短,只有某些對(duì)象的生存期較長(zhǎng)。為利用這一特點(diǎn),V8將堆進(jìn)行了分代。對(duì)象起初會(huì)被分配在新生區(qū)。在新生區(qū)的內(nèi)存分配非常容易:我們只需保有一個(gè)指向內(nèi)存區(qū)的指針,不斷根據(jù)新對(duì)象的大小對(duì)其進(jìn)行遞增即可。當(dāng)該指針達(dá)到了新生區(qū)的末尾,就會(huì)有一次清理(小周期),清理掉新生區(qū)中不活躍的死對(duì)象。對(duì)于活躍超過2個(gè)小周期的對(duì)象,則需將其移動(dòng)至老生區(qū)。而在老生區(qū)則使用標(biāo)記清除的算法來進(jìn)行垃圾回收。V8通過分別對(duì)新生代對(duì)象和老生代對(duì)象使用不同的垃圾回收算法來提升來及回收的效率。這就是所謂的分代策略。

默認(rèn)情況下,64位環(huán)境下的V8引擎的新生代內(nèi)存大小32MB、老生代內(nèi)存大小為1400MB,而32位則減半,分別為16MB和700MB

根據(jù)分代策略,V8將堆空間進(jìn)行了分隔:

  • 新生區(qū)

大多數(shù)對(duì)象被分配在這里,新生區(qū)是一個(gè)很小的區(qū)域,垃圾回收在這個(gè)區(qū)域非常頻繁,與其他區(qū)域相獨(dú)立。

  • 老生指針區(qū)

這里包含大多數(shù)可能存儲(chǔ)指向其他對(duì)象的指針的對(duì)象,大多數(shù)在新生區(qū)存活了一段時(shí)間(2個(gè)周期)的對(duì)象都會(huì)被挪到這里。

  • 老生數(shù)據(jù)區(qū)

這里存放只包含原始數(shù)據(jù)的對(duì)象,這些對(duì)象沒有執(zhí)行其他對(duì)象的指針,例如字符串,數(shù)字?jǐn)?shù)組等,它們?cè)谛律鷧^(qū)存活了一段時(shí)間后會(huì)被移動(dòng)到這里。

  • 大對(duì)象區(qū)

每一個(gè)區(qū)域都是由一組內(nèi)存頁(yè)構(gòu)成的。除大對(duì)象區(qū)的內(nèi)存頁(yè)較大之外,每個(gè)區(qū)的內(nèi)存頁(yè)都是1MB大小,且按1MB內(nèi)存對(duì)齊。對(duì)象超過一定大小時(shí)就會(huì)被放置到這個(gè)區(qū),垃圾回收期從不移動(dòng)這個(gè)區(qū)域的對(duì)象。

  • 代碼區(qū)

代碼對(duì)象,也就是包含JIT之后指令的對(duì)象,會(huì)被分配到這里。這里是唯一擁有執(zhí)行權(quán)限的內(nèi)存區(qū)。(如果代碼對(duì)象因過大而被放到大對(duì)象區(qū),則該大對(duì)象所對(duì)應(yīng)的內(nèi)存也是可執(zhí)行的。)

  • Cell區(qū)、屬性Cell區(qū)、Map區(qū)

這些區(qū)域存放Cell、屬性Cell和Map,每個(gè)區(qū)域因?yàn)槎际谴鎯?chǔ)相同大小的元素,因此內(nèi)存結(jié)構(gòu)很簡(jiǎn)單,這里也是為了方便進(jìn)行回收。

在 node-v4.x 之后,區(qū)域進(jìn)行了合并為:新生區(qū),老生區(qū),大對(duì)象區(qū),Map區(qū),Code區(qū)

此外,對(duì)于一個(gè)對(duì)象所占的內(nèi)存空間,也涉及兩個(gè)概念:shallow sizeretained size。

  • shallow size就是對(duì)象本身占用內(nèi)存的大小,不包含其引用的對(duì)象。常規(guī)對(duì)象(非數(shù)組)的shallow size有其成員變量的數(shù)量和類型決定

  • retained size是該對(duì)象自己的shallow size,加上從該對(duì)象能直接或間接訪問到對(duì)象的shallow size之和。換句話說,retained size是該對(duì)象被GC之后所能回收到內(nèi)存的總和。

這兩個(gè)概念在使用chrome的開發(fā)工具中會(huì)看到。

垃圾回收釋放的內(nèi)存即為Retained Size的大小。

2.2 新生區(qū)的半空間分配策略

新生代使用半空間(Semi-space)分配策略,其中新對(duì)象最初分配在新生代的活躍半空間內(nèi)。一旦半空間已滿,一個(gè)Scavenge操作將活躍對(duì)象移出到其他半空間中,被認(rèn)為是長(zhǎng)期駐存的對(duì)象,并被晉升為老生代。一旦活躍對(duì)象已被移出,則在舊的半空間中剩下的任何死亡對(duì)象被丟棄。

具體的如下:

YG被平分為兩部分空間From和To,所有內(nèi)存從To空間被分配出去,當(dāng)To滿時(shí),開始觸發(fā)GC。

例如說:

某時(shí)刻,To已經(jīng)為A、B和C分配了內(nèi)存,當(dāng)前它只剩下一小塊內(nèi)存未分配。而From所有的內(nèi)存都空閑著。

圖片描述

此時(shí),一個(gè)程序需要為D分配內(nèi)存,但D需要的內(nèi)存大小超出了To未分配的內(nèi)存,此時(shí)觸發(fā)GC,頁(yè)面停止執(zhí)行

圖片描述

接著From和To進(jìn)行對(duì)換,即原來的To空間被標(biāo)志為From,F(xiàn)rom被標(biāo)志為To。并且把活的變量值(B)標(biāo)志出來,而垃圾(A、C)未被標(biāo)志,它們將會(huì)被清掉。

圖片描述

活躍的變量(B)會(huì)被復(fù)制到To空間,而垃圾(A、C)則被回收。同時(shí),D被分配到To空間,最后的情況如下。

圖片描述

至此,整個(gè)GC完成,此過程中頁(yè)面會(huì)阻塞,所以要盡可能的快。

2.2.1 對(duì)象的晉升

當(dāng)一個(gè)新生代的對(duì)象在滿足一定條件下,會(huì)從新生代被移到老生代,這就是對(duì)象的晉升。具體的移動(dòng)的標(biāo)準(zhǔn)有兩種

  1. 對(duì)象從From空間復(fù)制到To空間時(shí),會(huì)檢查它的內(nèi)存地址來判斷這個(gè)對(duì)象是否經(jīng)歷過一次新生代的清理結(jié)果,如果是(說明存活了兩個(gè)周期了),則賦值到老生代中,否則則賦值到To空間中。

  2. 對(duì)象從From空間復(fù)制到To空間時(shí),如果To空間已經(jīng)被使用了超過25%,那么這個(gè)對(duì)象直接被復(fù)制到老生代。

2.3 老生代

V8在老生代中采用Mark-Sweep和Mark-Compact相結(jié)合的垃圾回收策略。

2.3.1 標(biāo)記

標(biāo)記-清除算法分為標(biāo)記和清除兩個(gè)階段。

標(biāo)記階段,所有堆上的活躍對(duì)象都會(huì)被標(biāo)記,每個(gè)內(nèi)存頁(yè)有一個(gè)用來標(biāo)記對(duì)象的位圖,位圖中的每一位對(duì)應(yīng)的內(nèi)存頁(yè)中的一個(gè)字,這個(gè)位圖需要占據(jù)一定的空間。另外還有兩位用來標(biāo)記對(duì)象的狀態(tài):

  • 如果一個(gè)對(duì)象為白對(duì)象,表示還未被垃圾回收器發(fā)現(xiàn)

  • 如果一個(gè)對(duì)象為灰對(duì)象,表示已經(jīng)被垃圾回收器發(fā)現(xiàn),但其鄰接對(duì)象尚未處理

  • 如果一個(gè)對(duì)象為黑對(duì)象,表示已經(jīng)被垃圾回收器發(fā)現(xiàn),其鄰接對(duì)象已全部處理

那么這里怎么理解標(biāo)記的過程?這就必須知道:內(nèi)存管理方式實(shí)際上基于的概念。

GC Root是內(nèi)存的根節(jié)點(diǎn),在瀏覽器中它是window,在Nodejs中則是global對(duì)象

  • 圖的節(jié)點(diǎn)名稱是創(chuàng)建它的構(gòu)造函數(shù)名

  • 圖的邊是引用它的屬性名或者變量名

有很多內(nèi)部的GC Root對(duì)用戶來說都不是很重要,從應(yīng)用的角度來說有下面幾種情況:

  • 全局變量或者全局函數(shù)會(huì)一直被window這種全局對(duì)象所指向,它們會(huì)一直占據(jù)著內(nèi)存

  • DOM節(jié)點(diǎn)只有在被javascript對(duì)象引用的情況下,會(huì)留在內(nèi)存中。

  • 在進(jìn)行debug或者console的時(shí)候,可能會(huì)由于保留了上下文,導(dǎo)致本該被釋放的對(duì)象被保留下來。

實(shí)際上,標(biāo)記的過程正是以由GC Root建立的圖為基礎(chǔ),來實(shí)現(xiàn)對(duì)象的標(biāo)記,標(biāo)記算法的核心是深度優(yōu)先搜索,大致實(shí)現(xiàn)如下:

  1. 初始時(shí),位圖為空,所有對(duì)象都是白對(duì)象。

  2. 從根對(duì)象(GC Root)到達(dá)的對(duì)象會(huì)被染為灰色,放到一個(gè)單獨(dú)的雙端隊(duì)列中。

  3. 標(biāo)記階段,每次都會(huì)從雙端隊(duì)列中取出一個(gè)對(duì)象,并將其轉(zhuǎn)變?yōu)楹趯?duì)象,其鄰接對(duì)象轉(zhuǎn)變?yōu)榛?,然后把其鄰接?duì)象加入到雙端隊(duì)列中。

  4. 如果雙端隊(duì)列為空或者所有對(duì)象都變成黑對(duì)象,則結(jié)束。

這個(gè)算法實(shí)現(xiàn)起來還是蠻繁瑣的,從的角度來看,其實(shí)標(biāo)記的過程實(shí)際上是區(qū)分活節(jié)點(diǎn)和垃圾節(jié)點(diǎn)的過程。

  • 從GC Root開始遍歷圖,所有能到達(dá)的節(jié)點(diǎn)稱為活節(jié)點(diǎn)。

  • GC Root不能到達(dá)的節(jié)點(diǎn),該節(jié)點(diǎn)就成為垃圾,將會(huì)被回收。

標(biāo)記結(jié)束后,所有的對(duì)象非黑(活躍節(jié)點(diǎn))即白(垃圾節(jié)點(diǎn))。

標(biāo)記時(shí)間取決于必須標(biāo)記的活躍對(duì)象的數(shù)目,對(duì)于一個(gè)大的web應(yīng)用,整個(gè)堆棧的標(biāo)記可能需要超過100ms。由于全停頓會(huì)造成了瀏覽器一段時(shí)間無響應(yīng),所以V8使用了一種增量標(biāo)記的方式標(biāo)記活躍對(duì)象,將完整的標(biāo)記拆分成很多小的步驟,每做完一部分就停下來,讓JavaScript的應(yīng)用線程執(zhí)行一會(huì),這樣垃圾回收與應(yīng)用線程交替執(zhí)行。V8可以讓每個(gè)標(biāo)記步驟的持續(xù)時(shí)間低于5ms。

舉個(gè)例子:

window.ob = 2;
window.oa = {
    b1 : 3,
    b2 : {
        c1 : 4,
        c2 : "字符串"
    }
};
window.ob = undefined;

圖片描述

例如圖中灰色的節(jié)點(diǎn),它原來代表ob變量值,當(dāng)window.ob = undefined后,此節(jié)點(diǎn)與GC Root連接的路徑ob被切斷了,它就成了垃圾,將會(huì)被回收。

2.3.2 清除(Sweep)

由于標(biāo)記完成后,所有對(duì)象都已經(jīng)被標(biāo)記,即不是活躍對(duì)象就是死亡對(duì)象,堆上有多少空間已經(jīng)確定。清除時(shí),垃圾回收器會(huì)掃描連續(xù)存放的死對(duì)象,將其變成空閑空間。這個(gè)任務(wù)是由專門的清掃線程同步執(zhí)行。

2.3.3 整理(Compact)

標(biāo)記清除有一個(gè)問題就是進(jìn)行一次標(biāo)記清楚后,內(nèi)存空間往往是不連續(xù)的,會(huì)出現(xiàn)很多的內(nèi)存碎片。如果后續(xù)需要分配一個(gè)需要內(nèi)存空間較多的對(duì)象時(shí),如果所有的內(nèi)存碎片都不夠用,將會(huì)使得V8無法完成這次分配,提前觸發(fā)垃圾回收。

標(biāo)記整理正是為了解決標(biāo)記清除所帶來的內(nèi)存碎片的問題。標(biāo)記整理在標(biāo)記清除的基礎(chǔ)進(jìn)行修改,將其的清除階段變?yōu)榫o縮極端。在整理的過程中,將活著的對(duì)象向內(nèi)存區(qū)的一段移動(dòng),移動(dòng)完成后直接清理掉邊界外的內(nèi)存。緊縮過程涉及對(duì)象的移動(dòng),所以效率并不是太好,但是能保證不會(huì)生成內(nèi)存碎片。

2.4 垃圾回收總結(jié)

  1. 新生代對(duì)象的Scavenge,這通常是快速的;

  2. 通過增量方式的標(biāo)記步驟,依賴于需要標(biāo)記的對(duì)象數(shù)量,時(shí)間可以任意長(zhǎng);

  3. 完整垃圾回收,這可能需要很長(zhǎng)的時(shí)間;

  4. 帶內(nèi)存緊縮的完整垃圾回收,這也可能需要很長(zhǎng)的時(shí)間,需要進(jìn)行內(nèi)存緊縮。

3. 內(nèi)存問題

3.1 內(nèi)存泄漏

內(nèi)存泄漏是指計(jì)算機(jī)可用內(nèi)存的逐漸減少,原因通常是程序持續(xù)無法釋放其使用的臨時(shí)內(nèi)存。

先來一個(gè)最簡(jiǎn)單的DOM泄漏的例子

var el = document.getElementById("_p");
el.mark = "marked";

//移除P
function removeP() {
    el.parentNode.removeChild(el);
    // el = null;
}

程序非常簡(jiǎn)單,只是把id為_p的HTML元素從頁(yè)面移除,在移除之前從GC Root遍歷此P元素有兩條路可走。在執(zhí)行removeP()之后,按理來說該元素應(yīng)該成為垃圾,所占有的內(nèi)存應(yīng)該被釋放掉,但是由于還存在這路徑el沒有被切斷,p元素占有的內(nèi)存無法被釋放,導(dǎo)致了內(nèi)存泄漏。

圖片描述

3.2 內(nèi)存占用過多

這個(gè)問題很容易理解。例如使用事件代理來減少事件監(jiān)聽的函數(shù),從而減少內(nèi)存分配的開銷。

3.3 gc卡頓

如果你的頁(yè)面垃圾回收很頻繁,那說明你的頁(yè)面可能內(nèi)存使用分配太頻繁了。頻繁的GC可能也會(huì)導(dǎo)致頁(yè)面卡頓。

在一些框架中,如果創(chuàng)建一個(gè)大對(duì)象之后,可能不會(huì)很快就將其釋放,而是會(huì)緩存起來,直到?jīng)]有用處為止。

4. chrome dev tools

在使用Chrome進(jìn)行內(nèi)存分析的時(shí)候,要先在chrome菜單-》工具,或者直接按shift+esc,找到內(nèi)存管理器,然后選上JavaScript使用的內(nèi)存(JavaScipt Memory)。

4.1 timeline

通過Timeline的內(nèi)存模式,可以在宏觀上觀察到web應(yīng)用的內(nèi)存情況,一般我們需要關(guān)注的點(diǎn):

  1. GC的時(shí)間長(zhǎng)度是否正常?

  2. GC頻率是否正常?過于頻繁會(huì)導(dǎo)致卡頓

  3. 內(nèi)存趨勢(shì)圖是否正常?

  4. DOM趨勢(shì)圖是否正常?

這些關(guān)注點(diǎn)都可以在timeline的內(nèi)存視圖中看到,如圖

圖片描述

timeline統(tǒng)計(jì)的內(nèi)存變化主要有:

  • js heap:堆空間

  • documents:文檔計(jì)數(shù)

  • node:dom節(jié)點(diǎn)數(shù)

  • event listener:事件監(jiān)聽器

  • CPU:在手機(jī)端暫時(shí)沒有

此外還可以通過event log看到這期間頁(yè)面執(zhí)行的操作

4.2 profile

profile面板我們關(guān)注的是Take Heap SnapshotRecode Heap Allocations

圖片描述

profile使用必須知道的:

  1. 標(biāo)志為黃色的表示可能內(nèi)存泄漏

  2. 標(biāo)志為紅色表示應(yīng)該是發(fā)生內(nèi)存泄漏

在profile中的幾個(gè)概念:

  1. (global property):全局對(duì)象,還有全局對(duì)象引用的對(duì)象

  2. (closure):閉包,這里需要關(guān)注一下

  3. (compiled code):V8會(huì)先代碼編譯成特定的語(yǔ)言,再執(zhí)行

  4. (array,string,number,regexp):這些內(nèi)置對(duì)象的引用

  5. HTML..Element:dom對(duì)象的引用

4.2.1 Take Heap Snapshot

使用快照,必須知道:

  1. 每次進(jìn)行快照時(shí),chrome都會(huì)先自動(dòng)執(zhí)行一個(gè)gc

  2. 只有活躍的值,才會(huì)反映在快照里

快照有三個(gè)視圖,它們分別有各自的作用

  1. Summary View

    默認(rèn)是以概要視圖顯示的,顯示了對(duì)象總數(shù),可以展開顯示具體內(nèi)容

  2. Comparison View

    該視圖用來對(duì)照不同的快照來找到快照之間的差異

  3. Containment View

    在這個(gè)視圖中,包括三個(gè)點(diǎn)

    • DOMWindow objects:js中的全局對(duì)象

    • GC Root:VM垃圾回收所使用的GC Root

    • Native Object:被放置到VM中的內(nèi)置對(duì)象

    好吧。暫時(shí)不知道有什么用?以后再補(bǔ)充。

4.2.2 Recode Heap Allocations

這個(gè)功能可以動(dòng)態(tài)監(jiān)控,通過次工具可以看到

  1. 什么時(shí)候分配了內(nèi)存,剛剛分配的內(nèi)存會(huì)以深藍(lán)色的柱子表示,柱子越高,內(nèi)存越大

  2. 什么時(shí)候回收了內(nèi)存,內(nèi)存被回收的時(shí)候,柱子變?yōu)榛疑?/p>

4.3 實(shí)踐

例子1:timeline來查看正常的內(nèi)存

圖片描述

例子2:通過timeline來發(fā)現(xiàn)內(nèi)存泄漏

圖片描述

可以看到隨著時(shí)間的增長(zhǎng),頁(yè)面占用的內(nèi)存越來越多,

在這種情況下就可以懷疑有內(nèi)存泄漏了,也有可能是瀏覽器還沒有進(jìn)行g(shù)c,這個(gè)時(shí)候我們可以強(qiáng)制進(jìn)行垃圾回收(垃圾筒圖標(biāo))

反復(fù)測(cè)試,如果發(fā)現(xiàn)無論怎么樣,內(nèi)存一直在增長(zhǎng),那么估計(jì)你就遇到內(nèi)存泄漏的問題了。

如果頁(yè)面中DOM節(jié)點(diǎn)的數(shù)量一直在攀升,那么肯定出現(xiàn)DOM泄漏了

圖片描述

例子3:驗(yàn)證快照之前會(huì)進(jìn)行g(shù)c

function Test (s) {
    this.s = s;
}
var _test1 = new Test("__________test___1_________");
var _test2 = new Test("__________test___2_________");
new Test("你看不到我,就是這么神奇");

圖片描述

例子4:通過snapshot來發(fā)現(xiàn)內(nèi)存泄漏

  1. 打開例子之后,先進(jìn)行一次快照

  2. 點(diǎn)擊action,代表這用戶的交互

  3. 再進(jìn)行一次快照

  4. 使用comparison視圖,對(duì)比兩次快照,如圖

圖片描述

可以看到,action之后,內(nèi)存的數(shù)量是增加的(注意,已經(jīng)gc過了),這說明web應(yīng)用極有內(nèi)存泄漏。

一個(gè)原則就是找到本不應(yīng)該存在卻還存在的那些值。

例子5:通過內(nèi)存分配的情況來分析

圖片描述

點(diǎn)擊藍(lán)色的柱子,可以看到詳細(xì)的情況,來進(jìn)行分析

例子6:通過timeline來分析gc過于頻繁導(dǎo)致卡頓的問題

圖片描述

此例子在移動(dòng)手機(jī)的瀏覽器進(jìn)行測(cè)試,頁(yè)面還是相對(duì)簡(jiǎn)單,在比較復(fù)雜的移動(dòng)web應(yīng)用,這種情況還是比較危險(xiǎn)的,可能會(huì)導(dǎo)致頁(yè)面卡死。

參考

  1. MDN:內(nèi)存管理

  2. Chrome開發(fā)者工具之JavaScript內(nèi)存分析

  3. Google V8的垃圾回收引擎

  4. 測(cè)試?yán)?/a>

  5. 如何編寫避免垃圾開銷的實(shí)時(shí)Javascript代碼

  6. 詳解js變量、作用域及內(nèi)存

  7. V8 concept

  8. 淺談V8引擎中的垃圾回收機(jī)制

  9. 使用 Google V8 引擎開發(fā)可定制的應(yīng)用程序

  10. a tour of v8 garbage collection

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多