Java编程是内存受限模式下的编程, 内存对于 Java 程序来说,是非常有限的资源,所以了解Garbage Collection的机制很重要。
我们只要加入java的启动参数 -verbose:gc,就可以用查看垃圾回收的情况。
下面是有关参数的几个说明
-verbose:gc在虚拟机发生内存回收时在输出设备显示信息,该参数用来监视虚拟机内存回收的情况。
-Xnoclassgc
关闭虚拟机对class的垃圾回收功能。
-Xincgc
启动增量垃圾收集器,缺省是关闭的。增量垃圾收集器能减少偶然发生的长时间的垃圾回收造成的暂停时间。但增量垃圾收集器和应用程序并发执行,因此会占用部分CPU在应用程序上的功能。
-Xloggc:<file>
将虚拟机每次垃圾回收的信息写到日志文件中,文件名由file指定,文件格式是平文件,内容和-verbose:gc输出内容相同。
JVM管理的内存,通常叫做堆(heap),如果 JVM 不能在 java 堆中获得更多内存来分配更多 java 对象,将会抛出 java 内存不足错误。如果 java 堆充满了活动对象,并且 JVM 无法再扩展 java 堆,那么它将不能分配更多 java 对象。
在这种情况下,JVM就出现 java.lang.OutOfMemoryError 。
JVM启动后,保留一段地址空间,这个空间的大小由-Xmx指定。这块空间的大小就是heap可能的最大值,但一开始不一定全都分配了物理内存,初始分配的heap大小由-Xms指定,一般的jdk都有最小值,如果-Xms小于-Xmx,需要的时候,向OS申请
当JVM进行GC的时候,是要消耗CPU资源和需要一定时间的,这会影响到程序的正常运行,因此需要尽可能减少GC消耗的时间。
Java程序运行过程中,对象的生命周期有长有短,所以Java Heap分为3个区,Young,Old和Permanent。当Young被填满时,GC会将对象移到Old区。Permanent是一个永久区域,分配给JVM,可以通过 命令行参数 -XX:MaxPermSize=64m 来设置,permanent generation添满后导致Full GC。
young generation由一块Eden和两块Survivor Space构成。新创建的对象的内存都分配自eden。一块Survivor Space是空闲的,用作copying collection的目标空间。Minor collection的过程就是将eden和在用survivor space中的活对象copy到空闲survivor space中。对象在survivor spaces 里经历了数次copied后,就会被移到tenured generation或tenured掉。
GC用较高的频率对young generation进行扫描和回收,这种叫做minor collection,而对old generation的检查回收频率要低很多,称为major collection。这样就不需要每次GC都将内存中所有对象都检查一遍。
老版本的JVM是分代垃圾收集(generational garbage collection),有两种收集方式:
第一个是scavenge,将所有活的对象搬到另外一块内存后,整块内存回收。这种方法有效率,但需要有一定的空闲内存,拷贝也有开销,用于minor collection。
第二个是mark-compact,将活的对象标记出来,然后搬迁到一起连成大块的内存,其他内存就可以回收了。这种方法不需要额外的空间,速度会慢一些,方法用于major collection。
注意这两种GC机制都不是并行的,是单线程的,GC运行时,所有的用户线程将暂停,Java应用程序不做任何工作,web应用停止响应。
JDK 1.4.1 中新的收集器都是为解决多处理器系统中垃圾收集器的问题而设计的。因为大多数垃圾收集算法会在一段时间里使系统停止,单线程的收集器很快会成为伸缩性瓶颈,因为在垃圾收集器将用户程序线程挂起时,除了一个处理器之外,其他的处理器都是空闲的。新收集器中的两个――并行复制收集器和并发标记-清除收集器――设计为减少收集暂停时间。另一个是并行清除收集器,它是为在大堆上的更高吞吐能力而设计的。
并行复制收集器用 JVM 选项 -XX:+UseParNewGC 启用,是一个年轻代复制收集器,它将垃圾收集的工作分为与 CPU 数量一样多的线程。并发标记-清除收集器由 -XX:+UseConcMarkSweepGC 选项启用,它是一个老代标记-清除收集器,它在初始标记阶段(及在以后暂短重新标记阶段)暂短地停止整个系统,然后恢复用户程序,同时垃圾收集器线程与用户程序并发地执行。并行复制收集器和并发标记-清除收集器基本上是默认的复制收集器和标记-整理收集器的并发版本。由 -XX:+UseParallelGC 启用的并行清除收集器是年轻代收集器,针对多处理器系统上非常大(吉字节以及更大的)堆进行了优化。
JDK 1.4.1 还包括大量的微调垃圾收集的选项。调整这些选项并衡量它们的效果可能会花费您大量时间,因此在试图微调垃圾收集器之前先对您的应用程序进行彻底的配置(profile)和优化,这样您的微调工作可能会得到更好的结果。
微调垃圾收集首先要做的是检查冗长的 GC 输出。这会使您得到垃圾收集操作的频率、定时和持续时间等信息。最简单的垃圾收集微调就是扩大最大堆的大小( -Xmx )。随着堆的增大,复制收集会变得更有效,所以在增大堆时,您就减少了每个对象的收集成本。除了增加最大堆的大小,还可以用选项 -XX:NewRatio 增加分配给年轻代的空间份额。也可以用 -Xmn 选项显式指定年轻代的大小。
注意:大的收集(major collection)既会收集年轻的代,也会收集老的代。 System.gc() 方法总是触发一个大的收集,这就是应该尽量少用(如果不能完全不用的话) System.gc() 的原因之一,因为大的收集要比小的收集花费长得多的时间。没有办法以编程方式触发小的收集,最好使用-XX:+DisableExplicitGC禁止ExplicitGC。
JVM的很多参数会影响里面各部分空间的分配。-XX:MinHeapFreeRatio与-XX:MaxHeapFreeRatio设定空闲内存占总内存的比例范围,这两个参数会影响GC的频率和单次GC的耗时。-XX:NewRatio决定young与old generation的比例,设置-XX:NewRatio=3意味着old和 young generation是1:3。Young generation空间越大,minor collection频率越低,但是old generation空间小了,又可能导致major collection频率增加。一般来说,eden大小在maximum heap大小的1/4到1/3之间,old generation的大小一定要大于young generation。
-XX:NewSize和-XX:MaxNewSize直接指定了young generation的最小值和最大值。
如果是Server-Side应用,请加 -server 参数。这样,缺省的NewRatio 是2,SurvivorRatio 是 25 ,适合大部分应用。也可以用NewSize、MaxNewSize来设置。 设置- Xms 和 -Xmx 的大小相等,可以避免在每次 GC 后调整堆内存的大小。同样道理设置NewSize、MaxNewSize相等。
对于web应用还要注意,由于经常生成和加载类,permanent generation对GC的影响较大,应设置的大些。如果应用程序中有长期活对象,尽可能减少这些对象的存在期。例如,调整 HTTP 会话超时值将有助于更快地回收空闲会话对象。存泄漏的一个例子是在应用服务器中使用数据库连接池。当使用连接池时,必须在 finally 块中显式关闭 JDBC 语句和结果集对象。这是因为,当从池中调用连接对象上的 close() 时,只是简单地把连接返回池中以供重用,并没有实际关闭连接和关联的语句/结果集对象。如果使用全局缓存,最好使用 Week Reference,例如用 WeakHashMap 代替 HashMap 。
posted on 2010-10-28 14:45
Daniel 阅读(788)
评论(0) 编辑 收藏 所属分类:
CoreJava