JVM的垃圾回收机制详解和性能调优

 
JVM的垃圾回收机制详解和性能调优
2008-05-28 16:38

转载:

JVM堆相关知识
     为什么先说JVM堆?
     JVM的堆是Java对象的活动空间,程序中的类的对象从中分配空间,其存储着正在运行着的应用程序用到的所有对象。这些对象的建立方式就是那些new一类的操作,当对象无用后,是GC来负责这个无用的对象(地球人都知道)。

GC是如何去回收这些对象的,当中会有什么影响?这跟JVM堆本身的构造是分不开的。
JVM堆的结构如下:

JVM堆

(1) 新域:存储所有新成生的对象

(2) 旧域:新域中的对象,经过了一定次数的GC循环后,被移入旧域

(3)永久域:存储类和方法对象,从配置的角度看,这个域是独立的,不包括在JVM堆内。默认为4M。

     GC是通过JVM上的一个或一组进程来实现,GC在运行时同样会占用堆空间,也占用cpu,而且,GC运行时,是对JVM堆进行操作,所以,应用程序会停止运行,也就是说GC运行时,不仅会消耗一定的资源,而且会影响应用程序的运行。当应用比较小而简单时,GC的影响可以忽略,但对于大型而复杂的应用,GC的处理机制就显得非常重要了。

GC浅谈: GC的工作目的很明确:在堆中,找到已经无用的对象,并把这些对象占用的空间收回使其可以重新利用。

     Java规范没有对GC进行明确的定义,所以不同的JVM,其GC的实现方法就不太相同,在<<Java编程思想>>那块砖头中讲述了一种方法:对活动对象和无用对象使用计数器,如果这个对象被引用一次,则其对应的计数器加1,如果对象不在作用域中使用,计数器减1,当计数器为0,这个对象就可以被回收了。
这个方法的优点是针对每个对象做操作,不会大规模地进行对堆的操作,所以不会长时间中断应用程序,缺点也明显,它必须实时运行,所以程序开销就大了,唉,鱼和熊掌不能兼得。

     大多数垃圾回收的算法思路都是一致的:把所有对象组成一个集合,或可以理解为树状结构,从树根开始找,只要可以找到的都是活动对象,如果找不到,这个对象就是凋零的昨日黄花,应该被回收了。

     从资料上查到的这种算法如下:
l     tracing算法(tracing collector): 从根集开始扫描,识别出哪些对象可达,哪些对象不可达,并用某种方式标记可达对象。基于tracing算法的垃圾收集也称为标记和清除 (mark-and-sweep)垃圾收集器。
l     compacting算法(compacting collector):在清除的过程中,将所有的对象移到堆的一端,堆的另一端就变成了一个相邻的空闲内存区,收集器会对它移动的所有对象的所有引用进行更新,使得这些引用 在新的位置能识别原来的对象。一般增加句柄和句柄表。
l     coping算法(coping collector):该算法的提出是为了克服句柄的开销和解决堆碎片的垃圾回收。它开始时把堆分成一个对象面和多个空闲面,程序从对象面为对象分配空间,当对象满了,基于 coping算法的垃圾收集就从根集中扫描活动对象,并将每个活动对象复制到空闲面(使得活动对象所占的内存之间没有空闲洞),这样空闲面变成了对象面,原来的对象面变成了空闲面,程序会在新的对象面中分配内存。
l     generation算法(generational collector):在程序设计中有这样的规律:多数对象存在的时间比较短,少数的存在时间比较长。因此,generation算法将堆分成两个或多个,每个子堆作为对象的一代 (generation)。由于多数对象存在的时间比较短,随着程序丢弃不使用的对象,垃圾收集器将从最年轻的子堆中收集这些对象。在分代式的垃圾收集器运行后,上次运行存活下来的对象移到下一最高代的子堆中,由于老一代的子堆不会经常被回收,因而节省了时间。
l     adaptive算法(adaptive collector):在特定的情况下,一些垃圾收集算法会优于其它算法。基于adaptive算法的垃圾收集器就是监控当前堆的使用情况,并将选择适当算法的垃圾收集器。

了解了GC的算法,也熟悉了JVM堆的构造,看看两者的关系。
GC与JVM堆的关系
在sun 的文档说明中,对JVM堆的新域,是采用coping算法,新域会被分为3个部分
l     第一个部分叫Eden。   (伊甸园??可能是因为亚当和夏娃是人类最早的活动对象?)
l     另两个部分称为辅助生存空间(幼儿园),我这里一个称为A空间,一个称为B空间。

对于新生成的对象,都放在Eden中;当Eden充满时(小孩太多了),GC将开始工作,首先停止应用程序的运行,开始收集垃圾,把所有可找到的对象都复制到A空间中,一旦当A空间充满,GC就把在A空间中可找到的对象都复制到B空间中(会覆盖原有的存储对象),当B空间满的时间,GC就把在B空间中可找到的对象都复制到A空间中,AB在这个过程中互换角色,那位客官说了:拷来拷去,烦不烦啊?什么时候是头?您别急,在活动对象经过一定次数的GC操作后,这些活动对象就会被放到旧域中。对于这些活动对象,新域的幼儿园生活结束了。

