最近在学习了JVM,感觉 JVM 需要记的特别多,理论性比较强,所以决定写一个 JVM 系列的博客,方便自己记录和学习,当然也欢迎大家评论,指出错误。
Java 与 C++
Java 与 C++ 之间有一堵内存动态分配和垃圾收集的高墙,外面的人想进去,里面的人想出来面,这话说的太对了,C++ 没有垃圾回收机制,需要开发人员手动去清除垃圾对象,如果垃圾处理不好,那么整个系统就会崩溃。而 Java 的虚拟机有垃圾回收机制,Java 开发人员不需要考虑垃圾回收的问题,然而一旦出现内存溢出,处理错误就会很困难,想要处理就必须要掌握 JVM 的知识。
虚拟机运行时区域
Java堆
对于大多数应用来说,Java堆是Java虚拟机所管理的内存中最大的一块,Java堆是所有线程共享的一块区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例和数组都在这里分配内存。Java堆是垃圾收集器主要的管理的区域,简称GC堆。线程共享的堆可能划分出多个线程私有的分配缓冲区(TLAB),Java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可,就像我们的磁盘空间一样。在实现时,既可以实现成固定大小的,也可以是可扩展的。
方法区
方法区与Java堆相同是线程共享的,主要用于存储已被虚拟机加载的类的信息,常量,静态变量和即时编译器编译后的代码等数据。
虚拟机栈
虚拟栈是线程私有的,它的生命周期和线程是一样的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
局部变量表:存放了编译期可知的各种基本类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型)和 returnAddress 类型(指向了一条字节码指令的地址)。
在Java虚拟机规范中,对这个区域规定了两种异常状况:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果虚拟机栈可以动态扩展(当前大部分的Java虚拟机都可动态扩展,只不过Java虚拟机规范中也允许固定长度的虚拟机栈),当扩展时无法申请到足够的内存时会抛出OutOfMemoryError异常,-Xss 指定每个线程的最大深度。
本地方法栈
区别于 Java 虚拟机栈的是,Java 虚拟机栈为虚拟机执行 Java 方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法(C语言实现的)服务。也会有 StackOverflowError 和 OutOfMemoryError 异常。
程序计数器
程序计数器是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器,是线程私有的。这个我感觉和PC寄存器很像,都是操控指令的,使程序按照正常的顺序执行。字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。
参考文章
- 《深入理解Java虚拟机》