JDI でオブジェクトの世代(Young・Old)を判別する2
前回 の処理を sun.jvm.hotspot.oops.ObjectHeap
を使って高速化してみたいと思います。(世代の判別方法などは前回と同じ)
使用した環境は前回と同じです。
- Groovy 2.4.6
- Java SE 8u92 64bit版 (JDK)
ソースは http://github.com/fits/try_samples/tree/master/blog/20160506/
ObjectHeap で Oop を取得
ObjectReference の代わりに、sun.jvm.hotspot.oops.ObjectHeap
の iterate(HeapVisitor)
メソッドを使えば Oop を取得できます。
今回のような方法では、以下の理由で iterate メソッドの引数へ SAJDIClassLoader がロードした sun.jvm.hotspot.oops.HeapVisitor
インターフェースの実装オブジェクトを与える必要があります。
- JDI の内部で管理している Serviceability Agent API は
sun.jvm.hotspot.jdi.SAJDIClassLoader
によってロードされている
下記サンプルでは SAJDIClassLoader がロードした HeapVisitor を入手し、asType
を使って実装オブジェクトを作成しています。
また、HeapVisitor の doObj
で false を返すと処理を継続し、true を返すと中止 ※ するようです。
※ 厳密には、 対象としている Address 範囲の while ループを break するだけで、 その外側の(liveRegions に対する)for ループは継続するようです (ObjectHeap の iterateLiveRegions メソッドのソース参照)
なお、ObjectHeap は sun.jvm.hotspot.jdi.VirtualMachineImpl
から saObjectHeap()
で取得するか、sun.jvm.hotspot.runtime.VM
から取得します。
check_gen2.groovy
import com.sun.jdi.Bootstrap def pid = args[0] def prefix = (args.length > 1)? args[1]: '' def manager = Bootstrap.virtualMachineManager() def connector = manager.attachingConnectors().find { it.name() == 'sun.jvm.hotspot.jdi.SAPIDAttachingConnector' } def params = connector.defaultArguments() params.get('pid').setValue(pid) def vm = connector.attach(params) // 世代の判定処理を返す generation = { heap -> def hasYoungGen = heap.metaClass.getMetaMethod('youngGen') != null [ young: hasYoungGen? heap.youngGen(): heap.getGen(0), old: hasYoungGen? heap.oldGen(): heap.getGen(1) ] } try { def uv = vm.saVM.universe def gen = generation(uv.heap()) def youngGen = gen.young def oldGen = gen.old println "*** youngGen=${youngGen}, oldGen=${oldGen}" println '' def objHeap = vm.saObjectHeap() // 以下でも可 //def objHeap = vm.saVM.objectHeap // SAJDIClassLoader がロードした HeapVisitor インターフェースを取得 def heapVisitorCls = uv.class.classLoader.loadClass('sun.jvm.hotspot.oops.HeapVisitor') // SAJDIClassLoader がロードした HeapVisitor インターフェースを実装 def heapVisitor = [ prologue: { size -> }, epilogue: {}, doObj: { oop -> def clsName = oop.klass.name.asString() if (clsName.startsWith(prefix)) { def age = oop.mark.age() // 世代の判別 def inYoung = youngGen.isIn(oop.handle) def inOld = oldGen.isIn(oop.handle) def identityHash = '' try { identityHash = Long.toHexString(oop.identityHash()) } catch (e) { } println "class=${clsName}, hash=${identityHash}, handle=${oop.handle}, age=${age}, inYoung=${inYoung}, inOld=${inOld}" } // 処理を継続する場合は false を返す false } ].asType(heapVisitorCls) objHeap.iterate(heapVisitor) } finally { vm.dispose() }
動作確認
前回と同じように、実行中の apache-tomcat-9.0.0.M4 へ適用してみました。
前回と異なり、クラス名が '/' で区切られている点に注意
実行例1 (Windows の場合)
> jps 3604 Bootstrap 4516 Jps
> groovy -cp %JAVA_HOME%/lib/sa-jdi.jar check_gen2.groovy 3604 org/apache/catalina/core/StandardContext *** youngGen=sun.jvm.hotspot.gc_implementation.parallelScavenge.PSYoungGen@0x0000000002149ab0, oldGen=sun.jvm.hotspot.gc_implementation.parallelScavenge.PSOldGen@0x0000000002149b40 class=org/apache/catalina/core/StandardContextValve, hash=0, handle=0x00000000c3a577d0, age=1, inYoung=false, inOld=true class=org/apache/catalina/core/StandardContext$NoPluggabilityServletContext, hash=0, handle=0x00000000c3a633d8, age=0, inYoung=false, inOld=true class=org/apache/catalina/core/StandardContext$ContextFilterMaps, hash=0, handle=0x00000000c3a63ef0, age=1, inYoung=false, inOld=true class=org/apache/catalina/core/StandardContext$NoPluggabilityServletContext, hash=0, handle=0x00000000ebc46da0, age=0, inYoung=true, inOld=false class=org/apache/catalina/core/StandardContext, hash=6f2d2815, handle=0x00000000eddfeaa0, age=1, inYoung=true, inOld=false class=org/apache/catalina/core/StandardContext, hash=21f2e66b, handle=0x00000000eddff238, age=3, inYoung=true, inOld=false ・・・
実行例2 (Linux の場合)
$ jps 2778 Jps 2766 Bootstrap
$ groovy -cp $JAVA_HOME/lib/sa-jdi.jar check_gen2.groovy 2766 org/apache/catalina/core/StandardContext *** youngGen=sun.jvm.hotspot.memory.DefNewGeneration@0x00007f0760019cb0, oldGen=sun.jvm.hotspot.memory.TenuredGeneration@0x00007f076001bfc0 class=org/apache/catalina/core/StandardContext, hash=497fe2c4, handle=0x00000000f821bf90, age=0, inYoung=true, inOld=false class=org/apache/catalina/core/StandardContext$ContextFilterMaps, hash=0, handle=0x00000000f821c5d8, age=0, inYoung=true, inOld=false class=org/apache/catalina/core/StandardContextValve, hash=0, handle=0x00000000f821ca60, age=0, inYoung=true, inOld=false ・・・ class=org/apache/catalina/core/StandardContext, hash=5478de1a, handle=0x00000000fb12b310, age=1, inYoung=false, inOld=true class=org/apache/catalina/core/StandardContext$NoPluggabilityServletContext, hash=0, handle=0x00000000fb12f6b0, age=0, inYoung=false, inOld=true class=org/apache/catalina/core/StandardContext$ContextFilterMaps, hash=0, handle=0x00000000fb131a80, age=0, inYoung=false, inOld=true class=org/apache/catalina/core/StandardContextValve, hash=0, handle=0x00000000fb1398b0, age=0, inYoung=false, inOld=true