程序计数器(PC)存储的是当前线程下一条待执行字节码指令的偏移量,而非源码行号、变量名或内存地址;该偏移量由字节码序号决定,随控制流动态跳转,不固定递增,且各线程独立保存。

程序计数器(PC)在 JVM 中存的不是源代码行号,也不是变量名或内存地址,而是当前线程下一条待执行的字节码指令的偏移量——这个偏移量就是 PC 的实际值。
字节码偏移量就是 PC 的具体表现形式
编译后的 class 文件里,每条字节码指令都带有一个序号,比如用 javap -c 反编译后看到:
- 0: aload_0
- 1: getfield #2
- 4: istore_1
这些数字(0、1、4)就是字节码在方法内从头开始计算的偏移量。JVM 执行时,PC 寄存器就保存其中某一个值,代表“接下来该取哪条指令”。它不关心这行指令干了什么,只管指向下一条要解释执行的字节码位置。
PC 不是固定递增,而是由控制流动态决定
顺序执行时,PC 通常会按当前指令长度自动推进;但遇到分支或跳转,它就直接被设为新的目标偏移量:
- if_icmpne 12:如果条件不成立,PC 跳到偏移量 12 处继续执行
- goto 5:无条件跳转,PC 立即变为 5
- return:方法结束,PC 恢复为调用点之后的偏移量(即返回地址)
也就是说,PC 的变化完全跟随字节码语义,而不是机械地 +1。指令长度不同(如 iconst_0 占 1 字节,ldc 可能占 3 字节),所以“自动递增”只是简化说法,真实推进量取决于当前指令。
偏移量 ≠ 行号,也不含调试信息
虽然偏移量看起来像“第几行”,但它和 Java 源码的行号没有一一对应关系。行号映射靠 class 文件里的 LineNumberTable 属性维护,PC 自身不携带该信息。调试器显示的断点位置,是 JVM 把 PC 值查表后换算出来的,并非 PC 本身存了行号。
native 方法没有字节码,所以此时 PC 值为 undefined,不会记录任何偏移量。
多线程中每个 PC 都独立保存偏移量
线程切换时,操作系统或 JVM 会把当前线程的 PC 值(即那个偏移量)连同其他寄存器状态一起保存下来;等该线程再次调度时,再把这个偏移量恢复进执行引擎。这样就能确保它从中断处的**下一条字节码**继续执行,而不是重头来过或跳错位置。
这个偏移量很小,一般用一个 native pointer 就能装下,所以 PC 区域永远不会发生 OutOfMemoryError。
















