Java虛擬機(五):性能監(jiān)控和分析工具

1 概述

在之前的文章,我們了解了Java內(nèi)存布局百匆、內(nèi)存模型、對象模型和垃圾回收的知識呜投。在實際生產(chǎn)中加匈,絕大多數(shù)Java開發(fā)者都不會參與到JVM的開發(fā)中,那我們?yōu)槭裁匆斫釰VM呢仑荐?因為我們要排查雕拼、解決生產(chǎn)中出現(xiàn)的問題,在理解了理論知識的基礎上粘招,使用一些輔助的工具啥寇,才能更快、更準的定位問題,并且更好的解決問題辑甜。所以衰絮,接下來我們一起討論一下一些常見的性能監(jiān)控和分析工具。

2 JDK自帶的工具

JDK自帶了很多優(yōu)秀磷醋,使用簡單的工具猫牡,例如Jps,Jconsole子檀,Jstat镊掖,Jstack等乃戈,都能在JDK目錄下的bin目錄找到褂痰,windows下的是.exe可執(zhí)行文件,在unix系系統(tǒng)下的是.sh腳本文件症虑,可以直接用vim打開缩歪。

筆者使用的windows操作系統(tǒng),下面的相關工具的使用都是在windows下進行的谍憔,可能和unix系統(tǒng)有些許差異匪蝙。

2.1 Jps

Jps即Java Process Status Tool的簡稱,從名稱可以看到习贫,應該是類似進程管理器的東西逛球,實際上差不多,但是從功能上來看苫昌,還不足以稱做“管理器”颤绕,因為Jps沒有提供關閉,殺死進程等功能祟身,僅僅提供了查看Java虛擬機進程的功能奥务。

Jps的命令格式如下所示:

usage: jps [-help]
       jps [-q] [-mlvV] [<hostid>]
Definitions:
    <hostid>:      <hostname>[:<port>]

試著執(zhí)行一下:

10320 sun.tools.jps.Jps
8756 org.jetbrains.jps.cmdline.Launcher
11288
2572 org/netbeans/Main

最前面的是虛擬機進程號,這個比較重要袜硫,因為后面的各種工具要連接的時候都需要知道虛擬機進程號氯葬,所以一般Jps除了查看當前有哪些進程之外,還有一個目的就是獲取進程號婉陷,通過-l參數(shù)列出主類帚称,可以知道該進程是由哪個主類(含有main的類)啟動的。

上圖是options的可選項秽澳,大家試試就知道了世杀,比較簡單,就不再贅述了肝集。

2.2 Jstat

Jstat是用于監(jiān)視虛擬機各種狀態(tài)信息的命令行工具瞻坝,包括類加載,GC,內(nèi)存等信息所刀,沒有提供GUI界面衙荐。其格式如下所示:

invalid argument count
Usage: jstat -help|-options
       jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]

Definitions:
  <option>      An option reported by the -options option
  <vmid>        Virtual Machine Identifier. A vmid takes the following form:
                     <lvmid>[@<hostname>[:<port>]]
                Where <lvmid> is the local vm identifier for the target
                Java virtual machine, typically a process id; <hostname> is
                the name of the host running the target Java virtual machine;
                and <port> is the port number for the rmiregistry on the
                target host. See the jvmstat documentation for a more complete
                description of the Virtual Machine Identifier.
  <lines>       Number of samples between header lines.
  <interval>    Sampling interval. The following forms are allowed:
                    <n>["ms"|"s"]
                Where <n> is an integer and the suffix specifies the units as
                milliseconds("ms") or seconds("s"). The default units are "ms".
  <count>       Number of samples to take before terminating.
  -J<flag>      Pass <flag> directly to the runtime system.

在使用之前,我們得先用Jps獲取虛擬機進程ID浮创,我在本機上啟動了一個Java進程忧吟,用Jps獲取的進程ID是14220。接下來執(zhí)行jstat命令:

