如何排查线上问题
Created|Updated|Java
|Word Count:30|Reading Time:1mins|Post Views:
cpu 偏高问题排查
数据库问题排查
Author: magicliang
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
Related Articles

2019-09-02
Java 服务 OOM 排查全链路(Linux 内核 + JVM 工具链 + 问题分析)
Java 进程的 OOM 跨两层:Linux 内核的 OOM Killer 在物理内存或 cgroup limit 触及时 kill 进程,事后只能从 dmesg 还原;JVM 的 OutOfMemoryError 在堆/元空间/直接内存耗尽时抛异常,常带堆栈和 dump。两层工具栈不同:Linux 侧看 dmesg、cgroup、内核参数,JVM 侧看 jstat、jmap、jstack、arthas、JFR、gperftools。 一、Linux 层:OOM Killer 机制 Linux 内核有个 OOM (Out of Memory) Killer 的机制,可以在系统内存不足的时候,通过主动杀死一些进程来释放更多的内存空间。 很多时候,可以 ping 通一台服务器,但无法 ssh 上去,因为 sshd 被 OOM Killer 杀掉了。ping 能 ping 通,是因为处在内核态协议栈还能工作,发出回送报文。sshd 则因为是用户态进程,直接被干掉了。 OOM Killer 工作流程 OOM Killer 的完整工作流程如下: 关键机制 oom_score 每个进程都有...
2019-09-05
重述双亲委派模型
何时加载类 根据 Java 语言规范(JLS §12.4),类或接口在首次主动使用时才会被初始化。主动使用包括以下情况: 遇到 new、getstatic、putstatic、invokestatic 等字节码指令时。这些指令分别对应创建对象实例、读取或设置静态字段、调用静态方法。 对类进行反射调用时,如 Class.forName() 或 Method.invoke()。 初始化某个类的子类时,父类会先被初始化(但父类接口不会)。 虚拟机启动时会先加载设置的主类,即包含 main() 方法的类。 使用 java.lang.invoke 包的动态语言支持特性时,如 MethodHandle 调用。 需要注意的是,被动引用(如通过数组引用、常量引用、访问子类的静态字段等)不会触发类初始化。 从 Java 到 cpp 源码分析 双亲委派模型的工作流程 双亲委派模型的核心逻辑在 java.lang.ClassLoader.loadClass(String name, boolean resolve) 方法中: 123456789101112131415161718192021222...
2026-05-17
从 OSGi 到 Jigsaw:Java 模块化、SPI 与类加载器切换
写在前面 Java 的模块化不是从 JPMS 才开始的。很长一段时间里,classpath 像一条足够宽的路,所有 jar 都往上面放,能跑就行。等应用长大,库的版本开始冲突,内部包被外部代码依赖,插件想热更新,SPI 实现又被错误的类加载器发现,这条路才慢慢暴露出它没有边界的问题。 OSGi、JBoss Modules、Jigsaw(JPMS)都试图在 classpath 之上重新划边界,只是三者选择的边界不一样。OSGi 把运行时动态性放在第一位,JBoss Modules 更关心应用服务器内部的静态隔离,JPMS 则把强封装和可靠配置做进了 Java 平台本身。 类加载器切换是这条线上的另一面。Tomcat reload 之后类没卸掉、OSGi 升级 bundle 之后老对象 ClassCastException、JDBC Driver 把整个 webapp 钉在内存里、Spring Boot DevTools 重启后某个 SPI 实现加载了旧版本,这些现象表面上属于不同框架,根子都在 JVM 的类身份模型:一个类不只由全限定名决定,还由定义它的 ClassLoader 决...

2018-10-13
卡表和 RSet
卡表和 RSet 问题定义:为什么需要跨区域引用记录 JVM 垃圾收集器的核心工作之一是确定 live set——哪些对象仍然存活、不可回收。确定 live set 的标准做法是从 GC Roots(栈帧中的局部变量、静态字段等)出发,沿引用链遍历所有可达对象。 问题在于:当堆被划分为多个区域(代、Region)并且只回收其中一部分时,如何高效地找到从"不回收区域"指向"回收区域"的引用? 以 Young GC 为例:只回收新生代,但老年代中可能持有指向新生代对象的引用。如果不处理这些跨代引用,就会错误地回收仍被老年代引用的新生代对象。最朴素的做法是扫描整个老年代来找出这些引用——但老年代通常远大于新生代,这样做的代价过高,违背了分代收集"只回收一部分堆"的初衷。 卡表(Card Table)和 RSet(Remembered Set)正是为解决这个问题而设计的辅助数据结构。二者的关系并非互相替代,而是层次不同、协作互补:卡表是底层的脏标记机制,RSet 是建立在卡表之上的更高层索引结构。 核心概念速览 在深入细节之前,...
2022-01-12
Java中的条件编译
一个流传已久的传说 在中文 Java 圈子里有这样一段传说:Google(或某家硅谷大厂)有一种「Java 条件编译」技术,可以在同一份源码里写「测试开关打开时才走的代码」,非生产流水线编译时整段代码都在,可以照常调试;上了生产、关掉开关,被 if 包围的那段 Java 代码就会被编译器当成死代码,从字节码里直接抹掉,线上绝对跑不到。 这段传说混杂了三个独立的问题: Java 是否真的存在「if 块在生产编译时从字节码消失」这种机制? 这是 Google 独有的私货,还是标准 Java 的能力? 如果代码真的被编译器去除,那调试还能怎么做? 回答它们需要三种证据:JLS §14.21、§13.4.9、§15.28 的规范条文给出语言契约;一组可复现的 javap 字节码实验给出实证;feature switch 工业谱系(javac 死代码消除、BuildConfig.DEBUG + R8、Manifold 预处理器、AspectJ 类加载织入、OpenFeature 运行时开关、HotSpot C2 运行时死分支折叠)给出工程参照系。 文中代码示例基准 Java 8,字节...
2017-12-24
client 与 server
client 模式 默认的jit编译器,c1。 默认的gc:serial-serial old。 需要更短的启动时间和初始堆大小,能做更保守的优化。 默认-Xms是1M,-Xmx是64M。 适合 GUI 程序。 server 模式 默认的jit编译器,c2。 默认的gc:ps-serial old即 PS MarkSweep(可以启用parallel old)。 需要更长的启动时间和更大的堆大小,能够做更有深度的优化。 默认-Xms是128M,-Xmx是1024M。 适合长时间运转的程序。 64 位JVM# 在64位 JVM 上有个 -d64 的模式,实际上就是禁止client模式单独启动,只允许server模式或者混合编译模式启动的模式。
Contents


