JAVA虚拟机

Java内存区域

  1. 程序计数器:当前线程所执行字节码的行号指示器,字节码解释器工做时就是经过改变这个计数器的值来选取下一条须要执行的字节码指令
  2. 虚拟机栈:指咱们日常所说的堆栈中的栈(或者说是虚拟机栈中的局部变量部分),线程私有的,生命周期与线程相同。用来描述Java方法执行的内存模型,用于存储局部变量表、操做栈、动态连接、方法出口等信息
  3. 本地方法栈:跟虚拟机栈相似,不过本地方法栈是为虚拟机使用到的Native方法服务
  4. 堆:被全部线程共享的一块内存区域,用来存放对象实例以及对象类型数据的地址信息,还有字符串常量池(JDK7.0后放入堆中,JDK6.0及以前的版本在方法区中),若是堆中没有内存完成实例分配,而且堆也没法再扩展时,将会抛出OutOfMemoryError异常
  5. 方法区:跟堆同样,是各个线程共享的内存区域,用来存储类信息(对象类型、父类、实现的接口、方法等)、静态变量,其中方法区还包含一个运行时常量池,用来存储编译期生成的各类字面量和符号引用

垃圾收集器及内存分配策略

对象存活判断

垃圾回收主要是回收堆内存。在垃圾回收期(GC)回收以前,须要肯定哪些对象能够回收,有如下几种方法:java

  1. 引用计数算法
    原理:给对象添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任什么时候刻计数器都为0的对象就是不可能再被使用的。这种算法效率高。不过很难解决对象之间的相互循环引用的问题。
  2. 根搜索算法(默认)
    原理:经过一系列的名为“GC Roots”的对象做为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,则证实此对象是不可用的。做为GC Roots的对象包括如下几种:
    • 虚拟机栈中的引用的对象
    • 方法区中的类静态属性引用的对象
    • 方法区中的常量引用的对象
    • 本地方法栈中JNI的引用的对象

引用

  • 强引用,相似"Object obj = new Object()"这种,只要强引用存在,则GC永远不会回收被引用的对象
  • 软引用,指还有用,可是并不是必须的对象,内存溢出以前进行回收,实现软引用能够经过SoftReference类,软引用主要用户实现相似缓存的功能,在内存足够的状况下直接经过软引用取值,无需从繁忙的真实来源查询数据,提高速度;当内存不足时,自动删除这部分缓存数据,从真正的来源查询这些数据。
  • 弱引用,跟软引用同样,不过强度比软引用弱一些,第二次垃圾回收时回收,实现弱引用能够经过WeakReference类,弱引用主要用于监控对象是否已经被垃圾回收器标记为即将回收的垃圾,能够经过弱引用的isEnQueued方法返回对象是否被垃圾回收器标记。
  • 虚引用,是最弱的一种引用关系,垃圾回收时回收,没法经过引用取到对象值。主要用于检测对象是否已经从内存中删除。

垃圾收集算法

  1. 标记-清除算法:首先标记出全部须要回收的对象,在标记完成后统一回收全部被标记的对象,不过该算法有如下缺点:
    • 效率低
    • 空间问题,该算法会产生大量不连续的内存碎片,这样致使程序在之后的运行中若是须要分配较大对象时没法找到足够的连续内存而触发另外一次垃圾收集动做
  2. 复制算法:将可用内存按容量划分大小相等的两块,每次只使用其中的一块。当一块内存用完了,就将还存活的对象复制到另一块上面,而后再把已使用过的内存空间一次清理掉。这种算法实现简单,效率高,不过会将可以使用的内存减小一半。若是对象存活率高就要执行较多的复制操做,将致使效率变低。目前在复制算法中,一般是将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次只使用Eden和一块Survivor。当回收时,将Eden和Survivor中还存活的对象一次性拷贝到另一块Survivor中,最后清理使用的Eden和Survivor。而且以老年代做为空间分配担保,即Survivor没法容纳的对象会直接进入老年代。目前新生代主要采用这个算法。
  3. 标记-整理算法:将全部存活的对象都向一端移动,而后直接清理掉边界之外的内存。
  4. 分代收集算法:根据对象的存活周期的不一样将内存划分为几块,通常是分为新生代和老年代。而后根据各个年代的特色采用最适当的收集算法。新生代一般采用复制算法,由于对象生存时间都不长。老年代通常采用"标记-清理"或者"标记-整理"算法回收,由于老年代中对象存活率高,没有额外空间对它进行分配担保。

