JVM 新生代老生代

  • Post author:
  • Post category:其他



  • 堆大小 =

    新生代

    + 老年代。默认下,新生代 ( Young ) = 1/3 的堆空间大小,老年代 ( Old ) = 2/3 的堆空间大小;

  • 新生代 ( Young ) 被细分为 Eden 和 两个 Survivor 区域,这两个 Survivor 区域分别被命名为 from 和 to,以示区分。默认的,Edem : from : to = 8 : 1 : 1;

  • JVM 每次只会使用 Eden 和其中的一块 Survivor 区域来为对象服务,所以无论什么时候,总是有一块 Survivor 区域是空闲着的。 因此,新生代实际可用的内存空间为 9/10 ( 即90% )的新生代空间;

  • GC 分为两种:老生代中采用标记-清除

    算法

    的Full GC ( 或称为 Major GC )和新生代中采用复制算法的Minor GC。新生代是 GC 收集垃圾的频繁区域;

  • **所谓的新生代和老年代是针对于分代收集算法来定义的,新生代又分为Eden和Survivor两个区。加上老年代就这三个区。数据会首先分配到Eden区 当中(当然也有特殊情况,如果是大对象那么会直接放入到老年代(大对象是指需要大量连续内存空间的java对象)。),当Eden没有足够空间的时候就会 触发jvm发起一次Minor GC。如果对象经过一次Minor GC还存活,并且又能被Survivor空间接受,那么将被移动到Survivor空 间当中。并将其年龄设为1,对象在Survivor每熬过一次Minor GC,年龄就加1,当年龄达到一定的程度(默认为15)时,就会被晋升到老年代 中了,当然晋升老年代的年龄是可以设置的。

    其实新生代和老年代就是针对于对象做分区存储,更便于回收等等**

新生代主要存放的是哪些很快就会被GC回收掉的或者不是特别大的对象(这个大就要看你是否设置了-XX:PretenureSizeThreshold 参数了)。新生代采用的复制

算法,即将新生代分为3个区:较大的Eden和两个较小的Survivor(默认的Eden:Survivor = 8:1)。发生在新生代的GC为Minor GC 。在Minor GC时会将新生代中

还存活着的对象复制进一个Survivor中,然后对Eden和另一个Survivor进行清理。所以,平常可用的新生代大小为Eden的大小+一个Survivor的大小。

年代则是存放那些在程序中经历了好几次回收仍然还活着或者特别大的对象(这个大就要看你是否设置了-XX:PretenureSizeThreshold 参数了)。老年代采用

的是标记-清除或者标记-整理算法,这两个算法主要看虚拟机采用的哪个收集器,两种算法的区别是:标记-清除可能会产生大量连续的内存碎片。在老年代中的GC

则为Major GC。Major GC和Full GC会造成stop-the-world。

-Xms2000m -Xmx2000m -Xmn800m -XX:PermSize=64m -XX:MaxPermSize=256m

Xms,即为jvm启动时得JVM初始堆大小,Xmx为jvm的最大堆大小,xmn为新生代的大小,permsize为永久代的初始大小,MaxPermSize为永久代的最大空间。

老年代和新生代也是和内存相关,虚拟机初始化时已经设定了使用的内存大小,并划分为三部分:新生代– 新创建的对象,

旧生代 – 经过多次垃圾回收没有被回收的对象或者大对象

持久代– JVM使用的内存,包含类信息等

**所谓的新生代和老年代是针对于分代收集算法来定义的,新生代又分为Eden和Survivor两个区。加上老年代就这三个区。数据会首先分配到Eden区 当中(当然也

有特殊情况,如果是大对象那么会直接放入到老年代(大对象是指需要大量连续内存空间的java对象)。),当Eden没有足够空间的时候就会 触发jvm发起一次

Minor GC。如果对象经过一次Minor GC还存活,并且又能被Survivor空间接受,那么将被移动到Survivor空 间当中。并将其年龄设为1,对象在Survivor每熬过一

