Android 性能優(yōu)化&內(nèi)存篇

本篇主要講解android內(nèi)存性能優(yōu)化之檢測(cè)方案。內(nèi)存性能主要包括內(nèi)存泄漏, 內(nèi)存抖動(dòng), 內(nèi)存持續(xù)增長(zhǎng)(但GC后會(huì)下降), 內(nèi)存占用過(guò)大等問(wèn)題。

Android內(nèi)存分析方向:

  • Java 內(nèi)存分析
    • Java中的內(nèi)存泄露主要特征:可達(dá),無(wú)用
    • 無(wú)用指的是創(chuàng)建了但是不再使用之后沒(méi)有釋放
    • 能重用但是卻創(chuàng)建了新的對(duì)象進(jìn)行處理
  • Native 內(nèi)存分析
    • 堆中new的對(duì)象未釋放
    • 對(duì)象引用導(dǎo)致無(wú)法釋放
  • JS 中內(nèi)存分析

本篇主要講解Java內(nèi)存分析。

一. 日志分析

查看日志中是否有頻繁的GC。通常通過(guò)log,我們可以初步定為大部分內(nèi)存等問(wèn)題。

二. 常見(jiàn)內(nèi)存泄漏查找

Context 泄漏, 主要為Activity 傳遞泄漏, context 未使用applciationConext 在單例創(chuàng)建時(shí)。
Handler 泄漏 , handler中持有view ,context 等做耗時(shí)操作。
Cursor 泄漏 , cursor未關(guān)閉
register 未 unregister
Bitmap
adapter 未使用convertView
不良代碼等

三. 命令dumpsys meminfo分析

adb shell dumpsys meminfo com.i2finance.shexpress 
Applications Memory Usage (kB):
Uptime: 142597122 Realtime: 236611715

** MEMINFO in pid 25126 [com.i2finance.shexpress] **
                   Pss  Private  Private  Swapped     Heap     Heap     Heap
                 Total    Dirty    Clean    Dirty     Size    Alloc     Free
                ------   ------   ------   ------   ------   ------   ------
  Native Heap    61111    61084        0        0    69888    64350     5537
  Dalvik Heap    49451    49316        0        0    71737    67348     4389
 Dalvik Other     3333     3332        0        0                           
        Stack      960      960        0        0                           
       Cursor       12       12        0        0                           
       Ashmem      130       88        0        0                           
      Gfx dev    23780    23780        0        0                           
    Other dev        4        0        4        0                           
     .so mmap     4373      396     3108        0                           
    .jar mmap       80        0       76        0                           
    .apk mmap    17986       64    17580        0                           
    .ttf mmap       96        0       80        0                           
    .dex mmap    15729       16    14244        0                           
    .oat mmap     2378        0      624        0                           
    .art mmap     1859     1624        8        0                           
   Other mmap     2039       12     1308        0                           
      Unknown    84240    84240        0        0                           
        TOTAL   267561   224924    37032        0   141625   131698     9926

 App Summary
                       Pss(KB)
                        ------
           Java Heap:    50948
         Native Heap:    61084
                Code:    36188
               Stack:      960
            Graphics:    23780
       Private Other:    88996
              System:     5605

               TOTAL:   267561      TOTAL SWAP (KB):        0

 Objects
               Views:      429         ViewRootImpl:        2
         AppContexts:        2           Activities:        1
              Assets:        7        AssetManagers:        3
       Local Binders:       37        Proxy Binders:       31
       Parcel memory:       26         Parcel count:       65
    Death Recipients:        2      OpenSSL Sockets:        6

 SQL
         MEMORY_USED:      567
  PAGECACHE_OVERFLOW:      157          MALLOC_SIZE:       62

 DATABASES
      pgsz     dbsz   Lookaside(b)          cache  Dbname
         4       24             45         5/24/6  /data/user/0/com.i2finance.shexpress/databases/pa_data_cache.db
         4       28             19         1/16/2  /data/user/0/com.i2finance.shexpress/databases/mpush.db
         4       60             37         5/18/6  /data/user/0/com.i2finance.shexpress/databases/fstandard.db
         4       60             91      466/22/11  /data/user/0/com.i2finance.shexpress/databases/fstandard.db (2)
         4       24             40         5/24/6  /data/user/0/com.i2finance.shexpress/databases/pa_data_cache.db

 Asset Allocations
    zip:/data/user/0/com.i2finance.shexpress/files/paanydoor_resource_3.5.0.36.jar:/resources.arsc: 67K

