首页 > Java > JVM内存模型及垃圾收集策略解析

JVM内存模型及垃圾收集策略解析

一 JVM内存模型

1.1 Java栈

Java栈是与每一个线程关联的,JVM在创建每一个线程的时候,会分配一定的栈空间给线程。它主要用来存储线程执行过程中的局部变量,方法的返回值,以及方法调用上下文。栈空间随着线程的终止而释放。如果在线程执行的过程中,栈空间不够用,那么JVM就会抛出此异常:StackOverflowError,这种情况一般是死递归造成的。

1.2 堆

Java中堆是由所有的线程共享的一块内存区域,堆用来保存各种JAVA对象,比如数组,线程对象等。

下图是我比较喜欢的一张java内存架构图


还有一张常见的图如下

不管如何,表达的意思都是一个意思
1.2.1 Generation

JVM一般又可以分为以下三部分:

◆ Perm

Perm代主要保存class,method,filed对象,这部分的空间一般不会溢出,除非一次性加载了很多的类,不过在涉及到热部署的应用服务器的时候,有时候会遇到java.lang.OutOfMemoryError : PermGen space 的错误,造成这个错误的很大原因就有可能是每次都重新部署,但是重新部署后,类的class没有被卸载掉,这样就造成了大量的class对象保存在了perm中,这种情况下,一般重新启动应用服务器可以解决问题。

◆ Tenured

Tenured区主要保存生命周期长的对象,一般是一些老的对象,当一些对象在Young复制转移一定的次数以后,对象就会被转移到Tenured区,一般如果系统中用了application级别的缓存,缓存中的对象往往会被转移到这一区间。

◆ Young

Young区被划分为三部分,Eden区和两个大小严格相同的Survivor区,其中Survivor区间中,某一时刻只有其中一个是被使用的,另外一个留做垃圾收集时复制对象用,在Eden区间变满的时候,minor GC就会将存活的对象移到空闲的Survivor区间中,根据JVM的策略,在经过几次垃圾收集后,任然存活于Survivor的对象将被移动到Tenured区间。注:1.From Survivor和To survivor的角色是不断的变化的,同一时间只有一块空间处于使用状态,这个空间就叫做From Survivor区,当复制一次后角色就发生了变化。2 如果复制的过程中发现To survivor空间已经满了,那么就直接复制到old generation.3比较大的对象也会直接复制到Old generation,在开发中,我们应该尽量避免这种情况的发生。

1.2.2 Sizing the Generations

JVM提供了相应的参数来对内存大小进行配置。正如上面描述,JVM中堆被分为了3个大的区间,同时JVM也提供了一些选项对Young,Tenured的大小进行控制。

◆ Total Heap

-Xms :指定了JVM初始启动以后初始化堆内存

-Xmx:指定JVM堆的最大内存,在JVM启动以后,会分配-Xmx参数指定大小的内存给JVM,但是不一定全部使用,JVM会根据-Xms参数来调节真正用于JVM的内存

-Xmx -Xms之差就是三个Virtual空间的大小

◆ Young Generation

-XX:NewRatio=8意味着tenured 和 young的比值8:1,这样eden+2*survivor=1/9

-XX:SurvivorRatio=32意味着eden和一个survivor的比值是32:1,这样一个Survivor就占Young区的1/34.

-Xmn 参数设置了年轻代的大小

◆ Perm Generation

-XX:PermSize=16M 非堆内存起始值

-XX:MaxPermSize=64M 非堆内存最大值

Thread Stack 线程栈值

-XX:Xss=128K

二 垃圾收集

2.1 垃圾收集策略(这边主要介绍分代GC策略)

先简单说下内存分配(申请)过程

1 JVM会试图为相关Java对象在Eden中初始化一块内存区域;
2 当Eden空间足够时,内存申请结束。否则到下一步;
3 JVM试图释放对Eden中所有不活跃的对象minor collection,释放后若Eden空间仍然不足以放入新对象,则试图将部分Eden中活跃对象放入Survivor区;
4 Survivor区被用来作为Eden及OLD的中间交换区域,当OLD区空间足够时,Survivor区的对象会被移到Old区,否则会被保留在Survivor区;
5 当OLD区空间不够时,JVM会在OLD区进行major collection;
6 完全垃圾收集后,若Survivor及OLD区仍然无法存放从Eden复制过来的部分对象,导致JVM无法在Eden区为新对象创建内存区域,则出现”Out of memory错误”

GC的执行时要耗费一定的CPU资源和时间的,在JDK1.2以后,JVM引入了分代收集的策略,其中对新生代采用”Mark-Compact”策略,而对老生代采用了“Mark-Sweep”的策略。用较高的频率对年轻的对象(young generation)进行扫描和回收,这种叫做minor collection,Minor collection的过程就是将eden和在用survivor space中的活对象copy到空闲survivor space中。对象在young generation里经历了一定次数的minor collection后,年纪大了,就会被移到old generation中,称为tenuring。而对老对象(old generation)的检查回收频率要低很多,称为”Full Gc 或者Major GC”.其中用System.gc()强制执行的是Full Gc。这样就不需要每次GC都将内存中所有对象都检查一遍,以便让出更多的系统资源供应用系统使用。GC不会在主程序运行期对PermGen Space进行清理,所以如果你的应用中有很多CLASS的话,就很可能出现PermGen Space错误。

参考资料
1.http://docs.oracle.com/javase/6/docs/technotes/guides/management/jconsole.html
2.http://blog.csdn.net/mahongming/article/details/5992083
3.http://developer.51cto.com/art/201002/184385.htm

分类: Java 标签: ,
  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.
*