《深入理解Java虚拟机》学习笔记-- Java内存区域

时间:2023-01-02 14:19:39

Java运行时的内存区域主要有5个区域,
线程共享的数据区 : 方法区、Heap
线程私有的区域 : 虚拟机栈(VM Stack)、本地方法栈(Native method stack)、程序计数器 。

在理解JVM内存区域时,最好结合多线程的场景去理解,才能更好地明白为什么上述区域有些是线程私有的,有些是线程共享的。

程序计数器
存放每个线程当前执行的字节码行号的指示器,可以理解为存放了当前线程执行到那条指令,我们知道多线程是通过时间切片来实现的,即按一定的时间间隔依次执行不同线程的代码,只不过这个时间间隔很短,所以我们可以近似看成是并行执行的,那么在线程切换时,我们需要知道这个线程的代码是执行到哪里的,因此,就不难理解,为什么程序计数器是线程私有的,它需要为每个线程存储各自的字节码。

虚拟机栈:
VM stack 也是线程私有的,其生命周期和线程相同,虚拟机栈是Java方法执行的的内存模型,每个方法在执行时会创建一个栈帧(stack frame),存放了局部变量表,局部变量表用于存放基本的数据类型,对象的引用reference。每个方法从调用到执行完,就是一个栈帧的入栈出栈的过程。
每个局部变量表所需的空间在编译期间就已经完成了分配,通常是固定大小的。如果线程请求的栈深度大于虚拟机允许的深度则会导致*Error(SOF),可能的情形是方法被大量调用,但又未及时结束,这种场景就有可能产生SOF异常。如果VMstack 是可以动态扩展的,当扩展时,如果没有足够的内存的分配了,就
会报OOM(OutOfMemoryError)异常。
从多线程的场景来看,启动每个线程时,都会为线程分配一个VM stack ,用于存放这个线程方法的局部变量,由于分配给VM stack 的内存是有限的,所以很显然,VM stack在配置时,不是越大越好,如果单个线程的stack 配置多了,那么对应可用的线程数就会减少。所以在配置时,不能忽略这个部分。

本地方法栈 Native method stack
本地方法栈其实和虚拟机栈的作用是类似的,只不过虚拟机栈是为执行Java方法服务的,本地方法栈是为了执行JVM自身的native方法服务的

方法区
主要用于存放类定义(Class文件),常量池等
堆(Heap) 存放对象实例

GC堆 Heap
Heap 所有线程共享的,它的唯一目的就是存放对象的实例,几乎所有的对象都在这里分配内存。Heap 常被人称为GC heap就是因为Heap是垃圾回收中管理的主要区域(本人这么理解,对于上面提到的程序计数器,它占用了很小的内存区域,其实回收的意义不大,而java栈 是在编译期间就确定了对应的内存分配大小,而且是线程相关的,所以回收的操作性也不强,而Heap占用内存最大,存放的是线程无关的内容,所以是垃圾回收

方法区
方法区和java堆一样,是线程共享的内存区域,其存放的是类的信息、常量、静态变量、即时编译器编译后的代码。

  • 运行时常量池
    运行时常量池是方法区的一部分,存放的是编译期生成的各种字面量和符号引用。 运行时常量池不是只有编译期才能产生,在运行期也可以将新的常量放入池中,例如String类的intern方法(主要应用场景是哪?http://blog.csdn.net/u012450329/article/details/52698621 看了一篇文章,讲解String.intern方法的,回过头来想,是不是为了减少重复的字面量占用过多的内存,所以通过留了这个机制,让开发者可以在运行时往常量池添加字面量?)

直接内存
(待学习)