meminfo的信息中各字段都是什么含義, 要理解各字段含義,我們才好進(jìn)行內(nèi)存的優(yōu)化。

首先了解兩個(gè)概念:

  • 私有內(nèi)存(Dirty and Clean):
    進(jìn)程獨(dú)占內(nèi)存。也就是進(jìn)程銷(xiāo)毀時(shí)可以回收的內(nèi)存容量。通常private Dirty內(nèi)存是最重要的部分,因?yàn)橹槐蛔约哼M(jìn)程使用。Dirty內(nèi)存是已經(jīng)被修改的內(nèi)存頁(yè),因此必須常駐內(nèi)存(因?yàn)闆](méi)有swap);Clean內(nèi)存是已經(jīng)映射持久文件使用的內(nèi)存頁(yè)(例如正在被執(zhí)行的代碼),因此一段時(shí)間不使用的話就可以置換出去。

  • 實(shí)際使用內(nèi)存(PSS):
    將跨進(jìn)程共享頁(yè)也加入進(jìn)來(lái), 進(jìn)行按比例計(jì)算PSS。這樣能夠比較準(zhǔn)確的表示進(jìn)程占用的實(shí)際物理內(nèi)存。

通常我們需要關(guān)注PSS TOTALPrivate Dirty .

  • Dalvik Heap
    dalvik虛擬機(jī)分配的內(nèi)存。PSS Total包含所有Zygote分配使用的內(nèi)存,共享跨進(jìn)程加權(quán)。PrivateDirty 是應(yīng)用獨(dú)占內(nèi)存大小,包含獨(dú)自分配的部分和應(yīng)用進(jìn)程從Zygote復(fù)制時(shí)被修改的Zygote分配的內(nèi)存頁(yè)。 HeapAlloc 是Dalvik堆和本地堆分配使用的大小,它的值比Pss Total和Private Dirty大,因?yàn)檫M(jìn)程是從Zygote中復(fù)制分裂出來(lái)的,包含了進(jìn)程共享的分配部分。
  • .so mmap & .dex mmap ... mmap 映射本地或虛擬機(jī)代碼到使用的內(nèi)存中。
  • Unknown 無(wú)法歸類(lèi)的其他項(xiàng)。主要包括大部分的本地分配。
  • Native Heap native代碼申請(qǐng)的內(nèi)存, 堆和棧,及靜態(tài)代碼塊等。
  • TOTAL進(jìn)程總使用的實(shí)際內(nèi)存。
  • Objects 中顯示持有對(duì)象的個(gè)數(shù)。這些數(shù)據(jù)也是分析內(nèi)存泄漏的重要數(shù)據(jù)。如activity等。

四. Heap Viewer

Heap Viewer 能做什么?

  • 事實(shí)查看內(nèi)存分配情況和空閑內(nèi)存大小
  • 發(fā)現(xiàn)memory Leaks

AS中點(diǎn)擊機(jī)器人圖標(biāo)打開(kāi)Android Device Mointor, 如下:
選中進(jìn)程進(jìn)行Heap 分析,點(diǎn)擊update heap, 查看右側(cè)的heap標(biāo)簽頁(yè)

Paste_Image.png

Heap視圖顯示了堆內(nèi)存使用的情況,每次垃圾回收都會(huì)更新,要查看更新情況, 點(diǎn)擊Cause GC即可。
下面的內(nèi)容顯示的是分配的內(nèi)存,按照類(lèi)型分類(lèi):


Paste_Image.png

如何檢查內(nèi)存泄漏

我們需要在執(zhí)行查看內(nèi)存是否有泄漏的用例之前和之后執(zhí)行GC,即手動(dòng)點(diǎn)擊Cause GC,觀察allocated大小,查看內(nèi)存是否在一個(gè)穩(wěn)定的數(shù)值,多次操作,只要內(nèi)存穩(wěn)定,即沒(méi)有內(nèi)存泄漏, 如果不斷變大,即表示有內(nèi)存泄漏。
該工具也可以用來(lái)查看是否會(huì)發(fā)生內(nèi)存抖動(dòng)

