从硬件的角度来说,它是一个寄存器,当然在jvm中它是被虚拟化出来的一个寄存器,它的寄存功能是用来寄存当前线程所执行的字节码的行号。特性:占用内存较小,线程私有,不会抛OutOfMemoryError异常,存的是一个地址,不是整数值,但是可以理解成只需要暂存一个整数。其实程序计数器的功能与特性也就差不多这些,但有两个疑问,程序正常执行的时候为何要记住当前行?当前行的地址在哪里?
为何要记住当前行
程序计数器是一个线程私有的,一个线程在一个时间片内可能执行不完,例如A线程执行到第5行时,时间片用完了,或者被高优先级的线程插队,总而言之线程A不能往下执行了,也就是所谓的挂起,那么当线程A重新回到运行状态时,于情于理都应该从第5行开始往下执行,这里的第5行就存放在线程A私有的程序计数器中。总之,使用程序计数器是为了在线程挂起时,当再次回到运行状态时,能够保证线程恢复到原来的位置。
当前行的地址在哪里
在分析虚拟机栈时,当时有如下一段代码以及其对应的字节码:
源程序:
public class JVMTest {
public int add(){
int a = 3;
int b = 10;
int c = a+b;
return c;
}
public static void main(String[] args) {
JVMTest jvmTest = new JVMTest();
int result = jvmTest.add();
System.out.println("The result is : "+result);
}
}
字节码:
Compiled from "JVMTest.java"
public class com.research.JVMTest {
public com.research.JVMTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public int add();
Code:
0: iconst_3
1: istore_1
2: bipush 10
4: istore_2
5: iload_1
6: iload_2
7: iadd
8: istore_3
9: iload_3
10: ireturn
public static void main(java.lang.String[]);
Code:
0: new #2 // class com/research/JVMTest
3: dup
4: invokespecial #3 // Method "<init>":()V
7: astore_1
8: aload_1
9: invokevirtual #4 // Method add:()I
12: istore_2
13: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
16: iload_2
17: invokedynamic #6, 0 // InvokeDynamic #0:makeConcatWithConstants:(I)Ljava/lang/String;
22: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
25: return
}
通过字节码可以发现每一个指令前都有一个数字,这个数字就是当前指令的地址,例如main方法中iload_2指令,它的地址就是16,当程序执行到iload_2时,可以理解成程序计数器中存放的是16,当然真正存放的是一段地址。
个人觉得jvm中程序计数器理解到这应该差不多了,如果想进一步的了解寄存器推荐看一下汇编语言。
至于jvm其他部分的工作原理请参考如下博文:
由浅入深了解JVM-内存结构
由浅入深了解JVM-堆
由浅入深了解JVM-虚拟机栈