JVM 内存区域

1 运行时数据区域


2 数据区域描述 

    2.1 程序计数器。

         程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。字节码的解释器工作就是通过改变这个计数器的值来选取下一条需要执行的字节码     指令,分支,循环,跳转,异常处理,线程恢复等基础功能都是需要依赖这个计数器来完成。

         由于Java虚拟机的多线程是通过线程轮流切换并分配处理器的执行时间的方式来实现的,在任何一个确定的时刻,一个处理器都只会执行一 条线程中的指令。因此,为了线程     切换后能恢复到正确的执行位置,每条线程的计数器都是独立的。每个线程都有一个自己的计数器,各条线程的计数器之间互不影响,独立存储。故每个计数器都是属于线程私有     的。

    2.2  Java虚拟机栈 

          与程序计数器一样,Java虚拟机栈也是线程私有的,它的生命周期与线程相同,虚拟机栈描述的是Java方法执行的内存模型。Java虚拟机栈用于存储局部变量表操作数栈动态链接,方法出口等信息 。如果线程请求的栈深度大于虚拟机所允许的最大栈深度,将抛出StackOverflowError异常,当虚拟机栈扩展的时候无法申请到足够的内存,就会抛出OutOfMemoryError异常。

           2.2.1  局部变量表

                     局部变量表:存放了编译期可知的各种基本数据类型(boolean, byte, char, short, int, float, long, double),对象引用(引用的指针或者句柄),returnAddress类型(一个                方法返回的字节码指令);局部变量的容量单位是变量槽(Slot)为最小单位,一个32位的虚拟机的一个Slot可以存放一个32位以内的数据类型,当数据类型超过32位的时候,                会占用两个局部变量空间(Slot)。

            2.2.2 操作数栈

                   操作数栈:操作栈跟局部变量表一样,也是以一个字长为单位的数组,但是不一样的是,操作数据栈不是通过索引而是通过标准的栈操作:入栈和出栈来操作 来访问的。操                 作数栈示例如下:


操作数栈工作示意图

           2.2.3 动态链接

                    Class文件的常量池中存在有大量的符号引用,字节码中的方法调用指令就以指向常量池的引用作为参符号引用在类加载阶段(解析)的时候就转化为直接引用,这种转化为静态                   链接,部分符号引用在运行期间转化为直接引用,这种转化为动态链接。                                                                                                                                                                                         一句话概括就是在编译期就能够确定的引用是静态链接,在运行期才能确定的引用是动态链接。

            2.2.4  方法出口

                   当一个方法开始执行后,只有2种方式可以退出这个方法 :                                                                                                                                                                                                    1, 方法返回指令 : 执行引擎遇到一个方法返回的字节码指令,这时候有可能会有返回值传递给上层的方法调用者,这种退出方式称为正常完成出口。                                                        2, 异常退出 : 在方法执行过程中遇到了异常,并且没有处理这个异常,就会导致方法退出。                                                                                                                                                       无论采用任何退出方式,在方法退出之后,都需要返回到方法被调用的位置,程序才能继续执行,方法返回时可能需要在栈帧中保存一些信息。一般来说,方法正常退出                    时,调用者的PC计数器的值可以作为返回地址,栈帧中会保存这个计数器值。而方法异常退出时,返回地址是要通过异常处理器表来确定的,栈帧中一般不会保存这部分信                    息。

      2.3 本地方法栈

              本地方法栈和虚拟机栈所发挥的作用是非常相似的,它们之间的区别是虚拟机栈执行的是java方法, 本地方法栈执行的是native方法。两者都会有StackOverFlowError和                    OutOfMemoryError的情况发生。如果堆中没有完成对象实例的内存分配,并且堆内存无法继续扩展便会出现OutOfMemoryError。

      2.4  java堆

             java 堆是java虚拟机所管理的内存中最大的一块,java堆是被所有的线程共享的一块内存区域,在虚拟机启动的时候便会创建。堆中存放的就是对象实例,几乎所有的对象实              例都要在这里分配内存。

            java堆是垃圾收集器管理的主要区域,从内存回收的角度来看,java堆可以分为新生代和老年代,新生代又可以分为Eden, From Survivor, To Survivor等。java堆内存示意图如              下:                                                                                                      


java 堆内存示意图

       2.5 方法区  

                方法区和 java堆一样,是各个线程共享的内存区域, 它用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。当方法区无法满足内存分配需           求时,将抛出OutOfMemoryError异常。

3  垃圾收集器

    3.1 垃圾收集器之引用计数算法

            引用计数算法是给每个对象中添加一个引用计数器,每当有一个地方引用它的时候,计数器的值就会加1,当引用失效的时候计数器的值就会减1,任何时刻计数器为0的对象就是          不可能再被使用的。引用计数算法示例如下:                                                                                                                                                                                                                                   

