Hedley

Stay Hungry, Stay Foolish.

OS X 通过命令行设置特定 IP

正文

由于我自己机器上的服务器配置,到公司联网后,需要手动设置 IP 10.0.xx.xx。一旦出了公司连入其他网络,又需要把 IP 改为自动获得。通常操作顺序是 系统偏好设置 -> 网络 -> Wi-Fi -> 高级 -> TCP/IP -> 使用 DHCP,很麻烦。

查阅了一些资料,Mac OS X 其实可以通过命令行来执行:

  • sudo networksetup -setmanualwithdhcprouter Wi-Fi 10.0.xx.xx - 手动设置 IP
  • sudo networksetup -setdhcp Wi-Fi - 自动获得 IP

可以写入脚本,配合 Alfred 的 Workflows 功能更赞。

引用

OS X Man Pages

OS X Command Line IPv4 interface Reset



HotSpot JVM 调试大杂烩

此文的 JVM 环境特指 HotSpot JVM

Understanding Garbage Collection

了解 JVM 内存回收机制,以及年轻代、伊甸园、年老代的意义。Full GC 发生很频繁时,关注是否分配内存不足1、死锁、或者内存泄露。

jstat - Statistics Monitoring

一系列调试选项,常用 jstat -gcutil 排查内存问题。

jstack - Stack Trace

了解不同的线程状态。通常用来排查是否有死锁发生,以及找出高 CPU 消耗的线程状态。

  • top -Hp [PID] - 找出进程内最耗费CPU的线程
  • printf "%x\n" [PID] - 得到线程号 TID
  • jstack [PID] | grep [TID] - 观察线程信息

jmap - Memory Map

分析 JVM 的内存状态,是排查内存泄露的关键。

  • jmap -heap [PID] - 进程堆内存使用情况
  • jmap -histo[:live] [PID] | head 15 - 对象占用图。对象占用图粒度很粗,无法获得更详细的对象状态
  • jmap -dump:format=b,file=dump.hprof [PID] - 生成 dump.hprof 文件。执行时间稍长,基于此可进行进一步的精准分析

Eclipse MAT

基于 dump.hprof 进行分析,首选主流工具 Eclipse MATMAT 可以开上帝视角,通过 OQL 精确定位到每个对象,用来做详细的分析挖掘很好用。不过它的缺点也很明显:重量级。如果 dump 文件不大,直接用 MAT 分析没有问题。但现实运行环境中,dump 文件通常很大2,一般需要面临以下问题:从服务器压缩传输到个人机器,压缩和传输的时间成本很高;解压缩后用 MAT 导入,如果个人机器的内存不够,无法进行解析3

内存不够装进 Eclipse 的解决方案

找到 org.eclipse.equinox.launcher_1*.jar 的本地目录,命令行执行生成索引过程:

java -jar /Users/hedley/eclipse/java-neon/Eclipse.app/Contents/Eclipse/plugins/org.eclipse.equinox.launcher_1*.jar -consoleLog -application org.eclipse.mat.api.parse dump.hprof org.eclipse.mat.api:suspects org.eclipse.mat.api:overview org.eclipse.mat.api:top_components 生成报告,其中 org.eclipse.mat.api:suspectsorg.eclipse.mat.api:overvieworg.eclipse.mat.api:top_components 是生成的三个主要模块,可根据需要配置。再用 Eclipse 打开报告目录。

如果以上命令还报内存不足,尝试加参数 -Xmx4g -XX:-UseGCOverheadLimit

OQL2

BHeapSampler

BHeapSampler 是一款轻量级的 JVM 内存分析工具。不,它不单单是轻量级,是羽毛级!如果用 MAT,通常需要把臃肿的 dump.hprof 传到本机进行操作4。而 BHeapSampler 是一个大小只有 26Kjar 包,上传到服务器一行指令即可进行分析。它分析得到的是一张大内存对象引用图,不能进行任何操作与细项查询索引。但是这张重点鲜明的内存图,有时会比 MAT 更击中要害。大写的赞。

  • java -Xmx40g -XX:-UseGCOverheadLimit -jar bheapsampler.jar dump.hprof - 得到 memory_graph.dot
  • 可以用 Graphviz 查看 .dot 文件,装完也可以将 .dot 转为 pdf 格式: /opt/local/bin/dot -Tpdf -omemory_graph.pdf memory_graph.dot