五. 生成Dump

分析內(nèi)存泄漏,我們需要生成相關(guān)的內(nèi)存Dump,那么我們?nèi)绾紊蒬ump文件來(lái)進(jìn)行分析。

目前有兩種方式:

  • 打開(kāi)Android Device Monitor
    點(diǎn)擊dump Hprof file


    Paste_Image.png

    會(huì)生成一份Hprof文件,但該hprof文件我們無(wú)法打開(kāi),需要進(jìn)行轉(zhuǎn)換之后才能用MAT工具打開(kāi),可以使用命令

hprof-conv com.i2finance.shexpress.hprof xxx.hprof 轉(zhuǎn)換生成可用的hprof文件。

  • 使用Android Studio
    打開(kāi)Android Studio 的Android Monitor , 選中Memory 標(biāo)簽:
    Paste_Image.png

    點(diǎn)擊Dump Java Heap 即可生成對(duì)應(yīng)的hprof文件,在側(cè)邊欄中打開(kāi)Captures文件,選中文件點(diǎn)擊右鍵,export 出標(biāo)準(zhǔn)的hprof文件。
    Paste_Image.png

六. Heap Snapshot

獲取Java 堆內(nèi)存詳細(xì)信息,可以分析出內(nèi)存泄漏的問(wèn)題。
打開(kāi)Android Studio 的Android Monitor , 選中Memory 標(biāo)簽, 點(diǎn)擊Dump heap,生成hprof文件。AS會(huì)自動(dòng)打開(kāi)該文件,見(jiàn)下圖,但是該功能有點(diǎn)弱,建議還是轉(zhuǎn)換成mat可識(shí)別的hprof,使用mat進(jìn)行分析。


Paste_Image.png

七. 使用LeakCanary

使用內(nèi)存檢測(cè)軟件leakCanary

  • 添加依賴包

build.gradle 中增加依賴

debugCompile 'com.squareup.leakcanary:leakcanary-android:1.4-beta2'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2'
  • 開(kāi)啟leakCanary

Applciationoncreate中增加語(yǔ)句

LeakCanary.install(this);

查看leak詳情。
當(dāng)發(fā)生內(nèi)存泄漏時(shí),會(huì)生成leak 報(bào)告, 報(bào)告中會(huì)詳細(xì)寫(xiě)明具體發(fā)現(xiàn)內(nèi)存泄漏的語(yǔ)句。
其原理,可以自行上網(wǎng)搜索查看一下。

八. Allocation Tracker(DeviceMonitor)

Allocation Tracker 能夠追蹤內(nèi)存分配信息, 按照順序排列,這樣我們能夠清晰的看出來(lái)每一個(gè)內(nèi)存對(duì)象是怎么一步一步的分配出來(lái)的。比如內(nèi)存抖動(dòng)的可疑點(diǎn),我們可以通過(guò)查看其內(nèi)存分配軌跡來(lái)查看段時(shí)間內(nèi)有多少相同或相似對(duì)象被創(chuàng)建,進(jìn)而找到問(wèn)題發(fā)生的代碼。

操作步驟:

  1. 進(jìn)入追蹤界面
  2. 點(diǎn)擊start Tracking 按鈕,開(kāi)始跟蹤內(nèi)存分配軌跡
  3. 操作用例
  4. 點(diǎn)擊Get Allocations,獲取內(nèi)存分配軌跡。


    Paste_Image.png

如上圖,上行app 從后臺(tái)切換道前臺(tái)時(shí)會(huì)調(diào)用onResume,可以追蹤到最后創(chuàng)建了多個(gè)Configuration對(duì)象。

上圖中,Allcated class 表示創(chuàng)建的類(lèi)型,第一個(gè)Allocated in 表示在哪個(gè)類(lèi)中, 第二個(gè)Allocated in 表示在哪個(gè)方法中。

查看源代碼如下:

public Resources getResources() {
    Resources res = super.getResources();
    Configuration config = new Configuration();
    config.setToDefaults();
    try {
        res.updateConfiguration(config, res.getDisplayMetrics());
    }catch (Exception e){
        e.printStackTrace();
    }
    return res;
}