> jstat -gcutil 14220
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0.00   0.00   0.00   1.04  71.42  74.38      2    0.046     1    0.002    0.048

其中S0和S1是Survivor區(qū)斩披,E是Eden溜族,O是Old,M是MetaSpace(Java8之后有的元空間垦沉,就是以前的方法區(qū))煌抒,CCS壓縮類空間,YGC是新生代GC的次數(shù)厕倍,YGCT是新生代GC消耗的時間寡壮,F(xiàn)GC是Full GC的次數(shù),F(xiàn)GCT是Full GC消耗的時間讹弯,GCT是GC消耗的總時間(FGCT + YGCT)况既。

其他主要的選項如下圖所示:

2.3 Jinfo

Jinfo的作用是實時的查看和修改虛擬機的各項參數(shù)。其格式如下所示:

Usage:
    jinfo [option] <pid>
        (to connect to running process)
    jinfo [option] <executable <core>
        (to connect to a core file)
    jinfo [option] [server_id@]<remote server IP or hostname>
        (to connect to remote debug server)

where <option> is one of:
    -flag <name>         to print the value of the named VM flag
    -flag [+|-]<name>    to enable or disable the named VM flag
    -flag <name>=<value> to set the named VM flag to the given value
    -flags               to print VM flags
    -sysprops            to print Java system properties
    <no option>          to print both of the above
    -h | -help           to print this help message

主要就是-flag參數(shù)组民,下面是一個使用示例:

> jinfo  -flag CMSInitiantingOccupancyFraction 14220
-XX:CMSInitiatingOccupancyFraction=-1

2.4 Jmap

Jmap用于生成堆轉(zhuǎn)儲快照棒仍,即dump文件,除了獲取dump文件臭胜,Jmap還可以查看堆莫其,垃圾收集器等信息。其格式如下所示:

Usage:
    jmap [option] <pid>
        (to connect to running process)
    jmap [option] <executable <core>
        (to connect to a core file)
    jmap [option] [server_id@]<remote server IP or hostname>
        (to connect to remote debug server)

where <option> is one of:
    <none>               to print same info as Solaris pmap
    -heap                to print java heap summary
    -histo[:live]        to print histogram of java object heap; if the "live"
                         suboption is specified, only count live objects
    -clstats             to print class loader statistics
    -finalizerinfo       to print information on objects awaiting finalization
    -dump:<dump-options> to dump java heap in hprof binary format
                         dump-options:
                           live         dump only live objects; if not specified,
                                        all objects in the heap are dumped.
                           format=b     binary format
                           file=<file>  dump heap to <file>
                         Example: jmap -dump:live,format=b,file=heap.bin <pid>
    -F                   force. Use with -dump:<dump-options> <pid> or -histo
                         to force a heap dump or histogram when <pid> does not
                         respond. The "live" suboption is not supported
                         in this mode.
    -h | -help           to print this help message
    -J<flag>             to pass <flag> directly to the runtime system

執(zhí)行如下命令:

> jmap -heap 14220
Mark Sweep Compact GC