新域为什么要这么折腾?
起初在这块我也很迷糊,又查了些资料,原来是这样:应用程序生成的绝大部分对象都是短命的,copying算法最理想的状态是,所有移出Eden的对象都会被收集,因为这些都是短命鬼,经过一定次数的GC后应该被收集,那么移入到旧域的对象都是长命的,这样可以防止AB空间的来回复制影响应用程序。
实际上这种理想状态是很难达到的,应用程序中不可避免地存在长命的对象,copying算法的发明者要这些对象都尽量放在新域中,以保证小范围的复制,压缩旧域的开销可比新域中的复制大得多(旧域在下面说)。

对于旧域,采用的是tracing算法的一种,称为标记-清除-压缩收集器,注意,这有一个压缩,这是个开销挺大的操作。所以,新域中采用copying算法是有道理的。
搜索到的结果与建议
     从上面的推导可以得出很多结论,下面是前辈的经验总结与自已的认识
l     JVM堆的大小决定了GC的运行时间。如果JVM堆的大小超过一定的限度,那么GC的运行时间会很长。
l     对象生存的时间越长,GC需要的回收时间也越长,影响了回收速度。
l     大多数对象都是短命的,所以,如果能让这些对象的生存期在GC的一次运行周期内,wonderful!
l     应用程序中,建立与释放对象的速度决定了垃圾收集的频率。
l     如果GC一次运行周期超过3-5秒,这会很影响应用程序的运行,如果可以,应该减少JVM堆的大小了。
l     前辈经验之谈:通常情况下,JVM堆的大小应为物理内存的80%。

在编程中应该注意到的:
l     大家都知道GC的执行时间是不确定的,调用System.gc()也无法确定GC什么时候执行,这个函数只是向JVM发一个申请,究竟什么时候做,还得老大JVM决定。
l     JVM也不能保证Finalize方法一定会被调用,而且对finalize方法的使用要注意,GC为了支持finalize,对覆盖这个函数的对象作很多附加的工作,同时,它要让在finalize运行之后,将要释放的对象变成可访问到的(树状结构中),GC还要再检查一次这个对象是否变成可访问到的,如果一来,GC的性能就打了折扣了。另外,finalize不同于Java其它的普通方法,它是通过类似于c语言的通过分配内存来干活的,相当于在finalize()内部调用了类似于full()的c函数。所以,资料上不推荐用finalize进行普通清除。
l     查了N多的资料,JVM的缺省选项在通常情况下是最优的。
l     尽早释放无用的对象,如果对象无用了,就将其设置为null,这就告诉GC,这个对象你可以回收了。但要注意,如果对象是很复杂的类型(树,图等),或有监听器,那就要小心了。

附录
下面的地址是讲JVM的一些选项设置,用于设置JVM堆的参数和输入GC调试信息等。
官方设置JVM的选项说明网页
http://java.sun.com/docs/hotspot/VMOptions.html
一个sun公司的人收集的JVM的选项
http://blogs.sun.com/roller/resources/watt/jvm-options-list.html

Some Useful -XX Options

Default values are listed for Java SE 6 for Solaris Sparc with -server. Some options may vary per architecture/OS/JVM version. Platforms with a differing default value are listed in the description.

  • Boolean options are turned on with -XX:+<option> and turned off with -XX:-<option>.
  • Numeric options are set with -XX:<option>=<number>. Numbers can include 'm' or 'M' for megabytes, 'k' or 'K' for kilobytes, and 'g' or 'G' for gigabytes (for example, 32k is the same as 32768).
  • String options are set with -XX:<option>=<string>, are usually used to specify a file, a path, or a list of commands

Flags marked as manageable are dynamically writeable through the JDK management interface (com.sun.management.HotSpotDiagnosticMXBean API) and also through JConsole. In Monitoring and Managing Java SE 6 Platform Applications, Figure 3 shows an example. The manageable flags can also be set through jinfo -flag.

The options below are loosely grouped into three categories.

Behavioral Options


