浅析JVM内存分区

内存管理是开发者必须掌握的基本功,否则程序老是会在各类难以捉摸的错误中崩溃,一些语言,例如C、C++开发者们本身申请内存,使用完本身释放,可是不当的代码书写习惯每每致使内存泄露,引用空指针等等错误,而Java借助于虚拟机帮咱们完成了许多工做,使开发者从内存管理的深坑中爬出来了,可是因为隔着这层虚拟机,出现问题时的应对策略更显功力,须要对虚拟机内存管理机制的深刻了解。算法

这篇文章只是粗浅的介绍下虚拟机内存区域的大概分布,让初学者在脑海中有个大概印象,而印象的开始则借助于下面的一幅图:编程

Java内存区域分为大的两个区域,一部分是线程共享的,另外一部分则是每一个线程所独有的。刚开始了解编程时,大体就有印象,栈内存存在于方法体中,定义的那些变量什么的都是栈内存,方法结束就没了,堆内存则是使用new关键字申请出来的。固然这只是粗线的认识,下面一块块的说上图中的内存分布。数组

程序计数器

相似于CPU中的PC寄存器,用于存放下一条指令的地址,可是虚拟机不使用CPU的程序计数器,而是本身在内存里设立一片区域模拟CPU的程序计数器。改变计数器的值来选取下一条须要执行的字节码指令,包括分支、循环、跳转、异常、线程恢复等基础功能都依赖于计数器。数据结构

Java的每一个线程都有其独立的计数器,计数器之间互不影响,这样当多线程操做时,一个挂起的线程在恢复时,仍然可以从计数器中恢复以前运行到的地方,继续执行。因此说程序计数器也是线程隔离的。执行Java方法时,计数器中存放的是虚拟机字节码的地址,而运行Native方法时则是空(undefined)。该区域不会产生OutOfMemoryError。多线程

虚拟机栈

Java虚拟机栈也是线程私有的,它的生命周期等同于线程的生命周期。虚拟机栈描述的是Java方法执行时的内存模型。当方法执行时,会建立一个栈帧,用于存储方法执行期间所用到的数据结构,包含局部变量表,操做数栈,动态连接,方法出口等信息。post

当一个方法执行时,一个包含以上元素的栈帧入栈,当方法退出时,栈帧出栈。通常咱们都会知道内存区分为堆区和栈区,实际上也是个粗浅的分法,栈指的就是虚拟机栈,而其中最重要的部分就是局部变量表。学习

Java的垃圾收集器是不会去回收栈上的内容的,由于栈上的内容老是随着方法的结束自动释放。局部变量表包含着各类编译期已知的基本数据类型对象引用returnAddress。基本数据类型就是Java的8大基本数据类型(boolean,byte,char, short, int, float, long, double),对象引用,你能够把它当成指向实际对象地址的指针或者一个表明对象的句柄,returnAddress则是一条字节码指令的地址。当进入一个方法时,它所须要分配的空间在编译期就是已知的了。线程

在虚拟机栈中可能会报如下两种异常:3d

  • StackOverflowError: 线程请求的栈深度大于所容许的深度
  • OutOfMemoryError: 大多数虚拟机栈是能够动态扩展的,若是没法申请到足够内存,就会抛出。

本地方法栈

区别于虚拟机栈执行的是Java方法,本地方法栈则是虚拟机使用的Native方法服务,咱们在看一些库的源码时正常定位到最后就是用Native方法实现的,可是在虚拟机规范里对本地方法使用的语言,使用方式进行硬性规定,因此虚拟机能够任意实现它。HotSpot中本地方法栈和虚拟机栈是合在一块儿的。指针

至少从学习C语言时,咱们就据说malloc方法会在堆上分配空间。Java的堆上也是分配实例对象的。虚拟机规范上讲,基本全部的对象实例和数组都分配的堆上,例如上面栈上对象引用指向的对象,都是在堆上分配的。可是随着编译器技术的发展,全部对象都在堆上分配就不是那么纯粹了。

堆也是垃圾收集(GC)的主阵地。现代收集器基本都采用了分代收集算法,因此堆也能够划分为新生代和老年代,再细致点还有Eden空间,From Survivor空间和To Survivor空间。因为虚拟机实现了自动垃圾收集,因此在Java中,堆中new出的对象是不须要手动释放的。

咱们能够经过-Xmx-Xms控制堆的默认大小,若是在堆中再也申请不到内存,则会抛出OutOfMemoryError异常。

方法区

方法区也是各个线程间共享的区域,通常存储已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。通常不少人也把方法区称为永久代。其实仅仅是HotSpot团队把GC分代收集也扩展到方法区了,让垃圾收集器能够一块回收方法区的内存,可是其它的虚拟机实现没有这么作,也不存在永久代的,HotSpot自身也已经在JDK1.7上移除了永久代中的常量池。

相对而言,垃圾收集在方法区是不怎么出现的。这个区域主要回收的是常量池和类型的卸载,可是实际上对两者的回收发生的条件极为苛刻,不多发生收集。

运行时常量池是方法区的一部分,Class文件中包含常量池信息,用于存放编译期生成的各类字面量和符号引用,这部分在类加载后进入方法区的运行时常量池。这部分能够参考个人:深刻理解JVM类文件格式

相关文章
相关标签/搜索