九. Allocation Tracker(AndroidMonitor)

功能同Allocation Tracker(Andorid Device) , 但是展示更酷炫,更全面。
打開(kāi)Android Monitor, 選中Memory 標(biāo)簽 , 點(diǎn)擊圖標(biāo)

Paste_Image.png

, 進(jìn)行內(nèi)存tracker, 再次點(diǎn)擊結(jié)束tracker。As會(huì)自動(dòng)打開(kāi)tracker文件。
下面我們?cè)敿?xì)看一下這個(gè)面板:
Paste_Image.png

AS給我們提供了多種展示方式

  • by Method :用方法來(lái)分類(lèi)我們的內(nèi)存分配
  • by Allocator : 用內(nèi)存分配器來(lái)分類(lèi)我們的內(nèi)存分配
    點(diǎn)開(kāi)每一項(xiàng),都能夠查看到方法調(diào)用棧, 點(diǎn)擊右鍵可以跳轉(zhuǎn)到源碼。

AS 還為我們提供了統(tǒng)計(jì),點(diǎn)擊餅狀圖標(biāo)按鈕即可。

分為兩種展示形式,有柱狀圖和輪胎圖,分配比例可選分配次數(shù)和占用內(nèi)存大?。?/p>

  • Sunburst
    輪胎圖是以輪胎為起點(diǎn),最外層是內(nèi)存實(shí)際分配的對(duì)象,每一個(gè)同心圓可能被分配為多個(gè)部分,代表不同的子孫,每一個(gè)同心圓代表他的一個(gè)后代。雙擊同心圓中某一個(gè)分割部分,會(huì)變成以你點(diǎn)擊的那一代為圓心再向外展開(kāi),如果想回到初始狀態(tài),雙擊圓心即可。
    下圖為 Sunburst + by Method


    Paste_Image.png

下圖為Sunburst + by Allocator
一個(gè)內(nèi)存的完整路徑


Paste_Image.png

比如上行的首頁(yè)中trace 的數(shù)據(jù), 我們看下我們自己的包:

Paste_Image.png

會(huì)發(fā)現(xiàn),最外圍有很多PageScrollEvent 對(duì)象, 我們?nèi)タ聪略创a:

代碼如下, 我們發(fā)現(xiàn)自動(dòng)loop的viewpager 每次滑動(dòng)都會(huì)創(chuàng)建多個(gè)PageScorllEvent 對(duì)象。這樣也就對(duì)應(yīng)上面這幅圖了。

private class PageChangeListener implements OnPageChangeListener {
    private PageChangeListener() {
    }

    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        if (isLoop) {
            int count = getAdapter().getCount();
            if (position < 1 || position > count - 2) {
                return;
            }
        }
        LoopViewPager.this.mEventDispatcher.dispatchEvent(new PageScrollEvent(LoopViewPager.this.getId(), SystemClock.uptimeMillis(), position, positionOffset));
    }
  • Layout
    柱狀圖是以左邊為起點(diǎn),從左到右的順序是某個(gè)的堆棧信息順序,縱坐標(biāo)上的寬度是以Count/Size 的大小決定的。其內(nèi)容和輪胎圖是一致的。
    下圖為L(zhǎng)ayout + by Method
Paste_Image.png

十. MAT

MAT工具全稱為Memory Analyzer Tool,一款詳細(xì)分析Java堆內(nèi)存的工具,該工具非常強(qiáng)大,為了使用該工具,我們需要hprof文件.

HPROF文件存儲(chǔ)的是特定時(shí)間點(diǎn),java進(jìn)程的內(nèi)存快照。有不同的格式來(lái)存儲(chǔ)這些數(shù)據(jù),總的來(lái)說(shuō)包含了快照被觸發(fā)時(shí)java對(duì)象和類(lèi)在heap中的情況。由于快照只是一瞬間的事情,所以heap dump中無(wú)法包含一個(gè)對(duì)象在何時(shí)、何地(哪個(gè)方法中)被分配這樣的信息。