垃圾收集器

  1. Serial收集器:这是一个单线程的收集器,该收集器在进行垃圾收集时,必须暂停其它全部的工做线程,不过该收集器简单而高效。它通常运行在Client模式下的虚拟机。新生代收集器。
  2. ParNew收集器:这是一个多线程版本的Serial收集器。新生代收集器。
  3. Parallel Scavenge收集器:新生代收集器,使用复制算法,并行多线程。它主要是控制吞吐量=(运行用户代码时间)/(运行用户代码时间+垃圾收集时间)。有两个参数能够用来精确控制吞吐量。
    • -XX:MaxGCPauseMillis:设置最大垃圾收集停顿时间
    • -XX:GCTimeRatio:设置吞吐量大小
    • -XX:+UseAdaptiveSizePolicy:开关参数,打开之后就由虚拟机自动调节策略。这也是跟ParNew收集器的一个重要区别
  4. Serial Old收集器:这是Serial收集器的老年代版本,使用"标记-整理"算法,该收集器有两大用途,一是与Parallel Scavenge收集器搭配使用。二是做为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure的时候使用。
  5. Parallel Old收集器:是Parallel Scavenge收集器的老年代版本。使用"标记-整理"算法。
  6. CMS收集器:以获取最短回收停顿时间为目标的收集器。重视服务的响应速度。主要用于互联网或B/S系统的服务端上。基于"标记-清除"算法。老年代收集器。使用CMS收集器时,不能像其它收集器那样等到老年代几乎被填满了再进行收集,须要预留一部分空间提供并发收集时的程勋运行使用。主要优势就是并发收集、低停顿。它有如下缺点:
    • 对CPU资源很是敏感
    • 没法处理浮动垃圾。即在垃圾收集阶段用户线程继续进行,这个过程会有新的垃圾产生,可是CMS收集器不会对这部分垃圾(浮动垃圾)进行标记。若是预留的内存空间不足,就会致使Concurrent Mode Failure。这时会临时启动Serial Old收集器从新进行老年代的垃圾收集,致使停顿时间过长。
    • 因为采用的算法,会致使收集结束时产生大量空间碎片。
  7. G1收集器:基于"标记-整理"算法实现。能够很是精确的控制停顿,而且在不牺牲吞吐量的前提下完成低停顿的内存回收。由于该收集器会将整个java堆划分为多个大小固定的独立区域,而且跟踪这些区域的垃圾堆积程度,每次根据容许的收集时间,优先回收垃圾最多的区域。 垃圾收集相关的经常使用参数参见下图:

内存分配

  1. 对象首先在新生代Eden区分配,当Eden没有足够的空间进行分配时,虚拟机将发起一次MinorGC
  2. 大对象直接进入老年代,经过-XX:PretenureSizeThreshold参数区分,大于这个值的表示大对象,该参数只对Serial和ParNew有效
  3. 长期存活的对象将进入老年代,识别对象长期存活经过参数-XX:MaxTenuringThreshold设定,对于每一个对象而言,虚拟机都会定义一个年龄计数器,若是对象在通过第一次MinorGC后仍然存活,而且可以被Survivor容纳的话,就将被移动到Survivor中,而且年龄增长一岁。每通过一次MinorGC,年龄就增长一岁,超过设定值或者默认值(15),就会进入老年代中。

名词解释

  1. MinorGC:指发生在新生代的垃圾收集动做,频繁,速度快
  2. MajorGC/Full GC:指发生在老年代的GC,速度慢
  3. 大对象:须要大量连续内存空间的Java对象,好比很长的字符串及数组,应当避免短命的大对象
相关文章
相关标签/搜索