Giter VIP home page Giter VIP logo

blog's People

Contributors

yikebocai avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

blog's Issues

Java内存溢出

Java内存模型中主要包括堆(Heap)栈(VM Stack)方法区(Method Area)这几部分,当需>要分配的对象、方法、常量等等超出所有区域的限定时就可能产生我们最常见的内存溢出错>误(OutOfMemoryError)或者堆栈溢出(StackOverFlowError),具体包括如下几种常见情况。

堆OOM(Heap OutOfMemory)

我们在日常编写程序时,最常见的就是堆OOM,这个内存是用来存储对象的,包括我们所知的新生代、老生代,一但创建的对象过多而没有及时释放无法被GC回收,就会产生堆OOM。可以通过-Xms-Xmx参数来控制堆的大小。阿里国际站的服务器一般为5G的虚拟机,配置堆大小为固定2G。

package org.bocai.jvm.gc;

/**
 * VM Args:-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
 * @author [email protected]
 * @since 2013-3-24
 * 
 */
public class HeapOOM {

    public static void main(String[] args) {
        int SIZE=1024*1024;
        byte[][] objs=new byte[100][];

        for(int i=0;i<objs.length;i++){
            System.out.print("try to create obj "+i+" ...");
            objs[i]=new byte[SIZE];
            System.out.println("OK");
        }
    }

}

栈溢出(Stack StackOverFlow)

虚拟机栈内存不足时会产生两种溢出,一种是StackOverflow,一种是OutOfMemory,虚拟机规范中对此有详细说明。虚拟机在进行方法调用时,每次调用都会产生一个栈桢(Stack Frame),用以记录局部变量表(Local Variables)、操作数栈(Operand Stacks)、动态链接(Dynamic Linking)等信息,当Stack的空间不足以分配更多栈桢时,会产生溢出。比如在递归调用时,每次调用都会产生一个新的栈桢,递归调用的次数是受-Xss这个参数的限制的,我之前在使用递归来实现快速排序时遇到这样的问题,当需要快排的列表很大时,递归次数也会很多,会产生这样的溢出异常。

package org.bocai.jvm.gc;

/**
 * VM Args:-Xss128k
 * 
 * @author [email protected]
 * @since 2013-3-24
 * 
 */
public class StackSOF {

    private int stackLength = 1;

    public void stackLeak() {
        stackLength++;
        stackLeak();
    }

    /**
     * stackLength is about 2403,average 54 bytes per stack frame
     */
    public static void main(String[] args) {
        StackSOF sof = new StackSOF();
        try {
            sof.stackLeak();
        } catch (Throwable e) {
            System.out.println("stack length:" + sof.stackLength);
            e.printStackTrace();
        }
    }

}

栈溢出(Stack OutOfMemory)

虚拟机为每个线程分配固定的大小,通过参数-Xss可以来调整,虚拟机栈的最大内存不超过整个物理内存减去堆内存的值,如果运行期产生过多线程,并且为而每个线程分配了比较多的内存,就会很快产生OOM异常。

package org.bocai.jvm.gc;

/**
 * VM Args:-Xss2M
 * 
 * @author [email protected]
 * @since 2013-3-24
 * 
 */
public class StackOOM {

    /**
     * VM Stack size is about equals  RAM size reduce Heap size
     * @param args
     */
    public static void main(String[] args) {
        int counter=0;

        while (true) {
            System.out.println("create thread "+(counter++)+" ...");
            Thread thread = new Thread(new Runnable() {
                public void run() {
                    while (true){
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) { 
                            e.printStackTrace();
                        }
                    }
                }
            });

            thread.start();
        }

    }

}

常量池OOM(Constant Pool OOM)

常量池保存字段(Field)的名称和描述符、方法的名称和描述符、类和接口的全限定名等,它是属于方法区(Method Area)的一部分,因此可以通过设置Perm区的大小来进行调整。最直接的测试方法是用String对象的intern()方法来实现,当常量池中有相等(equals)的值时返回该值,否则创建一个新值放在常量池中并返回该值的引用,详见JDK中String/intern()方法的说明。

package org.bocai.jvm.gc;

import java.util.ArrayList;
import java.util.List;

/**
 * VM Args: -XX:PermSize=10M -XX:MaxPermSize=10M
 * 
 * @author [email protected]
 * @since 2013-3-24
 * 
 */
public class ConstantPoolOOM {

    /**
     * constant pool included in Method Area,so can set parms PermSize and MaxPermSize
     */
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();

        int i = 0;
        while (true) {
            System.out.println("add string object into constant pool " + i);
            list.add(String.valueOf(i++).intern());
        }

    }

}

上面的测试代码运行的结果是:

...
add string object into constant pool 35925
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
    at java.lang.String.intern(Native Method)
    at org.bocai.jvm.gc.ConstantPoolOOM.main(ConstantPoolOOM.java:24)

如果没有使用intern()方法,测试代码运行的结果是:

...
add string object into constant pool 192210
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOfRange(Unknown Source)
    at java.lang.String.<init>(Unknown Source)
    at java.lang.StringBuilder.toString(Unknown Source)
    at org.bocai.jvm.gc.ConstantPoolOOM.main(ConstantPoolOOM.java:23)

或者使用javassist动态增加字段或方法的方式,来产生常量池OOM

package org.bocai.jvm.gc;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;

/**
 * @author [email protected]
 * @since 2013-3-24
 * 
 */
public class ConstantPoolOOM2 {

    @SuppressWarnings("unchecked")
    public static void main(String[] args) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass ctClazz = pool.get("org.bocai.jvm.gc.TestBean");

        // ctClazz.defrost();

        // add field sample
        CtField field = new CtField(pool.get("java.lang.String"), "test",
                ctClazz);
        ctClazz.addField(field);
        for (int i = 1; i < 10000; i++) {
            CtField field2 = new CtField(pool.get("java.lang.String"), "test1"
                    + i, ctClazz);
            ctClazz.addField(field2);
        }

        // add method sample
        CtClass[] paramTypes = { pool.get("java.lang.String") };
        CtMethod ctMethod = new CtMethod(pool.get("java.lang.String"),
                "getTest", paramTypes, ctClazz);
        ctMethod.setBody("{ return  $1 ;}");
        ctClazz.addMethod(ctMethod);

        Class<TestBean> clazz = ctClazz.toClass();

        // list fields
        Field[] fields = clazz.getDeclaredFields();
        for (Field f : fields) {
            System.out.println("Field: " + f.getName());
        }

        // list methods
        Method[] methods = clazz.getMethods();
        for (Method m : methods) {
            System.out.println("Method: " + m.getName());
        }

        // invoke method
        Method method = clazz.getMethod("getTest" ,
                new Class[] { String.class });
        TestBean bean = clazz.newInstance();
        String invoke = (String) method.invoke(bean,
                new Object[] { "parameter1" });
        System.out.println(invoke);

    }

}

class TestBean {

}

方法区OOM(Method Area OOM)

方法区除了常量池之外,主要存储虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等,和持久区的概念基本一致,可以使用参数-XX:MaxPermSize=10M来设定大小。下面通过CGLIB来动态产生大量的类,来测试方法区OOM。

package org.bocai.jvm.gc;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**
 * VM Args: -XX:PermSize=10M -XX:MaxPermSize=10M
 * 
 * @author [email protected]
 * @since 2013-3-24
 * 
 */
public class MethodAreaOOM {

    public static void main(String[] args) {
        while (true) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(OOMObject.class);
            enhancer.setUseCache(false);
            enhancer.setCallback(new MethodInterceptor() {
                public Object intercept(Object obj, Method method,
                        Object[] args, MethodProxy proxy) throws Throwable {
                    System.out.println("insert before");
                    return proxy.invokeSuper(obj, args);
                }

            });

            OOMObject obj=(OOMObject)enhancer.create();
            obj.test();
        }

    }

    static class OOMObject {
        public void test(){
            System.out.println("This is super class method");
        }
    }

}

直接内存区OOM(Direct Memory OOM)

直接内存区不属于虚拟机运行时内存的一部分,它访问的是操作系统内存,比如NIO中实现的方案就是用的直接内存,可以通过-XX:MaxDirectMemorySize来设定。

package org.bocai.jvm.gc;

import java.lang.reflect.Field;

import sun.misc.Unsafe;

/**
 * VM Args: -Xmx20M -XX:MaxDirectMemorySize=10M
 * @author [email protected]
 * @since 2013-3-24
 * 
 */
public class DirectMemoryOOM {

    public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
        Field unsafeField=Unsafe.class.getDeclaredFields()[0];
        unsafeField.setAccessible(true);
        Unsafe unsafe=(Unsafe)unsafeField.get(null);

        int counter=0;
        while(true){
            System.out.println("allocate direct memory "+(counter++));
            unsafe.allocateMemory(10*1024*1024);
        }
    }

}

参考
1.上述例子的源代码请查看这里
2.上述例子都是参考自《深入理解Java虚拟机-JVM高级特性和最佳实践》

使用leiningen进行clojure工程自动化管理

原文写于2012-10-17

Leiningen是类似Maven的一个Clojure自动化构建工具,它在工程里也会生成一个类似pom的文件叫project.clj,不过是clojure风格的,更简洁和简单,支持命令操作和IntelliJ idea集成环境。 从Leiningen主页下载1.7.0版本的standalone jar包,放到比如D:\app\lein目录下,然后拷贝下面的脚本内容到lein.bat文件中,也放置到该目录下。

@echo off

set LEIN_VERSION=1.7.0

setLocal EnableExtensions EnableDelayedExpansion

if "%LEIN_VERSION:~-9%" == "-SNAPSHOT" (
    set SNAPSHOT=YES
) else (
    set SNAPSHOT=NO
)

set ORIGINAL_PWD=%CD%
:: If ORIGINAL_PWD ends with a backslash (such as C:\),
:: we need to escape it with a second backslash.
if "%ORIGINAL_PWD:~-1%x" == "\x" set "ORIGINAL_PWD=%ORIGINAL_PWD%\"

call :FIND_DIR_CONTAINING_UPWARDS project.clj
if "%DIR_CONTAINING%" neq "" cd "%DIR_CONTAINING%"

:: LEIN_JAR and LEIN_HOME variables can be set manually.

if "x%LEIN_HOME%" == "x" (
    if exist "%CD%\.lein" (
        if /I NOT "%CD%"=="%USERPROFILE%" echo Running in bundled mode.
        set LEIN_HOME=%CD%\.lein
    ) else (
        set LEIN_HOME=%USERPROFILE%\.lein
    )
)

if "x%LEIN_JAR%" == "x" set LEIN_JAR="!LEIN_HOME!\self-installs\leiningen-!LEIN_VERSION!-standalone.jar"

if "%1" == "self-install" goto SELF_INSTALL
if "%1" == "upgrade"      goto NO_UPGRADE

set DEV_PLUGINS="
for %%j in (".\lib\dev\*.jar") do (
    set DEV_PLUGINS=!DEV_PLUGINS!;%%~fj
)
set DEV_PLUGINS=!DEV_PLUGINS!"

call :BUILD_UNIQUE_USER_PLUGINS
set CLASSPATH="%CLASSPATH%";%DEV_PLUGINS%;%UNIQUE_USER_PLUGINS%;test;src;resources

:: Apply context specific CLASSPATH entries
set CONTEXT_CP=
if exist ".lein-classpath" set /P CONTEXT_CP=&lt;.lein-classpath
if NOT "%CONTEXT_CP%"=="" set CLASSPATH="%CONTEXT_CP%";%CLASSPATH%

if exist "%~f0\..\..\src\leiningen\core.clj" (
    :: Running from source checkout.
    call :SET_LEIN_ROOT "%~f0\..\.."

    set LEIN_LIBS="
    for %%j in ("!LEIN_ROOT!\lib\*") do set LEIN_LIBS=!LEIN_LIBS!;%%~fj
    set LEIN_LIBS=!LEIN_LIBS!"

    if "x!LEIN_LIBS!" == "x" if not exist %LEIN_JAR% goto NO_DEPENDENCIES

    set CLASSPATH=%CLASSPATH%;!LEIN_LIBS!;"!LEIN_ROOT!\src";"!LEIN_ROOT!\resources";%LEIN_JAR%
) else (
    :: Not running from a checkout.
    if not exist %LEIN_JAR% goto NO_LEIN_JAR
    set CLASSPATH=%CLASSPATH%;%LEIN_JAR%
)

if not "x%DEBUG%" == "x" echo CLASSPATH=%CLASSPATH%
:: ##################################################

if not "x%INSIDE_EMACS%" == "x" goto SKIP_JLINE
if "%1" == "repl"             goto SET_JLINE
if "%1" == "interactive"      goto SET_JLINE
if "%1" == "int"              goto SET_JLINE
goto SKIP_JLINE

:SET_JLINE
set JLINE=jline.ConsoleRunner
:SKIP_JLINE

if "x%JAVA_CMD%" == "x" set JAVA_CMD="java"
if "x%JVM_OPTS%" == "x" set JVM_OPTS=%JAVA_OPTS%
set CLOJURE_JAR=%USERPROFILE%\.m2\repository\org\clojure\clojure\1.2.1\clojure-1.2.1.jar
goto RUN


:: Builds a classpath fragment consisting of user plugins
:: which aren&#039;t already present as a dev dependency.
:BUILD_UNIQUE_USER_PLUGINS
call :BUILD_PLUGIN_SEARCH_STRING %DEV_PLUGINS%
set UNIQUE_USER_PLUGINS="
for %%j in ("%LEIN_HOME%\plugins\*.jar") do (
    call :MAKE_SEARCH_TOKEN %%~nj
    echo %PLUGIN_SEARCH_STRING%|findstr ;!SEARCH_TOKEN!; &gt; NUL
    if !ERRORLEVEL! == 1 (
        set UNIQUE_USER_PLUGINS=!UNIQUE_USER_PLUGINS!;%%~fj
    )
)
set UNIQUE_USER_PLUGINS=!UNIQUE_USER_PLUGINS!"
goto EOF

:: Builds a search string to match against when ensuring
:: plugin uniqueness.
:BUILD_PLUGIN_SEARCH_STRING
for %%j in (".\lib\dev\*.jar") do (
    call :MAKE_SEARCH_TOKEN %%~nj
    set PLUGIN_SEARCH_STRING=!PLUGIN_SEARCH_STRING!;!SEARCH_TOKEN!
)
set PLUGIN_SEARCH_STRING=%PLUGIN_SEARCH_STRING%;
goto EOF

:: Takes a jar filename and returns a reversed jar name without version.
:: Example: lein-multi-0.1.1.jar -&gt; itlum-niel
:MAKE_SEARCH_TOKEN
call :REVERSE_STRING %1
call :STRIP_VERSION !RSTRING!
set SEARCH_TOKEN=!VERSIONLESS!
goto EOF

:: Reverses a string.
:REVERSE_STRING
set NUM=0
set INPUTSTR=%1
set RSTRING=
:REVERSE_STRING_LOOP
call set TMPCHR=%%INPUTSTR:~%NUM%,1%%%
set /A NUM+=1
if not "x%TMPCHR%" == "x" (
    set RSTRING=%TMPCHR%%RSTRING%
    goto REVERSE_STRING_LOOP
)
goto EOF

:: Takes a string and removes everything from the beginning up to
:: and including the first dash character.
:STRIP_VERSION
set INPUT=%1
for /F "delims=- tokens=1*" %%a in ("%INPUT%") do set VERSIONLESS=%%b
goto EOF


:NO_LEIN_JAR
echo.
echo %LEIN_JAR% can not be found.
echo You can try running "lein self-install"
echo or change LEIN_JAR environment variable
echo or edit lein.bat to set appropriate LEIN_JAR path.
echo.
goto EOF

:NO_DEPENDENCIES
echo.
echo Leiningen is missing its dependencies.
echo Please see "Building" in the README.
echo.
goto EOF

:SELF_INSTALL
if exist %LEIN_JAR% (
    echo %LEIN_JAR% already exists. Delete and retry.
    goto EOF
)
for %%f in (%LEIN_JAR%) do set LEIN_INSTALL_DIR="%%~dpf"
if not exist %LEIN_INSTALL_DIR% mkdir %LEIN_INSTALL_DIR%

echo Downloading Leiningen now...

set HTTP_CLIENT=wget --no-check-certificate -O
wget&gt;nul 2&gt;&1
if ERRORLEVEL 9009 (
    curl&gt;nul 2&gt;&1
    if ERRORLEVEL 9009 goto NO_HTTP_CLIENT
    set HTTP_CLIENT=curl --insecure -f -L -o
)
set LEIN_JAR_URL=https://github.com/downloads/technomancy/leiningen/leiningen-%LEIN_VERSION%-standalone.jar
%HTTP_CLIENT% %LEIN_JAR% %LEIN_JAR_URL%
if ERRORLEVEL 1 (
    del %LEIN_JAR%&gt;nul 2&gt;&1
    goto DOWNLOAD_FAILED
)
goto EOF

:DOWNLOAD_FAILED
echo.
echo Failed to download %LEIN_JAR_URL%
if %SNAPSHOT% == YES echo See README.md for SNAPSHOT build instructions.
echo.
goto EOF

:NO_HTTP_CLIENT
echo.
echo ERROR: Wget/Curl not found. Make sure at least either of Wget and Curl is
echo        installed and is in PATH. You can get them from URLs below:
echo.
echo Wget: "http://users.ugent.be/~bpuype/wget/"
echo Curl: "http://curl.haxx.se/dlwiz/?type=bin&os=Win32&flav=-&ver=2000/XP"
echo.
goto EOF

:NO_UPGRADE
echo.
echo Upgrade feature is not available on Windows. Please edit the value of
echo variable LEIN_VERSION in file %~f0
echo then run "lein self-install".
echo.
goto EOF


:SET_LEIN_ROOT
set LEIN_ROOT=%~f1
goto EOF

:: Find directory containing filename supplied in first argument
:: looking in current directory, and looking up the parent
:: chain until we find it, or run out
:: returns result in %DIR_CONTAINING%
:: empty string if we don&#039;t find it
:FIND_DIR_CONTAINING_UPWARDS
set DIR_CONTAINING=%CD%
set LAST_DIR=

:LOOK_AGAIN
if "%DIR_CONTAINING%" == "%LAST_DIR%" (
    :: didn&#039;t find it
    set DIR_CONTAINING=
    goto EOF
)

if EXIST "%DIR_CONTAINING%\%1" (
    :: found it - use result in DIR_CONTAINING
    goto EOF
)

set LAST_DIR=%DIR_CONTAINING%
call :GET_PARENT_PATH "%DIR_CONTAINING%\.."
set DIR_CONTAINING=%PARENT_PATH%
goto LOOK_AGAIN

:GET_PARENT_PATH
set PARENT_PATH=%~f1
goto EOF


:RUN
:: We need to disable delayed expansion here because the %* variable
:: may contain bangs (as in test!). There may also be special
:: characters inside the TRAMPOLINE_FILE.
setLocal DisableDelayedExpansion

if "%1" == "trampoline" goto RUN_TRAMPOLINE else goto RUN_NORMAL

:RUN_TRAMPOLINE
set "TRAMPOLINE_FILE=%TEMP%\lein-trampoline-%RANDOM%.bat"

