分类 默认分类 下的文章

Java 如何查看 Direct Memory 大小

遇到过几次 Java NIO 使用 DirectMemory 导致的 OOM, 在诊断的过程中, 其中一个问题就是如何定位当前使用的 Direct Memory 的大小.

在早期, 都是通过 Java NMT Native Memory Tracking 的工具, 首先启动的过程中添加 tracking 的参数, 然后启动后去重现, 然后使用 jcmd 命令去查看各种内存区域的使用情况.

当然, 也可做一个 heap dump, 通过 heap dump 中的 DirectByteBuffer 等实例的 capacity 来确定到底使用了多大的大小.

上次在 Java Performance 这本书的第二版看到, JVM 带的 MBean 就包含了一个 MBean, 它就能确定当前 JVM 使用 Direct Memory 的大小. 所以, 如果你在桌面环境, 可以通过 JConsole 或 JVisualVM 等工具去查看这个 MBean, 如那本书里的截图:
MBean.png

或者 google 一下 jmx command line, 能看到很多人写的命令工具, 去连接 JVM, 然后查看 MBean 状态的, 也能达到同样的目的.

我们公司的框架工具自动提供在页面查看各种 MBean 状态的功能, 非常好用. 直接能看到 DirectMemory 已经达到设置是上限.

为什么 CPU 不能到达100%

有时候为了让 CPU 贡献最大的能力, 我们会想尽办法让 CPU 在最高频上达到100%使用率, 可是无论怎么优化都达不到100%, 到底问题出在哪里?

  1. 各种 IO 拖慢了 CPU (waiting), 导致 CPU 在等取指令, 从网络到本地, 从磁盘加载到内存, 从内存加载到缓存, 从缓存到寄存器, 在内核态和用户态直接复制;
  2. 各种锁 线程/进程被 block;
  3. 线程数不够多;

使用 async-profiler 具体实践步骤

async-profiler 是一个对 Java 应用影响很小的 profiler 工具, 不仅能 sample Java 栈, 还能获取 perf event 的数据.

常见的基本步骤:

  1. 下载最新 async-profiler, 并且复制到远程目标机器

    scp ~/async-profiler-1.7.1-linux-x64.tar.gz  user1@server.tianxiaohui.com:/home/user1
    rsync ~/async-profiler-1.7.1-linux-x64.tar.gz  user1@server.tianxiaohui.com:/home/user1
  2. 解压, 并给运行 Java 应用的用户执行权限

    mkdir /tmp/profiler
    tar -xvf -C /tmp/profiler/  async-profiler-1.7.1-linux-x64.tar.gz
    sudo chown -R app1:app /tmp/profiler/
    sudo chmod -R 755 /tmp/profiler
  3. 执行 profiler

    /tmp/profiler/profiler.sh -e itimer -d 60 -o svg  -f c.log.html 74211
    /tmp/profiler/profiler.sh -e lock  -d 60 -o svg  -f f.log.html --reverse  74211 
    /tmp/profiler/profiler.sh -e com.ebay.configuration.console.CalServlet.service  -d 60 -o svg  -f f.log.html --reverse  74211
    /tmp/profiler/profiler.sh -d 30 -e itimer -o svg -t --reverse -f t.log.html 74211
  4. 展示

    sudo nc -v -4 -l 7070 < f.log.html
    sudo nc -v -l -p 7070 < f.log.html

    http://server.tianxiaohui.com:7070/

https://github.com/jvm-profiling-tools/async-profiler

关于 Connection Stacking 的思考

在 eBay, 对于线上应用的监控指标, 有个非常重要的报警数据就是 Connection Stacking alerts. 它是指某个应用的处理能力不足, 导致进来的连接(Connection)都堆积到应用前面的 Load Balancer 上. 其实监控的是 LB 上面的连接数是不是比平时多了很多, 如果多了很多, 那么就显示在这个报警仪表盘上.

什么情况下会发生 Connection Stacking? 通常有2种情况会发生 Connection Stacking: 1). 来访流量确实突然多了; 2). 进来流量还是跟往常一样, 该应用的处理能力变差了.