Heap Configuration:
   MinHeapFreeRatio         = 40
   MaxHeapFreeRatio         = 70
   MaxHeapSize              = 104857600 (100.0MB)
   NewSize                  = 34930688 (33.3125MB)
   MaxNewSize               = 34930688 (33.3125MB)
   OldSize                  = 69926912 (66.6875MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
New Generation (Eden + 1 Survivor Space):
   capacity = 31457280 (30.0MB)
   used     = 0 (0.0MB)
   free     = 31457280 (30.0MB)
   0.0% used
Eden Space:
   capacity = 27983872 (26.6875MB)
   used     = 0 (0.0MB)
   free     = 27983872 (26.6875MB)
   0.0% used
From Space:
   capacity = 3473408 (3.3125MB)
   used     = 0 (0.0MB)
   free     = 3473408 (3.3125MB)
   0.0% used
To Space:
   capacity = 3473408 (3.3125MB)
   used     = 0 (0.0MB)
   free     = 3473408 (3.3125MB)
   0.0% used
tenured generation:
   capacity = 69926912 (66.6875MB)
   used     = 725560 (0.6919479370117188MB)
   free     = 69201352 (65.99555206298828MB)
   1.03759765625% used

1764 interned Strings occupying 157712 bytes.

第一行就顯示了使用哪種垃圾收集器庇楞,在我這里是Mark Sweep Compact榜配,即CMS。其他信息包含了堆的新生代吕晌,老年代的空間大小蛋褥,使用量等。下圖是其他的一些主要參數(shù):

2.5 Jhat

Jhat用來配合Jmap使用的睛驳,用于分析Jmap生成的堆快照烙心。命令格式如下所示:

Usage:  jhat [-stack <bool>] [-refs <bool>] [-port <port>] [-baseline <file>] [-debug <int>] [-version] [-h|-help] <file>

        -J<flag>          Pass <flag> directly to the runtime system. For
                          example, -J-mx512m to use a maximum heap size of 512MB
        -stack false:     Turn off tracking object allocation call stack.
        -refs false:      Turn off tracking of references to objects
        -port <port>:     Set the port for the HTTP server.  Defaults to 7000
        -exclude <file>:  Specify a file that lists data members that should
                          be excluded from the reachableFrom query.
        -baseline <file>: Specify a baseline object dump.  Objects in
                          both heap dumps with the same ID and same class will
                          be marked as not being "new".
        -debug <int>:     Set debug level.
                            0:  No debug output
                            1:  Debug hprof file parsing
                            2:  Debug hprof file parsing, no server
        -version          Report version number
        -h|-help          Print this help and exit
        <file>            The file to read

For a dump file that contains multiple heap dumps,
you may specify which dump in the file
by appending "#<number>" to the file name, i.e. "foo.hprof#3".

All boolean options default to "true"

這個是提供GUI界面的,不過一般不會用在生產(chǎn)環(huán)境乏沸,一個原因分析dump文件時一件非常消耗資源的事淫茵,如果在生產(chǎn)環(huán)境上做了可能會導致一些資源方面的問題,另一個原因是Jhat分析的內(nèi)容比較簡陋蹬跃,有用信息比較少匙瘪。

在使用之前,我們得先使用Jmap生成一份dump文件,如下所示:

> jmap -dump:format=b,file=test.bin 14220
Dumping heap to C:\Users\72419\Desktop\test.bin ...
Heap dump file created

然后使用jhat讀入該文件丹喻,并分析:

> jhat test.bin
Reading from test.bin...
Dump file created Mon Sep 24 14:50:11 CST 2018
Snapshot read, resolving...
Resolving 10303 objects...
Chasing references, expect 2 dots..
Eliminating duplicate references..
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.

從日志可以看到薄货,Jhat啟動了一個HTTP服務,在7000端口監(jiān)聽碍论,啟動瀏覽器谅猾,輸入http://localhost:7000/ 即可進入GUI界面。如下所示:

點擊進去慢慢探索吧鳍悠。

2.6 Jstack

Jstack用于生成虛擬機當前時刻的線程快照税娜,生成該快照的目的是為了定位線程問題,例如死鎖藏研,活鎖敬矩,死循環(huán)或者請求外部資源導致的長時間等待等,通過分析快照的堆棧信息遥倦,就可以快速定位代碼中的哪個類谤绳,哪個方法占锯,甚至哪行出現(xiàn)了問題袒哥。下面是我寫的一個死鎖程序,啟動之后消略,我們通過Jstack來查看線程快照堡称,然后定位問題:

public class DeadLockTest {

    static class DeadLock extends Thread {

        private final String lockA;
        private final String lockB;

        public DeadLock(String lockA, String lockB) {
            this.lockA = lockA;
            this.lockB = lockB;
        }

        @Override
        public void run() {
            synchronized (lockA) {
                System.out.println(Thread.currentThread().getName() + " get lock " + lockA);
                try {
                    Thread.sleep(500);   //停頓一下,讓另一個線程運行
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lockB) {
                    System.out.println(Thread.currentThread().getName() + " get lock " + lockB);
                }
            }
        }
    }

    public static void main(String[] args) {
        String lockA = "lockA";
        String lockB = "lockB";
        Thread thread1 = new DeadLock(lockA, lockB);
        Thread thread2 = new DeadLock(lockB, lockA);

        thread1.start();
        thread2.start();

    }
}

簡單解釋一下程序艺演,啟動兩個線程却紧,這兩個線程都要獲取A和B兩把鎖,但是獲取的順序不同胎撤。這樣就導致了線程1獲取了A鎖晓殊,等待B鎖,但是B鎖已經(jīng)被線程2獲取了伤提,并且線程2也在等待A鎖巫俺,最終導致他們都無法獲取到鎖,也就無法繼續(xù)執(zhí)行肿男,導致了死鎖介汹。

使用Jstack來生成快照嘗試分析:

> jstack -l 12432
......
"Thread-1" #12 prio=5 os_prio=0 tid=0x00000000195bd000 nid=0x5e90 waiting for monitor entry [0x000000001a20f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at top.yeonon.ch10.DeadLockTest$DeadLock.run(DeadLockTest.java:29)
        - waiting to lock <0x00000000d5fc0588> (a java.lang.String)
        - locked <0x00000000d5fc05c0> (a java.lang.String)

   Locked ownable synchronizers:
        - None

"Thread-0" #11 prio=5 os_prio=0 tid=0x00000000195ba000 nid=0x5ed4 waiting for monitor entry [0x000000001a10f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at top.yeonon.ch10.DeadLockTest$DeadLock.run(DeadLockTest.java:29)
        - waiting to lock <0x00000000d5fc05c0> (a java.lang.String)
        - locked <0x00000000d5fc0588> (a java.lang.String)

   Locked ownable synchronizers:
        - None
......
Found one Java-level deadlock:
=============================
"Thread-1":
  waiting to lock monitor 0x000000000357ccf8 (object 0x00000000d5fc0588, a java.lang.String),
  which is held by "Thread-0"
"Thread-0":
  waiting to lock monitor 0x000000000357b858 (object 0x00000000d5fc05c0, a java.lang.String),
  which is held by "Thread-1"

Java stack information for the threads listed above:
===================================================
"Thread-1":
        at top.yeonon.ch10.DeadLockTest$DeadLock.run(DeadLockTest.java:29)
        - waiting to lock <0x00000000d5fc0588> (a java.lang.String)
        - locked <0x00000000d5fc05c0> (a java.lang.String)
"Thread-0":
        at top.yeonon.ch10.DeadLockTest$DeadLock.run(DeadLockTest.java:29)
        - waiting to lock <0x00000000d5fc05c0> (a java.lang.String)
        - locked <0x00000000d5fc0588> (a java.lang.String)

Found 1 deadlock.

信息比較多,而且我已經(jīng)省略了一些舶沛,主要看Found one Java-level deadlock后面的內(nèi)容吧嘹承,這說明Jstack發(fā)現(xiàn)了一個死鎖,并且將死鎖相關的線程堆棧信息打印出來了如庭,從上面可以看到“Thread-1”線程拿到了<0x00000000d5fc05c0>鎖叹卷,正在等待 <0x00000000d5fc0588>鎖,“Thread-0”拿著<0x00000000d5fc0588>鎖,正在等待 <0x00000000d5fc05c0>骤竹,顯然就是死鎖了餐胀,同時看堆棧可以看到問題發(fā)生在DeadLockTest.java:29瘤载,即該類的29行出現(xiàn)問題(雖然這個值不一定完全對應源碼位置否灾,但基本相差不大,也算是有用的信息)鸣奔,接下來就是到源碼相應的位置去分析代碼了墨技。

Jstack的其他主要參數(shù)選項如下所示:

2.7 Jconsole

Jconsole是一個可視化工具,命令行輸入jconsole挎狸,打開之后就看到GUI界面了扣汪,界面大致如下所示:

每個標簽的意義也比較明顯,可以試著自己玩玩锨匆,比較簡單崭别,不再贅述。

我之前有一篇文章中簡單提到過一些Jconsole的使用恐锣,可以去看看茅主。理解Java內(nèi)存泄露

3 第三方工具

3.1 VisualVM

VisualVM是一款非常強大的性能監(jiān)控和分析工具,集合了多項功能土榴,包括導出dump文件诀姚,分析dump文件,監(jiān)控線程狀態(tài)玷禽,監(jiān)控虛擬機各項狀態(tài)指標等赫段,還提供了諸多豐富的插件給用戶使用。

使用之前得先去官網(wǎng)下載矢赁,下載安裝糯笙、簡單使用就不多說了,網(wǎng)上有很多文章介紹撩银,本文只討論一個插件:BTrace给涕。

Btrace是一個很有趣的插件,他可以在運行時插入一些調(diào)試代碼蜒蕾,利用這個插件我們就可以實現(xiàn)不停止程序就能進行簡單調(diào)試的功能稠炬。先寫一些代碼用來測試:

public class BTraceTest {

    public int add(int a, int b) {
        return a + b;
    }

    public static void main(String[] args) {
        BTraceTest bTraceTest = new BTraceTest();
        Scanner scanner = new Scanner(System.in);
        for (int i = 0; i < 10; i++) {
            scanner.nextLine();
            int a = (int) Math.round(Math.random() * 1000);
            int b = (int) Math.round(Math.random() * 1000);
            System.out.println(bTraceTest.add(a, b));
        }
    }
}

就是加分運算而已,使用Scanner是為了慢慢的看日志變化咪啡,感受BTrace的功能首启。啟動程序之后,到VisualVM的側(cè)邊欄看看Local選項撤摸,點擊后選中剛剛運行的程序毅桃,如下所示:

右鍵點擊褒纲,選中Trace application,之后可以在右邊看到一個編譯器钥飞,可以在里面編寫Java代碼莺掠,這些代碼就是用于跟蹤調(diào)試源代碼的,如下圖所示:

加入如下代碼:

/* BTrace Script Template */
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;

@BTrace
public class TracingScript {
    /* put your code here */
    @OnMethod(clazz="top.yeonon.ch10.BTraceTest", method="add", location=@Location(Kind.RETURN))
    public static void func(@Self top.yeonon.ch10.BTraceTest instance, int a, int b, @Return int result) {
        println("調(diào)用堆棧:");
        jstack();
        println(strcat("方法參數(shù)A:",str(a)));    
        println(strcat("方法參數(shù)B:",str(b)));    
        println(strcat("返回值result:",str(result)));    
    }
}

然后點擊上方的start按鈕读宙,啟動BTrace彻秆,等看到下方控制臺中出現(xiàn)Done字樣的時候,我們就到IDE里隨便輸入字符(多輸入幾次)结闸,然后再回到VisualVM控制臺中看看結(jié)果唇兑,大致內(nèi)容如下:

這就完成了一次代碼動態(tài)調(diào)試,是不是感覺很屌桦锄。BTrace還有很多其他功能扎附,詳細可以到網(wǎng)上去搜索。

4 小結(jié)

對虛擬機進行監(jiān)控结耀,調(diào)優(yōu)以及出現(xiàn)問題的時候定位問題留夜,解決問題是我們學習Java虛擬機的目的,在掌握了虛擬機相關的知識理論之后再利用輔助工具图甜,才能更好的發(fā)現(xiàn)問題碍粥,解決問題。本文介紹了諸多工具具则,每種工具都有優(yōu)點和不足即纲,了解這些工具的特性具帮,多多嘗試使用博肋,才能更好的掌握它們。

5 參考資料

《深入理解Java虛擬機》

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蜂厅,一起剝皮案震驚了整個濱河市匪凡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌掘猿,老刑警劉巖病游,帶你破解...
    沈念sama閱讀 206,013評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異稠通,居然都是意外死亡衬衬,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,205評論 2 382
  • 文/潘曉璐 我一進店門改橘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來滋尉,“玉大人,你說我怎么就攤上這事飞主∈ㄏВ” “怎么了高诺?”我有些...
    開封第一講書人閱讀 152,370評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長碾篡。 經(jīng)常有香客問我虱而,道長,這世上最難降的妖魔是什么开泽? 我笑而不...
    開封第一講書人閱讀 55,168評論 1 278
  • 正文 為了忘掉前任牡拇,我火速辦了婚禮,結(jié)果婚禮上穆律,老公的妹妹穿的比我還像新娘诅迷。我一直安慰自己,他們只是感情好众旗,可當我...
    茶點故事閱讀 64,153評論 5 371
  • 文/花漫 我一把揭開白布罢杉。 她就那樣靜靜地躺著,像睡著了一般贡歧。 火紅的嫁衣襯著肌膚如雪滩租。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,954評論 1 283
  • 那天利朵,我揣著相機與錄音律想,去河邊找鬼。 笑死绍弟,一個胖子當著我的面吹牛技即,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播樟遣,決...
    沈念sama閱讀 38,271評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼而叼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了豹悬?” 一聲冷哼從身側(cè)響起葵陵,我...
    開封第一講書人閱讀 36,916評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎瞻佛,沒想到半個月后脱篙,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,382評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡伤柄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,877評論 2 323
  • 正文 我和宋清朗相戀三年绊困,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片适刀。...
    茶點故事閱讀 37,989評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡秤朗,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蔗彤,到底是詐尸還是另有隱情川梅,我是刑警寧澤疯兼,帶...
    沈念sama閱讀 33,624評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站贫途,受9級特大地震影響吧彪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜丢早,卻給世界環(huán)境...
    茶點故事閱讀 39,209評論 3 307
  • 文/蒙蒙 一姨裸、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧怨酝,春花似錦傀缩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,199評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至斤葱,卻和暖如春慷垮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背揍堕。 一陣腳步聲響...
    開封第一講書人閱讀 31,418評論 1 260
  • 我被黑心中介騙來泰國打工料身, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人衩茸。 一個月前我還...
    沈念sama閱讀 45,401評論 2 352
  • 正文 我出身青樓芹血,卻偏偏與公主長得像,于是被迫代替她去往敵國和親楞慈。 傳聞我的和親對象是個殘疾皇子幔烛,可洞房花燭夜當晚...
    茶點故事閱讀 42,700評論 2 345

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

  • 參考博客:https://blog.csdn.net/yx0628/article/details/3009121...
    susu2016閱讀 629評論 1 3
  • 泛黃的照片,落寞的街景抖部。坐在床角的我腦海里浮現(xiàn)出一幅幅扣人心弦的的畫面说贝。靈魂已不受控制,仿佛被一種不知名的東西所牽...
    柚萫閱讀 235評論 6 4
  • 今日體驗:早上一輛現(xiàn)代勝達菲進場 客戶反應車跑著跑著熄火 用電腦度數(shù)據(jù)流加上互相碼 判斷氧傳感器有問題 檢查發(fā)現(xiàn) ...
    酷酷的峰666閱讀 219評論 0 0
  • 自從無意驚覺于“螳螂捕蟬黃雀在后”背后當局者與旁觀者視角的自由精妙轉(zhuǎn)換 貓眼一詞便引申為各大網(wǎng)站的慣用昵稱 時常作...
    貓眼有態(tài)度閱讀 218評論 0 0