引用计数算法示意图

            当对象c中的计数为0时,则该对象便视为“垃圾”,会被垃圾收集器回收。引用计数法实现简单,但是判定效率也很高,但是有个缺陷,就是循环引用问题。                                         如:a对象中引用了b对象,b对象引用了c对象,c对象又引用了a对象。同时a,b,c又不与外面其它对象引用。这种情况属于循环引用,此时a, b,c对象的计数永远都不为0,但是               又没有被使用,导致对象一直在堆中占用内存而无法被清除。

     3.2  垃圾收集器之可达性分析算法(根可达算法)

            可达性分析算法就是从 "GCRoot"  的对象做为起点,从这些节点开始向下搜索。搜索时所经过的路径成为引用链。当一个对象到 “GCRoot” 没有任何一个引用链的时候,则该对         象被成为根不可达。根不可达的对象便会被视为“垃圾”。根可达性算法示意图如下: 


根可达算法示意图

                图中,对象a,b,c便是引用计数算法中无法处理的那类对象,在根可达算法中,对象a,b,c,都没有可以找到GCRoot的引用链,那么对象a,b,c便被视为垃圾,会被垃圾收集器回          收。

      3.3  回收方法区

             方法区一般存放的是“永久代”数据,但是并不表示方法区的内存就一定不会被回收,方法区的垃圾回收的要求比较苛刻。需要满足一下三个条件:

              一:改类的所有实例都已经被回收,也就是说在堆中已经不存在该类的任何实例。

              二 :  加载该类的类加载器ClassLoad已经被回收。

              三 :  该类对应的Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

      3.4  垃圾收集

            3.4.1 垃圾收集之标记 - 清除算法   

                        标记 - 清除算法:该算法分为两个阶段,第一个阶段就是标记垃圾,标记垃圾的算法已经在3.1和3.2中已经做了详细的说明,第二阶段就是清除垃圾,是将第一阶段的标                    记的垃圾全部清除,然后释放内存空间。标记清除的算法过程如下:

标记清除算法示意图

                 

                       标记 - 清除算法有两个缺点: 1 效率不高, 2 垃圾清除之后内存中存在很多垃圾碎片,导致系统内存的利用率不高。

            3.4.2  垃圾收集之复制算法

                           复制算法就是将内存区域分为两个大小相等的两块,每次只使用其中的一块,当一块用完了之后,便将这块的存活对象复制到另外一块上面,然后一次性将用完的那块                 内存数据清除掉。复制算法的过程如下图:                                                                                                                                                                                                                                             

复制清除算法示意图

                            复制清除算法效率很高,但是付出的是内存的代价,一块内存每次都只能被使用一半,内存的代码太高了。而且对于有很多存活率较高的对象存在时复制清除的操作便                  会出现的比较频繁,对计算机性能也是会带来一定的影响。

             3.4.3   垃圾收集之标记 - 整理算法

                            标记 - 整理算法:标记整理法就是将内存中存活的对象往一端移动,然后再将端边界以外的对象清除掉。                                                                                             

标记整理法示意图

                             标记 - 整理法和标记清除法一样,都是先标记出内存对象,但是标记整理法的后续步骤不同,它是将存活对象移到内存的一端,然后再将端边界以外的对象清除。                              标记 - 整理法没有像复制算法那样将内存一分为二。

     3.5  垃圾收集器描述

               垃圾收集器目前主要使用的有7种,Serial  ParNew, Parallel Scavenge,  CMS,  Serial Old, Parallel Old, G1。垃圾收集器的图示如下;图中直接连线上的垃圾收集器可以两两组       合使用。

垃圾收集器示意图

          3.5.1 Serial 垃圾收集器

                        Serial垃圾收集器是最基本的且历史最悠久的垃圾收集器,它是一个单线程的垃圾收集器,它在工作的时候,其它线程必须停止工作,即:“Stop The World”,直                             到它工作完。Serial垃圾收集器的工作示意图如下:                                                                                                                                                                                                                         

Serial垃圾收集器工作示意图

                    由于Stop The World的机制存在的关系,所以Serial 收集器会带来不太好的用户体验,因为一旦出现垃圾回收的时候,那么就无法及时的响应用户的请求。Serial的优点在                 于简单而高效(同其它收集器的单线程比), 对于限定单个CPU的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。

            3.5.2 ParNew 收集器

                    ParNew收集器其实就是Serial收集器的多线程版本,除了使用多线程进行垃圾收集之外,其余的行为,包括Serial收集器的所有控制参数,如:                                                                     -XX:SurvivorRatio (它定义了新生代中Eden区域和Survivor区域(From幸存区或To幸存区)的比例,默认为8,也就是说Eden占新生代的8/10,From幸存区和To幸存区                              各占新生代的1/10)                                                                                                                                                                                                                                                                 -XX:PretenureSizeThreshold (意思是超过这个值的时候,对象直接在old区分配内存,默认值是0,意思是不管多大都是先在eden中分配内存)                                                              等参数都是一样的,收集算法,Stop The World, 对象分配规则,回收策略等,都与Serial收集器完全一样。ParNew收集器的工作示意图如下:                                                                           