对于第一种情况, 也有2种细分: 一种是流量确实增多, 可是业务的平均处理时间几乎没啥变化, 或者还在合理范围内, 那么可以不去管它; 另外一种是流量的增加导致业务的平均处理时间变长, 应用的处理能力变弱, 这会导致各种预想不到的后果, 如用户在页面超时, 增加来自上游的 retry(retry storm), 或者导致该应用最终垮掉(当然这里会有各种自动扩容).

对于第二种情况, 在流量不变的情况下, 应用的处理能力变弱, 可能有各种因素. 不过我们还是大致可以分为三类. 1) 应用本身的问题导致处理变慢, 比如某些线程死锁, 导致正常处理业务的线程减少; 比如内存泄漏, 导致可用的内存减少, Java GC 时间增加; 比如缓存大量没命中; 比如业务根据用户的输入, 需要处理大量的正则表达式计算 等; 2) 依赖的其它下游其它应用服务导致本应用的变慢. 微服务的广泛使用, 一个中间层的应用通常要访问几个甚至十几个下游其它服务, 如果某个服务变慢, 同时设置的 timeout 时间或者 retry 次数不合理, 基本会导致从该服务到用户端的连锁变慢; 3) 依赖的数据库服务变慢. 数据库的种类不断增加, 企业应用使用的数据库的场景也逐渐增多. 数据库的 session 数量不够, 或者在处理大规模批处理, 或者数据库其它操作导致的锁, 都会导致访问数据库变慢.

如果要解决 Connection Stacking 问题, 就要分清当前是那种情况, 辨证施治. 对于上面提到的很多数据, 我们都有监控指标, 很容易的辨别出是那种问题, 比如: 进来请求的数据能分清是不是流量增加, 机器级别对于数据库访问的数据指标能分清是不是数据库变慢, 对于各种下游 service 调用的监控指标能反映是不是下游应用变慢;

今天要考虑的问题是针对上面第二种情况的第二种子情况: 即由下游应用变慢导致的本应用变慢. 如果我们不看各种基于微服务客户端的指标, 比如 Hystrix 暴露的指标, 有没有基于操作系统的数据能反映这种情况?

既然是下游调用, 基本是网络调用, 那么就是走网络连接, 绝大多数都是 TCP 不是 UDP, 另外一般的对于下游的服务, 我们都是使用443 或者 80 端口. 那么根据这些约束条件, 能找出某些基于 TCP 连接的数据, 能帮我诊断类似的问题吗?

在各种 Linux 的网络命令当中 ss 是一个比较好的反映网络连接的命令, 让我们看看下面这个 ss 命令的输出:

myhost:~$ ss -it '( dport = :443 or dport = :80 )'
State      Recv-Q Send-Q    Local Address:Port   Peer Address:Port                                                             
ESTAB      0      0         10.33.133.212:47314    10.33.119.204:http                                                       
     bbr wscale:8,2 rto:204 rtt:3.944/4.558 ato:40 mss:1460 cwnd:142 bytes_acked:1605128 bytes_received:1616022 segs_out:2857 segs_in:2977 send 420.5Mbps lastsnd:74840 lastrcv:74832 lastack:74832 pacing_rate 1946.1Mbps retrans:0/8 rcv_rtt:1903.35 rcv_space:96431
ESTAB      0      0         10.33.133.212:59648    10.166.224.89:https                                                          
     bbr wscale:8,7 rto:204 rtt:0.151/0.022 ato:40 mss:1460 cwnd:143 bytes_acked:832208 bytes_received:253799 segs_out:784 segs_in:610 send 11061.2Mbps lastsnd:32 lastrcv:28 lastack:28 pacing_rate 1437.4Mbps rcv_rtt:1924.95 rcv_space:37969

使用 ss 的 -i 选项, 可是使我们看到更多关于连接的详细信息, 这里我们关心的有:
lastsnd, lastrcv, lastack,
-- 未完待续