深入Java虚拟机第一篇(内存管理)
一、java虚拟机的内存划分
java虚拟机在执行java程序时会对所管理的内存划分为若干个不同数据的区域,这些区域会根据不同的用途创建和销毁,有的区域会随着虚拟机的启动而存在,有的区域会随着用户线程的开启和结束去创建和销毁。

1、程序计数器:是一块较小的内存空间,可以看作是当前线程执行的字节码文件所在的行号标识。字节码解释器的工作就是通过改变程序计数器的值来选择下一条需要执行的字节码指令。分支、循环、异常捕捉、线程恢复都需要依赖程序计数器来完成。
Java虚拟机的多线程是基于线程轮流切换和调度执行时间来完成的,在任一处理时间,一个人处理器(多核处理器也只有一个人内核)只能执行一条线程中的指令。线程切换后要恢复到正确的执行位置,每个线程都需要有一个独立的程序计数器,各线程之间的计数器不相互影响,独立存储,这块区域称为“线程私有”的内存
如果java虚拟机执行的是一个java方法,程序计数器记录的就是虚拟机字节码指令的地址,如果是一个native方法,计数器的值为空(undefined)。这是java内存中唯一没有OutOfmemoryError的区域。
2、虚拟机栈:跟程序计数器一样,也是线程私有的内存区域。生命周期跟线程一样。虚拟机栈描述的是java方法执行的内存模型:每个方法创建的时候都会创建一个栈帧用来保存方法的局部变量、操作栈、动态链接、出口信息等数据。,每个方法从开始直至执行完成对应着虚拟机的一个栈帧在虚拟机栈入栈出栈的过程。我们通常关注的“堆”和“栈”,其中栈就是指的虚拟机栈,也称为局部变量表。
局部变量表存放了编译时期可知的基本数据类型、引用类型(指向对象起始位置的指针),局部变量表的存储空间在编译期分配,当进入一个方法时他的局部变量表在帧中内存空间大小完成是确定的,在运行期间不可改变。在java虚拟机的规范中会出现两种异常:当线程请求的栈深度超过虚拟机所允许的深度则会报StackOverflowError;虚拟机栈也可以动态拓展,当拓展时无法申请到足够的内存就会报OutOfmemoryError。
3、本地方法栈:本地方法栈跟虚拟机方法栈的作用是一样的,一个是为虚拟机执行java方法(字节码)服务,一个是为虚拟机执行native方法服务(native方法每个虚拟机都有自己实现的方式,而Sun HotSpot则将本地方法栈和虚拟机栈合二为一)
4、java堆:java堆是java虚拟机内最大的一块内存区域,所有线程共享的一块区域。存放所有的对象实例和数组,也是垃圾回收的主要区域,也称为“GC堆”,现在的收集器主要采用的分代收集算法(新生代和老年代),目的都是为更好的回收内存和更快的分配内存,java堆在物理上可以是不连续的内存空间,只要逻辑上连续即可。如果在堆中没有足够空间分配给实例,并且也无法拓展内存空间就会报OutOfmemoryError
5、方法区:跟java堆一样也是被所有线程共享的。主要用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等信息,这个区域的内存回收主要针对常量池回收和对类型的卸载。
运行时常量池:存放的是编译期生成的各种字面量和符号,这部分在类加载后存放到方法区的运行时常量池中,运行期间也能将新的常量放入池中(不如String的intern方法)

浙公网安备 33010602011771号