Java禁止直接创建泛型数组,根本原因是类型擦除使JVM运行时无法获知T的具体类型,而数组需在运行时检查元素类型以保证安全,二者冲突。

Java 中无法直接创建泛型数组(如 T[] array = new T[10]),这是由类型擦除和 JVM 运行时类型安全机制共同决定的,与 Card Table 或 GC 标记阶段没有直接关系。
泛型数组的本质限制
泛型在编译期被擦除,JVM 在运行时不知道 T 的具体类型。而数组是协变的(String[] 是 Object[] 的子类型),且会在运行时检查元素赋值类型(通过 ArrayStoreException)。若允许 new T[10],就可能在运行时存入错误类型对象,破坏类型安全。
- 编译器禁止
new T[n],但允许绕过(如(T[]) new Object[n]),此时类型检查失效,需开发者自行保证安全 - 这种强制转型生成的是原始类型数组(如
Object[]),其 GC 行为与普通对象数组完全一致 - 数组对象本身存在于堆中,其 GC 生命周期取决于是否可达,与泛型无关
Card Table 与跨代引用的真实作用对象
Card Table 是 CMS、G1 等垃圾收集器用于记录老年代对年轻代的引用(即“跨代引用”)的辅助数据结构。它只关心对象字段级引用关系,不区分数组是否含泛型。
- 一个
Object[]存在老年代,其中某个元素引用了年轻代对象 → 对应 card 被标记为 dirty - 泛型擦除后,
List<String>[]实际是List[],底层仍是Object[],GC 视为普通对象数组处理 - Card Table 不感知泛型信息,也不扫描数组元素类型 —— 它只扫描对象字段(包括数组的每个 slot)是否有指向年轻代的引用
GC 标记阶段如何处理数组引用
在 Young GC(如 ParNew、G1 Young GC)中,标记从 GC Roots 出发,会递归扫描所有可达对象。对于数组:
立即学习“Java免费学习笔记(深入)”;
- 数组对象本身被标记(如果可达)
- 数组每个非 null 元素被当作引用字段处理:若该元素指向年轻代对象,则该元素所在 card 被标记(dirty);若指向老年代,则不影响 young gc 的扫描范围
- 泛型类型参数(如
T)已在字节码中擦除,JVM 不会基于泛型做任何特殊标记或过滤
实际开发中的关键点
不必担心泛型数组对 GC 或 Card Table 的影响 —— 它们不存在特殊处理逻辑。真正需要注意的是:
- 避免使用
(T[]) new Object[n]导致运行时类型错误,这属于程序逻辑问题,不是 GC 问题 - 大数组(无论是否泛型)长期存活会进入老年代,可能增加跨代引用数量,间接影响 Card Table 大小和扫描开销
- 若数组持有大量年轻代对象引用,会导致对应 card 频繁标记,但这是引用关系本身的体现,与泛型无关


















