Mac终端浏览器配置

工具

  • w3m
  • libsixel
  • ssserver, ss-local, polipo

安装配置使用

libsixel

用于w3m中图形化展示。

  • 下载源码git clone https://github.com/saitoha/libsixel
  • 配置、构建、安装

    • configure, make, make install

这里需要注意几个点:

  1. 请使用Iterm2 3.3+ 版本;
  2. 请构建完毕之后安装,即make install
  3. configure的时候,可以看下哪些包不存在,建议手动加上依赖

w3m

  • 下载源码git clone https://github.com/tats/w3m
  • 配置、构建、安装

    • configure, make, make install

需要注意:

  1. configure的时候,请注意SSL、并且加上--enable-image参数;

完成这步,其实就可以使用w3m -sixel 去尝试终端浏览网页了。但是,伟大的中国人民与世界之间存在着一堵看不见的高墙。这个时候,就需要“科学上网”软件的帮助。我们这里用到了ssserver和ss-local,这两个东西我就不加赘述。重要的是,本地ss-local启动的server是socks5协议,而w3m只支持HTTP。

所以,请brew install polipo,polipo可以帮助我们将socks5协议转为HTTP协议。但是download的时候一直hang住,所以我去GitHub上下载了源码,构建编译安装(这里没有额外的配置)。
polipo socksParentProxy=127.0.0.1:1086 2>&1 1>/tmp/polipo.log &启动服务。

接着,打开w3m,使用o查看options,在http proxy和https proxy中填写http://127.0.0.1:8123,别忘了点下面的[OK]保存。

Applescript + Automator = 快捷键开关蓝牙

tell application "System Preferences"   -- 进入系统面板
    set the current pane to pane id "com.apple.preferences.Bluetooth"  -- 设置当前tab为蓝牙
    tell application "System Events"  -- 触发系统事件
        tell process "System Preferences"  -- 进入系统面板程序
            tell window "Bluetooth"  -- 找到蓝牙窗口
                tell button 3  -- button 3是开启/关闭蓝牙按钮
                    click  -- 点击
                end tell
            end tell
        end tell
        quit
    end tell
end tell

同时,配合上Automator,制作成一个action,再为这个action添加一个快捷键

最后一周

离开阿里巴巴的最后一周

2019年05月24日,将会是我在阿里巴巴,毕业后第一份工作,的最后一个工作日。这最后期限越是接近,越是感受不到任何内心的波动,该有的心理活动,都已经有过了吧。如今,我只想把手头的工作尽可能收尾,带着对过去的美好会议于对未来的憧憬离开。

Farewell~~~

注定是难忘的日子

      2019年04月30日,是一个既伤感又开心的日子。

      四月尾巴的杭州,又开始阴沉沉的,厚重的云朵含着丰富的雨水,悬在空中。我和尽哥(孤尽)在家边上的临平水景公园有孚茶书屋,从早上十点聊到了下午四点半。从学习到工作,源码到生活,聊得是酣畅淋漓。想说什么,都说了,想表达的敬佩和遗憾,也都倾诉了,也许,尽哥,能亦师亦友吧。

      其实到了成年人,有些苦头是一定要去经历,也一定要去自己吃过,才知道的。正是这些苦头的存在,让人觉得人生有真实存在感,也正是大大小小的每一个选择,有了现在的人生和未来的可能。有些事,我懂,有些事,我需要去懂,内心怀着感激之情,对于过往的人和事,都有一颗感恩的心,我想我是幸运和幸福的。

      对于尽哥,我一直以来都是钦佩的:一个男人,能受得了委屈,担得起责任,抗得了大旗,这是难能可贵的。有时候,还能细腻如丝,关怀备至,着实让我在冰冷的企业机器中,感受到家的温暖,而不是战场的刀光剑影。

      今天,我们探讨了很多,生活做人方面的事,我想就写到这里,接下去的,想记录下今天从Java switch语句中学到的东西。

工具

  • java
  • javac
  • javap
  • diff

代码

第一段代码

public class Test {
    public static void main(String[] args) {
        switch ("H") {
            case "A":
                break;
            case "xxxx":
                break;
        }
    }
}

Test.class字节码

Compiled from "Test.java"
public class Test {
  public Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String H
       2: astore_1
       3: iconst_m1
       4: istore_2
       5: aload_1
       6: invokevirtual #3                  // Method java/lang/String.hashCode:()I
       9: lookupswitch  { // 2
                    65: 36
               3694080: 50
               default: 61
          }
      36: aload_1
      37: ldc           #4                  // String A
      39: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
      42: ifeq          61
      45: iconst_0
      46: istore_2
      47: goto          61
      50: aload_1
      51: ldc           #6                  // String xxxx
      53: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
      56: ifeq          61
      59: iconst_1
      60: istore_2
      61: iload_2
      62: lookupswitch  { // 2
                     0: 88
                     1: 91
               default: 91
          }
      88: goto          91
      91: return
}

         可以从第二部分第八行看到,源码中的switch("H")采用了StringhashCode方法。其实switch方法只支持int类型的匹配,其他基本数据类型都是会被语法糖转换为整型来做判断和匹配。