幾個(gè)關(guān)鍵概念

  • Histogram:列出內(nèi)存中的對(duì)象,對(duì)象的個(gè)數(shù)以及大小
  • Dominator Tree:列出最大的對(duì)象以及其依賴存活的Object (大小是以Retained Heap為標(biāo)準(zhǔn)排序的)
  • Top Consumers : 通過(guò)圖形列出最大的object
  • Duplicate Class:通過(guò)MAT自動(dòng)分析泄漏的原因
  • Shallow heap : 對(duì)象本身占用內(nèi)存的大小,不包含其引用的對(duì)象。
    (常規(guī)對(duì)象(非數(shù)組)的Shallow size有其成員變量的數(shù)量和類(lèi)型決定。數(shù)組的shallow size有數(shù)組元素的類(lèi)型(對(duì)象類(lèi)型、基本類(lèi)型)和數(shù)組長(zhǎng)度決定. 因?yàn)椴幌馽++的對(duì)象本身可以存放大量?jī)?nèi)存,java的對(duì)象成員都是些引用。真正的內(nèi)存都在堆上,看起來(lái)是一堆原生的byte[], char[], int[],所以我們?nèi)绻豢磳?duì)象本身的內(nèi)存,那么數(shù)量都很小。所以我們看到Histogram圖是以Shallow size進(jìn)行排序的,排在第一位第二位的是byte,char 。)
  • Retained Heap : 它表示如果一個(gè)對(duì)象被釋放掉,那會(huì)因?yàn)樵搶?duì)象的釋放而減少引用進(jìn)而被釋放的所有的對(duì)象(包括被遞歸釋放的)所占用的heap大小。
    (于是,如果一個(gè)對(duì)象的某個(gè)成員new了一大塊int數(shù)組,那這個(gè)int數(shù)組也可以計(jì)算到這個(gè)對(duì)象中。相對(duì)于shallow heap,Retained heap可以更精確的反映一個(gè)對(duì)象實(shí)際占用的大?。ㄒ?yàn)槿绻搶?duì)象釋放,retained heap都可以被釋放)。)
  • outgoing references :表示該對(duì)象的出節(jié)點(diǎn)(被該對(duì)象引用的對(duì)象)。
  • incoming references :表示該對(duì)象的入節(jié)點(diǎn)(引用到該對(duì)象的對(duì)象)。
  • GC Root: GC發(fā)現(xiàn)通過(guò)任何reference chain(引用鏈)無(wú)法訪問(wèn)某個(gè)對(duì)象的時(shí)候,該對(duì)象即被回收。所以JVM就是GC Roots。
  • Unreachable指的是可以被垃圾回收器回收的對(duì)象,但是由于沒(méi)有GC發(fā)生,所以沒(méi)有釋放,這時(shí)抓的內(nèi)存使用中的Unreachable就是這些對(duì)象。

1. 預(yù)覽信息

打開(kāi)dump 文件,通常我們需要關(guān)注一下幾個(gè)重要信息, 內(nèi)存占用餅圖,Actions部分的Histogram, Top Consumers.
我們打開(kāi)Top Consumers,會(huì)生成一個(gè)報(bào)告,我們可以Biggets Objects overview, 能夠看到主要內(nèi)存占用者

Paste_Image.png

點(diǎn)擊下面的biggest Objects 可以查看具體的地址。
還有Biggest Top Level Dominator Classes , 可以看到主要占用內(nèi)存的都是些什么東東。

2. dump分析

2.1 Histogram

MAT中Histogram的主要作用是查看一個(gè)instance的數(shù)量,一般用來(lái)查看自己創(chuàng)建的類(lèi)的實(shí)例的個(gè)數(shù)。 可以分不同維度來(lái)查看對(duì)象的Dominator Tree視圖,Group by class、Group by class loader、Group by package 和Histogram類(lèi)似,時(shí)間久了,通過(guò)多次對(duì)比也可以把溢出對(duì)象找出來(lái)。 Histogram 中可以分Group,Thread 區(qū)分信息。 通常為:選中某一項(xiàng)-> show objects and class -> by incoming reference->merge shortest path to gc root -> exclude weadk reference
等流程來(lái)查看具體情況。

可以在上面過(guò)濾相關(guān)包名,查看到具體類(lèi)型, 關(guān)注objects個(gè)數(shù), 表示內(nèi)存dump 中有多少個(gè)相關(guān)類(lèi)型對(duì)象, 比如不改存在的 對(duì)象存在了,或者有的對(duì)象內(nèi)存中有太多的份數(shù), 這樣就可以進(jìn)行一個(gè)全面分析。