次Minor GC,年龄就加1,当年龄达到一定的程度(默认为15)时,就会被晋升到老年代 中了,当然晋升老年代的年龄是可以设置的。

其实新生代和老年代就是针对于对象做分区存储,更便于回收等等**

  • 当对象在 Eden ( 包括一个 Survivor 区域,这里假设是 from 区域 ) 出生后,在经过一次 Minor GC 后,如果对象还存活,并且能够被另外一块 Survivor 区域所容纳( 上面已经假设为 from 区域,这里应为 to 区域,即 to 区域有足够的内存空间来存储 Eden 和 from 区域中存活的对象 ),则使用复制算法将这些仍然还存活的对象复制到另外一块 Survivor 区域 ( 即 to 区域 ) 中,然后清理所使用过的 Eden 以及 Survivor 区域 ( 即 from 区域 ),并且将这些对象的年龄设置为1,以后对象在 Survivor 区每熬过一次 Minor GC,就将对象的年龄 + 1,当对象的年龄达到某个值时 ( 默认是 15 岁,可以通过参数 -XX:MaxTenuringThreshold 来设定 ),这些对象就会成为老年代。 但这也不是一定的,对于一些较大的对象 ( 即需要分配一块较大的连续内存空间 ) 则是直接进入到老年代;

  • Full GC 发生的次数不会有 Minor GC 那么频繁,并且做一次 Full GC 要比进行一次 Minor GC 的时间更长;

  • 因为 jvm 每次只是用新生代中的 eden 和 一个 survivor,因此新生代实际的可用内存空间大小为所指定的 90%;

  • PermGen 即永久代 ( 方法区 ),它还有一个名字,叫非堆,主要用来存储由 jvm 加载的类文件信息、常量、静态变量等;

  • 每次调 System.gc(),是先进行 Minor GC,然后再进行 Full GC;

  • 当 Full GC 进行的时候,默认的方式是尽量清空新生代 ( YoungGen ),因此在调 System.gc() 时,新生代 ( YoungGen ) 中存活的对象会提前进入老年代;

题眼:

JVM使用的是分代垃圾回收的方式,可以将Java对象分为”年轻”对象和”年老”对象.

JVM将内存堆(Heap)分为两个区域,一个是”年轻”区,另一个是”老”区,Java将这两个区域分别称作是”新生代”和”老生代”。

详细:

JVM使用的是分代垃圾回收的方式,主要是因为在程序运行的时候会有如下特点:

◆大多数对象在创建后很快就没有对象使用它了。

◆大多数在一直被使用的对象很少再去引用新创建的对象。

因此就将Java对象分为”年轻”对象和”年老”对象,JVM将内存堆(Heap)分为两个区域,

一个放年轻对象,一个放年老对象,java对对内存的这两个区域分别成为 新生代,老生代。

新生代区域特点:

a) 绝大多数新创建的对象都存放在这个区域

b) 此区域一般来说较小而且JVM垃圾回收频率较高

c) 采用的算法和存放对象特点使得该区域JVM垃圾回收的效率也非常高


老生代区域特点:

a) 将在”新生代”中生存了较长时间的对象转移过来

b) 区域一般要大一些而且增长的速度相对于”新生代”要慢一些

c) 垃圾回收的执行频率也会低很多


了解这个对于我们的好处是:

JVM在JVM垃圾回收处理时会消耗一定的系统资源,如果我们在JVM启动的时候添加相关参数来控制”新生代”区域的大小

以达到调整JVM垃圾回收处理的频率,那么我们就会合理利用系统资源。

操作新生代参数设置写法:

-Xmn


操作案例如下:

我们就用系统自带的程序作为例子,在命令行上键入如下指令:

Java代码

收藏代码

  1. CDC:\java\demo\jfc\SwingSet2[回车]C:\java\demo\jfc\SwingSet2>
  2. java-jar-verbose:gc-Xmn4mXX:+PrintGCDetailsSwingSet2.jar[回车]

上面加入了一个新的参数”XX:+PrintGCDetails”,这个参数能够打印出GC的详细信息。屏幕输出如下(节选):

Java代码

