Java内存模型

Java内存模型、GC

Posted by candy1126xx on February 15, 2017

内存模型

  • 程序计数器:线程私有。记录当前线程运行到了哪条指令。
  • 虚拟机栈:线程私有。执行一个Java方法,会创建一个栈帧并入栈,方法执行完栈帧出栈。栈帧中记录着方法的相关信息,比如本地变量表、操作栈、动态链接、方法出口等。
  • 本地方法栈:线程私有。工作原理与虚拟机栈相同,不同的是它对应Native方法。
  • 堆区:线程共享。存放实例对象。
  • 方法区:线程共享。存放已经被虚拟机加载的类信息、final常量、静态变量、编译器即时编译的代码等。
  • Object obj = new Object()执行后:虚拟机栈的本地变量表中创建了一个reference类型数据,其中保存了Object实例对象在堆中的地址,堆中还保存了Object类的类型信息在方法区的地址。

年轻代/年老代/永久代

堆区被分为年轻代和年老代。年轻代又分为三个区:Eden、Survivor 0、Survivor 1。Eden区是连续的内存空间,在其上分配内存极快,因此将它作为创建对象的首选。Survivor 0、Survivor 1总是一个有对象另一个空,1次Minor GC后仍然存活的对象将被复制到Survivor 0、Survivor 1空的那个区。经历过n次Minor GC后仍然存活的对象将被复制到年老区,其中n由-XX:MaxTenuringThreshold控制,默认值是15;大于m的对象将不会分配在Eden区,而直接分配在年老代,其中m由-XX:PretenureSizeThreshold控制。

方法区是永久代。

GC

GC ROOT

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象;
  • 方法区中的类静态属性引用的对象、常量引用的对象;
  • 本地方法栈中JNI(Native方法)的引用对象

被GC的对象

从GC Root开始向下搜索,搜索所走过的路径称为引用链,当一个对象到任何GC Root都没有引用链,就称该对象不可到达,或者称该对象不可用,这样的对象就应该被GC。

Minor GC

当年轻代的Eden区满时执行Minor GC:对年轻代及card table执行Root搜索,把Eden区、Survivor区中仍然存活的对象复制到另一个空的Survivor区,然后清空这两个区。

Major GC

每次发生Minor GC时,虚拟机会检查进入老年代的对象总大小是否超过了老年代的剩余空间,如果超过了,会执行Major GC;否则,检查是否设置了-XX:+HandlePromotionFailure,如果设置了,会执行Major GC。

Major GC时,对老年代执行Root搜索,标记出仍然存活的对象,并将所有存活的对象向一端移动,删除其余对象。

GC策略

  • Copying:开辟另外一个内存空间,把扫描到可达的对象复制过去,然后把原内存空间全部清除即可。适用于存活对象较少的情况。用于年轻代Minor GC。
  • Mark-Sweep:把扫描到可达的对象都标记下来,然后把所有未标记的对象清除。但这个方法会引起内存碎片。适用于存活对象较多的情况。
  • Mark-Compact:在Mark-Sweep基础上,在把内存空间整理一下,让存储连续以消除内存碎片。用于年老代Major GC。

GC的具体实现

  • Serial收集器:新生代收集器,使用停止复制算法,使用一个线程进行GC,其它工作线程暂停。
  • ParNew收集器:新生代收集器,使用停止复制算法,Serial收集器的多线程版,用多个线程进行GC,其它工作线程暂停,关注缩短垃圾收集时间。
  • Parallel Scavenge 收集器:新生代收集器,使用停止复制算法,关注CPU吞吐量,即运行用户代码的时间/总时间,这种收集器能最高效率的利用CPU,适合运行后台运算。
  • Serial Old收集器:老年代收集器,单线程收集器,使用标记整理算法。
  • Parallel Old收集器:老年代收集器,多线程,多线程机制与Parallel Scavenge差不错,使用标记整理(与Serial Old不同,这里的整理是Summary(汇总)和Compact(压缩),汇总的意思就是将幸存的对象复制到预先准备好的区域,而不是像Sweep(清 理)那样清理废弃的对象)算法。
  • CMS(Concurrent Mark Sweep)收集器:老年代收集器,致力于获取最短回收停顿时间,使用标记清除算法,多线程,优点是并发收集(用户线程可以和GC线程同时工作),停顿小。适合前台交互。