Mac 安装 Graphviz




  1. For old generation space OutOfMemoryErrors, increase -Xms and -Xmx; For permanent generation OutOfMemoryErrors, increase -XX:PermSize and -XX:MaxPermSize

  2. 我曾经分析过的线上压力一般服务器的 dump 在 10G 左右

  3. Eclipse 在导入 dump 文件后,会进行索引分析,占用大量内存。内存不足时会报 OutOfMemoryError

  4. 很少有人在服务器上装 Eclipse 吧?

Sed 循环处理

sedshell 编程中很常见的流编辑命令,主要用于批量替换。

sed 的默认编辑单位是『行』,如果要处理的信息分布于多个行中,需要考虑多行处理命令:N、P、Dclick here, nice tutorial

考虑以下情景:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
% cat onetwo                                                                            
one two onw two one two xxx one
two yyy one
two zzz one    
two kkk two one
twx xxx yyy one two one
xxxxx
xxx

yyy one 
x two one
xx one

two x
x one
two one
two

如果一行以 one 结尾,而且下一行以 two 开头,那么就把这两个邻接行拼接成一行,并把 one two 替换为 1 2

在只了解 N、P、D 之后,想出一个比较有意思的解决方案:

1
2
3
4
5
6
7
8
9
10
11
% cat onetwo.sed
#!/usr/bin/sed -f
/one *$/ {
        # 连接下一行,$!N 表示如果读到最后一行单独处理 
        $!N
        # 替换 prefix + one + 回车符 + two 为 回车符 + prefix + 1 2
        s/^\(.*\)one *\ntwo/\
\11 2/
        /^\n/ !P
        D
}

表达式 s/a/b/ 中,查找字符串 a 如果包含回车符,用 \n 表示。但是如果替换字符串 b 中如果包含回车符,需要用 \ + 回车 表示。并且回车后的替换内容要顶头,不能排版加空格、tab1

以上解决方案的关键点在于替换时在开头又加了一个换行符,如此后面执行 D 则删除了模式空间中的这个空行,继续读入下一行处理,以此达到循环处理的目的。

1
2
3
4
5
6
7
8
9
10
11
12
% ./onetwo.sed onetwo
one two onw two one two xxx 1 2 yyy 1 2 zzz 1 2 kkk two one
twx xxx yyy one two one
xxxxx
xxx

yyy one 
x two one
xx one

two x
x 1 2 1 2

后面了解了 t 命令后,更直接的解决方案如下:

1
2
3
4
5
6
7
8
9
10
11
% cat onetwot.sed     
#!/bin/bash 
sed  '
/one/ {
        :again
                $!N
                s/one *\ntwo/1 2/
                t again

}
' $1

t 命令相当于一个 while,当 t 之前最近的一个 s/a/b/ 命令匹配成功时,继续循环。得到同样的运行结果。

此小实例只做趣味讨论,shell 中的流处理2都是面向3为单位的,对于内容有 overlap 的循环处理,效率低且不好用。




  1. 取决于不同的 sed 版本,但是顶头写肯定是 ok 的。

  2. sed, awk, grep etc..

  3. or 块,比如 awk 中的 FS=‘\n’

Java 如何实现 Prototype Pattern

看到 Software Architecture Design Patterns in JavaCREATIONAL PATTERNS: Prototype 1章节,忽感一阵恍惚。之前写负责任再谈 Callback 与 Delegation 时提到了 Java 不支持 Delegation,但是这篇设计模式却好像写如何在 Java 中实现 Delegation。此中涉及了 Object.clone() 方法、Cloneable 接口,真真一团乱麻。

