憨人笔记之JVM-运行时数据区(堆空间)

堆空间

在Java虚拟机运行时数据区中,堆内存是各种内存中最大的一块。堆内存的建立伴随着虚拟机的启动而建立。全部对象实例的建立都是在堆内存中。在Java虚拟机规范中明确的描述了:全部对象实例以及数组都要在堆上分配内存空间。垃圾回收的主要区域也是发生在堆内存中。算法

从内存回收的角度来看,如今的垃圾收集器基本上都采用了分代收集算法,因此,堆内存又能够分为新生代老年代数组

在为新建立的对象分配内存时,为了保证并发的安全,在堆空间中会为每一个线程建立一个TLAB(本地线程分配缓冲区)区域,因此虽然堆内存是线程共享的,可是在内存分配的角度来看,它又可以划分出多个线程私有的TLAB区域。安全

固然,重点关注的仍是堆内存中的新生代与老年代,首先看一下堆空间中新生代与老年代的划分比例:并发

新生代与老年代的空间大小比例为1:2,而在新生代中还划分了三个区域:Eden区、Surivivor From区、Surivivor To区。它们之间的比例分别为8:1:1。下面来详细分析不一样年龄代的区域。性能

新生代

新生代主要做用是用来放置新建立的对象,任何一个对象初次建立时都会被分配至此区域。在新生代中,98%的对象都是"朝生夕死"的,并不须要一比一来划份内存空间,而是讲内存空间划分为两个大的区域:Eden区、Survivor区(其中,Surivivor区域又被划分红两个Survivor From区,Survivor To区域)。每一次内存分配都是使用Eden区和Survivor区域中的一块。既然划分了三个区域,那么就来讲说对象在这三个区域中怎么流转的。线程

  1. 首先,新生对象会被分配在Eden区域,会触发一次Monir GC,此时会讲Eden区中还存活的对象复制到第一个Surivor区域(Surivor From)
  2. 继续建立对象,当Eden区内存不足觉得新对象分配内存空间的时候,会再次触发Monir GC,此时就会将Eden区和第一个Surivor区域中还存活的对象复制到另一个Survivor区中(Survivor To),而后Eden区和第一个Survivor区会被清空,以便为新对象腾出内存空间。
  3. 继续建立对象,此时第一个Survivor区域已经为空,第二个Survivor区域中已经存在了上次被复制过来的对象,那么再建立对象的时候,就只会在Eden区和第二个Survivor区中操做。
  4. 如此往复循环,当对象被复制的次数达到16次时,就会被送至老年代中。

强调的一点是,新生代中,只会Eden区和Survivor区域其中的一块被同时使用,另外一块Survivor区域始终为空的。cdn

为何要划分出两个Survivor区域呢?

上面说到,Survivor区域中,始终会有一块Survivor区域被空置,那么在有限的堆内存中,岂不是形成了内存的浪费。首先了解一下划分出Survivor区域的意义在哪里。对象

试想若是没有划分Survivor区域,那么Eden区每进行一次Monir GC,都会将对象直接送入老年代,老年代将会很快被填满,从而触发Major GC。Major GC的执行效率相对Monir GC来讲效率慢了十倍以上。那么与有Survivor区的状况相比,Major GC触发的频率则会相对提升,严重的将会影响到程序执行及相应的速度。blog

若是存在Survivor区域,它能够做为一个缓冲区,当Eden区触发Monir GC后,对象不直接送往老年代,而是复制到Survivor区,相对来讲就能够下降触发Major GC的机率。因此,Survivor区存在的根本意义是:减小对象被送往老年代的频率,从而减小Major GC和Full GC的发生,Survivor能够保证在经历了16次Monir GC还能在新生代存活的对才会被送到老年代内存

另一个方面就是Survivor区有效的解决了内存的碎片化。回顾以前说到的新生代对象流转流程。新建立的对象都被分配在Eden区,一旦该区域的内存满了,会触发Monir GC,而后对象会被从Eden送至Survivor区,往复循环。由此,问题来了,在进行Monir GC时,Eden和Survivor区都有一切存活的对象,此时将Eden取中的对象强行存放到Survivor区时,明显两部分的对象所占用的内存空间是不连续的,也就致使了内存的碎片化。

内存碎片化最终的结果就是会严重影响到程序的性能。试想当堆空间被散布的对象占据了不连续的内存,当有一个内存需求较大的对象被建立的时,堆中可能就没有足够大的连续内存空间来分配给该对象,那么就会触发Full GC。

若是将Survivor区划分红两块。在Eden区刚刚建立新对象时,经历一次Monir GC,Eden区存活的对象就被被复制移动到第一块Survivor区中,Eden被清空,等Eden区再满了的时,再次触发Monir GC,Eden区和第一块Survivor区存活的对象会被复制移动到第二块Survivor区。Eden和第一块Survivor区域被清空。

因此,为何要划分两个Survivor区呢?

  • Survivor区做为新生代与老年代之间的缓冲区,能够下降将对象送往老年代的频率,老年代也就没那么快就被对象堆满而致使发生垃圾回收
  • Survivor中采用的复制算法,复制算法能有效的下降内存的碎片化

老年代

老年代主要存放的是经历过几回垃圾回收以后还存活的对象,刚刚在说到新生代对象的复制转移的时候,当被标记了16次的对象若是还存活着,就会被送入到老年代。

另一种就是较大的对象,较大的对象也就被直接送入到老年代中。


不怕路歹行不怕大雨淋,心上一字敢 面对个人梦,甘愿来做憨人。 --<憨人>

相关文章
相关标签/搜索