二 JVM

  1、内存结构

    

 

   程序计数器 : 程序计数器是一块较小的内存空间(线程私有),它可以看作是当前线程所执行的字节码的行号指示器。字节码解释器工作时就是通过改变这个程序计数器的值来选取吓一跳执行的字节码指令,分支、循环、跳转,异常都要依赖这个计数器,所以程序计数器是线程私有的

  java虚拟机栈 :描述的是Java方法执行的内存模型,和线程的生命周期一样(线程私有)。每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、出口信息、动态链接等。这块区域会出现两种异常StackOverflowError和OOM

  本地方法栈 :为虚拟机使用到的Native方法服务和虚拟机栈类似(线程私有)

  Java堆:是Java虚拟机所管理的最大一块内存(线程共享),此内存最大的目的就是存放对象实例,几乎所有的对象都在这里分配内存。

  方法区:存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据(线程共享)

  运行时常量:方法区的一部分,类信息中除了有类的字段、方法、接口等描述信息外还有一项信息是常量池,用于存放编译期生成的各种字面量和符号引用

  直接内存: (也就是堆外内存) 内存对象分配在Java虚拟机的堆以外的内存,这些内存直接受操作系统管理(而不是虚拟机),这样做的结果就是能够在一定程度上减少垃圾回收对应用程序造成的影响。使用未公开的Unsafe和NIO包下ByteBuffer来创建堆外内存。 

 

 

  2、垃圾回收器  

      判断对象可否回收的方法是:可达性分析算法,从GcRoot对象从上往下搜索,搜索的过的路径叫引用链,当一个对象没有饮用链的时候就是垃圾对象了

      可以作为GcRoot的对象可以是:

        a、虚拟机栈(栈帧中的局部变量表)中引用的局部变量表

        b、方法区中的静态引用对象

        c、方法区中的常量引用对象

        d、本地方法栈中引用的对象

      引用方式

        a、强引用   永远不会回收

        b、弱引用 每次都回收,只能生存到下次垃圾回收发生之前

        c、软引用 先回收,不够再回收 

        d、虚引用 无法通过虚引用获取对象实例,唯一目的就是回收时只会收到一个回收的通知

      回收条件

        a、如果对象进行可行性分析后发现没有与GcRoot相连的引用链,则该对象被第一次标记并进行筛选,筛选条件为是否有必要执行该对象的finalize方法,如果对象没有覆盖finalize方法或者finalize方法已被虚拟机执行过了(finalize只会执行一次),则不会不必要执行finalize方法,对象就会被回收,反之如果对象覆盖了finalize或者没有被执行过,那么对象会被放进F-Queue的队列中,之后由虚拟机建立的低优先级的finalizer线程去执行,而虚拟机不必等待线程执行结束,虚拟机只负责建立线程,其他交由线程去做

         b、F-Queue的对象进行二次标记,如果对象在finalize方法中拯救了自己,机关联上了GcRoot的引用链,如把this关键字赋值给其他变量,那么在第二次标记的时候该对象从即将回收的集合中移除,如果对象还没有拯救自己,那么就会被回收

       方法区回收  

        1、废弃常量
            a、如果当前系统中没有任何引用某个变量的地方,他就会被回收
        2、无用的类
            a、该类的所有实例都已经被回收,即JAVA堆中不存在该对象的任何实例
            b、加载该类的classLoader已经被回收
            c、该类对应的java.lang.class对象没有任何地方被引用,无法在任何地方通过反射访问该类的方法
 
 
        垃圾回收算法
          a、标记-清除
             缺点:
                1、标记清除后会产生不连贯的碎片空间   如CMS有一个回收多少次之后整理一下的参数
                    2、标记和清除的效率都不高
             代表 :
                CMS
             
          b、复制(新生代大多数会采用此方案,大部分对象朝生夕死)
               缺点:
                1、会浪费一部分空间
             代表 :
               serial 新生代
               ParNew新生代
               Parallel Scavenge 新生代
          c、标记-整理
              代表 :
               serial 老年代
               ParNew老年代
               Parallel Scavenge 老生代
               G1基于标记整理的算法实现的收集器
          d、分代算法
        垃圾收集器
          a、Serial
            单线程垃圾收集器,工作时必须STW,直到它收集结束。新生代采用复制算法,老年代采用标记-整理算法
            优点是简单高效
          b、ParNew
            其实就是Serial的多线程版本,也会STW
          c、Parallel Scavenge
            新生代收集器,采用复制算法,吞吐量优先的垃圾回收器,吞吐量=用户代码运行时间/(用户代码运行时间+垃圾回收时间)
          d、Serial Old
            单线程收集器,采用标记-整理,作为CMS收集器的后备预案,在并发收集器出现Concurrent Mode Failure时使用
          e、Parallel Old
            SerialOld的多线程版本
          f、CMS
            CMS(Concurrent Mark Sweep)是获取最短回收时间的收集器,当超过预设值就会发生回收,包括4个步骤
              1、初始标记:(STW) 只标记GcRoot能关联到的对象
              2、并发标记: 从GcRoot从上往下搜索的过程
              3、重新标记:(STW)修正并发标记期间用户程序运行期间标记产生变动的对象,比初始标记时间长
              4、并发清除:
             优点:并发收集,低停顿
             缺点:
               1、对cpu资源敏感CMS默认cpu线程是 (CPU核心数+3)/4,cpu核心较少时资源占用较多,解决方案增量式并发收集器,和用户线程交交替运行,尽量减少占用时间
               2、无法处理浮动垃圾,浮动垃圾指的是 被初始标记之后新产生的垃圾,CMS无法处理掉,这个过程会出现Concurrent Model Failure错误这时就会使用备用方案使用Serial Old收集器
               3、采用标记清除,会产生大量碎片空间,解决方案是 收集多少次后进行压缩
          g、G1  
             G1(garbage first)优先处理垃圾多的内存块,大致分为4个步骤
               1、初始标记 STW
               2、并发标记   开始标记同时用户线程继续运行
               3、最终标记 STW
               4、筛选回收 选择一部分垃圾优先清除
              特点:E  S O三块区域不在泾渭分明,G1把内存氛围很多小块(Region),每个小块会被标记是S、O、E中的一个,内存粒度变小了,从而可以吧垃圾回收的工作更彻底的并行化。
              
            
    3、内存分配和回收策略
  

posted @ 2020-04-27 11:36  王南辉  阅读(126)  评论(0)    收藏  举报