Prototype Pattern

先看 Prototype PatternWiki,一开始提到的便是 Not to be confused with Prototype-based programmingDelegation 正是 Prototype-based programming 的语言特性2。总之,Prototype Pattern 是创建对象的一种方法,Delegation 是语言的一种天赋。

clone() && Cloneable Interface

了解 clone 之前,看 Effective Java, Item 11: Override clone judiciously 是必修课!

随便翻看几个 Java 实现 Prototype Pattern 的例子『戳这里戳这里戳这里』,也包括最开始提到的 CREATIONAL PATTERNS: Prototype,都是通过 Object 基类中的 clone() 方法直接或间接实现的。

clone() 方法是原生方法,它类似 new,会生成新对象,也是一种“构造方法”,所以对构造方法的一些约束对其同样适用3

clone() 方法是 protected 方法,想要调用之还需要绑定 Cloneable 接口。这个奇葩设定很恶心,虽说约定优于配置,但这种约定实在是太不优雅。按照地球人的理解,一个接口应该提供一种服务,Cloneable 接口提供 clone() 方法天经地义,但又由于其特殊性,clone() 方法的原生实现 Java 需要自己提供,而接口声明的方法又不能给出一个默认实现4,然后这个 clone() 方法便写在了 Object 里,Cloneable 接口只是作为一个标识接口用:所有实现了 Cloneable 的类需要重写 clone() 方法,通过 super.clone() 来调用 Object.clone() 的原生实现,然后根据需要再做一些适当的调整。

所谓适当的调整,是因为 Objectclone() 方法只是 shallow copy,如此 clone 出来的对象,其包含的 mutable 的引用还是同一个引用,并非副本。

By convention, the object returned by this method should be independent of this object (which is being cloned). To achieve this independence, it may be necessary to modify one or more fields of the object returned by super.clone before returning it. Typically, this means copying any mutable objects that comprise the internal “deep structure” of the object being cloned and replacing the references to these objects with references to the copies. If a class contains only primitive fields or references to immutable objects, then it is usually the case that no fields in the object returned by super.clone need to be modified.

Object.clone() 说明

了解了这些之后,你以为就可以妥妥的使用 clone() 了吗?还要注意这些限制

  • All classes that implement Cloneable should override clone with a public method whose return type is the class itself.
  • If you override the clone method in a nonfinal class, you should return an object obtained by invoking super.clone. If all of a class’s superclasses obey this rule, then invoking super.clone will eventually invoke Object’s clone method, creating an instance of the right class.
  • In effect, the clone method functions as another constructor; you must ensure that it does no harm to the original object and that it properly establishes invariants on the clone.
  • Like a constructor, a clone method should not invoke any nonfinal methods on the clone under construction.
  • The clone architecture is incompatible with normal use of final fields referring to mutable objects.
  • Object’s clone method is declared to throw CloneNotSupportedException, but overriding clone methods can omit this declaration. Public clone methods should omit it because methods that don’t throw checked exceptions are easier to use.
  • If you decide to make a thread-safe class implement Cloneable, remember that its clone method must be properly synchronized just like any other method.
  • Given all of the problems associated with Cloneable, it’s safe to say that other interfaces should not extend it, and that classes designed for inheritance (Item 17) should not implement it.

鉴于以上种种,我们可以很保守的说:珍爱生命,远离 clone()

Copy Constructor || Copy Factory

EJ 给出了一种替代解决方案:copy constructor or copy factory。它对比 clone() 简直好处多多:不需要受奇葩语意约束、不会和 final 标识的属性冲突、不抛出无意义的异常、不需要类型转换。

copy constructor 接受的参数是其自身实现的接口类型,它们其实类属于 conversion constructor。注意 HashMapCollection 的构造方法里,都有这种 conversion constructor,但是它们都是 shallow copy

序列化以及其他

