<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Sky's blog

    我和我追逐的夢

    常用鏈接

    統(tǒng)計

    其他鏈接

    友情鏈接

    最新評論

    ejb與java序列化(1)--發(fā)現(xiàn)并分析問題

            這是加入新公司后接手的第一個項目,使用weblogic9.2 + ejb2.0,壓力測試時發(fā)現(xiàn)速度非常慢,響應(yīng)時間很不理想,檢查日志發(fā)現(xiàn),某些ejb相互調(diào)用時方法調(diào)用的時間非常長,高達(dá)300-500毫秒。非??鋸?,因為兩個日志之間只是間隔了一個ejb調(diào)用。通過thread dump分析后發(fā)現(xiàn)有相當(dāng)多的線程在wait,檢查線程調(diào)用綻發(fā)現(xiàn)是在將參數(shù)進行序列化時,線程試圖加鎖但是鎖被占用,因此處于等待狀態(tài)??紤]到thread dump的這一瞬間,有多達(dá)30-50個線程都在同時試圖在同一個鎖上加鎖,很明顯這里的鎖競爭非常嚴(yán)重。

            因此強烈懷疑是java的序列化機制導(dǎo)致的問題,由于weblogic/ejb之類的太復(fù)雜不方便測試,因此單獨寫了一個類來模擬這種場景:
    1. 有大量線程在運行,期間都有序列化的操作
    2. 被序列化的對象比較大,有大量子對象(子對象的子對象...)

            運行測試代碼,問題重現(xiàn),測試用開了50個線程,thread dump并分析thread dump信息,發(fā)現(xiàn)49個線程處于waiting 狀態(tài),只有一個線程在干活!因此拋開weblogic/ejb單獨分析java的序列化機制,首先看thread dump的線程信息:

    占有鎖的線程:
    "testthread21" prio=6 tid=0x0ad2d3b8 nid=0x224 runnable [0x0b48f000..0x0b48fce4]
        at java.io.ObjectStreamClass.processQueue(Unknown Source)
        at java.io.ObjectStreamClass.lookup(Unknown Source)
        at java.io.ObjectOutputStream.writeObject0(Unknown Source)
        at java.io.ObjectOutputStream.writeObject(Unknown Source)
        at java.util.ArrayList.writeObject(Unknown Source)
        at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at java.io.ObjectStreamClass.invokeWriteObject(Unknown Source)
        at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
        at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
        at java.io.ObjectOutputStream.writeObject0(Unknown Source)
        at java.io.ObjectOutputStream.writeObject(Unknown Source)
        at test.Test.test(Test.java:68)
        at test.Test.run(Test.java:53)
        at java.lang.Thread.run(Unknown Source)

    等待加鎖的線程之一:
    "testthread49" prio=6 tid=0x0ad9a508 nid=0xa9c waiting for monitor entry [0x0bb8f000..0x0bb8fbe4]
        at java.lang.ref.ReferenceQueue.poll(Unknown Source)
        - waiting to lock <0x02fb1d40> (a java.lang.ref.ReferenceQueue$Lock)
        at java.io.ObjectStreamClass.processQueue(Unknown Source)
        at java.io.ObjectStreamClass.lookup(Unknown Source)
        at java.io.ObjectOutputStream.writeObject0(Unknown Source)
        at java.io.ObjectOutputStream.writeObject(Unknown Source)
        at java.util.ArrayList.writeObject(Unknown Source)
        at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at java.io.ObjectStreamClass.invokeWriteObject(Unknown Source)
        at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
        at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
        at java.io.ObjectOutputStream.writeObject0(Unknown Source)
        at java.io.ObjectOutputStream.writeObject(Unknown Source)
        at test.Test.test(Test.java:68)
        at test.Test.run(Test.java:53)
        at java.lang.Thread.run(Unknown Source)

    可以發(fā)現(xiàn)問題發(fā)生在類java.io.ObjectStreamClass中的方法processQueue()方法中:
        static void processQueue(ReferenceQueue<Class<?>> queue,
                        ConcurrentMap<? extends
                     WeakReference<Class<?>>, ?> map)
        {
        Reference<? extends Class<?>> ref;
        while((ref = queue.poll()) != null) {
            map.remove(ref);
        }   
        }

    這里的queue.poll()有加鎖,繼續(xù)跟進poll()的代碼:
        public Reference<? extends T> poll() {
        if(queueEmpty) return null;
        synchronized (lock) {
            return reallyPoll();
        }
        }
    可以看到通過synchronized 關(guān)鍵字,在lock對象有加鎖操作,和thread dump信息相符,看來問題就在這里了。

    開始分析了,首先看現(xiàn)象,“50個線程1個進入synchronized 塊獲得鎖,49個在試圖加鎖而處于waiting狀態(tài)”,首先想到的是,synchronized塊中的代碼執(zhí)行的時間比較長,造成鎖長時間處于被占有狀態(tài),其他線程只能等。但是查看reallyPoll()方法發(fā)現(xiàn)它的實現(xiàn)非常簡單,執(zhí)行應(yīng)該非常快,后面的profile信息也證實了這點。

    profile統(tǒng)計信息:
    method            num calls    cumulative time        mothod time    percentage1    percentage2

    thread=1
    invokeWriteObject         387         30209                       2       
    lookup                      774778         17499                 6605            57.9%
    processQueue          774778           5523                  2185       
    poll                          774786           3338                  2294                                    7.59%
    reallyPoll                  774793           1045                  1045       

    thread=2
    invokeWriteObject         739         59715                       3
    lookup                    1479482         34550               12815         57.85%
    processQueue         1479482        11878                  4236
    poll                         1479490          7642                  5656                                    9.47%
    reallyPoll                 1479497          1987                  1987       

    thread=5
    invokeWriteObject        721        147203                        3
    lookup                   1443446         99665                 24631        67.7%   
    processQueue        1443446         58211                   7044
    poll                        1443454         51167                 49196                                    51.33%
    reallyPoll                1443461           1971                   1971       

    thread=50
    invokeWriteObject        701        1438319                       3       
    lookup                   1403406        1369971             228508        95.24%
    processQueue        1403406        1125351                 6801       
    poll                        1403414        1118550            1116462                                   77.62%
    reallyPoll                1403421              2089                  2089       

    注釋:
    num calls = number of methed calls
    percentage1=lookup cumulative time / invokeWriteObject cumulative time
    percentage2=poll mothod time / invokeWriteObject cumulative time

    注意:當(dāng)我們序列化一個Data對象的實例時,因為這個實例里面帶有一個包含1000個DataItem實例的ArrayList,而每個DataItem實例有一個int和一個String。所以當(dāng)invokeWriteObject方法被調(diào)用一次時,方法lookup/processQueue/poll/reallyPoll將被調(diào)用2000次。同樣等待鎖,加持鎖和釋放鎖的操作也要執(zhí)行2000次,雖然真正的業(yè)務(wù)方法reallyPoll執(zhí)行的非???。


            可以看到當(dāng)線程增加時,percentage1、percentage2的變化已經(jīng)可以說明問題。因此,最后我給出我的見解:
    1. java序列化是cpu依賴型
    2. synchronized lock嚴(yán)重影響java序列化

    posted on 2008-07-29 10:21 sky ao 閱讀(1426) 評論(0)  編輯  收藏 所屬分類: ejb

    主站蜘蛛池模板: 亚洲国产成AV人天堂无码| 亚洲最大免费视频网| 久久精品亚洲日本波多野结衣 | 亚洲无吗在线视频| 韩国免费一级成人毛片| 亚洲精品天堂在线观看| 毛片大全免费观看| 国产亚洲情侣久久精品| 亚洲国产成人久久综合区| 国产成人精品免费视频大全| 亚洲精品高清无码视频| 99国产精品免费观看视频| 亚洲午夜精品在线| 在线观看免费亚洲| 国产特黄特色的大片观看免费视频| 亚洲伊人久久精品影院| 久久永久免费人妻精品下载| 亚洲18在线天美| 亚洲成a人片在线播放| 久久免费视频观看| 久久亚洲国产最新网站| 亚洲成av人在片观看| 久久国产精品一区免费下载| 亚洲一区二区三区四区视频 | 亚洲日韩国产精品第一页一区| 日韩视频在线观看免费| 亚洲一区精彩视频| 亚洲中文字幕无码爆乳av中文| 99久久免费中文字幕精品| 亚洲日韩AV一区二区三区四区| 免费jjzz在线播放国产| 你懂的免费在线观看网站| 亚洲午夜无码毛片av久久京东热| 国产L精品国产亚洲区久久| 中文字幕视频免费| 美女黄色毛片免费看| 亚洲国产精品久久久久网站 | 毛片免费全部播放一级| h片在线播放免费高清| 亚洲乱码中文字幕小综合| 亚洲国产精品自产在线播放|