%JAVA_CMD% -client %LEIN_JVM_OPTS% -Xbootclasspath/a:"%CLOJURE_JAR%" ^
 -Dleiningen.original.pwd="%ORIGINAL_PWD%" ^
 -Dleiningen.trampoline-file="%TRAMPOLINE_FILE%" ^
 -cp %CLASSPATH% %JLINE% clojure.main -e "(use &#039;leiningen.core)(-main)" NUL %*

if not exist "%TRAMPOLINE_FILE%" goto EOF
call "%TRAMPOLINE_FILE%"
del "%TRAMPOLINE_FILE%"
goto EOF

:RUN_NORMAL
%JAVA_CMD% -client %LEIN_JVM_OPTS% -Xbootclasspath/a:"%CLOJURE_JAR%" ^
 -Dleiningen.original.pwd="%ORIGINAL_PWD%" ^
 -cp %CLASSPATH% %JLINE% clojure.main -e "(use &#039;leiningen.core)(-main)" NUL %*


:EOF

增加环境变量LEIN_JAR=D:\app\leiningen-1.7.0-jar,PATH中增加D:\app\lein,使得在任意目录下可以直接执行lein命令。在命令行下输入lein version测试安装是否正常:

D:\work\myclj>lein version
Leiningen 1.7.0 on Java 1.6.0_24 Java HotSpot(TM) Client VM

执行命令lein new myclj可以生成一个新的clojure工程,目录结构如下:

myclj
|--src
  |--myclj
    |--core.clj
|--test
|--project.clj

既然使用IntelliJ idea作为集成开发环境,那么结合两者是最好不过,幸好已经有人都已准备好了,下载最新0.2.3版本的leiningen插件,在工作区最右侧会多出一个leiningen的栏目,点开后可以添加刚才生成的工程文件,点击展示的命令可以直接进行lein命令操作。点击Leiningen Setttings可以设置lein.bat和leiningen-1.7.0-standalone.jar的位置及lein的目录。 我们同样测试一下数据库访问,在project.clj中增加两行依赖,点击leiningen栏目框中的deps命令,它会自动下载依赖的jar到lib目录下,特别要注意的是2.0版本的leiningen是有bug的,无法自动生成依赖的jar包,至今依然没有解决。

 -11

好像没有自动把lib目录加到classpath中,需要手动点击右键open module settings->Libraries里导致lib目录,之后就可以正常运行了。

【算法】使用Clojure进行快速排序

原文写于2012-10-28

快速排序是数据结构中的经典算法,原理简单就是随便在列表中取一个值,不过一般是取中间值,然后把比它小的放左边,比它大的放右边,然后递归处理左右两边的部分。最常见的实现是使用递归,代码简单但有一个致命问题是如果列表很大递归的深度很深可能出现堆栈溢出,替代的方案是自己使用堆栈来记录每部分的中间结果,之前用java实现过,但因为java的Collections类里提供了现成的解决方案,很少人自己来实现了,不过原理了解一下还是有必要的。

该算法中主要使用到如下几个函数:
count:计算列表的长度,相当于java的List.size()
if:条件表达式,语法格式为(if condition true-expression false-expression)
nth:取列表中的第N个元素
concat:和Mysql中的功能类似,将多个元素或集合合并成一个

递归的实现方案如下:

