Where are generic types stored in java class files?

They are stored in Signature attributes; see section 4.8.8 of the updated Java Virtual Machine Specification, as well as section 4.4.4 for the format of the field type signature.

Here's an example using javap -verbose java.util.Map:

public interface java.util.Map
  SourceFile: "Map.java"
  Signature: length = 0x2
   00 1E 
  [other attributes omitted]

The Signature attribute here specifies (if you read this as big-endian, like all integer quantities in the JVM class file format are) constant pool value #30 (30 = 0x1E). So let's have a look there:

const #30 = Asciz       <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/lang/Object;;

Read this in the context of the grammar specified in 4.4.4. So, this uses two type parameters, K extends java.lang.Object and V extends java.lang.Object. The type itself (Map) also extends class java.lang.Object, and no interfaces.


Java generics are indeed implemented by type erasure, so there is no type information in the bytecode.

For example, let's take a look two classes which declare a List field, one in generic and the other in non-generic form:

class NonGeneric {
    List list;
}

And,

class Generic {
    List<String> list;
}

In both cases, the resulting bytecode is the following:

  Code:
   Stack=3, Locals=1, Args_size=1
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   aload_0
   5:   new #2; //class java/util/ArrayList
   8:   dup
   9:   invokespecial   #3; //Method java/util/ArrayList."<init>":()V
   12:  putfield    #4; //Field list:Ljava/util/List;
   15:  return

There is no reference to the String type used in the ArrayList nor List. So, we can see that generics is indeed implemented by type erasure.

However, if we look at the constant pool, we can find a difference.

The non-generic constant pool:

  Constant pool:
const #1 = Method   #6.#15; //  java/lang/Object."<init>":()V
const #2 = class    #16;    //  java/util/ArrayList
const #3 = Method   #2.#15; //  java/util/ArrayList."<init>":()V
const #4 = Field    #5.#17; //  NonGeneric.list:Ljava/util/List;
const #5 = class    #18;    //  NonGeneric
const #6 = class    #19;    //  java/lang/Object
const #7 = Asciz    list;
const #8 = Asciz    Ljava/util/List;;
const #9 = Asciz    <init>;
const #10 = Asciz   ()V;
// snip the rest //

The generic constant pool:

  Constant pool:
const #1 = Method   #6.#17; //  java/lang/Object."<init>":()V
const #2 = class    #18;    //  java/util/ArrayList
const #3 = Method   #2.#17; //  java/util/ArrayList."<init>":()V
const #4 = Field    #5.#19; //  Generic.list:Ljava/util/List;
const #5 = class    #20;    //  Generic
const #6 = class    #21;    //  java/lang/Object
const #7 = Asciz    list;
const #8 = Asciz    Ljava/util/List;;
const #9 = Asciz    Signature;
const #10 = Asciz   Ljava/util/List<Ljava/lang/String;>;;
const #11 = Asciz   <init>;
const #12 = Asciz   ()V;
// snip the rest//

As can be seen, in the Generic class, we can see there are two extra constants, #9 and #10, in the constant pool, which mentions that the List has the generic type of String.

(And incorporating new knowledge that I learned from Chris Jester-Young's answer)

Looking further at the disassembly of the class file, there is a reference to the constant #10 right before the Code: block of the Generic class:

java.util.List list;
  Signature: length = 0x2
   00 0A 

The hexadecimal value 0A is 10 in decimal, which refers to the constant pool #10:

const #10 = Asciz   Ljava/util/List<Ljava/lang/String;>;;

Therefore, the information from the constant pool is used in order to indicate that a field is of a generic type.