Eric 发布的文章

一个进程打开了哪些端口在监听

为了让Node.js 能够充分利用多核的CPU,会开一个进程多个worker的模式, 每个worker是一个Node.js event loop. 如何查看开了哪些端口.

eric@eric1:~$ sudo netstat --all --program | grep '8481'
tcp        0      0 localhost:6666          *:*                     LISTEN      8481/pm2: Daemon
tcp        0      0 localhost:ircd          *:*                     LISTEN      8481/pm2: Daemon
tcp6       0      0 [::]:http-alt           [::]:*                  LISTEN      8481/pm2: Daemon
tcp6       0      0 [::]:8082               [::]:*                  LISTEN      8481/pm2: Daemon
tcp6       0      0 [::]:10100              [::]:*                  LISTEN      8481/pm2: Daemon
tcp6       0      0 [::]:10101              [::]:*                  LISTEN      8481/pm2: Daemon
tcp6       0      0 [::]:10102              [::]:*                  LISTEN      8481/pm2: Daemon
unix  3      [ ]         STREAM     CONNECTED     573099484 8481/pm2: Daemon
unix  3      [ ]         STREAM     CONNECTED     573099481 8481/pm2: Daemon
unix  3      [ ]         STREAM     CONNECTED     573099491 8481/pm2: Daemon

eric@eric1:~$ sudo lsof -i -P |grep 8481
pm2:       8481 rebot    3u  IPv6 573099604      0t0  TCP *:10100 (LISTEN)
pm2:       8481 rebot   12u  IPv4 573098742      0t0  TCP localhost:6666 (LISTEN)
pm2:       8481 rebot   13u  IPv4 573098743      0t0  TCP localhost:6667 (LISTEN)
pm2:       8481 rebot   17u  IPv6 573099599      0t0  TCP *:8082 (LISTEN)
pm2:       8481 rebot   18u  IPv6 573099600      0t0  TCP *:8080 (LISTEN)
pm2:       8481 rebot   20u  IPv6 573099610      0t0  TCP *:10101 (LISTEN)
pm2:       8481 rebot   22u  IPv6 573099619      0t0  TCP *:10102 (LISTEN)

进程的线程之间的相互关系:

pstree -a -p -H 8481
pstree -a -l -p -s 8481
top -H -p 8481
ps -L H 8481
ps -eLf
htop 8481

如何debug System.gc() call

有时候, 你在GC log 中发现在年轻代, 老年代, 永久带, 物理内存(包括Java 8 metaSpace, DirectBuffer), DirectBuffer 都有很多空闲, 还在full GC 的时候, 就可以看看是不是System.gc() 或者Runtime.gc() 在作怪了.

  1. 首先使用 -XX:+DisableExplicitGC 去看看, 是不是消除了,如果消除了, 说明就是 这2中gc() call;
  2. 然后 拉代码, 本地debug, 在以上2个方法上设置断点, 进行debug.

另外 以前某书, 或文章说 System.gc() 都会在 gc verbose log 里加 System 字样, 其实不完全是这样, 如最近我遇到这个, 就没有 System 字样.

164638.058: [Full GC (System) [PSYoungGen: 22789K->0K(992448K)] [PSOldGen: 1645508K->1666990K(2097152K)] 1668298K->1666990K(3089600K) [PSPermGen: 164914K->164914K(166720K)], 5.7499132 secs] [Times: user=5.69 sys=0.06, real=5.75 secs]

2019-02-14T00:33:36.136-0700: 3014642.000: [Full GC2019-02-14T00:33:36.136-0700: 3014642.000: [CMS: 766173K->766173K(1433600K), 3.0342400 secs] 775885K->766173K(2170880K), [CMS Perm : 168960K->168960K(524288K)], 3.0345150 secs] [Times: user=3.03 sys=0.00, real=3.03 secs]
2019-02-14T00:33:39.272-0700: 3014645.136: [Full GC2019-02-14T00:33:39.272-0700: 3014645.136: [CMS: 766173K->766173K(1433600K), 2.9704160 secs] 776581K->766173K(2170880K), [CMS Perm : 168960K->168960K(524288K)], 2.9706910 secs] [Times: user=2.98 sys=0.00, real=2.97 secs]

eclipse tomcat: preparing launch delegate

重启了一下eclipse, tomcat 就每次停在那里不动了, 看detail, 就说 preparing launch delegate.

google 一下, 有不同动解决办法, 我这边是 找到那个进程在占用我tomcat 要用的 8080 端口, 然后kill 掉就好了