ParNew收集器工作示意图

                       ParNew是一种比较常用的垃圾收集器,其中有一个重要的原因是因为除了Serial收集器之外,只有它能和CMS收集器一起联合使用。ParNew收集器也是使用                                      -XX:+UseConcMarkSweepGC选项后的默认新生代收集器,也可以使用-XX:+UseParNewGC选项来强制制定它。

             3.5.3 Parallel Scavenge 收集器

                         Parallel Scavenge收集器是一个新生代收集器,它也是使用的复制算法,并行多线程收集器。Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量,                                   所谓吞吐量就是CPU用于代码运行的时间与CPU消耗总时间的比值。比值越大,停顿时间就越短,用户体验便会越好。高效的吞吐量则可以高效的利用CPU资源,尽快                             的完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。                                                                                                                                                                                         Parallel Scavenge 收集器提供了两个参数用于精确控制吞吐量, 分别是                                                                                                                                                                           -XX:MaxGCPauseMillis (控制最大垃圾回收时间)                                                                                                                                                                                                                   -XX:GCTimeRatio  (直接设置吞吐量大小)                                                                                                                                                                                                                                           Parallel Scavenge收集器的工作示意图与ParNew类似,这里就不做过多的介绍。

            3.5.4  Serial Old 收集器

                        Serial Old是Serial收集器的老年代版本,它同样是一个单线程的收集器,使用的是标记-整理算法。其工作示意图如下:                                                                                                                 

Serial Old 收集器工作示意图

            3.5.5 Parallel Old 收集器  

                    Parallel Old是Parallel Scavenge的老年代版本,使用的是多线程和标记-整理算法,这个收集器是在JDK1.6中才开始提供的。Parallel Old也是注重吞吐量优先,Parallel Old                 收集器的工作示意图如下:                                                                                                                                                                                                                                                           

Parallel Old收集器工作示意图

             3.5.6 CMS 收集器    

                      CMS 收集器是一种以获取最短的回收停顿时间为目标的收集器,目前很大的一部分的java应用集中在互联网站或者B/S系统的服务端上,这类应用尤其重视服务的响应速                    度,希望系统的停顿时间最短,以给用户带来较好的体验。CMS收集器就非常符合这类应用的需求。

                      CMS收集器是基于标记-清除算法实现的,它的运作过程一共分为4个步骤,                                                                                                                                                                                  1  初试标记                                                    
                            2  并发标记
                            3  重新标记
                            4  并发清除
                       其中初试标记,重新标记这两个步骤任然需要 Stop The World。初试标记仅仅只是标记一下GC Roots能直接关联到对象,速度很快。并发标记就是进行GC Roots                              Tracing 的过程,而重新标记标记阶段则是为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个时间比初试标记的时间稍微长一                     些但是远比并发标记的时间断。CMS收集器的工作示意图如下: 
                            

  CMS收集器的工作示意图        

                        CMS收集器有两个缺点,
                           1 无法收集“浮动垃圾” 可能会出现“Concurrent Mode Failure”失败而导致另外一次Full GC的产生。(浮动垃圾:由于cms并发清理阶段还伴随着 用户线程,                                                 程序的运行过程中必然会产生一些垃圾,这类垃圾便是浮动垃圾)
                           2 CMS清理垃圾的算法使用的是标记清除法,标记清除法有一个缺点就是会产生内存碎片,一旦内存碎片较多,在老年代内存空间中,有大对象的进入到内存中的时                                  候,这个时候无法给这个大对象提供足够的,连续的内存空间的时候就会产生Full GC。
                            CMS收集器提供了一个参数,-XX:+UseCMSCompactAtFullCollection开关参数来用于cms进行Full GC时来进行碎片整理的。默认是开着的。还有一个参数
                            -XX:+CMSFullGCsBeforeCompaction,这个是设置执行了多少次不压缩的Full GC之后,跟着来一次带压缩的(默认值是0,表示每次进入到Full GC时都要进行碎片整                                 理)。

             3.5.7  G1 收集器

                         G1收集器是一款面向服务端应用的垃圾收集器。 G1具备如下特点:
                                1 并行与并发 : G1能充分利用多CPU,多核环境的硬件优势,使用多个CPU来缩短“Stop The World”的时间,G1收集器可以通过并发的方式与java线程共同运行。
                                2  分代收集: G1不需要配合其它收集器,可以独立的管理整个GC堆
                                3 空间整合:与CMS的标记-清除算法相比, GC使用的是标记-整理算法,这样不会产生内存碎片,从而大大减少了Full GC产生的频率。
                                4 可预测停顿: 与CMS相比,G1在降低停顿时间的同时,还能建立可预测的停顿时间模型,能让使用者明确指定在一定长度的时间内,消耗在垃圾收集上的时间不                                                           得超过多少毫秒。
                         G1 收集器的工作流程分为4步:
                                1 初始标记
                                2  并发标记
                                3 最终标记
                                4 筛选回收 

                         G1 收集器的工作流程示意图如下:
                                

G1工作流程示意图

             3.5.8 垃圾收集器参数总结

                        垃圾收集器参数列表如下:


垃圾收集器参数列表

       

                  

   

                                       

你可能感兴趣的:(JVM 内存区域)