Jvm内存区域和GC

运行时数据区域

线程私有

  • 程序计数器 正在执行的字节码指令的地址(native方法时为undefined)
  • Java虚拟机栈 存储栈帧(局部变量表,操做数栈,动态连接,方法出口)OOM,StackOverflowError
  • 本地方法栈 与虚拟机栈相似,是native方法的栈

线程共享

  • Java堆 存放对象和数组 OOM
    • 新生带
      • Eden
      • Survivor * 2
    • 老年代
  • 方法区(永久代)存储已被虚拟机加载的类信息、常量、静态变量 OOM
  • 运行时常量池 方法区的一部分,编译器生成的字面量和符号引用 OOM

直接内存

不是运行时数据区的一部分,可是也会产生OOM。算法

GC

垃圾检测

  • 引用计数 实现简单且高效,但有循环引用的问题, 目前没有虚拟机的实现采用这个算法
  • 可达性分析 从GC Roots(虚拟机栈、方法区静态属性、方法区常量、本地方法栈)开始,沿引用链向下搜索,没被引用链链接的对象就是无用对象

引用类型

  • 强引用 代码中广泛存在的引用,只要强引用存在,被引用的对象就不会被回收
  • 软引用 SoftReference,用来描述有用但并不是必须的引用,若是一次GC以后内存仍然不足,会把这些对象列入回收范围进行第二次回收
  • 弱引用 WeakReference,下次GC一定会被回收
  • 虚引用 PhantomReference,不会对对象的生存时间产生影响,也没法经过虚引用取得对象实例,用于在对象被回收时收到一个通知

方法区

判断常量池中的对象是否须要回收与对象类似。 无用类的条件:数组

  • 该类的全部实例都被回收
  • 加载该类的类加载器已被回收
  • 对应的Class对象没在任何地方被引用

垃圾回收

  • 标记-清除
    • 标记须要回收的对象,再统一回收
    • 效率不高,内存碎片
  • 复制
    • 将内存分为两块,将存活的对象复制到to空间,将from空间统一清除
    • 实现简单,运行高效,内存利用率低,存活率较高时复制效率低
    • 新生代分为一个Eden和两个Survivor,默认大小比为8:1,每次只使用一个Survivor,GC时将新生代存活对象放到未使用的Survivor,放不下的晋升老年代(分配担保)
  • 标记-整理
    • 标记后将存活对象朝一边移动,清理掉端边界外的内存

分代收集

根据对象存活周期的不一样,将内存划分为几块。新生代使用复制算法,老年代使用标记整理安全

Stop The World

可达性分析必须在一个能确保一致性的快照中进行,所以在GC进行时须要中止用户线程。数据结构

安全点和安全区域

可达性分析的耗时较久(方法区就可能有几百兆),所以使用了一组OopMap的数据结构,来存储特定位置上对象内什么偏离量上是什么类型的数据,这样在GC扫描时就能够直接得到这些信息。多线程

可是会使OopMap内容变化的指令不少,若是每条指令都生成对应的OopMap,空间成本会很是高,所以只记录了特定位置的信息,这些位置就称为安全点,线程只有到达安全点才能暂停。并发

具备方法调用、循环跳转、异常跳转功能的指令才会产生安全点。spa

  • 抢先式中断(几乎没有虚拟机实现使用) GC发生时,中断全部线程,若是有线程不在安全点,恢复线程,让它跑到安全点
  • 主动式中断 不直接操做线程,而是在安全点检查中断标志,判断是否须要暂停

安全区域是安全点的扩展,引用关系不会变化的一段代码片断。线程进入安全区域时,标示本身进入了安全区域,要离开时则检查根节点枚举或整个GC过程是否完成,没有则等待到收到能够离开安全区的信号。线程

垃圾收集器

  • Serial
    • 新生代单线程收集器
    • 垃圾回收时只有GC线程在工做
  • ParNew
    • Serial的多线程版本,
    • 垃圾回收时只有多个GC线程在工做
  • Parallel Scavenge
    • 新生代收集器,
    • 并行的多线程收集器,
    • 可控制的吞吐量(吞吐量优先)
  • Serial Old
    • Serial的老年代版本
  • Parallel Old
    • Parallel Scavenge的老年代版本
  • CMS
    • 老年代收集器,
    • 使用标记清除算法,
    • 追求最短回收停顿时间,
    • 只在初始标记阶段(只记录GC Roots能关联到的对象)和从新标记阶段(修正并发标记时发生的引用变化,多线程)会STW,
    • 对CPU资源敏感,并发时占用必定资源,程序变慢,吞吐量变低
    • 没法清理浮动垃圾,须要预留必定空间在并发收集时供用户线程使用,若是预留空间不够,会临时启用Serial Old从新进行收集
    • 内存碎片,找不到足够大的连续内存时,提早触发一次Full GC,能够设置在要进行Full GC,进行一次随便整理(或每几回不压缩的Full GC,整理一次碎片)。
  • G1
    • 新生代和老年代收集器,面向服务端应用的垃圾收集器
    • 并行并发、分代收集、空间整合、可预测停顿
    • 将堆分为多个大小相等的独立区域,新老生代再也不物理隔离,
    • 有计划的避免在整个堆作垃圾回收,优先回收价值最大的区域
    • 追求低停顿可尝试,追求吞吐量无优点

分配和回收策略

  • 对象优先在Eden分配
  • 大对象直接进入老年代 避免在Eden和Survivor中复制大量内存
  • 长期存活对象进入老年代 每经历一次GC,对象的年龄+1,达到必定岁数就晋升老年代
  • 动态对象年龄判断 Survivor中,相同年龄的全部对象大小总大于Survivor的一半,年龄大于等于该年龄的对象晋升老年代
  • 空间分配担保 Survivor空间不足,晋升老年代,有老年代空间不足风险;老年代连续空间小于新生代对象总大小或历次晋升平均大小就会进行Full GC

其余

finalize()

忘掉这个方法code

参考资料

《深刻理解Java虚拟机》第2版 《Java虚拟机规范》Java SE 8版对象