收藏代码

  1. [GC[DefNew:3469K->84K(3712K),

    0

    .0007778secs]23035K->
  2. 19679K(28728K),

    0

    .0009191secs][GC[DefNew:3284K->
  3. 171K(3712K),

    0

    .0007283secs]22878K->
  4. 19766K(28728K),

    0

    .0008669secs][GC[DefNew:3476K->
  5. 260K(3712K),

    0

    .0008504secs]23071K->
  6. 19855K(28728K),

    0

    .0009862secs][GC[DefNew:3502K->
  7. 87K(3712K),

    0

    .0009267secs]23096K->
  8. 19682K(28728K),

    0

    .0010610secs]


我们需要解释一下输出的详细内容的意思,拿第一行输出来说:

Java代码

收藏代码


  1. “DefNew:3469K->84K(3712K),0.0007778secs”

    是指

    “新生代”

    的JVM垃圾回收情况,这里的意思是从占用3469K内存空间变为84K内存空间,用时

    0.0007778

    秒。

  2. “23035K->19679K(28728K),0.0009191secs”

    是指总体GC的回收情况,整体堆空间占用从23035K降低到19679K的水平,用时

    0.0009191

    秒。

那么,这时候我们在将”新生代”的内存设为8M,并把堆的最大可控值设定为32M,再去执行,键入如下指令:

Java代码

收藏代码

  1. java-jar-verbose:gc-Xmn8m-Xmx32mXX:+PrintGCDetailsSwingSet2.jar[回车]

得到的结果如下(节选):

Java代码

收藏代码

  1. [GC[DefNew:6633K->6633K(7424K),

    0

    .0000684secs]
  2. [Tenured:18740K->18820K(24576K),

    0

    .0636505secs]25374K->
  3. 18820K(32000K),

    0

    .0639274secs][GC[DefNew:6646K->
  4. 6646K(7424K),

    0

    .0002581secs][Tenured:18820K->
  5. 18884K(24576K),

    0

    .0651957secs]25467K->
  6. 18884K(32000K),

    0

    .0658804secs][GC[DefNew:6611K->
  7. 6611K(7424K),

    0

    .0000668secs][Tenured:18884K->
  8. 18505K(24576K),

    0

    .0931406secs]25496K->
  9. 18505K(32000K),

    0

    .0934295secs]


这个结果说明:

Java代码

收藏代码


  1. “[DefNew:6633K->6633K(7424K),0.0000684secs]”

    是指

    “新生代”

    的JVM垃圾回收情况,这里的意思是从占用6633K内存空间变为6633K内存空间,用时

    0.0000684

    秒。

  2. “25374K->18820K(32000K),0.0639274secs”

    是指总体GC的回收情况,整体堆空间占用从25374K降低到18820K的水平,用时

    0.0639274

    秒。

  3. “[Tenured:18740K->18820K(24576K),0.0636505secs]”

    是指

    “老生代”

    GC的回收情况,整体堆空间占用从18740K降低到18820K的水平,用时

    0.0009012

    秒。

通过这些参数的调整我们可以看到在处理垃圾收集问题时,从JVM垃圾回收的频率是时间方面的变化,我们可以根据不同程序的不同情况予以调整。

最后有必要提一下GC的相关参数:

Java代码

收藏代码

  1. -XX:+PrintGCDetails显示GC的详细信息
  2. -XX:+PrintGCApplicationConcurrentTime打印应用执行的时间
  3. -XX:+PrintGCApplicationStoppedTime打印应用被暂停的时间


注:”:”后的”+”号表示开启此选项,如果是”-“号那么表示关闭此选项。

补充:
1 通过设置-xx:PretenureSizeThreshold 来设置多大的对象直接进入老年代。你把这个值调的大一点,则可以保证大部分对象不会直接进入老年代,老年代对象的gc同长慢,一般内存不满时不会gc的,所以你的大对象一直都在不会被回收。

2 (-xx:MaxTenuringThreshold默认15)这个值也可以调调,这个表示在新生代折腾多少次后进入年老代。