Is Object deserialization a proper way to implement Prototype pattern in Java? 细致讨论了通过序列化实现 Prototype Pattern 的可能性。结论是可以,但代价是巨大的,还不如 clone 呢!其实 XML、JSON 都可以作为实现原型模式的手段,只要能得到一个对象的副本就行了呗。问题中的每一个回答每一个评论都值得细细品味。

kluge [英][k’lu:dʒ] [美][k’lu:dʒ]    n.由不配套的元件拼凑而成的计算机,异机种系统

Deep clone utility recomendation. 给出了实现 deep clone 的一个开源类库 The cloning library,此贴子的回答评论同样精彩。略看了一眼 The cloning library 的简介和评价,期待度较高,貌似是通过反射实现的,等有空研究一下代码再表。




  1. Page 95.

  2. language feature

  3. 主要就是这一条:Never call overridable methods from constructors, either directly or indirectly.

  4. JKD8 可以做到,咋不早出这个特性嘞

MySQL5.5 以下版本通过 Java 转义实现 Emoji 存取

我不输出好文,我只是好文的搬运工。十分钟搞清字符集和字符编码带你走进 UnicodeUTF-8 的前世今生。总而言之,Unicode 是标准,UTF-8 是实现。

MySQL 过招 Emoji

通常 MySQL 的默认字符集都会配置为 UTF-8,只支持单字符不超过 3 bytes 的存储1。常见的 EmojiUnicode 编码值 code point 位于 \u1F601 -- \u1F64F 区间,以 \u1F300 『🌀』举例,它的二进制有 17 位,无法用三字节的 UTF-8 编码表示2。如果通过 JDBC 将此字符尝试插入 MySQL 记录中,会得到以下异常

1
java.lang.RuntimeException: java.sql.SQLException: Incorrect string value: '\xF0\x9F\x8C\x80' for column 'message' at row 1 

Java 过招 Emoji

Java 存储字符的单元是 char,那么问题来了,16 位的 char 如何 handle 17 位的 Unicode 呢?

喜闻乐见,Java 的单个 char 确实无法 handle 🌀。那么 ⚡ 为什么可以通过编译?相信聪明如你,一定可以查到 ⚡ 的 Unicode\u26A1,Tada ~~ 所以 ⚡ 插入数据库中也是妥妥的。

速入 Java’s Unicode Notation 科普一下 Java 对 Unicode 的支持实现,配合通俗易懂版更赞3。原来 Java 存储单个字符的最小单元并不一定是单个 char,而是根据 code point 的范围确定。如果字符 ccode point 二进制表示不超过 16 位那么单个 char 可直接表示4;否则要进行以下处理,以 🌀 举例

  • \u1F300 的二进制表示 0001 11110011 00000000,其高 10 位与低 10 位拆开备用
  • 高位偏移量 W1 : 0xD7C0 与低位偏移量 W2 : 0xDC00 分别与其高低 10 位相加处理,得到 \uD83C\uDF005
  • 两个字符拼接 \uD83C\uDF00 得到 🌀
(EmojiCharacter.java) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package unicode;

public class EmojiCharacter {

    // high offset
    public static final int W1 = Integer.valueOf("D7C0",16);

    // low offset
    public static final int W2 = Integer.valueOf("DC00",16);

    public static void main(String[] args) {
        // 🌀的二进制字符串表示
        String doughnutBinary = Integer.toBinaryString(Integer.valueOf("1F300",16));
        // 拆成高低 10 位表示
        String highSetsBinary = doughnutBinary.substring(0, doughnutBinary.length() - 10);
        String lowSetsBinary = doughnutBinary.substring(doughnutBinary.length() - 10, doughnutBinary.length());
        System.out.println(highSetsBinary);     //    1111100
        System.out.println(lowSetsBinary);      //    1100000000

        // 分别与偏移量相加,得到两个编码值
        String highSetsHexFixed = Integer.toHexString(W1 + Integer.valueOf(highSetsBinary, 2));
        String lowSetsHexFixed = Integer.toHexString(W2 + Integer.valueOf(lowSetsBinary, 2));
        System.out.println(highSetsHexFixed);   //    d83c
        System.out.println(lowSetsHexFixed);    //    df00

        // 拼接这两个编码值,还原字符表示
        char highChar = (char)Integer.valueOf(highSetsHexFixed, 16).intValue();
        char lowChar = (char)Integer.valueOf(lowSetsHexFixed, 16).intValue();
        System.out.println(highChar);           //    ?     
        System.out.println(lowChar);            //    ?
        System.out.println(highChar + "" + lowChar);    //    🌀
    }
}

