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

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

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

    莊周夢蝶

    生活、程序、未來
       :: 首頁 ::  ::  :: 聚合  :: 管理

    2012年6月15日


    很久沒有更新博客,沒想到更新是搬遷公告。這個博客累計的訪問量突破百萬,是我建立的時候完全沒有想過的事情。博客對我來說更多是記錄、記憶的地方,我時常因為想不起某個東西,來翻自己的博客,查找舊知,發現新知。閱讀很多人的博客,也是我跟蹤、學習新知的主要方式。雖然微博興起,不過博客作為更系統性的記錄的地方,不會過時。

    非常感謝blogjava提供這么優秀的平臺。只是我今年給自己的一個目標是建立自己的博客,因此現在要搬遷,加上其實現在也寫的少,其實搬遷不搬遷,意義也不大了。算是一個通告,有興趣的可以訂閱我的新博客,沒興趣的請自行略過,謝謝大家。

    新博客地址:http://blog.fnil.net/
    RSS地址:http://blog.fnil.net/index.php/feed

    新博客的第一篇記憶是《Leiningen教程中文版》,從現在開始,這個博客將不再發布任何新的文章,已有的也不會刪除,部分可能會導到我的知識庫上去。

    最后,祝福blogjava越辦越好。

    posted @ 2012-12-10 01:24 dennis 閱讀(11896) | 評論 (6)編輯 收藏

    It's my weekend project——node-shorten: URL Shortener just like t.cn,goo.gl etc.

    Is is written in NodeJS,using express.js for MVC framework,and using MySQL for storage and Redis for caching.

    A demo online: http://fnil.me/

    The project is at https://github.com/killme2008/node-shorten

    Feel free to modify and use it.Have fun.

    posted @ 2012-11-25 20:31 dennis| 編輯 收藏

    很久沒寫博客,一是工作忙,二是沒有太多的事情可說。

    最近在公司大佬的支持下,建立了一個Clojure語言中文方面的博客和問答網站,歡迎任何對Clojure這門基于JVM之上的函數式語言感興趣的童鞋貢獻原創文章或者資料,申請帳號請看這里

    博客地址:  http://blog.clojure.cn/
    問答網站:  http://ask.clojure.cn/

    歡迎轉發和注冊使用,謝謝。

    郵件列表仍然使用google group:https://groups.google.com/group/cn-clojure/

    posted @ 2012-09-25 12:51 dennis 閱讀(12146) | 評論 (4)編輯 收藏

    Home: https://github.com/killme2008/ring.velocity

    A Clojure library designed to render velocity template for ring in clojure.

    Usage

    Adds dependency in leiningen project.clj:

      [ring.velocity "0.1.0-SNAPSHOT"] 

    Create a directory named templates in your project directory to keep all velocity templates.

    Create a template templates/test.vm:

      hello,$name,your age is $age. 

    Use ring.velocity in your namespace:

      (use '[ring.velocity.core :only [render]]) 

    Use render function to render template with vars:

      (render "test.vm" :name "dennis" :age 29) 

    The test.vm will be interpreted equals to:

      hello,dennis,your age is 29. 

    Use ring.velocity in compojure:

      (defroutes app-routes      
    (GET "/" [] (render "test.vm" :name "dennis" :age 29))
    (route/not-found "Not Found"))

    Use ring.velocity in ring:

      (use '[ring.util.response])   
    (response (render "test.vm" :name "dennis" :age 29))

    Custom velocity properties,just put a file named ring-velocity.properties to your classpath or resource paths.The default velocity properties is in src/default/velocity.properties.

    License

    Copyright © 2012 dennis zhuang[killme2008@gmail.com]

    Distributed under the Eclipse Public License, the same as Clojure.

    Home: https://github.com/killme2008/ring.velocity

    posted @ 2012-07-18 00:07 dennis 閱讀(9506) | 評論 (0)編輯 收藏


        Clojure的一大優點就是跟Java語言的完美配合,Clojure和Java之間可以相互調用,Clojure可以天然地使用Java平臺上的豐富資源。在Clojure里調用一個類的方法很簡單,利用dot操作符:

    user=> (.substring "hello" 3)
    "lo"
    user=> (.substring "hello" 0 3)
    "hel"

        上面的例子是在clojure里調用String的substring方法做字符串截取。Clojure雖然是一門弱類型的語言,但是它的Lisp Reader還是能識別大多數常見的類型,比如這里hello是一個字符串就可以識別出來,3是一個整數也可以,通過這些類型信息可以找到最匹配的substring方法,在生成字節碼的時候避免使用反射,而是直接調用substring方法(INVOKEVIRTUAL指令)。

        但是當你在函數里調用類方法的時候,情況就變了,例如,定義substr函數:
    (defn substr [s begin end] (.substring s begin end))

        我們打開*warn-on-reflection*選項,當有反射的時候告警:

    user=> (set! *warn-on-reflection* true)
    true
    user=> (defn substr [s begin end] (.substring s begin end))
    Reflection warning, NO_SOURCE_PATH:22 - call to substring can't be resolved.
    #'user/substr
       
        問題出現了,由于函數substr里沒有任何關于參數s的類型信息,為了調用s的substring方法,必須使用反射來調用,clojure編譯器也警告我們調用substring沒辦法解析,只能通過反射調用。眾所周知,反射調用是個相對昂貴的操作(對比于普通的方法調用有)。這一切都是因為clojure本身是弱類型的語言,對參數或者返回值你不需要聲明類型而直接使用,Clojure會自動處理類型的轉換和調用。ps.在leiningen里啟用反射警告很簡單,在project.clj里設置:

    ;; Emit warnings on all reflection calls.
      :warn-on-reflection true
       
    過多的反射調用會影響效率,有沒有辦法避免這種情況呢?有的,Clojure提供了type hint機制,允許我們幫助編譯器來生成更高效的字節碼。所謂type hint就是給參數或者返回值添加一個提示:hi,clojure編譯器,這是xxx類型,我想調用它的yyy方法,請生成最高效的調用代碼,謝謝合作:
    user=> (defn substr [^String s begin end] (.substring s begin end))
    #'user/substr
         
        這次沒有警告,^String就是參數s的type hint,提示clojure編譯器說s的類型是字符串,那么clojure編譯器會從java.lang.String類里查找名稱為substring并且接收兩個參數的方法,并利用invokevirtual指令直接調用此方法,避免了反射調用。除了target對象(這里的s)可以添加type hint,方法參數和返回值也可以添加type hint:
    user=> (defn ^{:tag String} substr [^String s ^Integer begin ^Integer end] (.substring s begin end))
    #'user/substr
        
        返回值添加type hint是利用tag元數據,提示substr的返回類型是String,其他函數在使用substr的時候可以利用這個類型信息來避免反射;而參數的type hint跟target object的type hint一樣以^開頭加上類型,例如這里begin和end都提示說是Integer類型。

        問題1,什么時候應該為參數添加type hint呢?我的觀點是,在任何為target object添加type hint的地方,都應該相應地為參數添加type hint,除非你事先不知道參數的類型。為什么呢?因為clojure查找類方法的順序是這樣:

    1.從String類里查找出所有參數個數為2并且名稱為substring方法
    2.遍歷第一步里查找出來的Method,如果你有設置參數的type hint,則
    查找最匹配參數類型的Method;否則,如果第一步查找出來的Method就一個,直接使用這個Method,相反就認為沒有找到對應的Method。
    3.如果第二步沒有找到Method,使用反射調用;否則根據該Method元信息生成調用字節碼。

       因此,如果substring方法的兩個參數版本剛好就一個,方法參數有沒有type hint都沒有關系(有了錯誤的type hint反而促使反射的發生),我們都會找到這個唯一的方法;但是如果目標方法的有多個重載方法并且參數相同,而只是參數類型不同(Java里是允許方法的參數類型重載的,Clojure只允許函數的參數個數重載),那么如果沒有方法參數的type hint,Clojure編譯器仍然無法找到合適的調用方法,而只能通過反射。
       
       看一個例子,定義get-bytes方法調用String.getBytes:

    user=> (defn get-bytes [s charset] (.getBytes s charset))
    Reflection warning, NO_SOURCE_PATH:26 - call to getBytes can't be resolved.
    #'user/get-bytes
    user=> (defn get-bytes [^String s charset] (.getBytes s charset))
    Reflection warning, NO_SOURCE_PATH:27 - call to getBytes can't be resolved.
    #'user/get-bytes

        第一次定義,s和charset都沒有設置type hint,有反射警告;第二次,s設置了type hint,但是還是有反射警告。原因就在于String.getBytes有兩個重載方法,參數個數都是一個,但是接收不同的參數類型,一個是String的charset名稱,一個Charset對象。如果我們明確地知道這里charset是字符串,那么還可以為charset添加type hint:
    user=> (defn get-bytes [^String s ^String charset] (.getBytes s charset))
    #'user/get-bytes
       
        這次才真正的沒有警告了。總結:在設置type hint的時候,不要只考慮被調用的target object,也要考慮調用的方法參數。

        問題2:什么時候應該添加tag元數據呢?理論上,在任何你明確知道返回類型的地方都應該添加tag,但是這不是教條,如果一個偶爾被調用的方法是無需這樣做的。這一點只對寫庫的童鞋要特別注意。

        Type hint的原理在上文已經大概描述了下,具體到clojure源碼級別,請參考clojure.lang.Compiler.InstanceMethodExpr類的構造函數和emit方法。最后,附送是否使用type hint生成substr函數的字節碼之間的差異對比:
    未使用type hint 使用type hint

      // access flags 1

      public invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;

       L0

        LINENUMBER 14 L0

       L1

        LINENUMBER 14 L1

        ALOAD 1

        ACONST_NULL

        ASTORE 1

        LDC "substring"

        ICONST_2

        ANEWARRAY java/lang/Object

        DUP

        ICONST_0

        ALOAD 2

        ACONST_NULL

        ASTORE 2

        AASTORE

        DUP

        ICONST_1

        ALOAD 3

        ACONST_NULL

        ASTORE 3

        AASTORE

        INVOKESTATIC clojure/lang/Reflector.invokeInstanceMethod (Ljava/lang/Object;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/Object;

       L2

        LOCALVARIABLE this Ljava/lang/Object; L0 L2 0

        LOCALVARIABLE s Ljava/lang/Object; L0 L2 1

        LOCALVARIABLE begin Ljava/lang/Object; L0 L2 2

        LOCALVARIABLE end Ljava/lang/Object; L0 L2 3

        ARETURN

        MAXSTACK = 0

        MAXLOCALS = 0

    public invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;

       L0

        LINENUMBER 15 L0

       L1

        LINENUMBER 15 L1

        ALOAD 1

        ACONST_NULL

        ASTORE 1

        CHECKCAST java/lang/String

        ALOAD 2

        ACONST_NULL

        ASTORE 2

        CHECKCAST java/lang/Number

        INVOKESTATIC clojure/lang/RT.intCast (Ljava/lang/Object;)I

        ALOAD 3

        ACONST_NULL

        ASTORE 3

        CHECKCAST java/lang/Number

        INVOKESTATIC clojure/lang/RT.intCast (Ljava/lang/Object;)I

        INVOKEVIRTUAL java/lang/String.substring (II)Ljava/lang/String;

       L2

        LOCALVARIABLE this Ljava/lang/Object; L0 L2 0

        LOCALVARIABLE s Ljava/lang/Object; L0 L2 1

        LOCALVARIABLE begin Ljava/lang/Object; L0 L2 2

        LOCALVARIABLE end Ljava/lang/Object; L0 L2 3

        ARETURN

        MAXSTACK = 0

        MAXLOCALS = 0


        
        對比很明顯,沒有使用type hint,調用clojure.lang.Reflector的invokeInstanceMethod方法,使用反射調用(具體見clojure.lang.Reflector.java),而使用了type hint之后,則直接使用invokevirtual指令(其他方法可能是invokestatic或者invokeinterface等指令)調用該方法,避免了反射。
          

        參考:

    posted @ 2012-07-10 20:37 dennis 閱讀(11952) | 評論 (1)編輯 收藏


        HouseMD是淘寶的聚石寫的一個非常優秀的Java進程運行時診斷和調試工具,如果你接觸過btrace,那么HouseMD也許你應該嘗試下,它比btrace更易用,不需要寫腳本,類似strace的方式attach到jvm進程做跟蹤調試。

        基本的安裝和使用請看這篇文檔《UserGuide》,恕不重復。以下內容都假設你正確安裝了housemd。

        本文主要介紹下怎么用housemd診斷跟蹤clojure進程。Clojure的java實現也是跑在JVM里,當然也可以用housemd。

        我們以一個簡單的例子開始,假設我們有如下clojure代碼:
    (loop [x 1]
      (Thread/sleep 1000)
      (prn x)
      (recur (inc x)))

        這段很簡單,只是間隔一秒不斷地打印遞增的數字x。我們準備用housemd跟蹤這個程序的運行,首先運行這個程序,你可以用lein,也可以直接java命令運行:
    java -cp clojure.jar clojure.main test.clj

        運行時不斷地在控制臺打印數字,通過jps或者ps查詢到該進程的id,假設為pid,使用housemd連接到該進程:
    housemd <pid>
        順利進入housemd的交互控制臺,通過help命令可以查詢支持的命令:

    housemd> help

    quit      terminate the process.
    help      display this infomation.
    trace     display or output infomation of method invocaton.
    loaded    display loaded classes information.

        要用housemd調試clojure,你需要對clojure的實現有一點點了解,有興趣可以看過去的一篇blog《clojure hacking guide》,簡單來說,clojure的編譯器會將clojure代碼編譯成java類并運行。對于JVM來說,clojure生成的類,跟java編譯器生成類沒有什么不同。
        具體到上面的clojure代碼,會生成一個名為user$eval1的類,user是默認的namespace,而eval1是clojure編譯器自動生成的一個標示類名,通過loaded命令查詢類的加載情況:
    housemd> loaded user$eval1 -h
    user$eval1 -> null
        - clojure.lang.DynamicClassLoader@1d25d06e
            - clojure.lang.DynamicClassLoader@1d96f4b5
                - sun.misc.Launcher$AppClassLoader@a6eb38a
                    - sun.misc.Launcher$ExtClassLoader@69cd2e5f

        通過-h選項打印了加載user$eval1的類加載器的層次關系,因為user$eval1是動態生成的(clojure啟動過程中),因此它不在任何一個class或者jar文件中。除了查詢user namespace的類之外,你還可以查詢clojure.core,clojure.lang,clojure.java等任何被加載進來的類,例如查詢clojure.core.prn的類,在clojure里這是一個函數,在jvm看來這只是一個類:
    housemd> loaded -h core$prn
    clojure.core$prn -> /Volumes/HDD/Users/apple/clojure/clojure.jar
        - sun.misc.Launcher$AppClassLoader@a6eb38a
            - sun.misc.Launcher$ExtClassLoader@69cd2e5f
       注意,不需要完整的namespace——clojure.core,直接core$prn即可。其他也是類似。小技巧:如果你實在不知道clojure編譯器生成的類名,你可以利用jvm自帶的jmap命令來查詢。

       接下來,我們嘗試用trace命令跟蹤方法的運行,例如例子中的clojure代碼用到了loop和recur兩個sepcial form,我們跟蹤下loop:
    housemd> trace -t 5 core$loop
    INFO : probe class clojure.core$loop
    core$loop.doInvoke(Object, Object, Object, Object)    sun.misc.Launcher$AppClassLoader@a6eb38a            0            -ms    null
    core$loop.getRequiredArity()                          sun.misc.Launcher$AppClassLoader@a6eb38a            0            -ms    null

    core$loop.doInvoke(Object, Object, Object, Object)    sun.misc.Launcher$AppClassLoader@a6eb38a            0            -ms    null
    core$loop.getRequiredArity()                          sun.misc.Launcher$AppClassLoader@a6eb38a            0            -ms    null

    core$loop.doInvoke(Object, Object, Object, Object)    sun.misc.Launcher$AppClassLoader@a6eb38a            0            -ms    null
    core$loop.getRequiredArity()                          sun.misc.Launcher$AppClassLoader@a6eb38a            0            -ms    null

    core$loop.doInvoke(Object, Object, Object, Object)    sun.misc.Launcher$AppClassLoader@a6eb38a            0            -ms    null
    core$loop.getRequiredArity()                          sun.misc.Launcher$AppClassLoader@a6eb38a            0            -ms    null

    core$loop.doInvoke(Object, Object, Object, Object)    sun.misc.Launcher$AppClassLoader@a6eb38a            0            -ms    null
    core$loop.getRequiredArity()                          sun.misc.Launcher$AppClassLoader@a6eb38a            0            -ms    null

    INFO : Ended by timeout
    INFO : reset class clojure.core$loop

        在5秒內,clojure.core$loop類有兩個方法各被調用了5次,doInvoke是實際的調用,而getRequiredArity用來查詢loop所需要的參數個數。trace還可以跟蹤到具體的方法,例如我們跟蹤prn函數的調用情況:
    housemd> trace -t 5 core$prn.doInvoke
    INFO : probe class clojure.core$prn
    core$prn.doInvoke(Object)    sun.misc.Launcher$AppClassLoader@a6eb38a            1            1ms    clojure.core$prn@3e4ac866

    core$prn.doInvoke(Object)    sun.misc.Launcher$AppClassLoader@a6eb38a            2           <1ms    clojure.core$prn@3e4ac866

    core$prn.doInvoke(Object)    sun.misc.Launcher$AppClassLoader@a6eb38a            3           <1ms    clojure.core$prn@3e4ac866

    core$prn.doInvoke(Object)    sun.misc.Launcher$AppClassLoader@a6eb38a            4           <1ms    clojure.core$prn@3e4ac866

    core$prn.doInvoke(Object)    sun.misc.Launcher$AppClassLoader@a6eb38a            5           <1ms    clojure.core$prn@3e4ac866

    INFO : Ended by timeout
    INFO : reset class clojure.core$prn
      
       trace打印了方法的調用次數(5秒內)和每次調用的時間(毫秒級別),以及調用的target object。小技巧:沒有可變參數的函數生成類最終調用的是invoke方法(參數個數可能重載),有可變參數的函數調用的是doInvoke方法。

       trace命令還支持打印調用堆棧到文件,例如:
    trace -t 5 -d -s  core$prn.doInvoke

       利用-s和-d命令會將詳細的調用信息輸出到臨時目錄,臨時目錄的路徑可以通過trace help命令查詢到,在我的機器上是/tmp/trace/<pid>@host目錄下。調用堆棧的輸出類似:
    example$square.invoke(Long) call by thread [main]
        example$eval9.invoke(test.clj:11)
        clojure.lang.Compiler.eval(Compiler.java:6465)
        clojure.lang.Compiler.load(Compiler.java:6902)
        clojure.lang.Compiler.loadFile(Compiler.java:6863)
        clojure.main$load_script.invoke(main.clj:282)
        clojure.main$script_opt.invoke(main.clj:342)
        clojure.main$main.doInvoke(main.clj:426)
        clojure.lang.RestFn.invoke(RestFn.java:421)
        clojure.lang.Var.invoke(Var.java:405)
        clojure.lang.AFn.applyToHelper(AFn.java:163)
        clojure.lang.Var.applyTo(Var.java:518)
        clojure.main.main(main.java:37)

       上面這個簡單的例子展示了使用housemd跟蹤診斷clojure進程的方法。

       自定義ns和函數的調試與此類似,假設我們有下面的clojure代碼:
    (ns example)
    (defn square [x]
      (* x x))

    (loop [x 1]
      (Thread/sleep 1000)
      (square x)
      (recur (inc x)))
     
       ns為example,自定義函數square并定期循環調用。使用housemd診斷這段代碼:
    loaded -h example$square     #查詢square的加載情況
    trace -t 10 -d -s example$square.invoke  #跟蹤10秒內square的調用情況

    posted @ 2012-06-15 02:52 dennis 閱讀(12162) | 評論 (2)編輯 收藏

    主站蜘蛛池模板: 免费播放春色aⅴ视频| 青草草色A免费观看在线| 免费日本黄色网址| 青草久久精品亚洲综合专区| 性做久久久久久免费观看| 69av免费观看| 久久久久亚洲AV无码专区首JN| 91在线视频免费播放| 亚洲视频.com| 午夜性色一区二区三区免费不卡视频| 日本免费一区二区久久人人澡 | 久久99国产亚洲精品观看| 热久久这里是精品6免费观看| 免费国产黄网站在线观看| 亚洲精品国产福利片| 国产桃色在线成免费视频| 亚洲婷婷综合色高清在线| 青青青免费国产在线视频小草| 国产亚洲美女精品久久久| 亚洲精品午夜国产va久久| 中文字幕不卡高清免费| 亚洲gv猛男gv无码男同短文| 一级a性色生活片久久无少妇一级婬片免费放| 国内少妇偷人精品视频免费| 久久亚洲AV成人无码| 一二三四视频在线观看中文版免费| 又黄又爽无遮挡免费视频| 亚洲欧洲日本精品| 成年人免费视频观看| 亚洲国产亚洲片在线观看播放| 中文日本免费高清| 亚洲成a人片在线观看无码| 国产在线观a免费观看| 亚洲最大免费视频网| 免费一区二区视频| 暖暖日本免费中文字幕| 精品国产亚洲一区二区三区在线观看| 午夜国产精品免费观看| 在线播放国产不卡免费视频| 亚洲AV无码专区国产乱码电影| 中文字幕在线免费看|