JVM 技术详解:面临复杂问题时的几个高级工具

前面提到了很多 JVM 的分析工具,本节里我们会再介绍几种有用的工具,大家可以在需要的时候按需使用。

OOM Killer

在前面的章节,我们简单提及过 Linux 系统上的 OOM Killer(Out Of Memory killer,OOM 终结者)。假如物理内存不足,Linux 会找出“一头比较壮的进程”来杀掉。

OOM Killer 参数调优

Java 的堆内存溢出(OOM),是指堆内存用满了,GC 没法回收导致分配不了新的对象。

而操作系统的内存溢出(OOM),则是指计算机所有的内存(物理内存 + 交换空间),都被使用满了。

这种情况下,默认配置会导致系统报警,并停止正常运行。当然,将 /proc/sys/vm/panic_on_oom 参数设置为 0 之后,则系统内核会在发生内存溢出时,自动调用 OOM Killer 功能,来杀掉最壮实的那头进程(Rogue Process,流氓进程),这样系统也许就可以继续运行了。

以下参数可以基于单个进程进行设置,以手工控制哪些进程可以被 OOM Killer 终结。这些参数位于 proc 文件系统中的 /proc/pid/ 目录下,其中 pid 是指进程的 ID。

  • oomadj:正常范围是 -16 到 15,用于计算一个进程的 OOM 评分(oomscore)。这个分值越高,该进程越有可能被 OOM Killer 给干掉。如果设置为 -17,则禁止 OOM Killer 杀死该进程。
  • proc 文件系统是虚拟文件系统,某个进程被杀掉,则 /proc/pid/ 目录也就被销毁了。

OOM Killer 参数调整示例

例如进程的 pid=12884,root 用户执行:

$ cat /proc/12884/oom_adj
0

# 查看最终得分
$ cat /proc/12884/oom_score
161

$ cat /proc/12884/oom_score_adj 
0

# 修改分值 ...
$ echo -17 > /proc/12884/oom_adj

$ cat /proc/12884/oom_adj
-17

$ cat /proc/12884/oom_score
0
# 查看分值修正值
$ cat /proc/12884/oom_score_adj 
-1000

# 修改分值
$ echo 15 > /proc/12884/oom_adj

$ cat /proc/12884/oom_adj
15

$ cat /proc/12884/oom_score
1160

$ cat /proc/12884/oom_score_adj 
1000

这样配置之后,就允许某个占用了最多资源的进程,在操作系统内存不足时,也不会杀掉他,而是先去杀别的进程。

案例

我们通过以下这个案例来展示 OOM Killer。

1. 问题描述

某个 Java 应用经常挂掉,原因疑似 Java 进程被杀死。

2. 配置信息

配置如下:

  • 服务器:阿里云 ECS
  • IP 地址:192.168.1.52
  • CPU:4 核,虚拟 CPU Intel Xeon E5-2650 2.60GHz
  • 物理内存:8GB

3. 可用内存

内存不足:4 个 Java 进程,2.1+1.7+1.7+1.3=6.8G,已占用绝大部分内存。

4. 查看日志

Linux 系统的 OOM Killer 日志:

sudo cat /var/log/messages | grep killer -A 2 -B 2

经排查发现,具有如下日志:

$ sudo cat /var/log/messages | grep killer -A 2 -B 2
May 21 09:55:01 web1 systemd: Started Session 500687 of user root.
May 21 09:55:02 web1 systemd: Starting Session 500687 of user root.
May 21 09:55:23 web1 kernel: java invoked oom-killer: gfp_mask=0x201da,order=0,oom_score_adj=0
May 21 09:55:24 web1 kernel: java cpuset=/ mems_allowed=0
May 21 09:55:24 web1 kernel: CPU: 3 PID: 25434 Comm: java Not tainted 3.10.0-514.6.2.el7.x86_64 #1
--
May 21 12:05:01 web1 systemd: Started Session 500843 of user root.
May 21 12:05:01 web1 systemd: Starting Session 500843 of user root.
May 21 12:05:22 web1 kernel: jstatd invoked oom-killer: gfp_mask=0x201da,order=0,oom_score_adj=0
May 21 12:05:22 web1 kernel: jstatd cpuset=/ mems_allowed=0
May 21 12:05:23 web1 kernel: CPU: 2 PID: 10467 Comm: jstatd Not tainted 3.10.0-514.6.2.el7.x86_64 #1

可以确定,确实是物理内存不足引起的。

注意:所有 Java 进程的 -Xmx 加起来,如果大于系统的剩余内存,就可能发生这种情况。

查询系统所有进程的 oom_score:

ps -eo pid,comm,pmem --sort -rss | awk '{"cat /proc/"$1"/oom_score" | getline oom; print $0"\t"oom}'

