JVM内存区域

来源:互联网 发布:流体计算软件 编辑:程序博客网 时间:2024/06/04 19:56

Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域,这些数据区域有各自的用途,以及创建的时间,有的区域随着虚拟机进程的启动而存在,有些区域则依赖用户线程的启动和结束而建立和销毁。
分为以下几个区域
1.程序计数器
程序计数器是一块比较小的内存空间,可以看成当前线程所执行的字节码的行号指示器,由于Java虚拟机的多线程是通过轮流切换并分配处理器执行时间的方式来实现的,因此,为了线程切换能恢复到正确的执行位置,每个线程都需要有一个独立的程序计数器,各线程之间计数器互不影响,所以是“线程私有的
如果线程在执行native方法,这个计数器的值则为空(Undefined)。此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域

2.Java虚拟机栈
同样是线程私有的,生命周期与线程相同,虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接等信息。每一个方法从调用到执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈和出栈的过程。
此区域有两种异常状况:如果线程请求的栈深度大于虚拟机允许的深度,将抛出StackOverflowError异常,如果虚拟机可以扩展,但是扩展是无法申请到足够的内存,将抛出OutOfMemoryError异常

3.本地方法栈
与Java虚拟机栈类似,区别是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务。有的虚拟机将虚拟机栈和本地方法栈合二为一。

4.Java堆
Java虚拟机管理的内存中最大的一块,线程共享,在虚拟机启动时创建。存放对象实例。
垃圾收集器管理的主要区域,也被称为”GC堆”(Grabage Collection Heap)。现在收集器基本采用分代手机算法,所以Java堆中可细分为新生代和老年代,再细致一点有Eden空间、From Survivor空间、To Survivor空间等。
4.1新生代
新生代分为三个区域,一个Eden区和两个Survivor区,它们之间的比例为(8:1:1),这个比例也是可以修改的。通常情况下,对象主要分配在新生代的Eden区上,少数情况下也可能会直接分配在老年代中。Java虚拟机每次使用新生代中的Eden和其中一块Survivor(From),在经过一次Minor GC后,将Eden和Survivor中还存活的对象一次性地复制到另一块Survivor空间上(这里使用的复制算法进行GC),最后清理掉Eden和刚才用过的Survivor(From)空间。将此时在Survivor空间存活下来的对象的年龄设置为1,以后这些对象每在Survivor区熬过一次GC,它们的年龄就加1,当对象年龄达到某个年龄(默认值为15)时,就会把它们移到老年代中。
在新生代中进行GC时,有可能遇到另外一块Survivor空间没有足够空间存放上一次新生代收集下来的存活对象,这些对象将直接通过分配担保机制进入老年代;
PS:
Minor GC:新生代GC,指发生在新生代的垃圾收集动作,因为Java对象大多具备朝生夕灭的特性,所以Minor GC非常频繁,一般回收速度也比较快
老年代GC(Major GC/Full GC):指发声在老年代的GC,出现了Full GC,经常会伴随至少一次的Minor GC(但非绝对,不同垃圾收集器不同)。Full GC的速度通常会比Minor GC慢10倍以上。
垃圾收集算法:
标记-清除算法:最基础的收集算法,标记所有需要回收的对象,在标记完成后统一回收所有被标记的对象,标记判断过程主要有引用计数法(无法解决对象之间相互循环引用)和可达性分析(主流),后续的算法都是基于标记-清除算法的这种思路并对其不足进行改进而得到的。此算法的不足有两个:一是效率问题,标记和清除两个过程的效率都不高,另一个是空间问题,标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致在程序运行过程中需要分配较大对象时,无法找到足够的连续的内存而不得不提前触发另一场垃圾收集动作。
复制算法:将可用内存划分一个Eden区和两个Survivor区,它们之间的比例为(8:1:1),每次只使用Eden和一个Survivor,当这一块内存用完了,就将还存活的对象复制到另外一块Survivor,然后把已使用的内存空间一次清理掉,当Survivor空间不够用时,需要依赖其他内存(这里指老年代)进行分配担保
标记-整理算法:标记,然后让所有存活对象向一端移动,清理掉端边界以外的内存。
分代收集算法:在新生代中,每次垃圾收集都发现有大批对象死去,只有少量存活,选用复制算法,老年代中对象存活率高,使用标记-*算法。
4.2老年代
老年代里面存放都是生命周期长的对象,对于一些较大的对象(即需要分配一块较大的连续内存空间),是直接存入老年代的,还有很多从新生代的Survivor区域中熬过来的对象。
老年代中使用的是Full GC,Full GC所采用的是标记-清除算法。老年代中的Full GC不像Minor GC操作那么频繁,并且进行一次Full GC所需要的时间要比Minor GC的时间长。

5.方法区
线程共享的区域,存储被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

6.运行时常量池
运行时常量池(Runtime Constant Pool)是方法区的一部分,用于存放编译器生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。

原创粉丝点击