_$ lsof -i:8080
java 26417 tian 45u IPv6 0x92e167181899ff9 0t0 TCP *:http-alt (LISTEN)

_$ kill -9 26417

之前一直报 端口被占用的, 很直观, 这次直接停在那里了.

后来发现 这样还不行, “preparing launch delegate” 这句一直不动的意思是: “我被什么block 了, 不能继续“. 最后发现我的问题是这样的:
我在debug那个jar 包的代码在call System.gc(), 所以在这个gc()方法上设置了断点, 当我让tomcat 以debug 模式启动时, 它在早起就可能 call 了 System.gc(), 这个时候就被 pause了, 直接不打任何log, 就停在那里了. 如果我直接不debug启动, 而是正常启动, 是可以的. 另外我把这个断点去掉, 也能正常启动.

所以 一定是什么block tomcat 了.

eclipse 绝对是浪费生命最多的地方.

Java Mission Control (JMC) and Java Flight Recorder (JFR)

JMC 是个图形化工具, 监控JVM 以及操作系统的一些指标; 它可以直接连JVM 去采集, 也可以读存档的JFR文件;
JFR 对于java应用程序进行诊断和profiling的工具, 它集成在HotSpot JVM 里面, 并且有很小的性能影响. 基于一组事件去采集,记录;可以通过JMC图形化界面控制JFR 事件,也可以通过jcmd 命令行来执行;

  1. 默认开启的JFR事件有小于1%的性能影响;

  2. 事件类型: 内存, 线程, I/O, code (编译, hot package, hot class),系统;
    -- 基于事件长度的event, 记录事件长度, 你可以设置超过每个长度的才记录;
    -- 瞬时事件
    -- 采样(sampling) 事件, 你可以配置采样频率;

  3. 每个事件有 事件名称, 时间戳, 和payload 组成;

  4. 通过各种纬度的事件, 你可以构建运行时系统状态;

  5. 数据流: JFR collects data from the JVM (through internal APIs) and from the Java application (through the JFR APIs). This data is stored in small thread-local buffers that are flushed to a global in-memory buffer. Data in the global in-memory buffer is then written to disk.

  6. JFR 架构
    -- JFR 运行时引擎, 它产生事件到buffer,并可选持久化到磁盘
    -- JFR plugin 在JMC里面分析JFR事件

  7. JFR 默认是disabled, 并且在HotSpot JVM里面是商业软件, 所以要在启动时通过2个flags 来启动它
    -- java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder MyApp
    -- java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:FlightRecorderOptions=defaultrecording=true MyApp
    -- for OpenJDK, 要使用JDK 11

  8. 启动app时, 可以通过 -XX:+FlightRecorderOptions=string 来设置参数, 多个参数用逗号隔开, 可选参数如下

    name=name
    The name used to identify the recording.
    defaultrecording=<true|false>
    Whether to start the recording initially. The default value is false; for reactive analysis, this should be set to true.
    settings=path
    Name of the file containing the JFR settings (see next section).
    delay=time
    The amount of time (e.g., 30s, 1h) before the recording should start.
    duration=time
    The amount of time to make the recording.
    filename=path
    Name of the file to write the recording to.
    compress=<true|false>
    Whether to compress (with gzip) the recording; the default is false.
    maxage=time
    Maximum time to keep recorded data in the circular buffer.
    maxsize=size
    Maximum size (e.g., 1024K, 1M) of the recording’s circular buffer.

  9. 上面参数可以在启动时传入, 但是更灵活的方式是, 通过jcmd 运行时传入, 如:
    jcmd process_id JFR.start [options_list] //start 时候可以选择模版 settings=
    jcmd process_id JFR.dump [options_list] //dump 一个连续的recording
    jcmd process_id JFR.check [verbose] //check 当前的recording
    jcmd process_id JFR.stop [options_list]

  10. 选取JFR 事件
    JFR 事件是基于模版的, HotSpot 默认有2个模版: default template & a profile template;
    这些event template 都是xml 文件, 2个默认的模版在$JAVA_HOME/ jre/lib/jfr 目录, 用户自定义的在 $USER_HOME/.jmc/ 目录;

Node.js heap analysis 内存分析

  1. 这个模块可以帮忙做heap dump: https://github.com/bnoordhuis/node-heapdump
  2. 参考文章列表:
    http://jayconrod.com/posts/55/a-tour-of-v8-garbage-collection
    https://blog.risingstack.com/finding-a-memory-leak-in-node-js/