也可以選擇Group by package ,這樣方便根據(jù)package來(lái)進(jìn)行分析。


Paste_Image.png

也可以選擇thread來(lái)進(jìn)行分析, 這樣查看占用內(nèi)存最多的線程,這些線程可能為有內(nèi)存問(wèn)題的線程。

點(diǎn)擊右鍵常用的幾個(gè)選項(xiàng):

  • List Objects -> with incoming references 查看這個(gè)對(duì)象被哪些外部對(duì)象引用
  • List Objects-> with outcoming references 查看這個(gè)對(duì)象持有的外部對(duì)象引用
  • Path to GC Roots -> exclude ... references 查看這個(gè)對(duì)象的GC Root,不包含xxx引用,剩下的基本就是強(qiáng)引用了。因?yàn)橹挥袕?qiáng)引用一直存在,gc就一直無(wú)法回收該對(duì)象,從而也就出現(xiàn)內(nèi)存泄露。
  • Merge shortest path to GC root 找到從GC根結(jié)點(diǎn)到一個(gè)對(duì)象或者一組對(duì)象的共同路徑。從這里可以查看到對(duì)象的引用關(guān)系。
2.2 Debug Bitmap

圖片一直是內(nèi)存占用的一個(gè)大頭,也是引起內(nèi)存泄露,OOM的??汀K詫?duì)圖片的分析是需要非常了解,這樣才能更好的優(yōu)化項(xiàng)目。*注意:圖片在內(nèi)存中占用的大?。篈RGB_8888 類(lèi)型的圖片 為 內(nèi)存中圖片寬度*內(nèi)存中圖片高度4, 此處需要注意原始圖片寬高和內(nèi)存圖片寬高不一致,包括拉伸和壓縮,尤其是圖片位置放錯(cuò),比如1080p設(shè)備,xxxhdpi下面沒(méi)有圖片,會(huì)去別的目錄下尋找圖片,此時(shí)將會(huì)對(duì)圖片拉伸。 **
下面我們來(lái)看一下圖片的處理。通常dump信息中圖片表現(xiàn)為兩種類(lèi)型,Bitmap, byte[]。我們需要知道該圖片是哪張圖片,這樣才能好優(yōu)化相關(guān)的圖片代碼。

  • Bitmap類(lèi)型
    在mat中通常能夠看到bitmap類(lèi)型,占用了大量的內(nèi)存,如下面這張圖片,在內(nèi)存中占用2M。 我們可以打開(kāi),查看mBuffer變量。


    Paste_Image.png

選中mBuffer-> 右鍵選中Copy-> 選擇Save Value To File -> 生成一個(gè)xxx.data 文件。

  • Byte[] 類(lèi)型
    如下,查看byte的 in comming, 即可看到它是一個(gè)bitmap,此時(shí)如下圖,我們可以直接將該byte數(shù)據(jù)寫(xiě)入xxx.data 文件。


    Paste_Image.png

下一步是選中對(duì)應(yīng)的bitmap,打開(kāi)Inspector 窗口,查看bitmap的尺寸,并且使用GIMP工具(可以安裝一個(gè),開(kāi)源的)打開(kāi)剛才的data文件,圖像類(lèi)型選擇RGB Alpha, 寬度和高度填入圖像的寬高,打開(kāi)即可。

Paste_Image.png
Paste_Image.png
2.3 堆對(duì)比

通常為了分析內(nèi)存是否泄露,內(nèi)存是否持續(xù)增長(zhǎng)但沒(méi)有釋放等問(wèn)題,我們需要dump兩次來(lái)進(jìn)行內(nèi)存堆的對(duì)比。

打開(kāi)兩個(gè)或多個(gè)dump文件,打開(kāi)Navigation History視圖,點(diǎn)擊Historgam,選擇Add to Comp are Basket,最后選中Compare the Result 。

Paste_Image.png

在對(duì)比結(jié)果中,主要分析類(lèi)型或者對(duì)象的數(shù)量是否有變化, 內(nèi)存是否有變化。

通過(guò)以上手段,我們可以定位到大部分內(nèi)存問(wèn)題。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容