合体

Client 发送 Emoji 到后台,Java 却无法通过 JDBC 将其存储到 MySQL。如果我们遵循某个特定的转换规则,存储前转义一次,读取时再逆转义一次,那不就妥妥妥的了。你当然可以自己写,但是懒逼如我,更倾向于找现货

引用




  1. MySQL5.5 版本以后有对 4 bytes 单字符的原生支持,字符集配置为 utf8mb4

  2. 请看十分钟系列的实现细节,三字节的 UTF-8 编码上限是 16 位『4 + 6 + 6』,11111 001100 000000 对应的 UTF-8 编码为 11110000 10011111 10001100 10000000,即 \xF0…,MySQL 经常会冒出这种异常

  3. 勘误,通俗版中所说的 W1 : 0xD800 不对,应是 0xD7C0,已在评论中反映给原作者

  4. 比如『⚡』

  5. Unicode 预留了替代区域 \uD800 -- \uDFFF 作为辅助,此区间的单个字符没有实际表示意义

bigfoot.js 注脚之 Octopress 篇

对于一个不懂前端技术的人,能实现 bigfoot.js 动态注脚实在是太刺激了1。其实官网上说的已经够多,但前端白痴如我,还是绕了好多弯子,捉急2

下载

下载 bigfoot.js 源码,解压缩后其中的 bigfoot.jsbigfoot-default.scss 有用,其他可删。

还需要 jquery 模块支持,我用的是 jquery-1.9.1.min.js。你可以通过 command + alt + i 组合键打开开发人员工具3,通过 Sourcejavascripts ▶️ jquery-1.9.1.min.js ▶️ 右键 ▶️ Save 来获取4

加载

bigfoot.js 拖到 source/javascripts 文件夹,然后编辑 source/_includes/custom/head.html。添加以下代码

1
2
3
4
5
<script src="/javascripts/jquery-1.9.1.min.js" type="text/javascript"></script>
<script src="/javascripts/bigfoot.js" type="text/javascript"></script>
<script type="text/javascript">
  $.bigfoot();
</script>

bigfoot-default.scss 拖到 sass/custom,在 sass/custom/_styles.scss 中注册。一行代码

1
@import "bigfoot-default.scss";

footnotes

保证你的 octopress 注脚插件已注册,_config.yml 默认是注册的,kramdown 也可以

1
2
3
4
markdown: rdiscount
rdiscount:
  extensions:
    - footnotes

然后语法是这样

1
2
3
4
对于一个不懂前端技术的人,能实现 bigfoot.js 动态注脚实在是太刺激了[^1]。
......

[^1]: 效果赞炸   

可能的问题

什么都做了但没卵用

到这里如果没有意外应该就是妥妥的了,不幸的是我正是意外制造机。折腾了半天发现没有卵用,还是默认的 octopress footnotes 的效果,打开控制台发现有一行诡异的错误输出

1
Uncaught TypeError: $.bigfoot is not a function

这是闹啥嘞,明明就是从官网 copy 下来的啊。找了两个使用了 bigfoot 的网页5看其源代码,根本就是一样的嘛为毛我这报错?

开始以为是和页脚自动生成原文链接那有冲突,卸了还是一样报错。几番折腾,发现了破解大法:把 bigfoot.js 的注册写在 head.html 的开头。为什么会这样我不知道,如果好心的你知道可能的原因就偷偷告诉我吧!

