答复: HotSpot VM 内存堆的两个Survivor区

把先前在论坛回复的一些帖打捞进来。这篇的原帖是:[url=http://www.iteye.com/topic/894148#1886654]HotSpot VM 内存堆的两个Survivor区[/url]
下面是回复内容,带补充。

=================================================================

[quote="kingkan"]请教下前辈们:

1.手动System.gc()与JVM自动gc有什么根本上的区别么?在程序里面一个对象用完的时候,马上使用System.gc(),通过内存使用数据查看,该对象的内存是还没释放的。但是对象使用完,马上设置为null,再System.gc(),该对象的内存就被释放了,不解。

2.用户能自己设置JVM选择何种GC算法来进行GC么?[/quote]
1、根本的区别之一:System.gc()可能在自动GC原本不会进入GC的位置上进入GC。
正常情况下,Java代码要尝试在GC堆上分配空间的时候才会触发GC;换句话说,基本上是“new”(或者类似的会隐式在GC堆上分配空间)的时候才会触发GC。但System.gc()、JVMTI的强制GC等动作都在正常情况之外提示系统要做一次GC。
[quote="kingkan"]在程序里面一个对象用完的时候,马上使用System.gc(),通过内存使用数据查看,该对象的内存是还没释放的。但是对象使用完,马上设置为null,再System.gc(),该对象的内存就被释放了,不解。[/quote]
这个情况即便在HotSpot上也不一定,要看被测的方法有没有被JIT编译过。解释执行的时候基本上没啥优化,所以局部变量的存活范围跟源码里看起来是一致的。但因为HotSpot的JIT编译带有优化,一个局部变量在一个方法里被使用过的赋值才会有效,而未被使用过的赋值很可能被消除掉。这样,在方法最后把局部变量设为null就是徒劳的,赋值动作本身都会被消除。事实上不需要额外的赋值为null的动作,JIT编译器也会尽可能的缩小变量的有效范围,所以完全没必要在方法末尾将局部变量置null。
在.NET的CLR上,由于方法总是要被编译了才可以执行(AOT或者JIT),而且编译也带有优化,源码里变量的引用状况跟实际运行时的引用状况的差异可能更明显些,像[url=http://rednaxelafx.iteye.com/blog/248610]这个例子[/url]就比较极端。

2011-07-11补充:Peter Burka发了篇博文也提到了对象的生命周期可能比源码上看起来的要短的情况,[url=http://thevirtualmachinist.blogspot.com/2011/07/subtle-issue-of-reachability.html]A subtle issue of reachability[/url]

话说回来,Runtime.totalMemory()和Runtime.freeMemory()都是很RP的方法,其实并不适合细粒度观察…不知道您是用什么方式来“通过内存使用数据查看,该对象的内存是还没释放的”呢?
HotSpot默认会对方法调用次数的计数器做“衰减”,每进一次GC就会检查是否已到半衰周期,到了就会把所有方法的调用次数的计数器减半。如果写microbenchmark的话,在被测的方法里插入System.gc()很可能会带来干扰,使被测方法的调用次数始终达不到编译的条件,导致其不被JIT编译。要禁用计数器衰减的话,启动VM的时候要给参数[b]-XX:-UseCounterDecay[/b]。要确认某个方法有没有被JIT编译请使用[b]-XX:+PrintCompilation[/b]。
靠microbenchmark来观察某个对象有没有被GC回收多半是不准确的。可以具体情况具体分析。

另外值得注意的是,System.gc()不一定是触发所谓的“full GC”或者叫“major GC”。
在Sun JDK6与OpenJDK 6的HotSpot里,"[url=http://hg.openjdk.java.net/jdk6/jdk6/hotspot/file/tip/src/share/vm/gc_interface/gcCause.hpp]GCCause[/url]是[b]_java_lang_system_gc[/b]"的时候,如果VM启动参数[url=http://hg.openjdk.java.net/jdk6/jdk6/hotspot/file/tip/src/share/vm/runtime/globals.hpp][b]DisableExplicitGC[/b][/url]为false,则会触发一次full GC,如果该参数为true则完全不触发任何GC。要将这个参数设置为true,启动的时候写上[b]-XX:+DisableExplicitGC[/b]就行。
HotSpot对System.gc()有特别处理,最主要的地方体现在一次System.gc()是否与普通GC一样会触发GC的统计/阈值数据的更新——HotSpot里的许多GC算法都带有自适应的功能,会根据先前收集的效率来决定接下来的GC中使用的参数,但System.gc()默认不更新这些统计数据,避免用户强行调用GC对这些自适应功能的干扰(可以参考HotSpot的[b]UseAdaptiveSizePolicyWithSystemGC[/b]参数,默认是false)。除此之外,在HotSpot里,System.gc()所触发的full GC跟普通的full GC没啥大差别。
当使用Concurrent Mark-Sweep (CMS)时,可以通过[b]-XX:+ExplicitGCInvokesConcurrent[/b] 或 [b]-XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses[/b] 参数来指定System.gc()触发并发GC而不是full GC。在CMS中,并发GC不对old gen做压缩,而full GC是stop-the-world的并且会做压缩。

------------------

在Oracle JRockit里,[url=http://download.oracle.com/docs/cd/E13150_01/jrockit_jvm/jrockit/geninfo/devapps/codeprac.html#wp998554]System.gc()触发的是nursery GC[/url](如果选择了分代GC的话;如果选择的不是分代式GC算法则谈不上nursery还是old)。与HotSpot相同,可以通过一个参数禁用System.gc():[b]-XXnoSystemGC[/b]。也可以通过另一个参数来强制System.gc()做full GC:[b]-XXfullSystemGC[/b]。
JRockit R28里,禁用System.gc()的推荐参数是[b]-XX:AllowSystemGC=false[/b],而设定System.gc()触发full GC的参数是[b]-XX:FullSystemGC=true[/b]。

------------------

在IBM JDK的JVM里,System.gc()同样可以禁用——使用[b]-Xdisableexplicitgc[/b]参数。另外也有一些可以调节System.gc()触发的GC内容的参数,如-Xcompactexplicitgc、-Xnocompactexplicitgc之类。

============================================================

2、对OpenJDK 6里的HotSpot VM,请看这个文件,grep出/Use.*GC,/就知道了:
curl 'http://hg.openjdk.java.net/jdk6/jdk6/hotspot/raw-file/tip/src/share/vm/runtime/globals.hpp' | grep -A 2 -E 'Use.*GC,'

  product(bool, UseSerialGC, false,                                         \
"Use the serial garbage collector") \
\
product(bool, UseG1GC, false, \
"Use the Garbage-First garbage collector") \
\
product(bool, UseParallelGC, false, \
"Use the Parallel Scavenge garbage collector") \
\
product(bool, UseParallelOldGC, false, \
"Use the Parallel Old garbage collector") \
\
--
product(bool, UseMaximumCompactionOnSystemGC, true, \
"In the Parallel Old garbage collector maximum compaction for " \
"a system GC") \
--
product(bool, UseConcMarkSweepGC, false, \
"Use Concurrent Mark-Sweep GC in the old generation") \
\
--
develop(bool, UseAsyncConcMarkSweepGC, true, \
"Use Asynchronous Concurrent Mark-Sweep GC in the old generation")\
\
--
product(bool, UseParNewGC, false, \
"Use parallel threads in the new generation.") \
\
--
product(bool, UseAdaptiveSizePolicyWithSystemGC, false, \
"Use statistics from System.GC for adaptive size policy") \
\

这样grep出来的启动参数中,UseMaximumCompactionOnSystemGC和UseAdaptiveSizePolicyWithSystemGC不是选择GC算法类型的参数,另外几个都是。它们分别是
·UseSerialGC
·UseG1GC
·UseParallelGC
·UseParallelOldGC
·UseAsyncConcMarkSweepGC(产品模式不可调)
·UseConcMarkSweepGC
·UseParNewGC
它们之间的关系请参考:[url=https://blogs.oracle.com/jonthecollector/entry/our_collectors]Jon Masamitsu: Our Collectors[/url]
[quote="Jon Masamitsu"][img]https://blogs.oracle.com/jonthecollector/resource/Collectors.jpg[/img][/quote]

Sun(=> Oracle)的产品版JDK 6里的HotSpot同上。

------------------

JRockit R28的话,GC算法的基本设定可以用下面几个参数:
[table]
| 参数名 | 意义 |
| -Xgc:singlecon 或 -Xgc:singleconcon | Concurrent Mark & Sweep 不分代,并发收集 |
| -Xgc:gencon 或 -Xgc:genconcon | Generational Concurrent Mark & Sweep 分代式,并发收集 |
| -Xgc:parallel 或 -Xgc:singlepar 或 -Xgc:singleparpar | Parallel Mark & Sweep 不分代,并行收集 |
| -Xgc:genpar 或 -Xgc:genparpar | Generational Parallel Mark & Sweep 分代式,并行收集 |
| -Xgc:singleconpar | Concurrent Mark, Parallel Sweep |
| -Xgc:genconpar | Generational Concurrent Mark, Parallel Sweep |
| -Xgc:singleparcon | Parallel Mark, Concurrent Sweep |
| -Xgc:genparcon | Generational Parallel Mark, Concurrent Sweep |
[/table]
不过更推荐并且也更简单的是设定优化的目标,例如这几个参数:
-XgcPrio:throughput
-XgcPrio:pausetime
-XgcPrio:deterministic

有[url=http://blogs.oracle.com/jrockitdetails/entry/on_garbage_collector_selection]老帖描述了当时的JRockit该如何选择GC实现[/url]。
JRockit很有趣的一点是,当用户指定的是GC“模式”而不是具体的GC“策略”时,JRockit有可能在运行时根据运行状态改变具体的GC“策略”(只有选择了singlepar时无法改变策略,因为这个策略不需要write barrier但其它策略都需要)。

------------------

IBM J9有诸如下面几种设定GC算法的VM参数:
-Xgcpolicy:optthruput
-Xgcpolicy:optavgpause
-Xgcpolicy:gencon
-Xgcpolicy:subpool
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值