JVM 运行时数据区域之程序计数器
Java 与 C ++ 之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙里面的人想进去,墙外面的人却想进去
———–《深刻了解 java 虚拟机》
在用 C 编程的时候,咱们 malloc 一块空间总是须要手动进行 free,不然就会造成内存泄露即应用过的内存没被开释,造成内存不够用了;而在 Java 中,垃圾回收器会主动帮咱们回收在后盾回收那些不再“应用”的内存,所以在 Java 编程中不容易呈现内存泄露和溢出的问题。然而一旦呈现例如 java.lang.OutOfMemoryError:Java heap space 之类的溢出,如果你不理解 JVM 运行时数据区域,那么你就可能又要默默的关上浏览器,面向百度开始编程了,所以说学习 JVM 还是十分重要滴!
tip: 接下来会带大家一步一步学习 JVM 的相干内容,这一篇先带大家理解一下程序计数器,比拟好了解,全当热身。
运行时数据区域
先概览一下,不同的 JDK 因为优化的问题所以内存区域会有不同,然而整体的一个的构造还不变的。
Java 虚拟机在执行 Java 程序的时候会把它治理的内存区域划分为若干个不同的数据区域,这些区域各有各有的用处,创立以及销毁工夫。有些内存区域由所有线程共享,有些则线程公有;有些内存区域随着用户线程的启动和完结而建设和销毁。
加点料
在 java 线程中有俩类线程,用户线程和守护线程;用户线程就是在咱们创立线程时的默认的一类线程,它容许在前台,例如咱们执行 main 办法的时候,JVM 会启动一个 main 线程,这个 main 线程就是用户线程;而守护线程就是运行在后盾的线程(默默守护,我哭辽),它服务于用户线程,例如 GC 时的 GC 内存回收线程。JVM 与用户线程共存亡,只有有用户线程 JVM 就不会进行执行,当只剩下守护线程时,JVM 才会进行执行
热身之程序计数器
在介绍 java 的计数器前,我先来讲一下 CPU 中的程序计数器,这俩个东东给大家一起介绍感觉可有助于了解
CPU 中的程序计数器(PC)
CPU 中的 PC 是一个大小为一个字的存储设备(寄存器),在任何时候,PC 中存储的都是内存地址(是不是有点像指针?),而 CPU 就依据 PC 中的内存地址,到相应的内存取出指令而后执行并且在更新 PC 的值。在计算机通电后这个过程会始终一直的重复进行。计算机的外围也在于此。这个过程我会在字节码执行引擎那局部深刻去介绍一下。
JAVA 运行时数据区域程序计数器
在 CPU 中 PC 是一个物理设施,而 java 中 PC 则是一个一块比拟小的内存空间,它是以后线程字节码执行的行号指示器。在 java 的概念模型中,字节码解释器就是通过扭转这个计数器中的值来选取下一条执行的字节码指令的,它的程序控制流的指示器,分支,线程复原等性能都依赖于这个计数器。
咱们晓得多线程的实现是多个线程轮流占用 CPU 而实现的,而在线程切换的时候就须要保留以后线程的执行状态,这样在这个线程从新占用 CPU 的时候能力复原到之前的状态,而在 JVM 状态的保留是依赖于 PC 实现的,所以 PC 是线程所公有的内存区域,这个区域也是 java 运行时数据区域惟一不会产生 OOM 的区域
加点料一:
先给大家看一下字节码的样子,前面在具体介绍
源程序
public class Main {public static void main(String[] args) throws IOException {
int a=1;
int b=2;
System.out.println(a+b);
}
}
javac 编译后的局部字节码
{public static void main(java.lang.String[]) throws java.io.IOException;
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=3, args_size=1 // 栈深度最大为 3,3 个变量槽
0: iconst_1 // 常量 1 压入栈
1: istore_1 // 栈顶元素出栈存入变量槽 1
2: iconst_2 // 常量 2 压入栈
3: istore_2 // 栈顶元素出栈存入变量槽 2
4: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
// 调用静态方法 main
7: iload_1 // 将变量槽 1 中值压入栈
8: iload_2 // 将变量槽 2 中值压入栈
9: iadd // 从栈顶弹出俩个元素相加并且压入栈
10: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
// 调用虚办法
13: return // 返回
LineNumberTable:
line 18: 0
line 19: 2
line 20: 4
line 21: 13
LocalVariableTable:
Start Length Slot Name Signature
0 14 0 args [Ljava/lang/String;
2 12 1 a I
4 10 2 b I
Exceptions:
throws java.io.IOException
}
加点料二:
操作系统治理多过程:每 fork() 创立一个过程,OS 会为这个过程创立一个 PCB(Process Control Block)来记录这个过程的信息例如 PID 等,当产生过程切换的时候,OS 会在 PCB 保留过程执行的以后的信息,例如内存,寄存器应用的状况等,在复原过程的时候,再依据 PCB 复原状态
// 代码,ax,bx,cs 就是寄存器
//pCur 以后过程 PCB,pNew 下一个执行线程的 PCB
switch_to(pCur,pNew){
// 以后过程执行信息保留到 PCB 中
pCur.ax=CPU.ax;
pCur.bx=CPU.bx;
pCur.cs=CPU.cs;
.....
// 复原过程执行信息
CPU.ax=pNew.ax;
CPU.bx=pNew.bx;
.....
}
程序计数器的内容感觉有点少,为了不让大家感到干燥,所以加了点料 ,peace;
最初
我是不想当码农的小白,平时会写写一些技术博客,举荐优良的程序员博主给大家还有本人遇到的优良的 java 学习资源,心愿和大家一起提高,独特成长。
以上内容如有谬误,还望指出,感激
公众号点击交换,增加我的微信,一起交换编程呗!
公众号: 小白不想当码农