第二段代码

public class Test {
    public static void main(String[] args) {
        A a = A.A;
        System.out.println("B ordinal is :" + A.B.ordinal());
        switch (a) {
            // 请特别注意这里case出现的顺序
            case B:
                System.out.println("This A");
                break;
            case C:
                System.out.println("This B");
                break;
            case A:
                System.out.println("This C");
                break;
            default:
                System.out.println("Default.");
                break;
        }
    }

    enum A {
        A, B, C;
    }
}

         这里使用了一个内部枚举类A,并作为switch中的判断类型。从之前知识来说,这里会将枚举值转换为整型来做条件匹配。所以我们编译一下,发现有三个class文件:Test.java、Test$1.java和Test$A.java。我们知道,Test.class是Test类生成的,Test$A.class是内部枚举类A生成的。但是Test$1.class呢?我们暂且放一下,来看下反编译的结果。

Test.class字节码

Compiled from "Test.java"
public class Test {
  public Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #2                  // Field Test$A.A:LTest$A;
       3: astore_1
       4: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
       7: new           #4                  // class java/lang/StringBuilder
      10: dup
      11: invokespecial #5                  // Method java/lang/StringBuilder."<init>":()V
      14: ldc           #6                  // String B ordinal is :
      16: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      19: getstatic     #8                  // Field Test$A.B:LTest$A;
      22: invokevirtual #9                  // Method Test$A.ordinal:()I
      25: invokevirtual #10                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      28: invokevirtual #11                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      31: invokevirtual #12                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      34: getstatic     #13                 // Field Test$1.$SwitchMap$Test$A:[I
      37: aload_1
      38: invokevirtual #9                  // Method Test$A.ordinal:()I
      41: iaload
      42: tableswitch   { // 1 to 3
                     1: 68
                     2: 79
                     3: 90
               default: 101
          }
      68: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
      71: ldc           #14                 // String This A
      73: invokevirtual #12                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      76: goto          109
      79: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
      82: ldc           #15                 // String This B
      84: invokevirtual #12                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      87: goto          109
      90: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
      93: ldc           #16                 // String This C
      95: invokevirtual #12                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      98: goto          109
     101: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
     104: ldc           #17                 // String Default.
     106: invokevirtual #12                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
     109: return
}

      此处对比第一段代码的反编译结果,需要关注两个点:

  1. 第二部分的第四十二行,switch在这里被编译成tableswitch指令,和之前的lookupswitch不同;
  2. tableswitch中的内容,条件成了顺序的1、2、3;
  3. switch(a),采用的是Test$A.ordinal()方法,也就是说,枚举值转换为整型来做switch判断,采用的是枚举值的ordinal方法;

      (省略Test$A.class字节码,与此处讨论内容关系不大)

Test$1.class字节码

Compiled from "Test.java"
class Test$1 {
  static final int[] $SwitchMap$Test$A;

  static {};
    Code:
       0: invokestatic  #1                  // Method Test$A.values:()[LTest$A;
       3: arraylength
       4: newarray       int
       6: putstatic     #2                  // Field $SwitchMap$Test$A:[I
       9: getstatic     #2                  // Field $SwitchMap$Test$A:[I
      12: getstatic     #3                  // Field Test$A.B:LTest$A;
      15: invokevirtual #4                  // Method Test$A.ordinal:()I
      18: iconst_1
      19: iastore
      20: goto          24
      23: astore_0
      24: getstatic     #2                  // Field $SwitchMap$Test$A:[I
      27: getstatic     #6                  // Field Test$A.C:LTest$A;
      30: invokevirtual #4                  // Method Test$A.ordinal:()I
      33: iconst_2
      34: iastore
      35: goto          39
      38: astore_0
      39: getstatic     #2                  // Field $SwitchMap$Test$A:[I
      42: getstatic     #7                  // Field Test$A.A:LTest$A;
      45: invokevirtual #4                  // Method Test$A.ordinal:()I
      48: iconst_3
      49: iastore
      50: goto          54
      53: astore_0
      54: return
    Exception table:
       from    to  target type
           9    20    23   Class java/lang/NoSuchFieldError
          24    35    38   Class java/lang/NoSuchFieldError
          39    50    53   Class java/lang/NoSuchFieldError
}

      (如果字节码看不清楚,可以使用其他反编译工具,让代码更可读)可以看到这个Test$1.class的字节码,其实是创建了一个SwtichMap的一维数组,下标是枚举类不同值的ordinal值,对应的值是顺序整型:1、2、3,也就是:

+------------------+-----------------+-----------------+
|                  |                 |                 |
|  A.A.ordinal()   |  A.B.ordinal()  |  A.C.ordinal()  |
|                  |                 |                 |
+--------+---------+--------+--------+---------+-------+
         |                  |                  |
         v                  v                  v
    +----+----+        +----+----+       +-----+----+
    |    3    |        |    1    |       |     2    |
    +---------+        +---------+       +----------+

      对照Test.class字节码的三条注意内容中的后两条,可以看到,switch()中会使用枚举值的ordinal方法获取枚举值在枚举类中的位置,而case中使用枚举值,但是条件值其实不是位置数字,而是SwitchMap中对应的值,而不是下标。

      那么,tableswitchlookupswitch指令又有什么不同呢?我们来看下第三段代码:

第三段代码

public class Test {
    public static void main(String[] args) {
        switch (2) {
            case 1:
                break;
            case 2:
                break;
            case 3:
                break;
            case 560:       // 特别注意
                break;
            case 5:
                break;
            case 6:
                break;
            case 7:
                break;
            case 8:
                break;
        }
    }
}

Test.class字节码

public class Test {
  public Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_2
       1: lookupswitch  { // 8
                     1: 76
                     2: 79
                     3: 82
                     5: 88
                     6: 91
                     7: 94
                     8: 97
                   560: 85
               default: 97
          }
      76: goto          97
      79: goto          97
      82: goto          97
      85: goto          97
      88: goto          97
      91: goto          97
      94: goto          97
      97: return
}

      可以看到,这里使用了lookupswitch。再看下第四段代码:

第四段代码

public class Test {
    public static void main(String[] args) {
        switch (2) {
            case 1:
                break;
            case 2:
                break;
            case 3:
                break;
            case 4:
                break;
            case 5:
                break;
            case 6:
                break;
            case 7:
                break;
            case 8:
                break;
        }
    }
}

Test.class字节码

Compiled from "Test.java"
public class Test {
  public Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_2
       1: tableswitch   { // 1 to 8
                     1: 48
                     2: 51
                     3: 54
                     4: 57
                     5: 60
                     6: 63
                     7: 66
                     8: 69
               default: 69
          }
      48: goto          69
      51: goto          69
      54: goto          69
      57: goto          69
      60: goto          69
      63: goto          69
      66: goto          69
      69: return
}

      这里使用的是tableswitch。看到这里,是不是有感觉了?

      其实使用tableswitch还是lookupswitch,完全是看这个整型条件的值遍历是否快捷,如果是连续的整型值,那么以数组方式存放条件执行语句是合理的,其查询复杂度为O(1)。但是如果条件代表的整型数不是连续的,譬如String常量的hashCode值,又比如出现了560这样的不连续数字。那就无法使用数组下标代替条件值来做映射关系,只能采用有序数组的二分查找方式来提升条件匹配效率,所以有了lookupswitch

新的旅途

一晃,离开学校已经两年多了,今年七月,将会在阿里度过三个春秋。然而,心却越来越躁动。

现代人困惑和不快乐的根源之一,即单一职业的选择和我们内心渴望多元的本性之间的冲突

这大概就是我内心最深处的躁动根源吧。

我也想做斜杠(slash)青年;我想了解、触摸、亲吻这个社会;我想经历更多,让自己的人生不再是追求金钱,而是活出自己的模样。

天下没有不散的宴席,很感谢能一毕业就加入阿里巴巴,快速地成为正规军,把一些优秀的品质扎根在血液里。我眼界得以开阔,能力得以增强,都依仗于阿里。

未来的日子里,我将独自前行!

We can

The greatest thing you’ll ever learn is just to love and be loved in return.

世界上最美妙的事莫过于两情相悦

Today is 2019’s valentine’s day, but we do not stay together.

It is sad to us.

But in my deep heart, I never doubt whether we can get married, live together.

So, please, my wife, give us little more time, we can make it!

Happy new year

The day, Spring festival is coming. And we will meet in this lunar new year! I feel so happy and exited, my nhung~

Between last meet, it has more than 3 months. Time goes fast now, because we are happy with each other everyday.I know we can, and now, we are making it.

We both miss each other everyday so much. Miss made us sad, doubt, scared, but miss will also make us stronger, maturer, and better.

My wife, I know you will stay with me. I love you.

lsof

sudo lsof -nP -iTCP:端口号 -sTCP:LISTEN
-n 表示不显示主机名
-P 表示不显示端口俗称
不加 sudo 只能查看以当前用户运行的程序

DNS

查询本机DNS状况

dig -t a taobao.com

; <<>> DiG 9.10.6 <<>> -t a taobao.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 26117
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4000
;; QUESTION SECTION:
;taobao.com.            IN  A

;; ANSWER SECTION:
taobao.com.     103 IN  A   140.205.220.96
taobao.com.     103 IN  A   140.205.94.189

;; Query time: 1 msec
;; SERVER: 10.65.1.3#53(10.65.1.3)
;; WHEN: Sat Dec 22 04:21:40 CST 2018
;; MSG SIZE  rcvd: 71

Java问题排查

  • jstat -gcutil
  • jmap -histo

    • 打印内存中的变量大小
  • jmap -map

    • 打印PID的内存大致用量
  • jmap -dump:format=b,file=heap.bin

    • heap dump
  • jstack -l > <file-path>

    • thread dump
  • JVMOPT: -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=<file-path>

    • OOM时dump出内存
  • -Xloggc:<file-path> -XX:+PrintGCDetails

    • 打印GC日志