隐藏类:查看版本 v1
由 朝武芳乃 于 2026-10-04 02:03 保存 · 2621 字节
''本条基于 Java 17(Minecraft 1.20.1 所用版本),HotSpot 行为以 JDK 17 为准。''
'''隐藏类'''(Hidden Class,JEP 371,Java 15 引入)是用 <code>MethodHandles.Lookup.defineHiddenClass</code> 定义的类,专供框架在运行时生成、且只被框架自己使用。Lambda 表达式生成的类(形如 <code>Foo$$Lambda$14/0x...</code>)和 LambdaForm 类都是隐藏类。
== 性质 ==
* '''不可按名字发现''':<code>Class.forName</code>、<code>ClassLoader.loadClass</code> 找不到它,其他类的常量池也无法按名字链接到它,只能通过 <code>defineHiddenClass</code> 返回的 <code>Class</code> 对象使用。
* 类名形如 <code>包名.类名/0x0000000800c01000</code>,斜杠后的后缀由 JVM 生成;<code>Class.isHidden()</code> 返回 true。
* 默认不被其定义类加载器强引用,不可达时可以单独卸载;传入 <code>ClassOption.STRONG</code> 则与类加载器同生命周期。
* 栈帧默认不出现在堆栈信息里。
* '''不可被 [[Instrumentation]] 修改''':HotSpot 的 <code>is_modifiable_class</code> 对隐藏类返回 false,所以 <code>isModifiableClass</code> 为 false,[[redefineClasses]] 和 [[retransformClasses]] 都不能用。
* '''定义时不触发 ClassFileLoadHook''':HotSpot 创建类时会跳过隐藏类的这一步,已注册的 transformer 看不到它的字节码。
== 用 Unsafe 去除标志 ==
隐藏状态只是 Klass 里的一个标志位,用 [[Unsafe]] 把它清掉,类就会被当作普通类,上面两个限制随之解除。思路与 [[klassptr]] 相同:先拿到 <code>Class</code> 对象对应的 Klass 指针,再按偏移读写其中的标志。
* JDK 17:标志是 <code>Klass::_access_flags</code> 中的 <code>JVM_ACC_IS_HIDDEN_CLASS</code>(0x04000000)。
* 较新的 JDK 已经把它挪到 <code>KlassFlags</code> 里,位置和位值都不同。
== 注意事项 ==
* '''依赖 JDK 内部布局''':Klass 的字段偏移、标志位置随 JDK 版本、构建和压缩类指针等变化,写死偏移很脆弱,一般要按版本从 HotSpot 导出的 VMStructs 信息里定位。
* 偏移或位写错会直接让 JVM 崩溃,而不是抛异常。
* 清除后只是让它“看起来像普通类”:<code>Class.isHidden()</code> 变为 false,但类名里仍带 <code>/0x...</code> 后缀,依赖该标志的其他行为(栈帧显示、卸载等)也会跟着改变。
* 能被 retransform / redefine 之后,仍受“只能改方法体、不能增删成员”的限制。
参见:[[Unsafe]]、[[klassptr]]、[[MethodHandle]]、[[Instrumentation]]
其他版本
- v1 · 2026-10-04 02:03 · 朝武芳乃 · 新建条目