JVM内存结构及内存分配策略
JVM是按照运行时的数据存储结构来划分内存结构的,JVM在运行Java程序时,将他们划分成几种不同的数据格式,分别储存在不同的数据区,统一称为运行时数据。
第一部分 JVM内存结构
在Java虚拟机规范中,将Java运行时数据分为6种:
一.pc寄存器
它也可以叫做程序计数器,可以看作是当前线程所执行的字节码的行号指示器,保存当前线程执行的内存地址,为了每个线程不断切换后还能恢复到正确的位置,pc寄存器是线程私有的,各线程计数器互不影响。
它是Java虚拟机规范没有规定任何OutOfMemoryError的区域
二.Java栈
每一个线程的创建,JVM就会为这个线程创建对应的栈,所以栈也是线程私有的,我们不用担心它的数据一致性问题。Java栈又会有多个栈,每运行调用一个方法就会创建一个栈桢,栈桢用于存储局部变量表,操作数栈,方法出入口等信息,每一个方法从调用到完成的过程就表示一个栈桢在虚拟机中入栈和出栈的过程。
三.Java堆
Java堆是被线程共享的,所以对它的访问需要注意同步,方法和属性都需要注意一致性。
Java虚拟机规范说的很清楚:所有对象实例和数组都在堆上分配,后面JIT技术的发展带来的逃逸分析我们暂时不谈。堆是垃圾收集器管理的主要区域,堆也应该是我们程序员最应该关心的,它是和应用程序关系最密切的区域。
四.方法区
方法区也是被线程共享的,它主要用来保存常量,静态变量,加载的类信息以及即时编译后的代码,它也属于Java堆的一部分,但是它不会被gc收集器频繁的回收,但是他并不是我们常说的“永久代”,只是gc对它的回收比较宽松,但这方面的回收也是很有必要的。
五.运行时常量池
它是方法区的一部分,它用于保存编译器就确认的数字常量以及符号引用,当然对比Class文件中的常量池具有动态性,并非预置于Class文件的常量池中的内容才会进入运行时常量池,运行期间也可将常量放到里面。比较多的就是String的intern()方法。
六.本地方法栈
它的作用和Java栈作用非常相似,只是本地方法栈为执行native方法服务。
第二部分 Java内存分配策略
基于Java内存结构我们可以得知,Java分配内存主要基于栈和堆(堆包括方法区,方法区包括运行时常量池)。
一.Java栈内存的分配
前面知道我们创建一个线程就会有一个新的栈,当线程激活一个新的方法,JVM就会在线程的Java堆栈中压入一个栈桢,在方法运行期间,这个栈桢用来保存局部变量、参数、中间计算过程和其他数据,而栈中主要存放一些基本的变量数据类型和句柄引用,栈中的数据存取速度比堆快,仅次于pc寄存器,并且栈数据可以共享,但栈中的数据的大小和生存期必须是确定的,也就缺乏了灵活性。
二.Java堆内存的分配
对主要用来保存对象,对象的存储空间都是在堆上分配的,对象的引用却在栈上分配,堆分配的内存是实际上建立这个对象,栈中分配的内存只是指向这个内存中的指针(引用)。
堆在分配内存和销毁的时候都需要消耗时间,所以效率很低,但是堆的优点在于编译器不需要知道从堆中要分配多少空间,由于面向对象的多态性,多态变量只有在运行时创建了对象才能确定,所以堆分配内存是面向对象必不可少的。
总结来说,Java对用来储存对象,栈用来执行。而c/c++所有的方法都是调用栈来执行,栈指针自动指引数据的位置,这样自然很快,但是要在编译时就确定大小,自然缺少了相关的灵活性!