Option and Default ValueDescription
-XX:-AllowUserSignalHandlersDo not complain if the application installs signal handlers. (Relevant to Solaris and Linux only.)
-XX:AltStackSize=16384Alternate signal stack size (in Kbytes). (Relevant to Solaris only, removed from 5.0.)
-XX:-DisableExplicitGCDisable calls to System.gc(), JVM still performs garbage collection when necessary.
-XX:+FailOverToOldVerifierFail over to old verifier when the new type checker fails. (Introduced in 6.)
-XX:+HandlePromotionFailureThe youngest generation collection does not require a guarantee of full promotion of all live objects. (Introduced in 1.4.2 update 11) [5.0 and earlier: false.]
-XX:+MaxFDLimitBump the number of file descriptors to max. (Relevant to Solaris only.)
-XX:PreBlockSpin=10Spin count variable for use with -XX:+UseSpinning. Controls the maximum spin iterations allowed before entering operating system thread synchronization code. (Introduced in 1.4.2.)
-XX:-RelaxAccessControlCheckRelax the access control checks in the verifier. (Introduced in 6.)
-XX:+ScavengeBeforeFullGCDo young generation GC prior to a full GC. (Introduced in 1.4.1.)
-XX:+UseAltSigsUse alternate signals instead of SIGUSR1 and SIGUSR2 for VM internal signals. (Introduced in 1.3.1 update 9, 1.4.1. Relevant to Solaris only.)
-XX:+UseBoundThreadsBind user level threads to kernel threads. (Relevant to Solaris only.)
-XX:-UseConcMarkSweepGCUse concurrent mark-sweep collection for the old generation. (Introduced in 1.4.1)
-XX:+UseGCOverheadLimitUse a policy that limits the proportion of the VM's time that is spent in GC before an OutOfMemory error is thrown. (Introduced in 6.)
-XX:+UseLWPSynchronizationUse LWP-based instead of thread based synchronization. (Introduced in 1.4.0. Relevant to Solaris only.)
-XX:-UseParallelGCUse parallel garbage collection for scavenges. (Introduced in 1.4.1)
-XX:-UseParallelOldGCUse parallel garbage collection for the full collections. Enabling this option automatically sets -XX:+UseParallelGC. (Introduced in 5.0 update 6.)
-XX:-UseSerialGCUse serial garbage collection. (Introduced in 5.0.)
-XX:-UseSpinningEnable naive spinning on Java monitor before entering operating system thread synchronizaton code. (Relevant to 1.4.2 and 5.0 only.) [1.4.2, multi-processor Windows platforms: true]
-XX:+UseTLABUse thread-local object allocation (Introduced in 1.4.0, known as UseTLE prior to that.) [1.4.2 and earlier, x86 or with -client: false]
-XX:+UseSplitVerifierUse the new type checker with StackMapTable attributes. (Introduced in 5.0.)[5.0: false]
-XX:+UseThreadPrioritiesUse native thread priorities.
-XX:+UseVMInterruptibleIOThread interrupt before or with EINTR for I/O operations results in OS_INTRPT. (Introduced in 6. Relevant to Solaris only.)

Back to Options


Performance Options


Option and Default ValueDescription
-XX:+AggressiveOptsTurn on point performance compiler optimizations that are expected to be default in upcoming releases. (Introduced in 5.0 update 6.)
-XX:CompileThreshold=10000Number of method invocations/branches before compiling [-client: 1,500]
-XX:LargePageSizeInBytes=4mSets the large page size used for the Java heap. (Introduced in 1.4.0 update 1.) [amd64: 2m.]
-XX:MaxHeapFreeRatio=70Maximum percentage of heap free after GC to avoid shrinking.
-XX:MaxNewSize=sizeMaximum size of new generation (in bytes). Since 1.4, MaxNewSize is computed as a function of NewRatio. [1.3.1 Sparc: 32m; 1.3.1 x86: 2.5m.]
-XX:MaxPermSize=64mSize of the Permanent Generation. [5.0 and newer: 64 bit VMs are scaled 30% larger; 1.4 amd64: 96m; 1.3.1 -client: 32m.]
-XX:MinHeapFreeRatio=40Minimum percentage of heap free after GC to avoid expansion.
-XX:NewRatio=2Ratio of new/old generation sizes. [Sparc -client: 8; x86 -server: 8; x86 -client: 12.]-client: 4 (1.3) 8 (1.3.1+), x86: 12]
-XX:NewSize=2.125mDefault size of new generation (in bytes) [5.0 and newer: 64 bit VMs are scaled 30% larger; x86: 1m; x86, 5.0 and older: 640k]
-XX:ReservedCodeCacheSize=32mReserved code cache size (in bytes) - maximum code cache size. [Solaris 64-bit, amd64, and -server x86: 48m; in 1.5.0_06 and earlier, Solaris 64-bit and and64: 1024m.]
-XX:SurvivorRatio=8Ratio of eden/survivor space size [Solaris amd64: 6; Sparc in 1.3.1: 25; other Solaris platforms in 5.0 and earlier: 32]
-XX:TargetSurvivorRatio=50Desired percentage of survivor space used after scavenge.
-XX:ThreadStackSize=512Thread Stack Size (in Kbytes). (0 means use default stack size) [Sparc: 512; Solaris x86: 320 (was 256 prior in 5.0 and earlier); Sparc 64 bit: 1024; Linux amd64: 1024 (was 0 in 5.0 and earlier); all others 0.]
-XX:+UseBiasedLockingEnable biased locking. For more details, see this tuning example. (Introduced in 5.0 update 6.) [5.0: false]
-XX:+UseFastAccessorMethodsUse optimized versions of Get<Primitive>Field.
-XX:-UseISMUse Intimate Shared Memory. [Not accepted for non-Solaris platforms.] For details, see Intimate Shared Memory.
-XX:+UseLargePagesUse large page memory. (Introduced in 5.0 update 5.) For details, see Java Support for Large Memory Pages.
-XX:+UseMPSSUse Multiple Page Size Support w/4mb pages for the heap. Do not use with ISM as this replaces the need for ISM. (Introduced in 1.4.0 update 1, Relevant to Solaris 9 and newer.) [1.4.1 and earlier: false]

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值