移魂大法以后不报错了,显示也妥,只是在页尾还是有冗余的注脚信息。参照 options 给我们的 bigfoot 加一些修饰

1
2
3
4
5
6
7
8
<script type="text/javascript">
  $.bigfoot({
    actionOriginalFN: "delete",
    activateOnHover: "true",
    deleteOnUnhover: "true",
    hoverDelay: "500"
  });
</script>

首页不显示效果

如果 Post 里用了 <!--more--> 分页,那么显示在首页的注脚是没有效果的

VS

这个没想到简单的解决办法6,要么分页前部分不包括注脚,要么不分页。

大功告成,撒花 🌺🌺




  1. 效果赞炸

  2. 但能实现才是最牛逼的

  3. For MAC, Windows 平台的快捷键是 F12

  4. 我是前端小白,我期待你也是,不许笑

  5. http://blog.honekamp.net/blog/2013/12/23/bigfoot-on-the-main-page/

  6. 80/20, leave it alone…

负责任再谈 Callback 与 Delegation

引出

话接上回不负责瞎说说 Patterns 与 Frameworks,还谈引出问题:Wrapper classes are not suited for use in callback frameworks。虽然最后给出了一个可以验证的 Demo Code,但是个中缘由仍影影绰绰,真相仿佛隔着一层薄纱在向我招手。心急难耐,遂从引出 SELF problem 的论文出发,顺藤摸瓜按图索骥,也许是我的真心打动了上天,也许是我太帅,幸得 yqj2065 老师指点,才把真相抱了个满怀。

幸福来的太快,虚幻又不真实,也许我看到的只是背影,又或许我看到的是庐山真面,放空思绪,容我一点点回忆。不知道待我从求知路上走的更远,回头再看今天的“负责任”是不是够炫酷呢。

先甩大招上结论

不负责瞎说说 Patterns 与 Frameworks

引出

Effective Java, Item 16 中说 Wrapper classes are not suited for use in callback frameworks,想不明白,一顿 Google 依旧看个似懂非懂。我想把这些凌乱的线索都整理出来,抽抽剪剪排列组合一下,也许就看的透彻点了呢。

关于 Wrapper class 的引出问题,个人认为比较炫酷的解答

Because a wrapped object doesn’t know of its wrapper, it passes a reference to itself(this) and callbacks elude the wrapper. This is known as the SELF problem [Lieberman86]. This leads to subtle bugs, like the wrapper missing the event. Or the wrapper and wrapped object both registering for the same events - leading to duplicate processing and potential concurrency issues as well. If you don’t know (don’t have the source code) where callbacks are registered, it may be impossible to work around this problem.

Panttern? Framework?

引出中的 Wrapper classDecorator pattern 的引出概念,callback frameworks 是一种 framework。从翻译上看,pattern 译为 模式framework 译为 框架。那么这个 patternframework 有毛的区别呢?

Sublime Text 光标焦点集中到 FOLDERS 状态栏

搜索了若干资料,如何让光标焦点从 Editor 移动到 FOLDERS 状态栏。偶然复现过一次,却百思不得其解。

经过不懈尝试,终得破解大法!这下一口气看 LOG 不要太爽!

  • 点击 FOLDERS 中任意一个文件
  • 再次左键点击这个文件,不要松开,紧接着点击右键
  • Bingo!

秘籍:左 C + [ 左 C + hold 2ms + 右 C ],很有我当年三国战记配无限风书的神韵啊!

危机四伏整型运算

哈?整型运算?听起来好 EASY 啊喂!直奔主题咯。

边界陷阱

无论何时,请注意整型运算的边界问题,考虑最大值,最小值,越界的可能性。

Puzzle 3: Long Division

1
2
3
4
5
6
7
public class LongDivision {
      public static void main(String[] args) {
      final long MICROS_PER_DAY = 24 * 60 * 60 * 1000 * 1000;
      final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000;
      System.out.println(MICROS_PER_DAY / MILLIS_PER_DAY);
  }
}