(defn quick-sort [mylist]
  ;(println "mylist:" mylist)

  ;计算列表的长度
  (def cnt (count mylist))
  ;计算中间位置
  (def mid (if (> cnt 1) (int (/ cnt  2)) 0))
  ;(println (str "mid:" mid))

  ;如果中间位置不大于0直接返回列表
  (if (> mid 0)
      (let [mid-value (nth mylist mid)  ;取中间位置的值
            left (filter #(< % mid-value) mylist) ;获取比中间值小的集合
            right (filter #(> % mid-value) mylist)]  ;获取比中间值大的集合
      ;(println "left:" left )
      ;(println "mid-value:" mid-value)
      ;(println "right:" right)
      ;递归
      (concat (quick-sort left) [mid-value] (quick-sort right ) )
      )

      mylist
  )
)

(println "sorted:"(quick-sort  [2 5 9 10 4 22 1 8]) )

输出结果如下:

sorted: (1 2 4 5 8 9 10 22)

程序员的自我修养

原文写于2013-3-10

自从12年初开始带团队以来,写代码的机会越来越少了,自己的技术能力这一年当中进步很慢,天天都在和业务方讨论需求、和团队讨论方案、团队管理、协调等等工作上面了,虽然这对工作来说也很重要,但我总觉得作为一个技术人员,一个程序员,远离了代码会让我快速落伍,一但离开了这个团队这家公司,我还有什么值得拿出手的东西来体现自己的价值?这种危机感深深地困扰着我,虽然工作这么多年我的技术能力充其量一般,但我还是想尽可能地提高自己,尤其是技术的深度。

深入理解JAVA虚拟机》是自我修养的第一步,去年买来的时候翻了一半,印象不是很深。前段时间开始放在订床头,每天晚上有时间有兴趣了就翻一翻,终于把这本书翻完了,并且还用IPAD做了笔记,这样感觉效果明显好多了,然后回过头来把最开始看的前半部分又认真看了一遍,当然做笔记还是少不了的,但是仅仅如此很难深入地理解JVM的知识,因此我想还是要练习一下。刚好看到Class文件结构这章,正好可以拿它来练手,自己写个反编译器玩玩,完全可以把这些知识使用上,于是就给自己留了作业,趁周末有空开始搞起来。

其实只要知道Class的文件结构,写个基本的反编译器并不很难,二进制的文件结构对我来说一点不陌生,刚毕业时做GPS数据解析,后来做短信网关接入,中文分词软件ICTCLASS4J等都用到二进制数据结构,在阿里做Web和后台服务应用,倒是很少了。昨晚一个晚上,今天下午加晚上,基本把Class的文件结果解析完毕了,只剩下自定义的Attributes了,这个稍微复杂一点。看着自己的想法一点点变成现实,重新找到了写程序的乐趣了。还给自己留了另外一个作业,把JVM GC的算法都测试一遍,恩,应该也会有不少收获。

作为一个程序员,不写代码是件很可怕的事情,我不想做个只会打杂的人,什么都懂什么都不精,浪费了太多光阴,从现在开始努力吧,起码一周保证7个小时的编码,平均一天一小时,这周任务算是完成了,继续加油吧。 查看我的Java反编译器的源代码  

Java堆内存分配和回收实践

Java运行期的内存结构包括堆(Heap)栈(VM Stack)方法区(Method Aread)本地方法栈(Native Method Stack)程序计数器(Pragram Counter Register)这几部分,其中和我们日常编程最密切的就是堆(Heap)了。

堆(Heap)又分为三部分,Young(New)Tenured(Old)Perm,对应的中文名叫法分别为新生代老年代永久代,而新生代又可以为分EdenFrom SurvivorTo Survivor,更详细的内存结构说明和GC回收算法请参考这篇文章《JDK5.0中JVM堆模型、GC垃圾收集详细解析》。

为了更深入地理解内存回收机制,下面的代码是我参照《深入理解JAVA虚拟机-JVM高级特性与最佳实践》提供的例子而写,分别触发Minor GC和Full GC。

package org.bocai.jvm.gc;

/**
 * VM Params:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:+PrintGCDetails -XX:MaxTenuringThreshold=1 -XX:+PrintTenuringDistribution
 * @author [email protected]
 * @since 2013-3-20
 * 
 */
public class GCDemo {

    private static final int _1MB=1024*1024;

    public static void main(String[] args) {
        testMinorGC();

    }

    /**
     * Minor GC Demo
     */
    private static void testMinorGC() {
        byte[] alloc1=new byte[2*_1MB];
        byte[] alloc2=new byte[2*_1MB];
        byte[] alloc3=new byte[1*_1MB];

        //first Minor GC,5M Eden->Old
        byte[] alloc4=new byte[4*_1MB];

        byte[] alloc5=new byte[_1MB/4];

        //second Minor GC,alloc5:Eden->Surivior
        byte[] alloc6=new byte[4*_1MB];

        //if MaxTenuringThreshold>2 ,then move alloc5 into old,but whatever alloc5 is moved into old,why? 
        alloc4=null;
        byte[] alloc7=new byte[4*_1MB];
        //System.out.println(alloc5.length);
    }


    /**
     * Full GC Demo
     */
    private static void testFullGC() {
        byte[] alloc1=new byte[2*_1MB];
        byte[] alloc2=new byte[2*_1MB];
        byte[] alloc3=new byte[1*_1MB];

        //first Minor GC
        byte[] alloc4=new byte[4*_1MB];
        byte[] alloc5=new byte[3*_1MB];

        //second Minor GC
        byte[] alloc6=new byte[4*_1MB];

        alloc1=null;
        alloc2=null;
        //first Full GC
        byte[] alloc7=new byte[2*_1MB];
    }


}

上面的例子很简单,我的困惑来自于Surivivor的对象处理机制,如果MaxTenuringThreshold大于1,在分配alloc7对象时,alloc5对象只经历了一次Minor GC,还没有达到MaxTenuringThreshold设定的值,为何放在Survivor区的alloc5对象就被移到了Old区,并且不管这个参数的值设为多少,只要经历alloc7对象的分配,都会被移到Old区,如下所示:

[GC [DefNew
Desired survivor size 524288 bytes, new threshold 1 (max 1)
- age   1:     150912 bytes,     150912 total
: 5463K->147K(9216K), 0.0705580 secs] 5463K->5267K(19456K), 0.0706022 secs] [Times: user=0.00 sys=0.08, real=0.07 secs] 
[GC [DefNew
Desired survivor size 524288 bytes, new threshold 1 (max 1)
- age   1:     262160 bytes,     262160 total
: 4586K->256K(9216K), 0.0106228 secs] 9706K->9619K(19456K), 0.0106669 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
[GC [DefNew: 4352K->4352K(9216K), 0.0000259 secs][Tenured: 9363K->9619K(10240K), 0.4481241 secs] 13715K->9619K(19456K), [Perm : 369K->369K(12288K)], 0.4482521 secs] [Times: user=0.00 sys=0.03, real=0.45 secs] 
Heap
 def new generation   total 9216K, used 4259K [0x322a0000, 0x32ca0000, 0x32ca0000)
  eden space 8192K,  52% used [0x322a0000, 0x326c8fe0, 0x32aa0000)
  from space 1024K,   0%  used [0x32aa0000, 0x32aa0000, 0x32ba0000)
  to   space 1024K,   0% used [0x32ba0000, 0x32ba0000, 0x32ca0000)
 tenured generation   total 10240K, used 9619K [0x32ca0000, 0x336a0000, 0x336a0000)
   the space 10240K,  93% used [0x32ca0000, 0x33604dd0, 0x33604e00, 0x336a0000)
 compacting perm gen  total 12288K, used 369K [0x336a0000, 0x342a0000, 0x376a0000)
   the space 12288K,   3% used [0x336a0000, 0x336fc718, 0x336fc800, 0x342a0000)
    ro space 10240K,  51% used [0x376a0000, 0x37bccf58, 0x37bcd000, 0x380a0000)
    rw space 12288K,  54% used [0x380a0000, 0x38738f50, 0x38739000, 0x38ca0000)

我们注意到from space 1024K,0%,Survivor区的对象被移走了,把alloc7对象分配那行注释掉,再看一下:

[GC [DefNew
Desired survivor size 524288 bytes, new threshold 1 (max 1)
- age   1:     150912 bytes,     150912 total
: 5463K->147K(9216K), 0.0067181 secs] 5463K->5267K(19456K), 0.0067671 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC [DefNew
Desired survivor size 524288 bytes, new threshold 1 (max 1)
- age   1:     262160 bytes,     262160 total
: 4586K->256K(9216K), 0.0081092 secs] 9706K->9619K(19456K), 0.0081469 secs] [Times: user=0.00 sys=0.02, real=0.01 secs] 
Heap
 def new generation   total 9216K, used 4515K [0x322a0000, 0x32ca0000, 0x32ca0000)
  eden space 8192K,  52% used [0x322a0000, 0x326c8fe0, 0x32aa0000)
  from space 1024K,  25% used [0x32aa0000, 0x32ae0010, 0x32ba0000)
  to   space 1024K,   0% used [0x32ba0000, 0x32ba0000, 0x32ca0000)
 tenured generation   total 10240K, used 9363K [0x32ca0000, 0x336a0000, 0x336a0000)
   the space 10240K,  91% used [0x32ca0000, 0x335c4dc0, 0x335c4e00, 0x336a0000)
 compacting perm gen  total 12288K, used 369K [0x336a0000, 0x342a0000, 0x376a0000)
   the space 12288K,   3% used [0x336a0000, 0x336fc700, 0x336fc800, 0x342a0000)
    ro space 10240K,  51% used [0x376a0000, 0x37bccf58, 0x37bcd000, 0x380a0000)
    rw space 12288K,  54% used [0x380a0000, 0x38738f50, 0x38739000, 0x38ca0000)

from space 1024K, 25% used里有alloc5对象,符合我们的预期,上面的情况一直没弄明白是什么原因.

在Wordpress中配置Clojure语法高亮插件

原文写于 2012-8-9

CodeColorer

很多语法高亮插件默认是不支持Clojure这种小众语言,codecolorer是个例外,内置支持clojure语法,效果如下所示: [cc lang="clojure"] ;两数相加 user=>(+ 2 3) 6 [/cc]

WP SyntaxHighlighter

WP SyntaxHighlighter这个语法高亮插件是我比较喜欢的,感觉比较清爽,默认支持大部分主流语言,但Clojure不在默认支持的列表中,不过有brush插件shBrushClojure.js可以自己添加,稍微麻烦一点。 首先先安装WP SyntaxHighlighter这个插件,但注意一点是千万先不要启用,启用后会在Mysql数据库表wp_options中写入这个插件相关的信息,包括wp_sh_language2、wp_sh_language3、wp_sh_brush_files的值,你再添加clojure的brush后,这几个字段是不会修改的,会导致你使用语法高亮插件时看不到Clojure这个语言的选择项。可以用这个SQL来查看这几个字段的值中是否包含clojure:

SELECT * 
FROM  `wp_options` 
WHERE option_name LIKE  'wp%'

然后下载shBrushClojure.js这个Javascript文件到本地,然后进入到你站点的后台管理页面,使用后台的文件管理功能,把这个文件上传到这两个目录,你会发现这个目录下有非常多的语言脚本

public_html/wp-content/plugins/wp-syntaxhighlighter/syntaxhighlighter2/scripts
public_html/wp-content/plugins/wp-syntaxhighlighter/syntaxhighlighter3/scripts

到目前为止还不算完,需要修改wp-syntaxhighlighter目录下的文件wp-syntaxhighlighter.php,在$wp_sh_language2/3 = array( 这一行增加

"clojure" => array('Clojure', 'true'),

在$wp_sh_brush_files = array( 这一行增加

"Clojure" => array('shBrushClojure.js', 'cf coldfusion', '0.9.1', 'true'),

保存文件后,再启用WP SyntaxHighlighter插件,这时你就可以看到SH Pre标签中已包含了Clojure的选项,效果如下:

;先定义一个对象
(defrecord Person [firstName lastName])
;创建一个实例
(def myboss (-> Person "Tom" "Zhang"))
#'user/myboss
;打印这个实例的值
myboss
#user.Person{:firstName "Tom", :lastName "Zhang"}
(defn browse-url
  "Open url in a browser"
  {:added "1.2"}
  [url]
  (or (open-url-in-browser url)
      (when *open-url-script* (sh/sh *open-url-script* (str url)) true)
      (open-url-in-swing url)))

Mac使用几天有感

虽然公司有配的IBM X201和台式机,但笔记本为了沟通方便装的是Windows几乎没法编程,过了年之后旺旺更是其名的反应迟钝让我忍无可忍,而台式机装的是Ubuntu只能用来编程无法移动,而Mac兼具二者优点,因此犹豫良久终于狠心在清明假期买了一个15寸的Macbook Pro Retina,花了一万三千大洋,是我迄今为止买的最贵的一台,要肉痛好一阵子了。

其实一开始看到RMBP就被它深深吸引了,高清大屏,机身纤薄,SSD硬盘,重量比较老款也轻很多,非常符合的要求,但高高在上的价格一直让我犹豫不决,甚至后来还想是不是买个Air或者很多同事用的13寸的MBP,价格几乎便宜一半。但是RMBP的种种优点,还是让人宁愿多付点银子,也要拥有它。买回来试用几天后,果然没有让我失望。

首先,15寸的高清大屏可视面积比我原来用的IBM X201几乎大了一倍,让我们这些码农写代码非常的爽,尤其是使用Eclipse时,年龄大了眼晴也不好了,为了多写几年代码,还是对自己好点吧。

其次,SSD硬盘比传统机械快了很多,不管是启动速度还是编译代码运行程序的速度,都是嗖嗖的,节省时间就是延长生命。之前的机器很大性能瓶颈都出在硬盘上。

最后,Mac系统提升了很多的工作效率。Mac是类Linux操作系统,也提供Terminal终端,支持大部分Linux命令,这让我们这些习惯在Ubuntu下开发的码农来说,学习成本很小。使用Terminal的一大优势是,命令行比图形界面有更高的效率,更加有效率的是可以写脚本来减少重复劳动,比如我昨天就写了一个脚本pull_codereview_branches.sh来自动从SVN服务器上拉需要review的分支代码和主干代码,来方便做codereview。另外,Mac下自带的spotlight功能(ctrl+space)可以查找本机上的任何文档、邮件、应用程序等,非常直接方便,如果再加上Alfred,几乎不需要Dock和文件夹浏览。

Mac下还很多好用的功能,比如多手势触摸板完全可以替代鼠标,非常多的快捷键等等,还有更多有用的功能亟待发掘,池建强写的Mac技巧系列微信文章是个不错的学习教程。最后不得不赞一下,百度Mac输入法支持我常用的五笔打字相当爽,这下齐全了,我的手机、ipad、Mac全是他们家的产品,看来当初收购梅花输入法团队真是非常明智的。

Node.js初探

原文写于2012-12-22

工作这么多年,一直以服务端的开发为主,有时也做一些内部系统的前端开发,但都是基于原有系统的功能,要么使用现在的Jquery框架,或者写一点简单的Javascript代码,前端一直是自己的一个短板,因此很想在这方面加强一下。考虑到Javascript在前端的重要性,就想深入学习之,后面发现js不仅可以用作前端开发,有了Node.js之后还可以用于服务器端的开发,并且因之无阻塞的原理在对高并发低延迟的场景中的很大的优势,隧有了学习js的切入点,就从Node.js入手。

首先从Node.js站点下载安装最新的程序,Windows下可以直接到开始->程序里找到安装程序,点击命令行模式,输入node --help验证安装是否成功。

学习新语言第一个经典的程序都是HelloWorld,我们也不例外,下面写一个简单的web程序来在页面输出HelloWorld。用文本编辑器创建一个新的js文件并命名为server.js,内容如下:

var http = require("http");

function onRequest(request, response) {
  console.log("Request received."  );

  response.writeHead(200, {"Content-Type": "text/plain"});
  response.write("Hello World");
  response.end();
}

http.createServer(onRequest).listen(8888);

console.log("Server has started.");

在浏览器里输入:http://localhost:8888,页面上即会出现Hello World,是不是非常简单并且轻量级?无需像Java或其它语言一样需要Tomcat或Apache这些web服务器或应用服务器,自身就是一个web服务器,特别适合做一些简单的web应用,当然说适合并不是只能做这些简单的事情。阿里就有用Node.js实现了MyFox这样一个MySQL数据库中间件查询系统,可见Node.js的强大。

书归正传,在上面的小程序中,我们加入了两条日志记录,在程序启动时会输出Server has started,在请求是会输出Request received,如下所示:

D:\work\mynodejs>node server.js
Server has started.
Request received.

这是怎么实现的呢?首先看第一行,我们引入了一个叫http的模块,这是Node.js内置的,提供了对http服务的封装,然后调用http创建了一个服务器进程并把侦听端口设在8888上。其实这已经实现了一个最基本的web服务端程序,但如果只是这么简单,web服务器将啥都做不了,因此才有了在创建服务器时传入了一个函数做为request请求的处理,输出Hello World并响应成功(消息头中设置200)。把函数作为参数传递给另外一个函数对Java或C程序员来说可能有点陌生,但这正是Javascript的灵活和强大之处,但js还不算真正的函数语言,像Clojure这些真正的函数编程语言才是把这些功能发挥到淋漓尽至的语言。

有些人可能会发现,在某些浏览器下刷新一次请求会打印出两行Request received内容,这是为什么呢?我们对上面的程序做一些简单的改造,把request对象的内容输出出来,新的程序如下:

var http = require("http");

function onRequest(request, response) {
  console.log("Request received." + request.method);
  console.log("Request url:"+ request.url);
  console.log("Request headers :");
  for(var item in request.headers){
    console.log("\t" + item +":" + request.headers[item]);
  }
  console.log("\n");
  response.writeHead(200, {"Content-Type": "text/plain"});
  response.write("Hello World");
  response.end();
}

http.createServer(onRequest).listen(8888);

console.log("Server has started.");

我们分别在Firefox、Chrome、IE下发起一次请求,输出结果如下:

##Chrome
Request received.GET
Request url:/
Request headers :
        host:localhost:8888
        connection:keep-alive
        cache-control:max-age=0
        user-agent:Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.4 (KHTML, like G
ecko) Chrome/22.0.1229.0 Safari/537.4
        accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
        accept-encoding:gzip,deflate,sdch
        accept-language:zh-CN,zh;q=0.8
        accept-charset:GBK,utf-8;q=0.7,*;q=0.3

Request received.GET
Request url:/favicon.ico
Request headers :
        host:localhost:8888
        connection:keep-alive
        accept:*/*
        user-agent:Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.4 (KHTML, like G
ecko) Chrome/22.0.1229.0 Safari/537.4
        accept-encoding:gzip,deflate,sdch
        accept-language:zh-CN,zh;q=0.8
        accept-charset:GBK,utf-8;q=0.7,*;q=0.3

##Firefox
Request received.GET
Request url:/
Request headers :
        host:localhost:8888
        user-agent:Mozilla/5.0 (Windows NT 6.1; rv:12.0) Gecko/20100101 Firefox/
12.0
        accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
        accept-language:zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
        accept-encoding:gzip, deflate
        connection:keep-alive
        cache-control:max-age=0

##IE7
Request received.GET
Request url:/
Request headers :
        accept:*/*
        accept-language:zh-CN
        user-agent:Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/5.
0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET4.0C;
.NET4.0E)
        accept-encoding:gzip, deflate
        host:localhost:8888
        connection:Keep-Alive

Request received.GET
Request url:/favicon.ico
Request headers :
        accept:*/*
        accept-encoding:gzip, deflate
        user-agent:Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/5.
0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET4.0C;
.NET4.0E)
        host:localhost:8888
        connection:Keep-Alive

从打印的url中可以看到Chrome、IE默认会发一次/favicon.ico的url请求,为何要默认有这么一次请求请参见百度百科的解释。 到此,我们用短短半个小时的时间就完成一个简单的web服务器,是不是很有成就感呢?

参考
1.Node.js入门
2.The Node.js Little Book 中文版

使用Clojure进行文件操作

原文写于2012-10-13

学习Clojure也有一段时间了,感觉虽然对Clojure有了点认识,但除了在REPL里做些简单的测试外,真要想在实际中使用好像还无从下手,正好IntelliJ IDEA环境也搭建好了,拿来练练手。想了想,还是从最常用的文件操作来实现,毕竟日常工作中有很多文本的处理,用clojure实现应该比java简单很多。

(require '[clojure.java.io :as io])

;;追加写入文件
(defn appendFile [fileName content]
  (binding [*out* (java.io.FileWriter. fileName true)]
    (println content)
    (flush )))

(appendFile "my.log" "hello,bob")
(appendFile "my.log" "hello,zhang")

;;按行读取文件中的内容,并放到List中
(defn read-file-into-list [fileName]
  (with-open [rdr (io/reader fileName)]
     (doall (line-seq rdr)) ))

(println (read-file-into-list "my.log"))

;;打印文件中的每一行内容
(defn printFile [fileName]
  (with-open [rdr (io/reader fileName)]
    (doseq [line (line-seq rdr)]
      (println line))))

(println "---------")
(printFile "my.log")

输出结果:

(hello,bob hello,zhang)
---------
hello,bob
hello,zhang

使用Clojure进行数据库操作

原文写于2012-10-14

数据库是应用程序中使用最频繁的东东,clojure中简化了数据库的操作,几行代码就可以实现对数据库的操作。

首先,我们要下载clojure的一个数据级组件,实现了对jdbc的封装,叫clojure/java.jdbc,以前的名称叫作clojure.contrib.sql,因为没有直接打包好的jar包,因此需求我们自己Git下载进行编译。如果没有安装Git,首先下载Git fow Windows并安装Git,然后在命令行下运行Git下载源代码:

git clone git://github.com/clojure/java.jdbc.git

然后切换到java.jdbc目录,执行mvn install进行编译打包,如果没有安装maven,请先安装maven。执行mvn命令后,会在target目录下生成编译好的jar包,将之拷贝到D:\work\lib目录下,同时也会自动下载依赖的相应jar,切换到C:\Users\Administrator.m2\repository\mysql\mysql-connector-java\5.1.6,将Mysql驱动也拷贝到D:\work\lib目录下。回到IDEA环境中,打开File->Project Structure->Lib,将D:\work\lib目录添加到CLASSPATH中,如下图所示:
 1

下面是进行MySQL数据库操作的实例:

(use 'clojure.java.jdbc)

;设置端口和数据库名称的值
(let [db-host "localhost"
      db-port 3306 ; 3306
      db-name "risk"]

  ; 定义Msql连接属性
  (def db {:classname "com.mysql.jdbc.Driver"
           :subprotocol "mysql"
           :subname (str "//" db-host ":" db-port "/" db-name)
           :user "root"
           :password "hello1234"})

  ;数据库插入
  (with-connection db
    (insert-records :test
      {:id 10 :name "Tom"}
      {:id 11 :name "Jack"}))

  ;数据库查询
  (with-connection db ; 创建一个连接
    (with-query-results rs ["select * from test"] ;执行一条SQL
      (doseq [row rs]      ;遍历ResultSet,打印结果
        (println  (row :id) ":" (row  :name))))))   

输入结果:

10 : Tom
11 : Jack

【算法】统计文本中字符串出现的个数

原文写于2012-10-26

基本思路是使用Java的IndexOf确定字符串的位置,然后把剩余的文本部分做递归处理

;;统计一段文本中出现某个字符串的次数
(defn count-string-in-text [str txt]
  ( loop [t txt
          cnt 0]   ;计数器
    (if (.contains t str) ;判断字符串是否包含在文本中
        (recur
          (subs t        ;取第一次命中位置后的文本内容
            (+ (.indexOf t str) (.length str))   ;下一次要匹配的剩余文本内容
            (.length t)) ;文本总长度
          (inc cnt))     ;匹配一次加1
       cnt  )  ;递归终止时得到计数值
    ))

(print (str "count string in text:"
         (count-string-in-text "abc" "helo,abc,workabcefabcllll")   ))

运行结果:

count string in text:3

版本管理工具Git入门

原文写于 2012-8-21

1.首先在Linux环境下安装Git,Ubuntu下很简单

simbo@desktop:~$sudo apt-get install git
simbo@desktop:~$sudo apt-get install gitk ##gui环境

2.设置全局用户名和邮件地址

simbo@desktop:~/study/git$ git config --global user.name "bocai"
simbo@desktop:~/study/git$ git config --global user.email "[email protected]"

3.创建一个本地目录做测试

simbo@desktop:~/study/git$ mkdir gittest 
simbo@desktop:~/study/git$ cd gittest/

4.进行本地仓库初始化

simbo@desktop:~/study/git/gittest$ git init
Initialized empty Git repository in /home/simbo/study/git/gittest/.git/
simbo@desktop:~/study/git/gittest$ git add .
simbo@desktop:~/study/git/gittest$ git commit
# On branch master
#
# Initial commit
#
nothing to commit (create/copy files and use "git add" to track)

5.git的基本使用

simbo@desktop:~/study/git/gittest$ vi helloworld   ##创建一个新文件,加一行内容
simbo@desktop:~/study/git/gittest$ git add helloworld  ##添加这个文件到仓库
simbo@desktop:~/study/git/gittest$ git commit      ##提交 
simbo@desktop:~/study/git/gittest$ vi helloworld  ##再增加一行内容
simbo@desktop:~/study/git/gittest$ git diff  ##查看变更,类似svn status
diff --git a/helloworld b/helloworld
index bda66a8..73a026a 100644
--- a/helloworld
+++ b/helloworld
@@ -1 +1,2 @@
Hello,World
+second 

//查看状态,类似svn status
simbo@desktop:~/study/git/gittest$ git status 
# On branch master
# Changed but not updated:
#   (use "git add &lt;file&gt;..." to update what will be committed)
#   (use "git checkout -- &lt;file&gt;..." to discard changes in working directory)
#
#     modified:   helloworld
#
no changes added to commit (use "git add" and/or "git commit -a")

 ##提交更新
simbo@desktop:~/study/git/gittest$ git add helloworld 
simbo@desktop:~/study/git/gittest$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD &lt;file&gt;..." to unstage)
#
#     modified:   helloworld
#
 simbo@desktop:~/study/git/gittest$ git log
commit c6828ed2024f208d2e4d480736a446edf66f32dd
Author: bocai &lt;[email protected]&gt;
Date:   Tue Aug 21 11:55:14 2012 +0800

    haaaa

6.分支管理

##创建新分支
simbo@desktop:~/study/git/gittest$ git branch temp1
simbo@desktop:~/study/git/gittest$ git branch
* master
  temp1

##切换到新分支
simbo@desktop:~/study/git/gittest$ git checkout temp1
Switched to branch &#039;temp1&#039;
simbo@desktop:~/study/git/gittest$ git branch
  master
* temp1

##在helloworld文件中增加一行内容并提交 
simbo@desktop:~/study/git/gittest$ vi helloworld
simbo@desktop:~/study/git/gittest$ git commit -a
[temp1 a54c05f] my temp1 branch work
1 files changed, 1 insertions(+), 0 deletions(-)
simbo@desktop:~/study/git/gittest$ git status
# On branch temp1
nothing to commit (working directory clean)
simbo@desktop:~/study/git/gittest$ git log
commit a54c05f75262475bf2389c9662410f8029d962b4
Author: bocai &lt;[email protected]&gt;
Date:   Tue Aug 21 13:36:21 2012 +0800

    my temp1 branch work

commit 1a712b4683d48474867473b90f1d2a059d874f9c
Author: bocai &lt;[email protected]&gt;
Date:   Tue Aug 21 12:34:47 2012 +0800

    llllll

commit 27446b28ff6aafd4361d689512ba90035c513c74
Author: bocai &lt;[email protected]&gt;
Date:   Tue Aug 21 12:33:31 2012 +0800

    eeeee

##回到master分支,再在helloword文件中增加一行内容
simbo@desktop:~/study/git/gittest$ git checkout master
Switched to branch &#039;master&#039;
simbo@desktop:~/study/git/gittest$ git log
commit 1a712b4683d48474867473b90f1d2a059d874f9c
Author: bocai &lt;[email protected]&gt;
Date:   Tue Aug 21 12:34:47 2012 +0800

    llllll

commit 27446b28ff6aafd4361d689512ba90035c513c74
Author: bocai &lt;[email protected]&gt;
Date:   Tue Aug 21 12:33:31 2012 +0800

    eeeee

commit c6828ed2024f208d2e4d480736a446edf66f32dd
Author: bocai &lt;[email protected]&gt;
Date:   Tue Aug 21 11:55:14 2012 +0800

    haaaa
simbo@desktop:~/study/git/gittest$ vi helloworld
simbo@desktop:~/study/git/gittest$ git diff
diff --git a/helloworld b/helloworld
index f4058f8..69cab0f 100644
--- a/helloworld
+++ b/helloworld
@@ -1,3 +1,4 @@
Hello,World
second
dddd
+my master branch work
simbo@desktop:~/study/git/gittest$ git commit -a
[master 37c3ec4] my master
1 files changed, 1 insertions(+), 0 deletions(-)
simbo@desktop:~/study/git/gittest$ git log
commit 37c3ec418682f178ea44704a9b89a1c6e38a0f74
Author: bocai &lt;[email protected]&gt;
Date:   Tue Aug 21 13:37:55 2012 +0800

    my master

commit 1a712b4683d48474867473b90f1d2a059d874f9c
Author: bocai &lt;[email protected]&gt;
Date:   Tue Aug 21 12:34:47 2012 +0800

    llllll

commit 27446b28ff6aafd4361d689512ba90035c513c74
Author: bocai &lt;[email protected]&gt;
Date:   Tue Aug 21 12:33:31 2012 +0800

    eeeee

##对两个分支进行合并
simbo@desktop:~/study/git/gittest$ git merge temp1
Auto-merging helloworld
CONFLICT (content): Merge conflict in helloworld
Automatic merge failed; fix conflicts and then commit the result.
simbo@desktop:~/study/git/gittest$ vi helloworld  

Hello,World
second
dddd
&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD
my master branch work
=======
temp1 branch
&gt;&gt;&gt;&gt;&gt;&gt;&gt; temp1
simbo@desktop:~/study/git/gittest$ git commit -a
[master 9626ced] Merge branch &#039;temp1&#039;

##删除临时分支 
simbo@desktop:~/study/git/gittest$ git branch -d temp1
Deleted branch temp1 (was a54c05f).
simbo@desktop:~/study/git/gittest$ git branch
* master

参考

1.看日记学Git

逻辑编程语言Prolog

原文写于2013-3-4

七周七语言》这本书中介绍了七种个性鲜明的七种小众化语言,其中Ruby最流行的,其次是ScalaClojure是我最爱的,而Prolog是特别吸引我的一种语言。我在Google搜索了一下,这门在国内用的人非常少,涉及的领域主要是人工智能机器学习等,在商业领域的使用以国内的保守几乎是不会使用的,不过它那类似规则引擎的语言特性还是让我觉得耳目一新。

Prolog的特点是你只要定义一组事实和一组规则,它就会自动通过决策树算法进行逻辑推导,告诉你想要的结果,你不必为复杂问题绞尽脑汁地想它的复杂想法,仅仅做一些简单的定义,简单和我们风控系统中用的规则引擎逻辑处理一模一样,不同的是商业系统中用的是Java或Groovy来实现规则引擎的执行。

JVM监控分析工具之jmap、jhat和jstack

接上篇JVM监控分析工具之jps、jstat和jinfo,继续介绍下面几个常用工具

jmap

jmap(Memeory Map for Java)命令主要用于成堆快照,或者查看堆内生成的对象实例。这个命令全部是用的查看heap内的信息或使用情况,参照jstack的命名,感觉叫jheap会更直接一点:)。生成堆快照文件除了jmap命令外,常用的还有-XX:+HeapDumpOnOutOfMemoryError参数,在OOM异常时生成,甚至通过-XX:HeapDumpOnCtrlBreak参数使用CTRL+Break键生成。32位虚拟机下jmap命令格式为:

jmap [option] vmid

如果64位虚拟机下命令格式为:

jmap -J-d64 [option] vmid

option的主要参数选项:

OptionDescription
-dump生成堆快照。格式为:-dump:[live,]format=b,file=,其中live参数说明只是dump出存活的对象
-heap显示堆详细信息,如使用哪种回收器、参数配置、分代状况等。只在Linux/Solaris平台有效
-histo显示堆中对象的情况,包括类、实例数量和合计容量
-finalizerinfo显示在F-Queue中等待Finalizer线程执行finalize方法的对象。只在Linux/Solaris平台有效
-permstat以ClassLoader为统计口径显示永久代内存状态。只在Linux/Solaris平台有效
-F强制生成堆快照。只在Linux/Solaris平台有效

使用-dump生成堆快照,可以使用jhat来启动一个内置浏览器来查看,但一般情况使用MAT(Eclipse Memory Analyzer Tool)在本地分析更方便:

[7080@icbu-qa-006 intl-risk]$ jmap -dump:live,format=b,file=dump.bin 2041
Dumping heap to /home/7080/work/intl-risk/dump.bin ...
Heap dump file created

使用-heap打印堆使用情况:

[7080@icbu-qa-006 ~]$ jmap -heap 2041
Attaching to process ID 2041, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 20.0-b11

using thread-local object allocation.
Parallel GC with 8 thread(s)

Heap Configuration:
   MinHeapFreeRatio = 40
   MaxHeapFreeRatio = 70
   MaxHeapSize      = 1073741824 (1024.0MB)
   NewSize          = 1310720 (1.25MB)
   MaxNewSize       = 17592186044415 MB
   OldSize          = 5439488 (5.1875MB)
   NewRatio         = 2
   SurvivorRatio    = 2
   PermSize         = 268435456 (256.0MB)
   MaxPermSize      = 268435456 (256.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 351076352 (334.8125MB)
   used     = 129532184 (123.5315170288086MB)
   free     = 221544168 (211.2809829711914MB)
   36.895730305412314% used
From Space:
   capacity = 3276800 (3.125MB)
   used     = 0 (0.0MB)
   free     = 3276800 (3.125MB)
   0.0% used
To Space:
   capacity = 3407872 (3.25MB)
   used     = 0 (0.0MB)
   free     = 3407872 (3.25MB)
   0.0% used
PS Old Generation
   capacity = 715849728 (682.6875MB)
   used     = 243342936 (232.06990814208984MB)
   free     = 472506792 (450.61759185791016MB)
   33.99357804882759% used
PS Perm Generation
   capacity = 268435456 (256.0MB)
   used     = 132895480 (126.73900604248047MB)
   free     = 135539976 (129.26099395751953MB)
   49.50742423534393% used
[7080@icbu-qa-006 ~]$ jmap -heap 2041
Attaching to process ID 2041, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 20.0-b11

using thread-local object allocation.
Parallel GC with 8 thread(s)

Heap Configuration:
   MinHeapFreeRatio = 40
   MaxHeapFreeRatio = 70
   MaxHeapSize      = 1073741824 (1024.0MB)
   NewSize          = 1310720 (1.25MB)
   MaxNewSize       = 17592186044415 MB
   OldSize          = 5439488 (5.1875MB)
   NewRatio         = 2
   SurvivorRatio    = 2
   PermSize         = 268435456 (256.0MB)
   MaxPermSize      = 268435456 (256.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 351076352 (334.8125MB)
   used     = 166729928 (159.00605010986328MB)
   free     = 184346424 (175.80644989013672MB)
   47.49107339476969% used
From Space:
   capacity = 3276800 (3.125MB)
   used     = 0 (0.0MB)
   free     = 3276800 (3.125MB)
   0.0% used
To Space:
   capacity = 3407872 (3.25MB)
   used     = 0 (0.0MB)
   free     = 3407872 (3.25MB)
   0.0% used
PS Old Generation
   capacity = 715849728 (682.6875MB)
   used     = 243342936 (232.06990814208984MB)
   free     = 472506792 (450.61759185791016MB)
   33.99357804882759% used
PS Perm Generation
   capacity = 268435456 (256.0MB)
   used     = 132895952 (126.73945617675781MB)
   free     = 135539504 (129.2605438232422MB)
   49.50760006904602% used

使用-histo打印类和对象使用情况,分析内存不足分析哪些对象占用过多是非常有用:

 num     #instances         #bytes  class name
----------------------------------------------
   1:        976429       61598952  [C
   2:       1014749       32471968  java.lang.String
   3:        117790       31030592  [I
   4:        941548       30129536  java.util.HashMap$Entry
   5:        195992       29365832  <constMethodKlass>
   6:        195992       26666416  <methodKlass>
   7:        188663       20477160  [Ljava.util.HashMap$Entry;
   8:         17040       19159896  <constantPoolKlass>
......
7649:             1             16  sun.reflect.GeneratedSerializationConstructorAccessor1358
Total       6364828      405048144

使用-permstat查看永久代内存状态,这个过程会比较慢:

[7080@icbu-qa-006 intl-risk]$ jmap -permstat 2041
Attaching to process ID 2041, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 20.0-b11
45152 intern Strings occupying 4494952 bytes.
finding class loader instances .. 
Finding object size using Printezis bits and skipping over...
Finding object size using Printezis bits and skipping over...
.....
done.
computing per loader stat ..done.
please wait.. computing liveness..............................................liveness analysis may be inaccurate ...
class_loader    classes bytes   parent_loader   alive?  type

<bootstrap> 2771    17192496      null      live    <internal>
0x00000000c1918f98  1   1968    0x00000000c02b6f78  dead    sun/reflect/DelegatingClassLoader@0x00000000b00685b0
0x00000000ca5c8b08  1   1984    0x00000000c02b6f78  dead    sun/reflect/DelegatingClassLoader@0x00000000b00685b0
0x00000000c4070e98  7   27872   0x00000000c0007d58  dead    com/thoughtworks/xstream/core/util/CompositeClassLoader@0x00000000b434c868
0x00000000c6895d18  1   1968    0x00000000c02b6f78  dead    sun/reflect/DelegatingClassLoader@0x00000000b00685b0
0x00000000c9e68780  1   1968    0x00000000c02b6f78  dead    sun/reflect/DelegatingClassLoader@0x00000000b00685b0
0x00000000c445d288  1   3144    0x00000000c02b6f78  dead    sun/reflect/DelegatingClassLoader@0x00000000b00685b0
0x00000000c9e55528  1   3144    0x00000000c02b6f78  dead    sun/reflect/DelegatingClassLoader@0x00000000b00685b0
......
0x00000000c0924ab8  23  166728  0x00000000c0007d10  dead    org/jboss/mx/util/MBeanProxyExt$2@0x00000000b0869818
0x00000000c0a53830  0   0   0x00000000c0007d58  live    java/net/URLClassLoader@0x00000000b01d3770
0x00000000c6cfac30  1   3144    0x00000000c02b6f78  dead    sun/reflect/DelegatingClassLoader@0x00000000b00685b0

total = 907 21288   138310208       N/A     alive=104, dead=803     N/A  

jhat

JDK提供jhat(JVM Heap Analysis Tool)来解析jmap生成的堆快照文件,执行jhat命令后会启动一个小型的HTTP服务,通过http://host:7000来查看解析后的堆快照文件,不过这个命令很少用,一般工作过程中也不允许在服务器启动这样一个HTTP服务,通常是scp到本地使用Eclipse MAT来分析。

jstack

jstack(Stack Trace for Java)用于生成虚拟机当前时刻的线程快照,常用来查找线程死锁的问题。

jstack [option] vmid

option参数如下:

OptionDescription
-l显示锁的额外信息
-m如果调用到本地方法的话,可以显示C/C++的堆栈
-F强制生成线程栈信息

打印栈信息:

[7080@icbu-qa-006 intl-risk]$ jstack 2041
2013-03-31 06:39:10
Full thread dump Java HotSpot(TM) 64-Bit Server VM (20.0-b11 mixed mode):

"Thread-9702" daemon prio=10 tid=0x00002aaab5d89800 nid=0x321 in Object.wait() [0x000000005876c000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000000f78cd908> (a EDU.oswego.cs.dl.util.concurrent.LinkedNode)
        at EDU.oswego.cs.dl.util.concurrent.SynchronousChannel.poll(SynchronousChannel.java:353)
        - locked <0x00000000f78cd908> (a EDU.oswego.cs.dl.util.concurrent.LinkedNode)
        at EDU.oswego.cs.dl.util.concurrent.PooledExecutor.getTask(PooledExecutor.java:723)
        at EDU.oswego.cs.dl.util.concurrent.PooledExecutor$Worker.run(PooledExecutor.java:747)
        at java.lang.Thread.run(Thread.java:662)

"/127.0.0.1:50796 -> /127.0.0.1:15777 session read" daemon prio=10 tid=0x00002aaab3a84800 nid=0x7845 runnable [0x0000000059b80000]
   java.lang.Thread.State: RUNNABLE
        at java.net.SocketInputStream.socketRead0(Native Method)
        at java.net.SocketInputStream.read(SocketInputStream.java:129)
        at java.net.SocketInputStream.read(SocketInputStream.java:182)
        at java.io.DataInputStream.readShort(DataInputStream.java:295)
        at com.alibaba.dragoon.common.protocol.transport.socket.SocketSessionImpl.readMessage(SocketSessionImpl.java:87)
        at com.alibaba.dragoon.common.protocol.transport.socket.SocketSessionImpl.access$000(SocketSessionImpl.java:32)
        at com.alibaba.dragoon.common.protocol.transport.socket.SocketSessionImpl$1.run(SocketSessionImpl.java:213)

参考
1.JDK Development Tools

我为什么要学习Clojure?

原文写于2011.8.3

写Java程序也有10年了,期间学习过Python、PHP、Ruby、Groovy等动态语言,前几个因为没有使用场景,很快就荒废了,后者倒是在部门里被其它同事发扬光大实现了一个规则引擎。Java语言比之C、C++来说有明显的优势,可以说是一种更高级的语言,高级语言带的优势是能用更少的代码写出同样的功能,代码更接近于人的自然表达。Java语言虽然已诞生了十几年了,目前仍然充满活力是业界最流行的开发语言,但这不意味着Java语言能一直统辉煌下去。各种更高级的动态语言如雨后春笋,百花齐放,虽然目前还没有成为主流但它们终究有一天成为主流,但他们先进的理念和特色还是非常值得我们学习,从更高级语言上学习到的东西可以反过来更好地帮助自己写好Java代码,这也是想学习一门新语言的初衷。

目前的高级语言非常多,但从流行度、使用场景、学习成本等等方面考虑过后,我最终选择了Clojure做为自己要学习的新语言,原因如下:

  1. Clojure是Lisp的一种方言版本,继承了Lisp的绝大多数特点,而Lisp在IT业务大牛Paul Graham的名著《黑客与画家》中被大力推荐,这门最古老的语言之一虽然也诞生了50多年,但它的先进性仍然是其它语言不可比拟的,大多数的高级语言都或多或少地借鉴了Lisp的先进理念。Paul Graham在《What Make Lisp Different》中有详细的说明。
  2. Clojure可以在JVM上运行,能够方便地调用java类库,不用担心之前在Java上积累的经验完全无用武之地,每个人从内心深处来说都是害怕改变的,平滑地过渡不失为一个好办法
  3. Twitter非常著名的实时计算框架Strom采用的就是Clojure,说明它在高性能高并发上据有特别优势

参考:

  1. Clojure的主要特性
  2. 为什么Lisp语言如此先进?

如何消除焦虑感?

去年11月份我加入阿里三周年,阿里有句土话叫三年成人,我终于成人了本应该感到开心,但我开始变得焦虑了。开始我并不知道为何有这种感觉,按理说工作也顺手了,家庭关系也不错,在阿里这几年完成了人生中的几件大事,收获颇丰,丫还有什么不满足的?

我说不清楚,如果仔细回味一下,感觉没有像之前刚进公司时那么有激情了,或者说兴奋感降低了,或者说陷入焦虑期了,我发现自己的这种周期大约是三年左右一个轮回。在第一家公司做了三年之后无法解决自己的焦虑于是我离职了,接受别人的邀请来杭创业。做了三年我实在无法说服自己继续苦熬下去搏未知的前程,挣扎一段时间后再次选择离开。之后,有幸来到阿里巴巴这家伟大的公司,又三年过去了我不可避免地再次陷入焦虑漩涡。虽然我依然觉得阿里是家非常伟大的公司,我也暂时没有打算离开,但这种焦虑感如影随形始终围绕着我,让我感觉不安非常不舒服。

也许,换个环境给自己一些新的挑战是个不错的选择,但是放弃之前的积累从头开始也是件挺残酷的事情,新的环境也许是个机会,也许是个陷阱,谁也说不好。改变外因相对容易,但这种焦虑感我觉得更多地需要从自身找原因。追根溯源,还是看到身边的年轻才俊快速成长而自己原地踏步的压迫感导致的,年龄越来越大,技术能力没有突破,管理能力也是刚入门,并且我不想是因为技术能力跟不上了而不得不转到管理岗位,各个方面没有突出的强项,说好听点叫综合能力比较好,说难听点就是中庸普通毫无特色。这种现状置我于一个非常尴尬的境地,上不去下不来,实在是难受。

最近看了罗永浩的锤子发布会视频,他说他有工匠情结,我也很喜欢工匠,我爷爷爸爸都是出色的木匠,我也想成为一个出色的工匠,用自己的专业技能创造一片天地。其实业界和公司里,不管是数据库专家,还是精通Java虚拟机、擅长前端、专注网络安全,或者对大数据处理有独到见解,我觉得这些人都是优秀的工匠,用自己的专业技能为公司为客户创造价值。而自己游离于出色的工匠之外实在太久了,但还好亡羊补牢为时未晚,任何时候觉醒都不晚。开放的互联网有太多的学习材料,关键是怎么养成一个持续学习的习惯。

其实在过去十年的工作中,不管是在第一阶段的小公司中,还是第二阶段自己的创业公司中,还是在阿里这几年,虽然一直都在学习,但总是三天打鱼两天晒网,想起来了就学习一下忙了或者没心情了或者休闲娱乐了就搁置一边,也想强迫自己每天抽出一段时间读书学习写代码,耐何理智始终战胜不了本能,以致于现在始终在仰望我的同龄人,能做的除了羡慕嫉妒恨之外,也许就只有焦虑了。

抱着不甘和屡败屡战的精神,于一个月前再次尝试,自己给自己定的目标是一周至少要花7小时在读书学习写代码上,平均每天约一个小时,看看这次能不能坚持下来。出乎自己的意料,竟然奇迹般地坚持了一个月,每天晚上回来后稍微放松一下,就开始躲在书房看书写代码,把深入理解Java虚拟机一书再次认真看了一遍,并自己对class文件做了解析模拟了javap的功能,实现了几种OOM的例子,顺带把Java虚拟机规范也读了一遍,从来没有这么认真而持续地学习过。并且竟然没有再一回来就追我非常希欢的几部网络小说,没有不停地刷微博网上闲逛到该12点不得不睡觉为止,这真是一个奇迹。而更神奇地是我的焦虑感减轻了很多,即使工作中缺少兴奋点,我也能在业余时间的学习中找到平衡,我知道自己在不断地进步。保持一个持续学习的习惯,将来能带给我多少成就我不知道,但我知道我肯定不会空手而归,技术能力的成长毋庸置疑,其它方面也会或多或少地受益,这种可预知的将来让我内心踏实,让我不再迷茫不再焦虑。

clojure IDE开发环境Itellij IDEA介绍

原文写于2012-10-13

想用clojure开发大型程序,找一个趁手的开发利器是必不可少的,就像Java的开发利器Eclipse,首先想到也是它,支持各种编程语言,只要下载相应的插件就可以了。但使用了一下并不很爽,并且所说REPL容易假死,因此又去试用了一下Linux下的神器Emacs,但一开始的使用体验就让我很不爽,已经习惯了VIM的操作方式,Emacs的操作都要同时按CTRL或ALT键让我很难受,并且还要记各种命令,安装各种插件,烦的要死,也放弃了。最后找到了评价颇高的IntelliJ IDEA+La Clojure的组合,简单使用了还是很不错的。虽然使用很简单,但对第一次使用IDEA开发的人来说,还是有点不熟悉,并且最新的La Clojure版本0.5.7和IDEA不兼容(试用了社区版11.1.3、10.5.4,终极版11.0.2都不行),有必要介绍介绍一下,避免大家和我一样走弯路。

我是在Windows下配置开发环境的,首先去IDEA的官方网站下载免费的最新社区版本11.1.3(ULTIMATE版本只能合作30天,社区版本功能已经很强大了),按提示安装。然后下载La Clojure 0.4.216版本,解压缩到IDEA安装目录的plugins目录下,然后重新启动IDEA,可以到File->Settings->Plugins下查看是否安装成功,也可以直接到这里点击“Install Plugin from disk"选择La Clojure的zip文件安装或者点击”Browse resposities"按钮搜索La Clojure直接从线上下载安装,然后重新启动。
 7

然后再来创建一个Clojure工程,亲自体验一下IDEA的强大
1
 2
 3

如果是第一次创建Clojure工程,上面一步之后多出一步会让你先配置一下JDK的路径,之后将不会再出现。IDEA默认clojure的最高版本是1.3.0,选择Download会自动从线下载,但目前clojure已经到1.4.0版本了,所以可以自己指定使用最新的版本。
 4

和Java工程类似的使用环境,一切都很自然没有太大学习成本,按快捷键CTRL+SHIFT+F10或者点击菜单Tool->Start Clojure Console可以调出REPL,方便命令行操作,IDEA特别强大的地方在于可以像Java一样自动提示和补全代码,REPL里也有这个功能,现在的人越来越懒的记太多东西了,这个功能超赞:)
 5

要不要重复造轮子?

看完深入理解Java虚拟机不过瘾,想把虚拟机规范再读一遍,加强对JVM的理解。在Google上一搜,就找到了英文版的,随然自己英文不怎么样,但是读懂还是没有问题的,当时也没有想着直接想中文版的。读了一小部分后,觉得如果翻译成中文是不是会理解的更深入一点呢?于是就开始动手干了,把第二章JVM结构翻译了一部分。翻译是个很苦逼的事情,需要不断地琢磨怎么用中文表达才更流畅并且不曲解原意,干了半天也才翻了一点点。

后来想想,网上肯定应该有人干过这事儿了,一搜果然找到了,还是周志明等翻译的Java虚拟规范(Java SE 7),排片优美翻译质量比自己强N个档次,顿感十分沮丧,我做这件事情还有意义吗?这不是重复造轮子吗?并且还是更差的轮子。

但从自己学习角度来说,看英文再翻译比直接看中文会更能深入理解一点,就当练习吧,反正也是给自己看的。淡定!

def和defn的区别

原文写于2012-9-29

def只评估一次,而defn每次执行都会评估,我的理解是def相当于java中的field声明,而defn相当于java中的method定义,看下面的例子:

user=> (def t1 (rand 10))
#'user/t1
user=> t1
7.032128762937022
user=> t1
7.032128762937022
user=> t1
7.032128762937022

user=> (defn t2 [] (rand 10))
#'user/t2
user=> (t2)
5.699600921636614
user=> (t2)
5.041377242233345
user=> (t2)
7.382964166904083

但是如果def后面跟上了fn匿名函数,那么结果和defn是一样的

user=> (def t3 (fn [] (rand 10)))
#'user/t3 
user=> (t3)
0.8802383384771573
user=> (t3)
9.385945393402316
user=> (t3)
6.777121776820955
user=> (t3)
3.9441466825577565

【算法】去除字符串中多余的空格

原文写于2012-10-28

经典算法书《Algorithms in java》中给出的方案是循环比对字符串中的每个字符,如果当前字符不为空或前一个字符不为空就继续,否则就忽略掉这个字符,利用clojure的特性,可以更简单点。该算法中用到如下几个函数,我们来分别了解一下它的含义。

split:进行字符串切分,可以指定分隔的字符
blank:判断一个字符串是否为nil、空字符或者只包含空字符
not:取反
filter:对集合进行过滤,过滤时可以指定条件,以#开头
let:可以绑定若干表达式到声明的变量上(可以理解成变量初始化),然后执行后面的表达式,绑定表达式都放在[]里面,后面可以跟多个表达式,但最后一个是该语句的返回结果
join:把集合中的元素组合成一个字符串,可以指定组合时的分隔符
use:相当于clojure中的reqire和refer,和java中的import一样,可以把非默认命名空间的包引用进来,并且可以不用全路径就可以使用

(use  'clojure.string  )

;;去除字符串中多余的空格
(defn remove-redundant-space [str]
  ;按空格分隔并过滤空字符串
  (let [x (filter #(not (blank? %)) (split str #" "))]
    (println x )
    x )  )

(println (join " "  ;将字符串序列按空格分隔并合成一个字符串
           (remove-redundant-space
             "  i ride bike   to longjing   mountain   ")
           )  )

输出结果:

i ride bike to longjing mountain

Clojure学习笔记之二:编程初探

原文写于 2012-8-12

1.和java的语法对比

clojure_vs_java

2.Numeric

clojure_numeric1

3.Collection

;读取容器Vector中的数据
user=> (def v [42 "foo" 99.2 [5 12]])
#'user/v
user=> (first v)
42
user=> (second v)
"foo"
user=> (last v)
[5 12]
user=> (third v)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: third in this context, compiling:(NO_SOURCE_PATH:128)
user=> (v 2)
99.2
user=> ((v 3) 0)
5

;读取Map中的数据
user=> (def m {:a 5 :b 6 
              :c [7 8 9] 
              :d {:e 10 :f 11} 
              "foo" 88 
              42 false})
#'user/m
;把map绑定到指定的表达式上
user=> (let [{a :a b :b} m] (+ a b))
11
user=> (let [{a :a b :b c :c} m] (+ a b (c 2)))
20
;取key为42的值,如果为真打印T,否则打印F
user=> (let[{f 42} m]  (if f "T" "F") )
"F"
;取嵌套map中的值
user=> (let [{{e :e} :d} m] (* 2 e))
20
user=> (def mv ["Bob" {:birthday (java.util.Date. 79 11 8)}])
#'user/mv
user=> mv
["Bob" {:birthday #inst "1979-12-07T16:00:00.000-00:00"}]
user=> (let [[name {bd :birthday}] mv]
   (str name " was born on " bd))
"Bob was born on Sat Dec 08 00:00:00 CST 1979"

4.Comments 有两种方式,注释掉一代用分号(;),注释掉一个clojure的表式用#_,比如:

;注释掉其中的(* 2 2)
user=> (read-string "(+ 1 2 #_(* 2 2) 8)")
(+ 1 2 8)

5.Namespace

默认的namespace是user,可以用ns来查看当前的namespace,也可自己指定使用哪个

user=> *ns*
#<Namespace user>
user=> (ns myNS)
nil
myNS=> *ns*
<Namespace myNS>

6.do

;do表示按次序执行clojure表达式
user=> (do (println "hi") (apply * [4 5 6]))
hi
120

7.def

user=> (def p "foo")
#'user/p
user=> p
"foo"

8.let

;定义一个本地变量,或者说绑定(* x x)到x2这个变量上
user=> (defn hypot
[x y]
(let [x2 (* x x)
      y2 (* y y )]
   (Math/sqrt (+ x2 y2))))
#'user/hypot
user=> (hypot 2 3)
3.605551275463989
user=> (+ (first v) (first (last v)))
47

9.自定义函数fn

;使用内嵌函数进行加法运算
user=> ((fn [x] (+ 10 x)) 5)
15
;使用内嵌表达式计算
user=> (let [x 8] (+ 10 x))
18

10.loop & recur

;循环递减打印每一个值,直到负数为止
user=> (loop [x 5] (if(neg? x) "STOP" (do (println x) (recur (dec x)))))
5
4
3
2
1
0
"STOP"

11.和java进行交互

clojure_java_interop

使用Nmap进行代理IP识别的可行性研究

原文写于2012-8-8

IP地址在互联网上被广泛使用,用来判定用户所在国家、地区、城市等位置信息,在网络反欺诈中也经常使用IP地址来判断用户是否存在欺诈嫌疑,比如:福建莆田以骗子众多而出名,如果IP地址出现在这个地区要特别小心,我们可以根据这样的位置判断来减少交易的风险。《Geolocation -Knowing Your Enemy》这篇文章中提到了几种常用的、通过Geolocation位置信息来识别欺诈的方法。但是,随着在线代理的兴起和骗子技术手段的日益升级,这些方法慢慢失灵了,因为要想通过Geolocation来反欺诈,首先要拿到骗子的真实IP地址,确定他们的真实位置,而通过在线代理的方式很容易绕过这些规则。如果识别用户是否使用了代理IP来恶意绕过系统规则,是保证Geolocation技术有效的重要手段。

用户不管通过何种Proxy访问网站时,走的都是HTTP协议,而HTTP协议头中其实也有相应的字段来标明是否使用了代理,比如这篇《检测代理IP匿名程度的方法》中详细介绍,但现在很多代理服务器很容易地把协议中的代理信息屏蔽掉,因为这些值并非必需的,从而使从HTTP协议中识别的技术失效,需要借助其它技术手段来识别。

从常识中可以知道,普通用户一般常用的操作系统为Windows(XP、Vista、7),Mac OS,而服务器一般是Linux、Windows Server 2003等服务器操作系统,并且个人电脑一般很少开对外端口提供服务,当前有现成的OS Fingerprinting技术可以识别用户主机上面的操作系统是什么版本,打开的端口都有哪些,通过操作系统的判定和端口的判定,基本可以准确识别是否是代理IP。这种探测技术又分两种:主动式和被动式,主动式是主动发送协议包到对方主机上,通过返回的包信息来确定,这种方式比较直接但隐蔽性不太好,Nmap是代表。被动式是当对方访问我们的主机时通过抓包来判断,这种方式比较隐蔽对方完全不知道,但感觉效果不太好,p0fettercap

每一种操作系统对网络协议包的返回内容都有所不同,主要是在TTL、Windows Size等标识位上有所区别,比如不同的操作系统的TTL和WS如下所示:

<th>
  IP Initial TTL
</th>

<th>
  TCP window size
</th>
<td>
  64
</td>

<td>
  5840
</td>
<td>
  64
</td>

<td>
  5720
</td>
<td>
  64
</td>

<td>
  65535
</td>
<td>
  128
</td>

<td>
  65535
</td>
<td>
  128
</td>

<td>
  8192
</td>
<td>
  255
</td>

<td>
  4128
</td>
Operating System (OS)
Linux (kernel 2.4 and 2.6)
Google's customized Linux
FreeBSD
Windows XP
Windows 7, Vista and Server 2008
Cisco Router (IOS 12.4)

使用Nmap探测我的一台装有Windows XP操作系统的笔记本

xxx@desktop:~$ sudo nmap -O -v 10.19.214.97

Starting Nmap 6.01 ( http://nmap.org ) at 2012-08-03 16:25 CST
Initiating Ping Scan at 16:25
Scanning 10.19.214.97 [4 ports]
Completed Ping Scan at 16:25, 0.02s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 16:25
Completed Parallel DNS resolution of 1 host. at 16:25, 0.00s elapsed
Initiating SYN Stealth Scan at 16:25
Scanning 10.19.214.97 [1000 ports]
Discovered open port 135/tcp on 10.19.214.97
Discovered open port 445/tcp on 10.19.214.97
Discovered open port 139/tcp on 10.19.214.97
Discovered open port 8090/tcp on 10.19.214.97
Completed SYN Stealth Scan at 16:25, 4.86s elapsed (1000 total ports)
Initiating OS detection (try #1) against 10.19.214.97
Retrying OS detection (try #2) against 10.19.214.97
Nmap scan report for 10.19.214.97
Host is up (0.0045s latency).
Not shown: 995 filtered ports
PORT     STATE  SERVICE
135/tcp  open   msrpc
139/tcp  open   netbios-ssn
445/tcp  open   microsoft-ds
3389/tcp closed ms-wbt-server
8090/tcp open   unknown
Device type: general purpose|WAP|broadband router
Running (JUST GUESSING): Microsoft Windows 2003|XP|2000 (93%), IBM AIX 6.X (88%), SMC embedded (88%), Belkin embedded (85%), Philips embedded (85%), Express Logic ThreadX G3.X (85%), T-Home embedded (85%)
OS CPE: cpe:/o:microsoft:windows_server_2003::sp2 cpe:/o:microsoft:windows_xp::sp3 cpe:/o:ibm:aix:6 cpe:/o:microsoft:windows_2000::sp4 cpe:/o:expresslogic:threadx:g3
Aggressive OS guesses: Microsoft Windows Server 2003 SP2 (93%), Microsoft Windows XP SP3 (93%), Microsoft Windows XP (92%), Microsoft Windows XP SP2 - SP3 (90%), Microsoft Windows Fundamentals for Legacy PCs (XP Embedded derivative) (88%), IBM AIX 6.1 (88%), SMC 7904WBRA-N wireless ADSL router (88%), Microsoft Windows 2000 SP4 (87%), Microsoft Windows XP SP2 or SP3 (87%), Microsoft Windows Server 2003 (87%)
No exact OS matches for host (test conditions non-ideal).
TCP Sequence Prediction: Difficulty=259 (Good luck!)
IP ID Sequence Generation: Incremental

Read data files from: /usr/bin/../share/nmap
OS detection performed. Please report any incorrect results at http://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 9.12 seconds
           Raw packets sent: 2057 (93.904KB) | Rcvd: 191 (8.700KB)

探测一个IP-Address网站公布的代理IP

xxx@desktop:~$ sudo nmap -O -v 174.34.243.140
[sudo] password for simbo: 

Starting Nmap 6.01 ( http://nmap.org ) at 2012-08-03 17:52 CST
Initiating Ping Scan at 17:52
Scanning 174.34.243.140 [4 ports]
Completed Ping Scan at 17:52, 0.01s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 17:52
Completed Parallel DNS resolution of 1 host. at 17:52, 0.45s elapsed
Initiating SYN Stealth Scan at 17:52
Scanning unknown.carohosting.net (174.34.243.140) [1000 ports]
Discovered open port 22/tcp on 174.34.243.140
Discovered open port 80/tcp on 174.34.243.140
Completed SYN Stealth Scan at 17:52, 4.47s elapsed (1000 total ports)
Initiating OS detection (try #1) against unknown.carohosting.net (174.34.243.140)
Retrying OS detection (try #2) against unknown.carohosting.net (174.34.243.140)
Nmap scan report for unknown.carohosting.net (174.34.243.140)
Host is up (0.17s latency).
Not shown: 996 closed ports
PORT    STATE    SERVICE
22/tcp  open     ssh
80/tcp  open     http
139/tcp filtered netbios-ssn
445/tcp filtered microsoft-ds
Device type: general purpose|VoIP adapter|load balancer
Running (JUST GUESSING): Linux 2.6.X (89%), Cisco embedded (86%), F5 Networks embedded (85%)
OS CPE: cpe:/o:linux:kernel:2.6 cpe:/a:cisco:unified_communications_manager:7.1.1
Aggressive OS guesses: Linux 2.6.15-28-amd64-server (Ubuntu, x86_64, SMP) (89%), Linux 2.6.18.pi (x86) (88%), Linux 2.6.21-gentoo-r4 (PowerPC) (88%), Linux 2.6.11 - 2.6.18 (87%), Cisco Unified Communications Manager VoIP gateway (86%), F5 BIG-IP load balancer (85%)
No exact OS matches for host (test conditions non-ideal).
Uptime guess: 49.303 days (since Fri Jun 15 10:36:34 2012)
TCP Sequence Prediction: Difficulty=200 (Good luck!)
IP ID Sequence Generation: All zeros

Read data files from: /usr/bin/../share/nmap
OS detection performed. Please report any incorrect results at http://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 13.62 seconds
           Raw packets sent: 1044 (49.044KB) | Rcvd: 1029 (42.116KB)

通过对几十个公开代理IP的测试,Nmap准确识别操作系统的概率还是非常大的,但Nmap每次探测花费的时间有时也比较长,可能要好几秒钟,怎么运用到实际的系统当中还需要进一步研究。

参考

1.TCP/IP Stack Fingerprinting
2.OS Fingerprinting

永远的读书梦

今天是发奖金的日子,既有惊喜又有愤怒,喜的是你在公司有一分付出就有一分收获,怒的是万恶的税务局拿走了我三分之一的收入,比TMD黑社会还黑。一边提供这最烂的服务,污染的空气污染的水有毒的食品,一边收着世界上最多最贵的税,好事都让丫占了,草泥马!

@Fenng 在一篇微信里说,现在书籍跟十年前几乎没有太大的变化,IT行业的人越来越不喜欢读书了,确实12年我没有买一本书只读了半本书,貌似数学之美也是去年读的,有点记不清楚了,反正读的很少。但读书梦一直没有放弃,古人有云:书有自有黄金屋,书中自有颜如玉,虽然功利了点但也有几分道理。人的成长需要汲取的东西很多,但读书无疑是一种最好的方式之一,有无数牛人的**和智慧集结成册,只需要花上几十块钱就能买到,是多么划算的一件事儿呀。难怪@Fenng 有感叹说,一本书哪怕只学到一点东西,就值了。

但市面上的书参差不齐,而我们的时间又非常宝贵,没有功夫浪费在烂书上面,因此@刘未鹏 在暗时间提到,读书只读经典的书,读那些不会随着时间变迁而价值急剧衰减的书,去豆瓣上读书榜上按图索骥是个简单而又省事的方法。

今天趁着有钱买七本书,三本心理学经典著作,特便宜总共才一百块钱,四本计算机经典著作,贵了不少花了两百多块,买书时总时很慷慨,但愿这些书籍不要躺在那里睡太久,计划今年把它们看完。有些书是历史上欠的帐,比如http和javascript那两本书,人真的不能偷懒呀,出来混迟早是要还的。

附我的书单:

使用WordPress搭建自己的Blog站点

2012.7.11 原文:http://yikebocai.com/?p=18

  1. 首先注册一个域名,比如万网现在正在搞活动,com和net域名只需要55块钱一年,时间长的话更便宜
  2. 申请一个主机空间,最好是国外的,不用备案,说到这里可能很多有过经历的人可能忍不住要骂通信管理局这帮滚蛋了,吃人饭不干人事。我找的是BlogHost,也是看到别人的博客用的这个才知道的,服务器托管在美国,支持php和MySQL,一年100块钱,600M空间,每月12G流量,足够用了
  3. 然后要设置DNS域名解析,进入万网后台管理,把申请的主机IP和域名绑定,过一两个小时就生效了
  4. 当前最流行的博客软件就是Wordpress,模板很多插件很多,到英文官方站点被墙了(想不明白为啥被墙,它只不过是套工具软件而已),可以到国内站点下载,最新版本是3.4.1
  5. 将wordpress压缩包通过FTP客户端比如FileZilla,或者直接通过后台提供的文件管理功能上传,然后解压缩到public_html目录下,注意要把内容直接放到这个目录下
  6. 在浏览器输入http://xxx.com/readme.html ,按照教程安装即可。wordpress的内容都是存在数据库中,安装前要先在主机上创建一个数据库,安装是在数据库连接配置页面填上去就可以了,测试连通就好了 7.安装好之后可以设置自己的站点名称,建立文章的类别,设置自己喜欢的主题和插件,对码农来说肯定少不了放源代码,可以下载代码高亮插件WP SyntaxHighlight,支持大部分编程语言

JVM监控分析工具之jps、jstat和jinfo

日常工作中难免会遇到一些Java的异难杂症,尤其是线上环境,不可能让你去debug,而jdk
自带的一些小工具能让你快速定位问题所在,这些工具都是基于命令行的,远程连接服务器在终端下就可以操作,可以说十分方便。

jps

jps命令类似Linux下的ps,只不过显示的是虚拟机的进程号,后面介绍到的jstat等命令都需要用到这个虚拟机进程号

选项 作用
-q 只输出LVMID,省略主类的名称
-m 输出虚拟机进程启动时传给主类main()函数的参数
-l 输出主类的全名,如果进程执行的是Jar包,输出Jar的路径
-v 输出虚拟机进程启动时的JVM参数

通过一段源代码来测试一下这几个参数的含义

package org.bocai.jvm.monitor;


/**
 * @author [email protected]
 * @since 2013-3-25
 * 
 */
public class Testjps {

    public static void main(String[] args) throws InterruptedException {

        if (args != null && args.length == 1) {
            System.out.println("Hello," + args[0] + "!");
        }

        while (true) {
            Thread.sleep(1000);
        }
    }

}

在Eclipse里运行Testjps程序,然后在命令行下可以看到jps的执行结果:

xinbo.zhangxb@ALI-031884N /d/work/jvm/gc/target/classes (master)
$ jps
3596 Jps
920 Testjps
4056

xinbo.zhangxb@ALI-031884N /d/work/jvm/gc/target/classes (master)
$ jps -q
920
5544
4056

-m参数是会显示传给main函数的参数,为了测试这个参数,我们在命令行下执行Testjps,并传入一个任意字符串参数。

xinbo.zhangxb@ALI-031884N /d/work/jvm/gc/target/classes (master)
$ java org/bocai/jvm/monitor/Testjps world
Hello,world!

xinbo.zhangxb@ALI-031884N /d/work/jvm/gc/target/classes (master)
$ jps -m
3944 Jps -m
4600 Testjps world
4056

-l参数会输出主类的全名,如下所示

xinbo.zhangxb@ALI-031884N /d/work/jvm/gc/target/classes (master)
$ jps -l
4912 sun.tools.jps.Jps
4600 org/bocai/jvm/monitor/Testjps
4056

如果执行的jar包,则输出jar的路径。为了测试这一情况,我们先对class文件进行打包。首先先创建一个MANIFEST文件,用来指定jar包运行时的主类是什么,文件名可以任意取,内容如下:

Main-Class: org.bocai.jvm.monitor.Testjps

然后用指定的MNIFEST文件和Java源文件目录来打包:

jar cvfm mymanifest .

最后运行jar:

xinbo.zhangxb@ALI-031884N /d/work/jvm/gc/target/classes (master)
$ java -jar Testjps.jar  jps
Hello,jps!

xinbo.zhangxb@ALI-031884N /d/work/jvm/gc/target/classes (master)
$ jps -l
288 sun.tools.jps.Jps
4056
2060 Testjps.jar

jstat

jstat(JVM Statistics Monitoring Tool)是用来监控JVM虚拟机运行时的各种状态信息,比如GC情况、类装载情况等。jstat的使用方法如下:

jstat -gc 1s 10

其中第一个可选参数如下所示,第二个参数表示输出内容的间隔时间,第三个参数表示输出的总次数

选项作用
-class监控类加载、卸载数量、总空间及类加载所耗费的时间
-compiler输出JIT编译器编译过的方法、耗时等信息
-gc监视Java堆状况,包括Eden区、两个Survivor区、老年代、永久代的容量、已用空间、GC时间合计等信息
-gccapacity同-gc,额外输出堆中各个区使用到的最大和最小空间
-gccause同-gc,额外输出导致上一次GC的原因
-gcnew监视新生代GC情况
-gcnewcapacity同-gcnew,额外输出新生代各区使用到的最大和最小空间
-gcold监视老年代GC情况
-gcoldcapacity类似-gcnewcapactiy
-gcpermcapacity输出永久代使用到的最大和最小空间
-gcutil同-gc,主要关注已使用空间的百分比
-printcompilation输出已被JIT编译的方法

下面先运行一个简单的程序:

package org.bocai.jvm.monitor;

/**
 * VM Args:-Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:+PrintGCDetails
 * 
 * @author [email protected]
 * @since 2013-3-26
 * 
 */
public class Testjstat {
    private static final int _1MB = 1024 * 1024;

    public static void main(String[] args) throws InterruptedException {
        while (true) {
            byte[] alloc1 = new byte[2 * _1MB];
            byte[] alloc2 = new byte[2 * _1MB];
            byte[] alloc3 = new byte[1 * _1MB];

            // first Minor GC
            byte[] alloc4 = new byte[4 * _1MB];
            byte[] alloc5 = new byte[3 * _1MB];

            // second Minor GC
            byte[] alloc6 = new byte[4 * _1MB];

            alloc1 = null;
            alloc2 = null;
            // first Full GC
            byte[] alloc7 = new byte[2 * _1MB];

            Thread.sleep(100);
        }

    }

}

-class

先查看类加载情况:

xinbo.zhangxb@ALI-031884N /d/work/jvm/gc (master)
$ jstat -class 2124
Loaded  Bytes  Unloaded  Bytes     Time
    14    16.4        0     0.0       0.03

其中几列所代表的含义如下:

ColumnDescription
Loaded加载的Class文件数目
Bytes加载的字节数,KB为单位
Unloaded卸载的Class文件数目
Bytes卸载的字节数
TimeClass文件加载和卸载花费的时间

-compiler

JIT编译情况:

xinbo.zhangxb@ALI-031884N /d/work/jvm/gc (master)
$ jstat -compiler 2124
Compiled Failed Invalid   Time   FailedType FailedMethod
       4      0       0     0.00          0

其中几列所代表的含义如下:

ColumnDescription
Compiled执行的编译任务个数
Failed编译失败的个数
Invalid无效的编译任务个数
Time编译任务花费的时间
FailedType最后一个编译失败的编译类型
FailedMethod最后一个编译失败的Class名称和方法

-gc

监控Java堆状况:

xinbo.zhangxb@ALI-031884N /d/work/jvm/gc (master)
$ jstat -gc 2124
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       PC     PU    YGC     YGCT    FGC    FGCT     GCT
1024.0 1024.0  0.0    0.0    8192.0   4096.0   10240.0     8327.7   12288.0 363.9    4302    5.927 6452    81.285   87.212

其中几列所代表的含义如下:

ColumnDescription
S0CSurvivor0的大小,单位为KB,下同
S1CSurvivor1的大小
S0USurvivor0的使用大小
S1USurvivor1的使用大小
ECEden区的大小
EUEden区的使用大小
OCOld区的大小
OUOld区的使用大小
PCPerm区的大小
PUPerm区的使用大小
YGCYoung GC次数
YGCTYGC花费时间,单位为秒
FGCFull GC次数
FGCTFGC花费时间
GCTGC总时间

-gcutil

监控Java堆的百分比情况,其中-t在第一列输出JVM启动到当前的时间单位为秒,-hn表示每隔几行输出一行标题:

xinbo.zhangxb@ALI-031884N /d/work/jvm/gc (master)
$ jstat -gcutil -t -h5 4488 1s 10
Timestamp         S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT
           18.2   0.00   0.00  75.00  81.33   2.96    248    0.337   371    4.227    4.564
           19.2   0.00   0.00  75.00  81.33   2.96    262    0.355   392    4.458    4.813
           20.2   0.00   0.00  75.00  81.33   2.96    276    0.372   413    4.680    5.052
           21.2   0.00   0.00  75.00  81.33   2.96    290    0.389   434    4.909    5.298
           22.2   0.00   0.00   0.00  51.33   2.96    306    0.410   457    5.141    5.551
Timestamp         S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT
           23.2   0.00   0.00   0.00  51.33   2.96    320    0.428   478    5.377    5.805
           24.2   0.00   0.00  87.50  91.33   2.96    334    0.446   500    5.630    6.075
           25.2   0.00   0.00  75.00  81.33   2.96    348    0.464   521    5.856    6.320
           26.2   0.00   0.00  50.00  81.33   2.96    362    0.482   542    6.081    6.564
           27.2   0.00   0.00  75.00  81.33   2.96    376    0.500   563    6.311    6.811

其中几列所代表的含义如下:

ColumnDescription
S0Survivor0使用的百分比,单位为%
S1Survivor1使用的百分比
EEden区使用的百分比
OOld区使用的百分比
PPerm区使用的百分比
YGCYoung GC次数
YGCTYGC花费时间,单位为秒
FGCFull GC次数
FGCTFGC花费时间
GCTGC总时间

其它的几个参数说明请参见这里

jinfo

jinfo(Configuration Info for Java)用来实时查看和调整虚拟机的各项参数,格式如下:

jinfo [option] pid

其中option选项主要包括:

OptionDescription
no option相当于同时使用-sysprops和-flags参数
-flag name打印某个指定的虚拟机参数值
-flag [+/-]nameenable或disbale某个JVM参数
-fag name=value设定指定的参数值
-fags打印所有的JVM参数
-sysprops打印所有系统属性信息

举例如下:

[7080@icbu-qa-006 bin]$ jinfo -flag PrintGCDetails 2041
-XX:+PrintGCDetails

[7080@icbu-qa-006 bin]$ jinfo -flag -PrintGCDetails 2041
[7080@icbu-qa-006 bin]$ jinfo -flag PrintGCDetails 2041
-XX:-PrintGCDetails

[7080@icbu-qa-006 bin]$ jinfo -flags 2041
Attaching to process ID 2041, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 20.0-b11

-Dprogram.name=run.sh -Xms1024m -Xmx1024m -XX:PermSize=256m -XX:SurvivorRatio=2 -XX:+UseParallelGC -Dtrace.flag=true -Dtrace.output.dir=/home/7080/work/intl-risk/deploy/trace/ -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=7050,server=y,suspend=n -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -Dfile.encoding=UTF-8 -DdisableIntlRMIStatTask=true -Dcom.alibaba.dragoon.KV_TASK_KEY=com.alibaba.intl.risk.comsat.RiskPaymentPerformanceTask -Dcommons.log.query.size.limit=100000 -Djava.endorsed.dirs=/usr/alibaba/jboss/lib/endorsed

7080@icbu-qa-006 ~]$ jinfo -sysprops 2041
Attaching to process ID 2041, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 20.0-b11
java.vendor = Sun Microsystems Inc.
sun.java.launcher = SUN_STANDARD
catalina.base = /home/7080/work/intl-risk/deploy/../.myjboss
sun.management.compiler = HotSpot 64-Bit Tiered Compilers
catalina.useNaming = false
os.name = Linux
...

但不知道为什么无法设置某个参数的值:

[7080@icbu-qa-006 bin]$ jinfo -flag SurvivorRatio=5 2041
Exception in thread "main" java.io.IOException: Command failed in target VM
    at sun.tools.attach.LinuxVirtualMachine.execute(LinuxVirtualMachine.java:200)
    at sun.tools.attach.HotSpotVirtualMachine.executeCommand(HotSpotVirtualMachine.java:195)
    at sun.tools.attach.HotSpotVirtualMachine.setFlag(HotSpotVirtualMachine.java:172)
    at sun.tools.jinfo.JInfo.flag(JInfo.java:105)
    at sun.tools.jinfo.JInfo.main(JInfo.java:58)

其它更多的用法请参见这里

参考
1.JDK Development Tools

GitHub入门

原文写于 2012-8-23

Git是目前最流行的版本管理工具,而GitHub是基于Git协议的一个代码托管站点,很多著名的开源程序都在上面。其实GitHub不仅仅是个代码托管站点,也可以做为一个在线文档写作平台,或者免费搭建自己的Blog站点

虽然之前也写过开源软件ictclas4j,但那已是往事很久都没有去维护了,我现在使用的GitHub的主要目的是为了托管自己写的一些小程序,之前也写过很多都弄丢了,而GitHub是个很好的代码托管站点,也许有机会也会再写一些开源软件。

1.注册帐号
GitHub主页上注册一个新的免费帐号,注意免费帐号的内容都必做是公开的,如果要用私有帐号,必做花钱购买。
github_signin

2.创建仓库
github_respo

helloword项目的主页如下所示,包括项目介绍,项目地址,项目源代码等
github_hellworld

3.设置SSH访问权限
如果想在本地提交自己的代码,必段在帐号设定里增加公匙,公匙可以从/home/xxx/.ssh/id_rsa.pub文件中获取,没有这个文件的话需要执行如下命令生成:

ssh-keygen -t rsa

github_ssh

4.checkout代码本地

#把代码checkout到本地,和svn co类似
git clone [email protected]:yikebocai/helloword.git helloword

5.修改一个文件并提交到服务器

#在README.md文件中增加一行内容,然后提交 
git add README.md
#windows下一定要用双引号,否则会报error: pathspec &#039;commit&#039;&#039; 
#did not match any file(s) known to git.
git commit -m &#039;add one line&#039;
git push origin master 
#如果没有设置SSH免登录,需要输入GitHub的用户名和密码才能提交代码成功

6.查看变更
查看helloword项目主页,可以发布README.md文件已被修改

参考
GoGitHub,写的非常好的GitHub使用教程

近几个月的读书清单

原文写于2012-9-22

数学之美
其实我小时候挺喜欢数学的,成绩也一直不错,到但到了大学,枯燥无味不知道学了有什么用的数学让我很反感,当然也成绩也一塌糊涂,还挂过科。毕业之后的几年,写程序也用不到太多数学知识,数学离我越来越远,知道从Google黑板报上读到吴军的数学之美系列文章,才发觉原来数学的神奇之处。虽然数学依然离我很远,我也觉得这辈子是学不好了,但我至少不会坐井观天地说,数学对写程序没太大用处,如果有人问我怎么才能写好程序,我会告诉他把数学学好,数学可以解决哪些问题。

创业36条军规
这本书的大名早已在微博上耳闻,本来想在ipad上找本免费的来看看,结果搜到了一家叫多看的公司发行的单行本,竟然要6块钱,搞软件的人不来不想付费想吃免费的午餐,因为我知道可以找到免费的发行本,结果不出所料但用户体验和付费的天壤之别,狠心花钱买了下来。它没让我失望,不管是内容还是排版都是上佳,一下子让我喜欢了阅读电子书,并专门下载了多看阅读的软件,除了数学之美,下面看完的书竟然都是从多看上买的。利用晚上睡觉前的一段时间,使我的阅读量猛然上涨,花小钱获取大收益,值。 看完这本书之后,我很遗憾,当初自己创业时怎么就没有读到这么好的书呢,随说不会看了之后也不一定能帮助我们把公司做好,但起码少走很多弯路,或者早点迷途知返结束掉公司节省点时间和青春,不用苦苦挣扎那么久。虽然说现在没有创业,最近也没有创业的想法,但读了这本写给创业人的书还是收益匪浅,其中提到的一点是联想的传统--复盘,遇到问题之后要第一时间进行复盘,总结教训制定后续行动计划,让我大有收获。之前团队遇到问题,我总在犹豫要不要马上说呢,结果一耽搁最后往往是不了了之,悔不当初。

黑客与画家
这本书在IT界名气很大,作者更是大名鼎鼎的硅谷教父、Y Combinator的创始人Paul Graham,一直没搞懂它是写什么的,直到自己真正开始阅读它。文中强烈推荐的黑客精神也许只适用于精英型的小团队,比如craglist,我并不完全认同,但这不妨碍我对文中强烈推荐的Lisp的仰慕。看完此书后,直接催生了我对Lisp的强烈兴趣,并开始学习它的方言Clojure。这和传统的C/C++/Java等语言有这天壤之别,完全颠覆了我对程序的认识,一开始还真有点接受不了。不过慢慢熟悉它的语法后,真心地叹服这种语言的简洁和强大。

门后的秘密
这是一本讲软件企业里管理故事的书,虽然不是互联网企业,但起码是我的行业最接近的一本讲管理的书。虽然自己不是一个专门的管理人员,但在工作中也需要一定的管理技能,工作也碰到很多管理的问题,所以还是饥不择食地读完了它。过去一段时间了,讲的内容大多都忘了,但有一条印象深刻就是一对一的沟通。很多问题在全体会议上是表现不出来的,因为各种各样的顾虑和**人天生的含蓄内向,深层次的问题需求单独的空间、合格的时间才能有效果。

结网:互联网产品经理改变世界
这是一位前腾讯产品经理写的书,之前也阅读过淘宝一位产品经理写的《人人都是产品经理这本书》,可惜没有读完。我一直觉得产品经理在互联网公司应该有很重要的地位,他是唯一联结客户、技术和运营的人,他懂得客户的需求了解公司的技术,能够协调公司的运营保证持续的提供客户价值。当然做好一名产品经理非常不容易,要懂的东西非常多,包括设计、技术、市场、心理学等等,不是技术人员能快速掌握的,但是多了解产品,能够好地让技术服务于客户,从而实现技术价值。

你应该懂点经济学
面对越来越严重的通货膨胀,手里那点工资越来越不值钱,况且不知道哪一天自己会失业,没有任何收入如何养活一家老小是个不得不面对的问题,因此看到此书的简介就买下来看看,说实在阅读价值不是太大,里面的内容也忘的一干而净了,但改变了我随遇而安的想法,未雨绸缪才是正确的做法。公司不会养你一辈子,国家更是言而无信只知道吸血的主儿,唯有靠自己。

一个证券分析师的醒悟
这是一位在股市打拼了二十多年的人的肺腑之言,让人理性的投资股票。自己之前一直对股票都不感兴趣,始终觉得**股市黑幕太多,不是我们这种小散民能赚钱的地方,而国外的股市相对规范但咱一没资金二没渠道,想想还是算了。正好之前的香港帐户上有那么一点钱,转回来太麻烦不值当,放着吧太浪费,刚好拿它去练练手。这本书里提到的价值投资,我非常认可,咱又不是职业炒客哪有时间精力天天盯着股票,最好是买了之后放个一年半载甚至更长时间,能涨过CPI不贬值就好了。

**,你一定要去
**,是很多**人的梦,近在咫尺却远隔天涯,**的人文地理美食风景,对我的吸引力越来越大,也在计划明年去环**骑行,这本书比较全面地介绍了**的风土人情,让人恨不能生出一对翅膀马上飞过去。

我去!拉萨
川藏线是每一个骑行人的梦想,自从06年爱上骑车以来,这个梦想也一直徘徊在我的心底,虽然觉得是那么的遥不可及,但梦想总是带给我无数的希望。这本书写的是7个大学生的毕业之旅,想到自己当年大学毕业时平淡的如不杯白开水真是懊恼的要死,大把的青春浪费在无知当中,由衷地佩服现在的年轻人,有想法有勇气有毅力。也许这一辈子都没有机会去骑川藏线,但与书同行,放飞自己的想想,追随着青春的车轮,也是一种安慰。

谈谈程序员写作

今天早上四川雅安发生七级地震,震中距离汶川只有不到一百公里,很多地方房屋损毁严重,伤亡人数不断攀升,举国上下都动员起来了,自己也向壹基金献出了微薄之力,祝愿当地的老百姓尽快挺过难关。

写作的初衷

从三月初开始给自己定下目标后,晚上平均有两个小时的时间可以用来读书学习写代码,带来的直接结果之一就是写博客勤快了很多,因为读书学习过程中都会有一些结果产出,或者是共鸣的心有戚戚焉,或者是发现新大陆的兴奋雀跃,或者是被新思维颠覆的震撼惊讶,都有一吐为快的冲动。最近在读重来:更为简单有效的商业思维暗时间,书中都提到了写作的重要性,因此也想谈谈程序员写作。

因为我自己本来是做IT行业的,对互联网行业出现的新事物也比较关注,应该是比较早就开始关注博客这个新生事物的,最早喜欢上方兴东的博客**看一些专栏作者文章。后来博客越来越流行,于是于2005年初在CSDN开了一个技术博客,写一些学习笔记啥的,当时在做短信的智能搜索平台时,研究了中科院张华平写的ICTLAS中文分词软件,并写了几篇分析文章,也收获了不少少粉丝,但后来到阿里之后因为工作特别忙以及自己懒的原因就闲置了。2006年的时候,我还在搜狐上开了一个生活博客,专门记录我的骑行生活和一些非技术的碎碎念,两三年间也写了有150篇文章,随着我的骑行生活的减少和新工作的忙碌也废弃了。

在阿里工作的前三年里,工作基本上都是比较忙的,但其实时间想挤总还是有的随然平时晚上加班比较多但周末时间还是相对空闲的,但总是自己给自己找借口没有写过东西,人一但把一件事情放下就很有可能永远被无限期地搁置,就好像我去年坚持了大半年的每天早上起来跑步,因为阑尾炎不能剧烈运动搁置了两三个星期,我再也不愿每天早起跑步段锻练了。用刚刚从暗时间学到的心理学理论来讲就是人有两种情绪,一种是感性的它是倾向于让人选择做舒服的事情而不是正确的事情,比如贪图享乐、只顾眼前不顾长远等,一种是理性的它倾向于让人综合各种情况做出理性的选择,比如我想做个好的程序员就得不断学习各自专业技能,为自己的长远发展做知识的储备。当感性和理性天人交战痛苦纠结时,大多数人选择了趋吉避凶的作法,以后事情还不知道怎么样,学习了不一定就有收获,还不如先让自己舒服一会儿呢。

正是因为控制不了本能的反应,因此也浪费了很多大好时光。从去年下半年开始,痛定思痛,觉得还是有必要重拾博客写作。重新买了独立域名和空间,自己搭建了一棵波菜的博客站点,开始写一些新技术的学习记录,虽然到目前写的不多质量也不高,但好在开头之后没有很快放弃。写博客纯属自己的思维直觉,觉得我应该这么干,或者是在看一些大牛的博客时受到的影响,但从来没有仔细想过程序员为什么要写作,直到最近看了这两本书之后。

写作的必要性

可能有程序员要问,我只要把代码写好就行了,或者如果是偏管理的说我只要把项目带好就行了,为什么要具备写作能力呢?在上周我们小团队的周会我做了个小实现,请大家在5分钟之内用不多于100字的内容给不了解的人介绍一下我们开发的CTU风控系统是干什么的,内容如下:

CTU系统是风控部门用于对各种接入的业务事件:如登录,发offer等等进行风险判断的系统;通过规则匹配、关键词匹配等手段以判定该事件是否具有风险,以决定下一步如何处理;比如“通过”、“删除”、“人工审核”等 

记录客户的操作分析客户行为数据,通过一系列行为规则去保护客户的利益,维护网站的安全  

ctu系统用于预防、发现、审查网站上任何高危用户行为行为的,比如:盗号,发布虚假产品等行为,从而去保证整个网站交易环境的安全可信度,避免用户不敢在网站上进行交易等行为。

CTU是电子商务网站的主动安全防护体系,通过人工规则和机器模型对用户行为进行实时检测,从而发现异常行为或风险,并及时做出响应将对网站用户或网站本身的损害消除在萌芽状态。

虽然每个人描述都或多或少地涉及到了CTU系统的本质,但局外人估计也不能一下子就能理解,从这个小测试证明,即使自己最熟悉的系统,你想把它说清楚也不是一件简单的事情,你知道和讲给别人知道完全是两回事,这就是我觉得程序员应该有写作能力的初衷。如果你觉得籍籍无名的我讲的东西可能没有说服力,那么我们来看看业界大牛是怎么说的:

勉强过得去的程序员跟杰出程序员的不同之处,不在于他们掌握了多少种编程语言,也不在于他们谁更擅长Python或Java。真正关键的是,他们能不能把他们的想法表达清楚。杰出的程序员通过说服别人来达成协作。通过清晰的注释和技术文档,他们让其他程序员能够读懂他们的代码,这也意味着其他程序员能够重用他们的代码,而不必重新写过。要不然,他们代码的价值就大打折扣了。(软件随想录作者,StackOverFlow创始人Joel Spolsky)

如果你准备在一堆人中挑出一个人来做某份工作,那就挑文章写得最好的那个。至于他有没有做过市场、销售、设计、编程或其他什么工作,倒并不重要。这种人的写作才华就值得雇用。这是因为,一个优秀的写手,其优点并不仅仅在于写作。文法清晰代表思路明晰。优秀的写手都懂得如何与人沟通。他们使事情变得易于理解,他们善于换位思考,懂得抓重点、砍枝节,这些都是合格的应聘者身上应具备的特点。今天,社会上再次掀起了写作热潮。你可以看到有多少人不用电话聊天,而是转向写电子邮件和文本消息。也可以看看有多少人选择通过即时消息和博客进行交流。如今,会写就代表会思考。(重来,37signals公司的副产品,他们的副产品还大名鼎鼎的Ruby On Rails)

我经常在走路和睡前总结所学过的内容,思考遗留的问题,一段时间的阅读和思考之后,一个总体的知识框架就会逐渐浮现在脑海中。然后我会将它书写下来,然而,我往往非常惊讶地发现,当我书写的时候,新的内容仍然源源不断地冒出来,就像我的键盘自己也会思考一样。(刘未鹏,暗时间

我最近也在定阅了不少微信的公众帐号的内容,这些作者都非常勤奋好多都是每日更新,比如小道消息Mac技术道哥的黑板报WTP等,这些作者都是业界大牛,他们不是专业的写手,都有自己繁忙的本职工作,也不是不讲名利无私奉献的圣人,是什么东西吸引了他们每天晚上笔耕不缀的呢,我想无非是他们自己从写作中收获的东西比传递给别人的东西更多,从经济学原理上来讲,满足自己的逐利需求是最大的动力源泉。

写作的好处

我自己觉得程序员写作有如下几点好处:

  • 锻练逻辑思维能力。人在学习某个知识点的时候,总是自认为自己懂了,但这可能是假象,对知识的了解是浮于表面的,因为这也许只是填鸭式的记忆,而不是真正的思考,即使你认为自己真的思考了,但如果不能顺畅地写出来,说明还是没有思考到位。
  • 提升沟通和表达能力。绝大多数人都不是像马云那样天生的口才好,能不用事先准备随时随地都能清晰完整地表达自己的想法,并且让听众有深深的认同感,所以别指望自己不经过大量练习在某些场合就能犹如神助超长发挥。恰恰相反地是,一到关键场合,自认为思路很清楚也可能语无论次,因此需要练习。沟通和表达有很多方面,有面对面的,也有电话视频的,更多的是通过文字比如邮件、文章、论坛等的方式,不管哪种方式,如果有好的沟通和表达能力,都能做到事半功倍,而写作是锻炼这项技能的一项重要练习。我记得前年公司里搞技术领域申报,我们提交的风控与反欺诈领域进入最后一轮答辩,到时要面向所有部门老大、公司里的技术大牛以及围观同事阐述自己的想法,从来没有这么正式的场合发过言,紧张是很自然的。PPT准备好之后,为了能在答辩时流畅地讲解,我需要对答辩的内容做练习,练习之前我把每面PPT的主要内容先用文字写出来,而不是直接对着PPT开始模拟练习,这样做的好处就是我可以不断地修改直到我觉得这样讲能让我比较满意为止而不用担心顾了东顾不西的情况发生,然后再顺着我的思路去模拟练习,会简单很多。最后我们的这个技术领域顺利入围技术站四大技术领域,虽然最主要的原因是因为这个领域本身大家都比较看好而不是我讲的好,但如果没有前面的准备,我很有可能把这事儿给搞砸了。
  • 经验的积累和沉淀。程序员工作个几年,或多或少都有积累,要说一点没有那真是白混了,这些经验如果只存储在我们的脑子中,很可能过段时间就忘了。人的大脑就像计算机的内存容量有限,只能存储最近的数据,时间一长当有新的数据进来后,就会把前面的覆盖掉,为了避免这种情况发生,一个好的办法就是要把内存中的数据定期存储到硬盘上永久保留。开始的时间可能是杂乱无章的也很粗糙,但没有关系,随着内容的积累,你就会发现存储了大量的东西,可以分门别类建立一个个的主题,在梳理的过程中可能会产生新的想法沉淀更多的东西出来。
  • 加深学习。写作我觉得是另一种方式的教学,常言到老师想要给学生一碗水自己就得有一桶,每个点都不是你觉得自己清楚就行了,想要写出来你自己必须得清楚,可能过程中要不断地再去查其它资料来补充自己的认识,无形中加深了自己对该点的学习。比如我在再这篇JVM监控分析工具之jsp、jinfo和jstat学习笔记时,为确定我从书本上获取的都是正确的知识,我自己要写测试程序尝试每一个命令是否如预期的那样,还要通过查阅JDK Tools and Utilities的官方文档来弄清楚每一个命令参数的含义,因为书中有些可能说的不是很清楚。暗时间里说到,其实我们学习过的东西都存储在脑海的某一处地方,只是很多时候你在用的时候想不起来而已。能不能想起来,取决于当时你在学习时留下的各种线索是否丰富,如果仅仅是把书中内容读了一遍,线索可能只有一条,如果写下来可以增加一条线索,你写过代码又增加了一条,你还阅读过官方文档又增加一条,这样在需要的时候就可以通过四条线索去搜索,比之前有4倍的机率找到。
  • 扩大影响力。酒香也怕巷子深,你再牛x也可能只有一个部门一个公司的人知道,而如今互联网时代,有这么多先进的传播渠道,只要写的东西言之有物,总会影响到很多人,这是大多数做技术的人的G点,虚荣心、兴奋感、成就感等等都来了^_^。有了影响力,就意味着你有更多的机会可以结识更多的牛人学到更多的东西产出更多高质量的文章形成良性循环,有更多的机会被其它公司注意到事业的天地会更广阔一点。

写作的实践

说了一堆写作的好处,谈谈写作的工具。强烈推荐使用Markdown的格式来写文章,因为它是一种轻量级的标记语言,能够让人们更加方便地阅读更加方便地书写纯文本内容,并把它转成有效的HTML文档,像GithubStackOverflow,国内的图灵社区等都在使用这种格式,Github Flavored Markdown还对原生Markdown做了增强可以非常方便地让代码语法高亮。Markdown可以让你专著于写作内容本身而不是表现格式本身,比Wiki格式更加简洁。另外如果是用WordPress自建的网站,可以下载Markdown插件来实现,但没有GFM功能代码无法高亮。Markdown的语法格式非常简单,对程序员来说估计半小时就可以掌握了,可以参考这里

如果是本地写作,Mac上推荐使用Mou,所见即所得模式非常不错并且是免费的,帮助文档就是一篇教程,左边源码右边实际展示,可以直接复制出来一篇进行练习。跨平台的可以用Sublime Text 2这个神器,下载安装一个Markdown插件,就可以在浏览器里面预览,略有不便。还有更多的Markdown专用编辑器,自己可以去找。

如果你不想公开,可以用Evernote记录到自己的私密空间,如果想和大家分享可以到Github上用Issues写博客,免去自己建站的麻烦节省金钱,如果你想定制自己的网站可以在Github上搭建自己的博客,也可以自己花钱注册独立域名购买空间使用WordPress搭建自己的独立站点,还不满意可以自己写个博客网站。Github上写的文章的表现形式,简洁素雅,深得我心。

写作的注意事项

写作的技巧相信我们在上学时都训练过,或者有专门的介绍,不再赘述,我觉得有几点需要注意:

  • 文字排版。这是给人的第一印象,如果排版混乱,代码没有高亮,除非里面的内容我在其它地方找不到,否则我是不愿意再看第二眼的,即使内容再好。混乱的排版也说明作者根本没有用心在写,面子活都没有做好你能指望里子活能好到哪里去。之前的时候,我看电子版的书都是下载免费的,直到去年我阅读了[多看阅读]发行的创业36条军规单行本之后,我成了多看的铁杆粉丝,迄今为止大概在上面买了有20本左右的书籍,我再也不愿看那些免费的电子书了,就像你用了Mac之后再也不想用Windows一样,这样的阅读体验才真正能和纸版书有得一拼并且能做的更好,读书真正成了一种享受。
  • 条理性。看别人的文章最怕思路不清没有条理,像碎碎念东拉西扯,文章的所有内容都是为主题服务的,不相干的内容最好不要硬塞进去,中心**明确一二三四点,合理使用标题。复杂的问题可能还需要交待清楚背景,问题解决的思考过程,资源引用的出处等。你的文章要达到的效果即使不能让读者读完之后有醍醐灌顶的感觉,也要有噢,不错,没有白看的效果,如果是一头雾水就太糟糕了。
  • 坚持。这一点可能是最难做到的,人的本能是感性大于理性,喜欢做让自己舒服而不是正确的事情,很容易半途而费。我之前的几次经历也是如此,坚持了很久最后放弃了,现在想来后悔的想撞墙。写文章不一定每天每周都写,如果能做到当然厉害,但坚持一月写一篇有内容的东西相对还是容易的,几年下来也有不小的积累,俗话说不积硅步无以至千里,不积小流无以成江海,坚持就是胜利。

现在就开始

心动不如行动,现在就开始吧,收获的惊喜在前面等着你呢。


这个主题上周我在团队内部做了分享,做了个简单的PPT,我只是想把这个PPT转成文字记录下来,但是没有想到的是花了4个小时洋洋洒洒写了这么多,有些是我分享中讲到的,还有很多是写作时蹦出来的,正如暗时间里说的,就像键盘自己会思考一样。

看到那些写博客坚持3年、5年甚至10年以上的,会佩服得五体投地,再差的写作功底坚持这么久也能超越绝大多数人,向这些牛人致敬。在写作上我还是个雏鸟,希望这次顿悟之后能飞的更高更远一点。

深入理解Java字符串连接

网上很多Java面试题都会有问到String字符串连接的性能,大家也都知道+号的性能远远差于StringBuffer或StringBuilder的append的性能,给出的答案是因为每次都要创建新的String对象,而StringBuffer或StringBuilder是不用创建的,其实不仅仅如此,如果比较一下字节码指令的话很容易弄明白。

先看一个简单的测试程序:

public class TestString {

    /**
     * @param args
     */
    public static void main(String[] args) {
        testPlus();
        testStringBuilder();
    }

    public static void testPlus() {
        long start = System.currentTimeMillis();

        String str = "";
        for (int i = 0; i < 10000; i++) {
            str += i;
        }

        long end = System.currentTimeMillis();
        System.out.println("testPlus spend time:" + (end - start));
    }

    public static void testStringBuilder() {
        long start = System.currentTimeMillis();

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 10000; i++) {
            sb.append(i);
        }

        long end = System.currentTimeMillis();
        System.out.println("testStringBuilder spend time:" + (end - start));
    }



}

运行结果如下,不出所料,StringBuilder要快的多:

testPlus spend time:492
testStringBuilder spend time:2

为什么StringBuilder会快这么多呢,我们用javap把Class文件反编译成字节码指令:

javap -v TestString

去除常量池,只看两个方法翻译成字节码的部分发现,+操作符其实在经过编译之后也会转成StringBuilder的append来做字符串连接,但是对比字节码之后发现,前者循环多了StringBuilder对象的创建以外额外三条指令,而后者要简洁很多,因此速度要快的多,如下图所示:
testString
testStringBuilder

后记
发现学习JVM之后,之前不是很明白的基本概念,通过字节码的方式很容易就能理解,底层技术有可能永远在工作中用不到,但对于我们更深入地掌握日常工作中用的技能还是有很大帮助。

Mac终端语法高亮设置

做为一名攻城狮,Mac操作系统下的终端软件Terminal不可不用,而Mac下的终端并不像Linux下那样,可以默认高亮,全部是一种颜色,尤其是VIM里面无法忍受,除非您练就一双火眼金睛,于万千行代码中一眼分辨出自己关注的重点。不过,这难不倒咱们做技术的人,Mac本身也是类Unix操作系统,Linux能干的,它也能干,不过得自己花点功夫而已。

Vim是我最常用的文本编辑器,但并不会自动进行语法高亮,通过vim命令打开文件后,需要输入:syntax on,显式地指示vim进行语法高亮才行。但每次这样未免太麻烦了,一个简单的方法是在用户目录下创建一个.vimrc的环境配置文件,里面添加上一行内容即可:

syntax on

有些文档中说要把这个文件的权限设置成777的可读写模式,我在本机上测试了一下,并不需要如此,建立一个普通文件即可,再次打开一个shell脚本文件,熟悉的感觉回来了,显示如下:
 13-4-4_ 9_32-10

但如果想要在使用ls这些Linux命令时,也要高亮,就需要花费点周折了。首先需要安装一个类似Ubuntu下面apt-get命令的终端安装工具homebrew,这个工具是用ruby写的,Mac本身已自带的有,我的机器的版本是:

zmbp:~ zxb$ ruby --version
ruby 1.8.7 (2012-02-08 patchlevel 358) [universal-darwin12.0]

安装homebrew,只要在终端里执行下面一行代码即可:

ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)"

安装虽然最终显示成功了,但是细心的话你会注意到,中间的一个Warning,提示缺少一个命令行工具,如果不安装这个命令行工具你在下面进行软件安装时会发现安装失败。

zmbp:~ zxb$ ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)"
==> This script will install:
/usr/local/bin/brew
/usr/local/Library/...
/usr/local/share/man/man1/brew.1

Press ENTER to continue or any other key to abort
==> /usr/bin/sudo /bin/mkdir /usr/local
Password:
==> /usr/bin/sudo /bin/chmod g+rwx /usr/local
==> /usr/bin/sudo /usr/bin/chgrp admin /usr/local
==> Downloading and Installing Homebrew...
Warning: Install the "Command Line Tools for Xcode": http://connect.apple.com
==> Installation successful!
You should run `brew doctor' *before* you install anything.
Now type: brew help

其中Xcode是苹果公司提供的一套完整的IDE环境,可以用来开发OS X或iOS下的软件,但是它非常庞大有一个多G,如果你今后没有这方面的打算用不着下载这么庞大的一个软件,只用下载Command Line Tools for Xcode即可,只有一百多M。
 13-4-4_ 10_36

安装完毕之后,homebrew就可以正常工作,我们先和做一次更新,保持和服务器同步,它的软件源应该是通过Git来同步到同地的:

zmbp:~ zxb$ brew update
Initialized empty Git repository in /usr/local/.git/
remote: Counting objects: 107514, done.
remote: Compressing objects: 100% (44571/44571), done.
remote: Total 107514 (delta 76368), reused 90280 (delta 62012)
Receiving objects: 100% (107514/107514), 16.24 MiB | 514 KiB/s, done.
Resolving deltas: 100% (76368/76368), done.
From https://github.com/mxcl/homebrew
 * [new branch]      gh-pages   -> origin/gh-pages
 * [new branch]      go         -> origin/go
 * [new branch]      master     -> origin/master
 * [new branch]      superwip   -> origin/superwip
HEAD is now at 0a273da tmux: patch for Snow Leopard
Already up-to-date.

完成上面的工作后,才真正可以开始安装终端高亮的组件coreutils了,不过这个需要xz这个解压缩工具,感觉下载速度挺快但编译安装的过程有点慢要等几分钟,安装命令如下:

zmbp:~ zxb$ brew install xz coreutils
==> Downloading http://tukaani.org/xz/xz-5.0.4.tar.bz2
######################################################################## 100.0%
==> ./configure --prefix=/usr/local/Cellar/xz/5.0.4
==> make install
 /usr/local/Cellar/xz/5.0.4: 58 files, 1.5M, built in 21 seconds
==> Downloading http://ftpmirror.gnu.org/coreutils/coreutils-8.21.tar.xz
######################################################################## 100.0%
==> ./configure --prefix=/usr/local/Cellar/coreutils/8.21 --program-prefix=g
==> make install
==> Caveats
All commands have been installed with the prefix 'g'.




If you really need to use these commands with their normal names, you
can add a "gnubin" directory to your PATH from your bashrc like:




    PATH="/usr/local/opt/coreutils/libexec/gnubin:$PATH"




Additionally, you can access their man pages with normal names if you add
the "gnuman" directory to your MANPATH from your bashrc as well:




    MANPATH="/usr/local/opt/coreutils/libexec/gnuman:$MANPATH"




==> Summary
 /usr/local/Cellar/coreutils/8.21: 210 files, 9.6M, built in 80 second

然后生成颜色自定义文件:

gdircolors --print-database > ~/.dir_colors

并在.bash_profile文件中加入以下配置信息:

if brew list | grep coreutils > /dev/null ; then
  PATH="$(brew --prefix coreutils)/libexec/gnubin:$PATH"
  alias ls='ls -F --show-control-chars --color=auto'
  eval `gdircolors -b $HOME/.dir_colors`
fi

执行source .bash_profile命令使这些配置生效,然后测试一下,终于可以看到和Ubuntu等Linux操作系统类似的结果了,刀磨好开始努力砍柴吧!
 13-4-4_ 11_17-5

参考
1.让Mac OS X的终端多姿多彩

读《暗时间》有感

上周天气骤冷从31度降到11度,周末两天时间也没有出门骑车,把新到的《暗时间》读了一遍,其实并没有全部读完,还剩下最后的三分之一不到,后面全部数学证明太枯燥。但只是前面的一多半已让我收获颇丰了,豁然开朗、醍醐灌顶、羞愧难当、暗自加油等等各种感觉都有,这又是一本让我相见恨晚的好书。

祭奠失去的青春

眨眼工作已快11年了,这么多年人生也经历了过起起伏伏,虽然至今专业上没有取得长足进展,都不好意思说在这个行业混了这么多年,但追求上进的心里始终有一股火苗,虽然时大时小但终端不曾熄灭,从心底来说我还是一个追求上进的人。但我始终没有掌握好自己的工作和生活,把大把大把的时间浪费在无意义的事情上,每每想起都痛不欲生,失去的青春呀,再也会不复返了。

尤其是看了暗时间之后,后悔的更是肠子都青了,本书的作者是84年的,但我觉得人家工作几年的收获是我远远无法相提并论的。如果要衡量人的成长,用学习时间*学习效率*思考深度应该是比较合适的,而我觉得作者恰恰是能把这三者发挥到极致的典范,因此能够进入微软亚洲研究院这样具有牛叉的地方,并能把自己的思考集结成书,影响了无数的人。在Google上搜索暗时间竟然有1.4亿的索引量,不得不赞叹这本书的力量。

利用好暗时间

书中扉页中写到:

每个人的生命就像沙漏,里面装的沙子总量大致相当,不同的是,有的沙漏颈部较细,有的沙漏颈部较粗。颈部较细的沙漏能够抓到每一粒时间之沙,即使沙子总量一样,也能拥有更长的生命。

每个人都希望自己拥有更长的生命,能不能学会时间管理是很关键的环节。有些人玩的不见的比你少,但学的比你更多,同样是24小时,为何别人看起来会"多出"那么多时间呢,就是人家把吃饭、走路、坐车、睡觉、上厕所等这些暗时间利用了起来。当你在坐车睡觉时,别人也许在读书听英文,当你在走路寻找丝袜美腿时,人家在思考问题消化之前读过的文章,这些看起来做不了什么事情的暗时间,正是人家提升自己的好机会。

最近我也体会到了利用暗时间的好处,有时候要去城西办公室,从滨江坐班车大概要一个小时,一般人都会在班车上补个觉,因为我正在读Mac技术的历史文章,之前是每天早上早起一小时花四十分钟读十篇,不想因为去城西而中断,大家都知道一件事情一但中断很难再续上,因此我在别人都睡觉的时候继续读Mac技术的历史文章,虽然是在有点颠簸的车上,但还是无障碍地完成了阅读任务,坚持了十来天就把所有历史文章读完了,学习了很多有用的知识,对于提高工作和学习效率非常有帮助。另外在晚上躺下睡觉前或者早上睡来懒床那会儿,我会听一集潘吉Jenny告诉你的英文节目,到现在也听了有十几集了,这些不起眼的时间好好利用,效果相当惊人。

思维改变生活

如果此书的第一部分看起来还比较轻松像是时间管理的实践,那么第二部分那多地像心理学研究,就像作者在前言中写到的一样,人人都应该学点心理学。作者通过大量的心理学研究,来试图找到人性的弱点,并通过合理的方式去避免这些弱点控制自己,并通过心理学的知识来训练自己的思维方式,达到更高效的学习。

说起心理学,我早好几年前就认识到它的作用了,也想通过研究心理学更深入地了解自己和他人,来达到更好地控制自己的本能和别人顺畅沟通的目的,为此还买了好几本心理学书,包括经典的心理学教材心理学与生活影响力,以及最近入手的怪诞心理学社会心理学等,但可惜的光有远见是没用的,这些书除了影响力之外,都没有好好读完。

作者在书中提到,为什么人控制不了自己,用了遇见20万年前的自己来揭示人类很多本能是在人类长期进化过程中形成的,人倾向于采取能够马上获取愉悦的短视行为,虽然理性告诉我们不应该这样做。想想看,我们有多少次在准备读书时是说先刷一会儿微博再我看书结果一晚上再也没有拾起书本。

思维方式的改变涉及到要战胜20万年前的自己,涉及到理智与感情的对抗,确实很难,涉及到长期的思维训练,要想达到不是那么容易的一件事儿,但话又说回来了,想成事儿哪一件又会容易呢。

最后一部分没有读完,是讨论如何解题的,探讨解决问题的思路,我之前很多次都梦想着如果有一天上帝给我一个梦想成真的机会的话,我想要自己有超强思考问题解决问题的能力。但里面涉及到了大量的数学问题,看起来比较枯燥,自从上大学后就患了数学恐惧症,因此只看了一半,虽然知道里面仍有金矿,但还是没能坚持看下去,我也知道如果前面的东西能消化吸收了就够我受益一辈子了。

Clojure学习笔记之三:函数编程

原文写于 2012-8-19

1.函数编程(Functional Pragramming)的定义是指计算机的运算都像数学上的函数运算一样,没有状态,值是不可变的;函数能作为另一个函数的参数传入,一个函数的返回结果也可以是一个函数。比如著名函数编程语言有Common LispClojureErlangScalaHaskell

2.把一个函数作为参数传进去,可以传入任意多个参数,调用任意多个函数

user=> (defn call-twice [f x y] (f x y) (f x y))
#'user/call-twice
user=> (call-twice println 123 456)
123 456
123 456

3.map,接收一个函数,后面跟上若干个集合,并使用传入的函数来对集合中的值做运算,返回计算后的集合序列

user=> (map clojure.string/upper-case ["a" "b" "c"])
("A" "B" "C")
user=> (map * [1 2 3] [5 6 7])
(5 12 21)

4.reduce,用指定的某个函数来计算集合中的值

user=> (reduce max [0 -3 10 48])
48
user=> (reduce min [0 -3 10 48])
-3
;;还可以指定初始值
user=> (reduce + 50  [0 -3 10 48])
105
;使用内置函数
user=> (reduce
   (fn [m v]
   (assoc m v (* v v))) ;assoc相当于java中的put,表示把参数v和v的平方作为key/value放到map m中
{} ;初始化一个空的map
[1 2 3 4])
{4 16, 3 9, 2 4, 1 1}

5.apply

user=> (def args [2 -2 10])
#'user/args
user=> (apply * 0.5 3 args)
-60.0

;;和reduce的区别是,apply可以在集合前加任意多个单个元素,reduce仅仅对集合生效 
user=> (apply *  1 3 [4 5])
60
user=> (reduce *  1 3 [4 5])
ArityException Wrong number of args (4) passed to: core$reduce  clojure.lang.AFn.throwArity (AFn.java:437)

6.parital,过滤

user=> (def s (partial filter string?))
#'user/s
user=> (s ["a" 1 2 "c"])
("a" "c")
user=> (def s (partial filter number?))
#'user/s
user=> (s ["a" 1 2 "c"])
(1 2)

7.composition function

;;注意参数前面的&符号
user=> (defn s [& nums] (str (- (apply + nums)))) 
#'user/s
user=> (s 10 20 3.2)
"-33.2"
 ;把&符号去掉看看
user=> (defn s [nums] (str (- (apply + nums))))
#'user/s
;再直接这样写就会出错
user=> (s 10 20 3.2) 
ArityException Wrong number of args (3) passed to: user$s  clojure.lang.AFn.throwArity (AFn.java:437)
;需要把一个集合传进去
user=> (s [10 20 3.2]) 
"-33.2"
;使用合成函数,写法会简洁很多
user=> (def s (comp str - +)) 
#'user/s
user=> (s 10 20 3.22)
"-33.22"
;也可以使用自函数
user=> (defn f [x] (Math/abs x))
#'user/f
user=> (def s (comp f - +))
#'user/s
user=> (s 10 20)
30

Blog搬家到Github

去年7月份的时候,买了域名和空间搭建了自己独立的Blog站点,之前也了解了一下在Github上搭建免费站点的过程,感觉稍显复杂并且独立性不够强,后来决定用Wordpress自己搭建了一个。半年多的时间写了20多篇,过年前后耽搁了一套时间基本空白,其实这段时间在学习JVM做了很多笔记,都放在了Evernote上,并没有放上来是觉得,非有感而发而不必为了写而写。

在建自己的独立博客过程中,花费了很多的时间选模板,才找到自己比较满意的,另外还包括测试源代码语法高亮功能,这是码农必备功能。后来发现出现大量垃圾评论,也花了一定精力去清理,并找反垃圾的插件做过滤。独立博客除了要多花一点钱之外,管理也是个费精力的事情,其实我只是想有个地方可以记录下自己的所思所想而已。看到玉伯的Blog全部用Github Issues来写,也挺好的,用Markdown格式写作,省心省力,支持评论不用担心垃圾,并且Github提供了Github Flavored Markdown的增强功能,插入源代码比在Wordpress里使用插件更简单。这些功能打消了我的顾虑,最终决定把Blog全部搬过来。

花了一个晚上外加半个上午,都20多篇文章都人肉搬迁了过来,Markdown天然支持Hmtl格式,省了我很多麻烦,只是把源代码的高亮改成GFM支持的格式,调整了一下格式,还算顺利。

唯一有点不太满意是的,issues很好地支持评论,而code可以pull到本地编辑,也有版本变更记录,但code不支持评论issues不支持本地和版本,并且markdown也不能像Html一样直接引用另外一个文件,把两者的优势结合起来,留下些许遗憾。

Java Class文件解析

花了约10个晚上的时间,终于完成了第一版的Java文件反编译,不过离真正的反编译器还差很远,目前只做到了类似javap -v的功能,但Class文件的文件结构解析工作已基本完工,后面的工作就是做测试了,保证反编译的健壮性,发现反编译复杂的长文件时还会报错。另外就是还原源代码,真正做到反编译器的功能。但如果是仅仅从学习Class文件结构的角度出发的话,还原源代码的意义并不是很大。

Class文件结构最权威的还是《The Java® Virtual Machine Specification》这个官方文档,不过因为内容太多并且是英文版的读起来比较费劲,我参考的主要是周志明的《深入理解Java虚拟机:JVM高级特性与最佳实践》以及很早之前国内翻译的第一本关于JVM的专著《深入Java虚拟机(第二版)》,如果有不清楚的地方或者有些新增加的内容比如StackMapTable,我会再参考虚拟机规范的官方文档。

参考
Java版反编译源代码

笑看潮起潮落

一年一度的晋升落下帷幕,有人欢喜有人忧。这里面有你觉得名副其实晋升是理所当然的,也有你觉得能力很普通出乎你意料之外的,也有你觉得能力和职业精神都不怎么样的心里面会忍不住说一声bullshit的,也有你觉得能力不错但意外落选令人惋惜的,晋升主要靠的是实力但也要靠运气靠人缘,虽然冠冕堂皇的说法是公平公正,但既然是考核既然有评委难免就会带有个人倾向,这世界上就没有绝对公平的事情,相对其它行业的种种内幕我还是比较相信公司的信誉。

有人说站着说话不腰疼,晋升失败的又不是你,你当然无所谓了。确实如此,站在局外才能有更客观清醒的认识,但是不管你是否腰疼,终究都要接受现实。与其期期艾艾的浪费时间,不如多从自己身上找原因,努力提升自己,当有一天你的光芒连乌云都遮不住的时候,晋升还会是个问题吗?

经历风雨才能见彩虹,经历挫折才会真正成长,笑看潮起潮落,静观花开花谢。

也谈时间管理

最近在微信上看到玉伯写的《互联网时代的时间管理》,和李开复写的《时间管理》两篇文章,尤其是前者给我触动很大。

曾经有那么一段时间,我深陷网络小说和微博的黑洞中,每天从公司回来后收拾好上床之后,就开始看我追的网络小说,刷微博,网上闲逛,不知不觉中,总是到了12点钟,起码有三个小时会浪费在这种毫无收获的时间陷阱中,只有睡觉前才会感觉到,哇,这个晚上又过去了,心里一阵惆怅,那种深深的空虚感会长久不散,但下一天又是如此,日复一日。

我也想好好利用晚上的时间,因为白天工作很忙不可能有时间去学习去充实自己,只能利用自己的业余时间,很多大牛们技术能力已经很强,但更令人敬佩的是他们依然非常努力,在不断地提升自己。本身能力就不足,再不好好努力,终有一天会被淘汰,而这是我永远不想面对的。所以,对时间管理,对自我管理,是自己最大的挑战,战胜自己的本能。

首先,给自己定了个简单的目标,每周七小时的编程时间,平均一天一小时,每天晚上回头陪女儿玩一会儿,洗个澡之后开始写代码,完成自己之前的homework,通过写java反编译器来深入学习jvm class文件格式,完成一个小阶段的工作之后,再看我追的小说,刷微博,看微信等,这些东西一时半会儿还是戒不掉,但把它们放在正事完成之后再做,并控制时间依然不能超过12点。

从上周六开始,实践了一整周,每晚花在正事上的时间都超过一个小时的基本要求,并实现了基本的反编译功能,类似javap的功能。花在正事上的时间多了,心里也感觉踏实多了,心在哪里,成绩就会在哪里,加油!

对自己最近的状态比较满意

坚持了三个星期,每天晚上或编码或写文章,完成了一个基本的Class文件解析器,把OOM和GC的各种情况都自己写代码实现了一遍,另外还写了几篇相关的博文,收获颇多,心里感觉特别踏实。

之前看书都比较随意,看完一遍基本不会看第二遍,结果是了解的东西很浮浅,很快就忘掉了。《深入理解Java虚拟机》这本书,到现在是第三遍,第一遍看过只有个大概印象,第二遍用Evernote做笔记,有了粗浅的理解,第三把其中的重点都用代码实现了一遍(只开了个头,完成前面几章),对JVM的理解虽然还谈不上很深入,但基本上算是入门了。反复读,动手去实践,是我最近的最大收获之一,不贪多,但求精。

有人说过,你把时间花在哪里,你的收获就在哪里,深有同感。加油!坚持!

Clojure学习笔记之四:集合与数据结构

原文写于 2012-8-25

1.不同集合的表示形式

'(a b :name 12.5) 
;;list
['a 'b :name 12.5] ;;vector
{:name "Bob" :age:32} ;;map
#{1 2 3}  ;;set
{Math/PI "~3.14"
  [:composite "key"] 42
  nil "nothing"}  ;;another map
#{{:first-name "bob" :last-name "zhang"}
   {:first-name "tom" :last-name "wang"}} ;;a set of map

2.对集合的操作,可以看到集合是不可变的

user=> (def v [1 2 3])
#'user/v
user=> (conj v 4)
[1 2 3 4]
user=> (conj v 4 5)
[1 2 3 4 5]
user=> (seq v)
(1 2 3)
user=> (def m {:a 5 :b 6})
#'user/m
user=> (conj m [:c 7])
{:a 5, :c 7, :b 6}
user=> (conj m {:c 7})
{:a 5, :c 7, :b 6}
user=> (seq m)
([:a 5] [:b 6])
user=> (def s #{1 2 3})
#'user/s
user=> (conj s 4)
#{1 2 3 4}
user=> (conj s 4 3)
#{1 2 3 4}
user=> (seq s)
(1 2 3)
user=> (def lst '(1 2 3))
#'user/lst
user=> (conj lst 2 4)
(4 2 1 2 3)
user=> (seq lst)
(1 2 3)
user=> (seq (conj lst 2 4))
(4 2 1 2 3) 

;;使用into代替conj和seq,into操作符是建立在这个之上
user=> (seq (conj lst 2 4))
(4 2 1 2 3)
user=> (into v [4 5])
[1 2 3 4 5]
user=> (into m [[:c 7] [:d 8]])
{:a 5, :c 7, :b 6, :d 8}
user=> (into #{1 2} [2 3 4])
#{1 2 3 4}
user=> (into [1] {:a 1 :b 2})
[1 [:a 1] [:b 2]]

3.clojure共7种数据结构
Collection Sequence Associative Indexed Stack Set Sorted

4.Collection
所有的集合都有如下几种操作: conj:把一个或多个元素添加到一个集合中 seq:获取一个集合的序列 count:获取集合中元素的数量 empty:定义一个同类型的空集合实例 =:比较两个集合中的值是否相同

5.Sequence

;;创建一个序列
user=> (cons 1 (range 1 5))
(1 1 2 3 4)
user=> (cons :a [:b :c])
(:a :b :c)
;;cons和list*等同
user=> (list* :a [:b :c])
(:a :b :c)

6.Associative
assoc:把一个key/value和一个集合合并 dissoc:从集合中移除 get:取指定key的value值 contains:判断集合中是否包含某个key/value

user=> (def m {:a 1,:b 2, :c 3})
#'user/m
user=> (get m :b)
2
user=> (get m :d)
nil
user=> (get m :d "not found")
"not found"
user=> (assoc m :d 4)
{:a 1, :c 3, :b 2, :d 4}
user=> (dissoc m :b)
{:a 1, :c 3}
user=> (contains? m :b)
true

7.Indexed

user=> (nth [:a :b :c] 2)
:c
user=> (nth [:a :b :c] 3)
IndexOutOfBoundsException   clojure.lang.PersistentVector.arrayFor (PersistentVector.java:106)
user=> (get [:a :b :c] 2)
:c
user=> (get [:a :b :c] 3)
nil

8.Stack
conj:push进去一个值 pop:移除最顶端的一个值 peek:取最顶端的一个值

user=> (conj '() 1)
(1)
user=> (conj '(2) 1)
(1 2)
user=> (peek '(2 3 1))
2
user=> (pop '(2 3 1))
(3 1)

9.Set

user=> (get #{1 3 4} 2)
nil
user=> (get #{1 3 4} 3)
3
;;移除指定的值
user=> (disj #{1 2 3} 3 1)
#{2}

10.Sorted

user=> (def sm (sorted-map :z 5  :y 0 :b 2 :a 3 :c 4))
#'user/sm
user=> sm
{:a 3, :b 2, :c 4, :y 0, :z 5}
user=> (rseq sm)
([:z 5] [:y 0]  [:c 4] [:b 2] [:a 3])
user=> (subseq sm &lt;= :c)
([:a 3] [:b 2] [:c 4])
user=> (subseq sm &lt;= :c &gt;= :b)
([:c 4] [:x 9] [:y 0] [:z 5])
;;必须先写大于操作符再写小于操作符,否则会报错
user=> (subseq sm &gt;= :b &lt;= :c)
([:b 2] [:c 4])
user=> (rsubseq sm &gt;= :b &lt;= :c)
([:c 4] [:b 2])
;;相等返回0,左边大返回1,右边大返回-1
user=> (compare 2 2)
0
user=> (compare 2 3)
-1
user=> (compare 4 3)
1
user=> (compare "ab" "abc")
-1
user=> (compare "ab" "ac")
-1
user=> (compare "abc" "ac")
-1
user=> (compare "adbc" "ac")
1

世界,你好

2011.7.11 原文: http://yikebocai.com/?p=1

工作时间也挺久了,Blog刚兴起的时候就开始玩,但都无疾而终,感觉个人在专业上的积累缺少沉淀,从几个月前开始有Evernote做笔记,零零碎碎记了很多,但都缺少系统的思考。现在总有种感觉这么多年过去了好像什么都没有留下,总感觉心里面很慌。写文章是一种很好的问题思考方式,也是一种有效的学习方式,不要管那么多,Just Do it!

Clojure学习笔记之一:安装运行

原文写于 2012-8-14

安装

Clojure主页下载最新的安装程序包,直接解压到一个目录下即可。Clojure是在JVM上运行,所以需要先下载JDK进行安装。

运行

切换到clojure解压目录,执行java命令即可启动并进入命令行模式:

java -cp clojure-1.4.0.jar clojure.main

演示

clojure和其它动态语言一样,可以直接在命令行下进行交互式编程,并且clojure的语法比较特别,代码部分都在小括号之内,运算符放在最前面,运算因子放在后面,它们之间用空格隔开

;两数相加
(+ 2 3)
#6
;打印源代码
&#039;(+ 2 3)
#(+ 2 3)

实现自己的一个方法

;定义一个函数
(defn hello [name] (str "Hello, " name))
;调用函数执行
(hello "bob")
#"Hello, bob"

执行一个clojure文件

;源文件内容为(println "Hello,World")
(load-file "test\\clojure\\hello.clj")
#Hello,World
#nil

Java中定义一个DataObject对象,需要Get和Set方法,虽然Eclipse可以自动生成,但那么多代码还是让人很不爽,Clojure中就非常简单了

;先定义一个对象
(defrecord Person [firstName lastName])
;创建一个实例
(def myboss (-&gt;Person "Tom" "Zhang"))
#&#039;user/myboss
;打印这个实例的值
myboss
#user.Person{:firstName "Tom", :lastName "Zhang"}

查询文档、源代码

;查看str方法的文档定义
user=> (doc str)
-------------------------
clojure.core/str
([] [x] [x & ys])
  With no args, returns the empty string. With one arg x, returns
  x.toString().  (str nil) returns the empty string. With more than
  one arg, returns the concatenation of the str values of the args.
nil
;查看str方法的源代码
user=> (source str)
(defn str
  "With no args, returns the empty string. With one arg x, returns
  x.toString().  (str nil) returns the empty string. With more than
  one arg, returns the concatenation of the str values of the args."
  {:tag String
   :added "1.0"
   :static true}
  (^String [] "")
  (^String [^Object x]
   (if (nil? x) "" (. x (toString))))
  (^String [x & ys]
     ((fn [^StringBuilder sb more]
          (if more
            (recur (. sb  (append (str (first more)))) (next more))
            (str sb)))
      (new StringBuilder (str x)) ys)))
nil
;搜索包含string字符串的方法
user=> (find-doc "string")
-------------------------
clojure.pprint/add-english-scales
([parts offset])
  Take a sequence of parts, add scale numbers (e.g., million) and combine into a string
offset is a factor of 10^3 to multiply by
-------------------------
clojure.pprint/base-str
([base val])
  Return val as a string in the given base
-------------------------
clojure.pprint/capitalize-string
......

参考

1.4clojure:各个练习题,从入门到高级应有尽有
2.blackstag的blog:关于clojure的入门介绍
3.Programming Clojure,2nd Edition:我正在读的一本书,内容200多页算是比较少的一本clojure的书,并且出第二版了说明反响不错

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.