重要提示:

如果调整过某个进程的 oom_adj 配置,那么由该进程创建的所有进程,都会继承 oom_score 分值。例如,假设某个 sshd 进程受 OOM Killer 的保护,则所有的 SSH 会话也将受到保护。这样的配置,如果发生 OOM,有可能会影响 OOM Killer 拯救系统的功能。

我们现在设想一个场景,假如我们想要随时调试跟踪线上运行的系统,需要用什么样的工具呢?下面就介绍 2 款这样的工具。

BTrace 诊断分析工具

BTrace 是基于 Java 语言的一款动态追踪工具,可用于辅助问题诊断和分析。

BTrace 项目地址:

https://github.com/btraceio/btrace/

Wiki 页面 中有一些简单的介绍:

BTrace 基于 ASM、Java Attach API、Instruments 开发,提供很多注解。通过这些注解,可以通过 Java 代码来编写 BTrace 脚本进行只读监控,而无需深入了解 ASM 对字节码的操纵。

下面我们来实际操作一下。

BTrace 下载

找到 Release 页面,找到最新的压缩包下载:

下载完成后解压即可使用:

1613271.png

可以看到,bin 目录下是可执行文件,samples 目录下是脚本示例。

示例程序

我们先编写一个有入参有返回值的方法,示例如下:

package demo.jvm0209;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class RandomSample {
    public static void main(String[] args) throws Exception {
        //
        int count = 10000;
        int seed = 0;
        for (int i = 0; i < count; i++) {
            seed = randomHash(seed);
            TimeUnit.SECONDS.sleep(2);
        }
    }
    public static int randomHash(Integer seed) {
        String uuid = UUID.randomUUID().toString();
        int hashCode = uuid.hashCode();
        System.out.println("prev.seed=" + seed);
        return hashCode;
    }
}

这个示例程序很简单,循环很多次调用某个方法,使用其他程序也是一样的。

然后运行程序,可以看到控制台每隔一段时间就有一些输出:

prev.seed=1601831031
...

BTrace 提供了命令行工具,但使用起不如在 JVisualVM 中方便,下面通过 JVisualVM 中集成 BTrace 插件进行简单的演示。

JVisualVM 环境中使用 BTrace

安装 JVisualVM 插件的操作,我们在前面的章节《JDK 内置图形界面工具》中介绍过。

细心的同学可能已经发现,在安装 JVisualVM 的插件时,有一款插件叫做“BTrace Workbench”。安装这款插件之后,在对应的 JVM 实例上点右键,就可以进入 BTrace 的操作界面。

1. BTrace 插件安装

打开 VisualVM,选择菜单“工具–插件(G)”:

82699966.png

然后在插件安装界面中,找到“可用插件”:

82770532.png

勾选“BTrace Workbench”之后,点击“安装(I)”按钮。

如果插件不显示,请更新 JDK 到最新版。

82937996.png

按照引导和提示,继续安装即可。

82991766.png

接受协议,并点击安装。

83219940.png

等待安装完成:

83257210.png

点击“完成”按钮即可。

BTrace 插件使用

85267702.png

打开后默认的界面如下:

85419826.png

可以看到这是一个 Java 文件的样子。然后我们参考官方文档,加一些脚本进去。

BTrace 脚本示例

我们下载的 BTrace 项目中,samples 目录下有一些脚本示例。 参照这些示例,编写一个简单的 BTrace 脚本:

import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;

@BTrace
public class TracingScript {
    @OnMethod(
            clazz = "/demo.jvm0209.*/",
            method = "/.*/"
    )
    // 方法进入时
    public static void simple(
                @ProbeClassName String probeClass,
                @ProbeMethodName String probeMethod) {
        print("entered " + probeClass);
        println("." + probeMethod);
    }

 @OnMethod(clazz = "demo.jvm0209.RandomSample",
             method = "randomHash",
             location = @Location(Kind.RETURN)
    )
    // 方法返回时
 public static void onMethodReturn(
                @ProbeClassName String probeClass,
                @ProbeMethodName String probeMethod,
                @Duration long duration,
                @Return int returnValue) {
      print(probeClass + "." + probeMethod);
      print(Strings.strcat("(), duration=", duration+"ns;"));
      println(Strings.strcat(" return: ", ""+returnValue));
 }
}

执行结果

可以看到,输出了简单的执行结果:

还有 63% 的精彩内容
©著作权归作者所有,转载或内容合作请联系作者
支付 ¥4.99 继续阅读
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,525评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,203评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,862评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,728评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,743评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,590评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,330评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,244评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,693评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,885评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,001评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,723评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,343评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,919评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,042评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,191评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,955评论 2 355

推荐阅读更多精彩内容