一 java内存管理
java内存管理主要分为stack, Heap ,method area ,pc register,native method stack五部分.
如下图所示:
1.程序计数器
程序计数器是一块较小的的内存空间,它的作用可以看作是当前线程所执行的字节码的行号指示器。
如果线程正在执行的是一个 Java 方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是 Native 方法,这个计数器值则为空。 此内存区域是唯一一个在 Java 虚拟机规范中没有规定任何OutOfMemoryError情况的区域。
2.Java 虚拟机栈
线程私有,生命周期和线程一样。是描述java方法执行的内存模型。在每个方法执行时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机中入栈到出栈的过程。局部变量表包括了编译器可知的各种基本数据类型、对象引用和returnAddress类型(指向了一个字节码指令的地址)。其中64位长度的long和double类型的数据会占用2个局部变量空间,其余的数据类型只占用1个。局部变量表所占用的内存在编译器就完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。
栈内存的管理:通过压栈和弹栈操作来完成的,以栈帧为基本单位来管理程序的调用关系,每当有函数调用时,都会通过压栈方式创建新的栈帧,每当函数调用结束后都会通过弹栈的方式释放栈帧
3.Java 本地方法栈
与java虚拟机栈发挥的作用十分相似,区别是java虚拟机栈是为虚拟机执行java方法的,本地方法栈是为虚拟机执行native方法的。在本地方法栈中使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。甚至有的虚拟机(如sun hotspot)直接把本地方法栈和虚拟机栈合二为一。
4.堆
是java虚拟机所管理的内存中最大的一块。java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。所有的对象实例和数组都要在堆上分配。java堆是垃圾收集器管理的主要区域,因此很多时候也被称作GC堆。
从内存回收的角度来看,由于现在收集器基本都采用分代手机算法,所以java堆还可以细分为:新生代和老年代;再细致一点的有Eden空间、From Survivor空间、To Survivor空间等。
从内存分配的角度来看,线程共享的java堆中可能划分出多个线程私有的分配缓冲区。不论如何划分,都与存放内容无关,无论哪个区域,存储的都仍然是对象实例,进一步的划分目的是为了更好地回收内存和更快地分配内存。java堆可以处于物理上的不连续的内存空间中,只要逻辑上是连续的即可,就像我们的磁盘空间一样。在实现时,既可以实现成固定大小的,也可以是可扩展的,不过当前主流的虚拟机都是按照可扩展来实现的。如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。
每个Java程序都运行在一个单独的JVM实例上,每一个实例唯一对应一个堆,一个Java程序内的多个线程也就运行在同一个JVM实例上,因此这些线程之间会共享堆内存。
5.方法区
全局共享,作用是存储 Java 类的结构信息、常量、静态变量、即时编译器编译后的代码 JVMS 不要求该区域实现自动内存管理,但是商用 Java 虚拟机都能够自动管理该区域的内存 可能出现 OutOfMemoryError 异常.
字符串常量池在JDK6的时候还是存放在方法区(永久代)所以它会抛出OutOfMemoryError:Permanent Space;而JDK7后则将字符串常量池移到了Java堆中.
至于JDK8则是纯粹取消了方法区这个概念,取而代之的是”元空间(Metaspace)“