Arthas classloader + sc 实战:JVM 类加载与手动加载

📅 2026/7/30 9:46:34
Arthas classloader + sc 实战:JVM 类加载与手动加载
大家好我是程序员天天困。这是「Arthas 线上诊断实战」系列第 6 篇。上一篇 Profiler 火焰图 解决的是 CPU 热点定位这篇讲Arthas classloader和 sc——回到一个更基础但更让人懵的场景线上突然报ClassNotFoundException代码明明就在那儿依赖也引了JVM 偏偏说找不到。点个收藏我们直接上手。一、ClassNotFoundException 不是代码写错了是类没被加载线上最常见的类加载问题不是类不存在而是类没被 JVM 加载过。这种问题有一个非常典型的场景一个报表服务引入了工具类ReportUtils本地跑得好好的上预发环境突然报ClassNotFoundException。第一反应查 pom 依赖——在的反编译 jar——class 也在。折腾快一个小时才发现那个类从来没被加载过因为预发环境走的代码路径根本没触发到它。这就引出一个很多人没意识到的真相你写在项目里的类、maven 引进来的依赖不等于已经被 JVM 加载了。类是「用到才加载」的没用到的类可能永远躺在 jar 里不进内存。那问题就变成两个当前 JVM 到底加载了哪些类能不能手动让 JVM 加载某个类Arthas 给了两个命令分别对付这两件事sc 搜类classloader 管类加载器。二、类什么时候被加载不是写在项目里就一定加载类的加载是懒加载JVM 不会一启动就把所有 class 全塞进内存而是「用到才加载」。什么是「用到」JVM 规范里类的主动使用场景主要包括这几种主动使用场景举例new 实例化new ReportUtils()调用静态方法ReportUtils.format(...)访问静态字段ReportUtils.DEFAULT_FORMAT反射调用Class.forName(com.ttk.ReportUtils)子类被加载父类会先被加载main 方法所在的类启动时自动加载我用一个简单的示例场景说明。假设有个com.ttk.ReportUtils类里面只有一个静态代码块// 示例静态代码块在类被加载时执行packagecom.ttk;publicclassReportUtils{static{System.out.println(ReportUtils 被加载了);}}然后有个启动类Main// 示例Main 类引用了 ReportUtils 但没主动使用packagecom.ttk;publicclassMain{publicstaticvoidmain(String[]args){System.out.println(Hello World);// 注意这里没有 new ReportUtils()也没调它的静态方法}}跑一下输出只有Hello WorldReportUtils 被加载了这行不会出现。为什么因为Main只是和ReportUtils在同一个包里并没有主动使用它——JVM 没有理由加载它。相同的逻辑也出现在依赖上你用 maven 引了一个坐标jar 包里的所有类也不会全部加载只有被实际用到的才会进 JVM。这就解释了为什么ClassNotFoundException会在线上突然冒出来–代码在 jar 里但运行时恰好没走到那条加载路径。三、Arthas sc 命令搜出 JVM 里到底加载了哪些类scSearch Class是 Arthas 用来搜索已加载类的命令它能告诉你 JVM 里现在到底有哪些类。Arthas scSearch ClassArthas 命令按类名模糊匹配搜索 JVM 已加载的类支持查看类加载器、字段、方法等详细信息。你可以把它理解为「JVM 内存里的类文件搜索器」。前提还是先把 Arthas Attach 到目标 JVM前面几篇讲过java -jar arthas-boot.jar选进程进到[arthasPID]$。进来之后搜com.ttk包下的所有类sc com.ttk.*从输出结果能直观看到只有Main被加载了ReportUtils没出现。sc 支持模糊匹配*通配符搜包路径很方便。想看某个类的详细信息——是哪个 ClassLoader 加载的、有哪些字段方法——加-dsc-dcom.ttk.Main输出里有个关键字段叫classLoaderHash这是一串十六进制的 hash 值代表加载这个类的类加载器。记住这个 hash后面 classloader 手动加载类的时候要用。可能有人会问sc 和 jad 有什么区别jad 是把已加载的 class 反编译成源码给你看sc 是列出 JVM 里有哪些类被加载了看的是「有没有」而不是「源码长啥样」。排查类加载问题先用 sc 看类在不在再用 jad 看源码对不对。sc 的完整用法见 Arthas sc 官方文档支持-E正则匹配、-f查看字段、-m查看方法等日常排查sc 类名和sc -d 类名就够用了。四、Arthas classloader 命令查看当前所有类加载器想手动加载类得先搞清楚 JVM 里有哪些类加载器classloader 命令就是干这个的。Arthas classloaderArthas 命令查看 JVM 内所有 ClassLoader 实例、加载的类数量、层级关系并支持手动加载类。你可以把它理解为「JVM 类加载器的管理员」。类加载器是 JVM 加载类的执行者。JVM 里不止一个类加载器它们按双亲委派模型分层协作核心逻辑就一句话收到加载请求时先交给父加载器去加载父加载器搞不定才自己加载。这样保证java.lang.String永远由 Bootstrap 加载不会被你的自定义 ClassLoader 篡改。双亲委派模型JVM 类加载器的分层委派机制子加载器收到请求先委托父加载器加载父加载器加载不了才自己加载。你可以理解为「有事先请示上级上级处理不了再自己来」。用 classloader 命令查看当前 JVM 有哪些类加载器classloader输出会列出每个 ClassLoader 的 hash、类型和已加载的类数量。加-t能看层级关系classloader-t树形输出里能直观看到层级关系以 JDK 11 为例BootstrapClassLoader在最顶层C 实现没有父加载器。PlatformClassLoaderJDK 8 叫ExtClassLoaderJDK 9 改名挂在 Bootstrap 下。AppClassLoader挂在 PlatformClassLoader 下负责加载你 classpath 下的类。注意因为 Arthas attach 到了这个 JVM所以输出里还会多一个ArthasClassloader和 AppClassLoader 平级挂在 PlatformClassLoader 下 —— 这是 Arthas 用来加载自身命令类的。五、手动加载一个类classloader --load 实战前面铺了这么多现在到正题用 classloader --load 把一个没被加载的类手动塞进 JVM。为什么要手动加载排查类加载问题时有时需要验证「这个类能不能被加载」「加载后行为对不对」而不想为了触发加载去改业务代码、走一遍完整调用链。classloader --load 给你一条直通路径。操作分两步第 1 步拿到目标 ClassLoader 的 hash。用classloader命令列出所有类加载器找到你要用的那个通常是 AppClassLoader 或业务自定义的 ClassLoader记下它的 hash 值比如1dbd16a6。第 2 步用 --load 指定 hash 和类全名classloader-c1dbd16a6--loadcom.ttk.ReportUtils拆开看-c 1dbd16a6指定用哪个 ClassLoader 加载hash 来自上一步。--load com.ttk.ReportUtils要加载的类全限定名。执行成功后终端没有报错就是加载成功了。再用 sc 验证一下sc com.ttk.ReportUtils这次ReportUtils出现在输出里了——说明它已经被 JVM 加载进去了。有个细节要注意-c后面跟的 hash 必须是真实存在的 ClassLoader hash写错了会报classloader not found。这个 hash 每次启动 JVM 都可能不一样别想偷懒记死值。六、sc classloader 怎么和前面的命令搭配sc 和 classloader 解决的是「类在不在 JVM 里」的问题和前面几篇的命令是配合关系不是替代。六篇写下来整个排查链路已经比较完整了阶段优先命令你在解决什么入参 / 返回对不对watch结果对错走了哪条分支、哪层慢trace路径与耗时没请求也要看对象状态vmtool堆内实例已定位到方法体写错mc retransform在线换逻辑CPU 飙高找全局热点profiler性能瓶颈类没被加载、手动加载sc classloader类加载问题确认字节码是否已换jad反编译核对推荐排查顺序报 ClassNotFoundException 先用 sc 看类在不在 JVM 里 - 不在就用 classloader 查有哪些类加载器、拿到 hash - 用 classloader --load 手动加载 - sc 再验一次确认加载成功。这条链路走完类加载问题基本就闭环了。结语Arthas classloader 和 sc 解决的是工具箱里最基础但最容易被忽略的一环——「类到底加载没加载」。ClassNotFoundException不一定是类不存在很多时候是 JVM 没走到加载路径。sc 帮你查 JVM 里有哪些类classloader 帮你查有哪些类加载器、还能手动把类塞进去两者配合类加载问题基本不用再靠猜。下一篇我会写这个系列的ognlArthas 里最灵活也最容易上头的表达式引擎一行命令在运行时调任意方法、访问私有字段。本文命令参数以 Arthas sc 官方文档 与 Arthas classloader 官方文档 为准版本更新了以官网为准。我是程序员天天困持续分享编程干货。觉得有用的话记得点赞收藏和关注~也欢迎在评论区聊聊你有没有在线上踩过 ClassNotFoundException 的坑用过 Arthas classloader 手动加载吗