- 浏览: 362537 次
- 性别:
- 来自: 珠海
文章分类
最新评论
-
wujyou:
大神啊,我也是这期间读的大学,比起成就,惭愧不已
[Private] 大学回忆,纪念2002-2006 -
hclovehf:
估计大佬现在已经不做java了吧,关于第二版中的,6.3.7里 ...
发布《Java虚拟机规范 (Java SE 7 中文版)》 -
u012149894:
目前 这本书在清华大学出版社网站 还存在销售
针对清华大学出版社《解析Java虚拟机开发:权衡优化、高效和安全的最优方案》一书抄袭本人作品的声明 -
homesangsang:
18年毕业菜鸡,前来膜拜,又是好几届人过去了
[Private] 大学回忆,纪念2002-2006 -
tuablove:
不得不赞,谢谢。
发布《Java虚拟机规范 (Java SE 7 中文版)》
纯粹记录一下,JE随便遇个30帖路人都很厉害,日后多思少言,验证问题不能想当然。
http://www.iteye.com/topic/894148?page=2
一楼:
1、根本的区别之一:System.gc()可能在自动GC原本不会进入GC的位置上进入GC。
正常情况下,Java代码要尝试在GC堆上分配空间的时候才会触发GC;换句话说,基本上是“new”的时候才会触发GC。但System.gc()、JVMTI的强制GC等动作都在正常情况之外提示系统要做一次GC。
这个情况即便在HotSpot上也不一定,要看被测的方法有没有被JIT编译过。解释执行的时候基本上没啥优化,所以局部变量的存活范围跟源码里看起来是一致的。但因为HotSpot的JIT编译带有优化,一个局部变量在一个方法里被使用过的赋值才会有效,而未被使用过的赋值很可能被消除掉。这样,在方法最后把局部变量设为null就是徒劳的,赋值动作本身都会被消除。事实上不需要额外的赋值为null的动作,JIT编译器也会尽可能的缩小变量的有效范围,所以完全没必要在方法末尾将局部变量置null。
在.NET的CLR上,由于方法总是要被编译了才可以执行(AOT或者JIT),而且编译也带有优化,源码里变量的引用状况跟实际运行时的引用状况的差异可能更明显些,像这个例子就比较极端。
话说回来,Runtime.totalMemory()和Runtime.freeMemory()都是很RP的方法,其实并不适合细粒度观察…不知道您是用什么方式来“通过内存使用数据查看,该对象的内存是还没释放的”呢?
HotSpot默认会对方法调用次数的计数器做“衰减”,每进一次GC就会检查是否已到半衰周期,到了就会把所有方法的调用次数的计数器减半。如果写microbenchmark的话,在被测的方法里插入System.gc()很可能会带来干扰,使被测方法的调用次数始终达不到编译的条件,导致其不被JIT编译。要禁用计数器衰减的话,启动VM的时候要给参数-XX:-UseCounterDecay。要确认某个方法有没有被JIT编译请使用-XX:+PrintCompilation。
靠microbenchmark来观察某个对象有没有被GC回收多半是不准确的。可以具体情况具体分析。
另外值得注意的是,System.gc()不一定是触发所谓的“full GC”或者叫“major GC”。
在Sun JDK6与OpenJDK 6的HotSpot里,"GCCause是_java_lang_system_gc"的时候,如果VM启动参数DisableExplicitGC为false,则会触发一次full GC,如果该参数为true则完全不触发任何GC。要将这个参数设置为true,启动的时候写上-XX:+DisableExplicitGC就行。
HotSpot对System.gc()有特别处理,最主要的地方体现在一次System.gc()是否与普通GC一样会触发GC的统计/阈值数据的更新——HotSpot里的许多GC算法都带有自适应的功能,会根据先前收集的效率来决定接下来的GC中使用的参数,但System.gc()默认不更新这些统计数据,避免用户强行调用GC对这些自适应功能的干扰。除此之外,在HotSpot里,System.gc()所触发的full GC跟普通的full GC没啥大差别。
------------------
在Oracle JRockit里,System.gc()触发的是nursery GC(如果选择了分代GC的话;如果选择的不是分代式GC算法则谈不上nursery还是old)。与HotSpot相同,可以通过一个参数禁用System.gc():-XXnoSystemGC。也可以通过另一个参数来强制System.gc()做full GC:-XXfullSystemGC。
JRockit R28里,禁用System.gc()的推荐参数是-XX:AllowSystemGC=false,而设定System.gc()触发full GC的参数是-XX:FullSystemGC=true。
------------------
在IBM JDK的JVM里,System.gc()同样可以禁用——使用-Xdisableexplicitgc参数。另外也有一些可以调节System.gc()触发的GC内容的参数,如-Xcompactexplicitgc、-Xnocompactexplicitgc之类。
============================================================
2、对OpenJDK 6里的HotSpot VM,请看这个文件,grep出/Use.*GC,/就知道了:
这样grep出来的启动参数中,UseMaximumCompactionOnSystemGC和UseAdaptiveSizePolicyWithSystemGC不是选择GC算法类型的参数,另外几个都是。它们分别是
·UseSerialGC
·UseG1GC
·UseParallelGC
·UseParallelOldGC
·UseAsyncConcMarkSweepGC(产品模式不可调)
·UseConcMarkSweepGC
·UseParNewGC
它们之间的关系请参考:Jon Masamitsu: Our Collectors
Sun(=> Oracle)的产品版JDK 6里的HotSpot同上。
------------------
JRockit R28的话,GC算法的基本设定可以用下面几个参数:
-Xgc:singlecon
-Xgc:gencon
-Xgc:singlepar
-Xgc:genpar
不过更推荐并且也更简单的是设定优化的目标,例如这几个参数:
-XgcPrio:throughput
-XgcPrio:pausetime
-XgcPrio:deterministic
------------------
IBM J9有诸如下面几种设定GC算法的VM参数:
-Xgcpolicy:optthruput
-Xgcpolicy:optavgpause
-Xgcpolicy:gencon
-Xgcpolicy:subpool
真心感谢RednaxelaFX大哥的解答,又学到了很多东西,对于JVM的迷雾又可以清晰点了。
对于JAVA内存的适合细粒度观察,有没有一些建议?
真是大牛,研究细致到这个级别了,膜拜一下^_^ , 到这个级别都研究源码去了吧
二楼:
真心感谢RednaxelaFX大哥的解答,又学到了很多东西,对于JVM的迷雾又可以清晰点了。
对于JAVA内存的适合细粒度观察,有没有一些建议?
撒加应该是把你的问题想复杂了,你说的设置为null才会回收应该是指下面的情形:这3段代码加-verbose:gc运行。
结果:placeholder没有被干掉,这是天经地义的,因为执行System.gc()的时候PC计数器还没越过placeholder的作用域。
结果:placeholder还是没有被干掉,这时候PC计数器越过了它的作用域,但是它在本地变量表中的Slot还没有被清除,也没有被其他变量复用。
结果:这次placeholder完蛋了。虽然执行System.gc()的时候这个方法的栈帧没有被回收,但本地变量表那个slot被填null了,使用int a= 0的话就被变量a复用了,这时候GC Roots才没有placeholder。
这时候设null也不能说一点意义都没有,意义真的不大就是了,等jit编译了就会削除掉。
另外,要在程序中精确得知某个变量有没有挂掉,如撒加所说,靠Runtime.getRuntime().freeMemory()是不靠谱的,毕竟正常人不会和上面例子一样没事搞个64M的对象来耍,变量挂掉内存变化就会很明显看出来。要做到这点可以考虑用PhantomReference,回收后在ReferenceQueue中会收到消息。
三楼:
四楼:
请问楼上的大大,测试的环境和启动参数是什么?
小的也看了下,发现fillHeap在两种条件下都编译了
小的测试的环境是XP SP2,JDK 1.6.0_14 fastdebug
client编译的fillHeap:
server编译的fillHeap:
两个版本的placeholder = null都被干掉了。编译的代码结构差不多,快速路径中,
·先检查是否将有栈溢出(mov %eax,-0x4000(%esp)),
·然后保存上一栈帧(push %ebp)并建立新栈帧(sub $0x8,%esp),
·然后尝试从TLAB申请空间,
·然后初始化数组的对象头,并对数组内容清零,
·(注意这里没有了placeholder = null的动作),
·然后判断go的真假,真的时候调用System.gc(),
·然后撤销栈帧(mov %ebp,%esp)并恢复上一栈帧(pop %ebp),
·然后临返回前检查safepoint(test %eax,0x9b0000),
·最后返回(ret)。
client代码中,指向新建数组实例的指针存在%eax里,也就是代码中placeholder变量被分配到了%eax上。但给数组分配了空间并将数组内容清零后没有再出现对%eax赋值的指令,可准确判断placeholder = null的代码被消除了。
server代码中,0x00bdae95: xor %eax,%eax用于数组内容清零。在这之前,指向新建数组实例的指针就存在%eax,也就是placeholder变量分配在前半部分代码里分配到%eax上。但执行了异或指令后%eax的值就被破坏掉了,虽然实际效果跟placeholder = null一样,但因为后面%eax转为用作数组清零所以这个赋值并不能看作是placeholder = null。
五楼:
那把前面的话换一个说法:“placeholder = null并不是无意义的,是否存在placeholder = null会直接影响jit后的编译结果”。兄弟认同否?
六楼:
小的不敢认同,对不对还等大大们自己定夺
小的把placeholder = null注释掉再跑了一次,
client编译出来的fillHeap:
代码与带有placeholder = null时编译出来的一模一样。唯有的差异是原始字节码短了2字节,所以PrintAssembly出来的注释的基本块大小有点差异。
server编译出来的fillHeap:
与client的情况一样,去掉placeholder = null之后编译出来的代码没有变化。直接diff能看到的都是地址上的差异,因为多次运行编译出来的代码不一定在同一地址上,这是正常的;除地址之外,操作指令都一模一样。
IcyFenix大大,小的也请教了问题,大大还没回答呢
七楼:
小的把placeholder = null注释掉再跑了一次,
与client的情况一样,去掉placeholder = null之后编译出来的代码没有变化。直接diff能看到的都是地址上的差异,因为多次运行编译出来的代码不一定在同一地址上,这是正常的;除地址之外,操作指令都一模一样。
IcyFenix大大,小的也请教了问题,大大还没回答呢
请问楼上的大大,测试的环境和启动参数是什么?
公司里测试的是6u21,现在在家里用笔记本,下面测试用的是刚刚下的openjdk7-b127-fastdebug
D:\>java -version
java version "1.7.0-ea-fastdebug"
Java(TM) SE Runtime Environment (build 1.7.0-ea-fastdebug-b127)
Java HotSpot(TM) Client VM (build 20.0-b06-fastdebug, mixed mode)
参数是就2个:-verbose:gc -XX:+PrintCompilation,由于没有梯子,下载不到墙外的hsdis-i386.dll,所以没加PrintAssembly,如果打印出来的asm都一样的话,对于下面这两次测试的结果,黑体字部分我觉得很疑惑,请兄弟不吝指教。
不带placeholder = null的版本
225 1 java.lang.String::hashCode (67 bytes)
VM option '+PrintCompilation'
228 2 java.lang.String::charAt (33 bytes)
229 3 java.lang.String::indexOf (87 bytes)
230 4 java.lang.Object::<init> (1 bytes)
230 5 java.lang.String::indexOf (166 bytes)
242 6 org.fenixsoft.test.Test::fillHeap (12 bytes)
243 1% org.fenixsoft.test.Test::main @ 5 (26 bytes)
[GC 2080K->267K(15872K), 0.0042400 secs]
[Full GC 267K->267K(15872K), 0.0117231 secs]
[Full GC 65893K->65803K(81476K), 0.0140037 secs]
带placeholder = null的版本
253 1 java.lang.String::hashCode (67 bytes)
VM option '+PrintCompilation'
258 2 java.lang.String::charAt (33 bytes)
259 3 java.lang.String::indexOf (87 bytes)
261 4 java.lang.Object::<init> (1 bytes)
261 5 java.lang.String::indexOf (166 bytes)
274 6 org.fenixsoft.test.Test::fillHeap (17 bytes)
275 1% org.fenixsoft.test.Test::main @ 5 (26 bytes)
[GC 2080K->267K(15872K), 0.0043529 secs]
[Full GC 267K->267K(15872K), 0.0124955 secs]
[Full GC 65893K->267K(81476K), 0.0235428 secs]
八楼:
小的也没用梯子呀,这里就有:http://hllvm.group.iteye.com/group/share
这数字是被编译的方法原本字节码的大小。用javap能看到。
小的跑不带placeholder = null测试用的完整代码是这样:
用javap看fillHeap
字节码就12字节,能对上。
把注释去掉,带上placeholder = null的fillHeap,用javap看
就比不带placeholder = null多两条指令,字节码大小增加到14字节。IcyFenix大大的测试里带上placeholder = null是17字节是因为大大用的测试代码与小的的不一样。所以才有必要把完整的测试代码,运行环境,启动参数都拉出来晒晒,不然讨论的就不是同一个东西。
不知大大是如何跑测试的。小的继续在XP SP2,JDK 1.6.0_14 fastdebug上跑,不带placeholder = null跑出来几组结果
D:\test>java -Xmx128m -Xms128m -XX:+PrintCompilation -XX:+PrintGC -XX:+PrintGCDetails Test
VM option '+PrintCompilation'
VM option '+PrintGC'
VM option '+PrintGCDetails'
1 java.lang.String::hashCode (60 bytes)
2 java.lang.String::charAt (33 bytes)
3 Test::fillHeap (12 bytes)
1% Test::main @ 2 (26 bytes)
[Full GC (System) [Tenured: 65536K->137K(121024K), 0.0829719 secs] 67324K->137K(130112K), [Perm : 2284K->2284K(12288K)], 0.0835611 secs] [Times: user=0.08 sys=0.00, real=0.08 secs]
Heap
def new generation total 9088K, used 162K [0x10010000, 0x109e0000, 0x109e0000)
eden space 8128K, 2% used [0x10010000, 0x10038aa8, 0x10800000)
from space 960K, 0% used [0x10800000, 0x10800000, 0x108f0000)
to space 960K, 0% used [0x108f0000, 0x108f0000, 0x109e0000)
tenured generation total 121024K, used 137K [0x109e0000, 0x18010000, 0x18010000)
the space 121024K, 0% used [0x109e0000, 0x10a02530, 0x10a02600, 0x18010000)
compacting perm gen total 12288K, used 2313K [0x18010000, 0x18c10000, 0x1c010000)
the space 12288K, 18% used [0x18010000, 0x18252710, 0x18252800, 0x18c10000)
No shared spaces configured.
D:\test>java -server -Xmx128m -Xms128m -XX:+PrintCompilation -XX:+PrintGC -XX:+PrintGCDetails -XX:+UseParallelGC Test
VM option '+PrintCompilation'
VM option '+PrintGC'
VM option '+PrintGCDetails'
VM option '+UseParallelGC'
1 java.lang.String::charAt (33 bytes)
2 Test::fillHeap (12 bytes)
1% Test::main @ 2 (26 bytes)
2 made not entrant (2) Test::fillHeap (12 bytes)
[GC [PSYoungGen: 1970K->168K(12736K)] 67506K->65704K(129280K), 0.3141070 secs] [Times: user=0.63 sys=0.00, real=0.31 secs]
[Full GC (System) [PSYoungGen: 168K->0K(12736K)] [PSOldGen: 65536K->137K(116544K)] 65704K->137K(129280K) [PSPermGen: 2286K->2286K(16384K)], 0.0629148 secs] [Times: user=0.06 sys=0.00, real=0.06 secs]
Heap
PSYoungGen total 12736K, used 218K [0x1b1e0000, 0x1c010000, 0x1c010000)
eden space 10944K, 2% used [0x1b1e0000,0x1b216bf0,0x1bc90000)
from space 1792K, 0% used [0x1bc90000,0x1bc90000,0x1be50000)
to space 1792K, 0% used [0x1be50000,0x1be50000,0x1c010000)
PSOldGen total 116544K, used 137K [0x14010000, 0x1b1e0000, 0x1b1e0000)
object space 116544K, 0% used [0x14010000,0x140325e8,0x1b1e0000)
PSPermGen total 16384K, used 2315K [0x10010000, 0x11010000, 0x14010000)
object space 16384K, 14% used [0x10010000,0x10252f90,0x11010000)
D:\test>java -server -Xmx128m -Xms128m -XX:+PrintCompilation -XX:+PrintGC -XX:+PrintGCDetails -XX:+UseSerialGC Test
VM option '+PrintCompilation'
VM option '+PrintGC'
VM option '+PrintGCDetails'
VM option '+UseSerialGC'
1 java.lang.String::charAt (33 bytes)
2 Test::fillHeap (12 bytes)
1% Test::main @ 2 (26 bytes)
2 made not entrant (2) Test::fillHeap (12 bytes)
[Full GC (System) [Tenured: 65536K->137K(116544K), 0.0755938 secs] 67410K->137K(129664K), [Perm : 2286K->2286K(16384K)], 0.0761244 secs] [Times: user=0.08 sys=0.00, real=0.08 secs]
Heap
def new generation total 13120K, used 234K [0x10010000, 0x10e40000, 0x10e40000)
eden space 11712K, 2% used [0x10010000, 0x1004a960, 0x10b80000)
from space 1408K, 0% used [0x10b80000, 0x10b80000, 0x10ce0000)
to space 1408K, 0% used [0x10ce0000, 0x10ce0000, 0x10e40000)
tenured generation total 116544K, used 137K [0x10e40000, 0x18010000, 0x18010000)
the space 116544K, 0% used [0x10e40000, 0x10e625e8, 0x10e62600, 0x18010000)
compacting perm gen total 16384K, used 2315K [0x18010000, 0x19010000, 0x1c010000)
the space 16384K, 14% used [0x18010000, 0x18252f90, 0x18253000, 0x19010000)
No shared spaces configured.
看到的结果跟大大的不一样。那个64M的byte数组能看到被收集了。打上-XX:+PrintGCDetails看到Full GC (System)的是System.gc()的日志。
九楼:
悲剧完毕,over。
其实人家还没把话说完吧。你看帖里的日志里有:
这个made not entrant表示日志中compile_id为2的fillHeap()的编译结果已经报废了。要调查具体原因需要别的信息。
而上面一行main()的日志则说明main()已经被OSR方式编译,更前面带有-XX:+PrintInlining的日志则说明main()中两处对fillHeap()的调用都已经被内联到main()里了。所以真正到那个fillHeap(true, 64 * 1000 * 1000)的调用的时候,执行的代码是已经被内联到main()里的代码。真的要精确分析的话,应该拿main()被编译后的代码来看更准确。
当然,直接看fillHeap()的编译结果也足够回答楼主提出的疑问就是了,关于placeholder = null是否被消除了的问题。于是我就不用费力回答了,哈哈
另外简单说说,HotSpot在GC中做root enumeration的时候,对解释执行中的Java方法与已编译后的Java方法是用不同方式来做的。解释执行比较好办,本身不会对Java代码做什么优化所以可以很直观的获取root信息。而JIT编译器则会为每个编译出来的代码(nmethod对象)带上一组OopMap来记录一些特定位置上的root信息。之前分享的演示稿里有提到这点,没详细说。啥时候我再把它说得详细点更新到演示稿里去。
i see~~thx~~
现在大年初一了,新年快乐。
其实人家还没把话说完吧。你看帖里的日志里有:
这个made not entrant表示日志中compile_id为2的fillHeap()的编译结果已经报废了。要调查具体原因需要别的信息。
而上面一行main()的日志则说明main()已经被OSR方式编译,更前面带有-XX:+PrintInlining的日志则说明main()中两处对fillHeap()的调用都已经被内联到main()里了。所以真正到那个fillHeap(true, 64 * 1000 * 1000)的调用的时候,执行的代码是已经被内联到main()里的代码。真的要精确分析的话,应该拿main()被编译后的代码来看更准确。
当然,直接看fillHeap()的编译结果也足够回答楼主提出的疑问就是了,关于placeholder = null是否被消除了的问题。于是我就不用费力回答了,哈哈
另外简单说说,HotSpot在GC中做root enumeration的时候,对解释执行中的Java方法与已编译后的Java方法是用不同方式来做的。解释执行比较好办,本身不会对Java代码做什么优化所以可以很直观的获取root信息。而JIT编译器则会为每个编译出来的代码(nmethod对象)带上一组OopMap来记录一些特定位置上的root信息。之前分享的演示稿里有提到这点,没详细说。啥时候我再把它说得详细点更新到演示稿里去。
http://www.iteye.com/topic/894148?page=2
一楼:
mingjian01 写道
kingkan 写道
RednaxelaFX 写道
kingkan 写道
请教下前辈们:
1.手动System.gc()与JVM自动gc有什么根本上的区别么?在程序里面一个对象用完的时候,马上使用System.gc(),通过内存使用数据查看,该对象的内存是还没释放的。但是对象使用完,马上设置为null,再System.gc(),该对象的内存就被释放了,不解。
2.用户能自己设置JVM选择何种GC算法来进行GC么?
1.手动System.gc()与JVM自动gc有什么根本上的区别么?在程序里面一个对象用完的时候,马上使用System.gc(),通过内存使用数据查看,该对象的内存是还没释放的。但是对象使用完,马上设置为null,再System.gc(),该对象的内存就被释放了,不解。
2.用户能自己设置JVM选择何种GC算法来进行GC么?
1、根本的区别之一:System.gc()可能在自动GC原本不会进入GC的位置上进入GC。
正常情况下,Java代码要尝试在GC堆上分配空间的时候才会触发GC;换句话说,基本上是“new”的时候才会触发GC。但System.gc()、JVMTI的强制GC等动作都在正常情况之外提示系统要做一次GC。
kingkan 写道
在程序里面一个对象用完的时候,马上使用System.gc(),通过内存使用数据查看,该对象的内存是还没释放的。但是对象使用完,马上设置为null,再System.gc(),该对象的内存就被释放了,不解。
这个情况即便在HotSpot上也不一定,要看被测的方法有没有被JIT编译过。解释执行的时候基本上没啥优化,所以局部变量的存活范围跟源码里看起来是一致的。但因为HotSpot的JIT编译带有优化,一个局部变量在一个方法里被使用过的赋值才会有效,而未被使用过的赋值很可能被消除掉。这样,在方法最后把局部变量设为null就是徒劳的,赋值动作本身都会被消除。事实上不需要额外的赋值为null的动作,JIT编译器也会尽可能的缩小变量的有效范围,所以完全没必要在方法末尾将局部变量置null。
在.NET的CLR上,由于方法总是要被编译了才可以执行(AOT或者JIT),而且编译也带有优化,源码里变量的引用状况跟实际运行时的引用状况的差异可能更明显些,像这个例子就比较极端。
话说回来,Runtime.totalMemory()和Runtime.freeMemory()都是很RP的方法,其实并不适合细粒度观察…不知道您是用什么方式来“通过内存使用数据查看,该对象的内存是还没释放的”呢?
HotSpot默认会对方法调用次数的计数器做“衰减”,每进一次GC就会检查是否已到半衰周期,到了就会把所有方法的调用次数的计数器减半。如果写microbenchmark的话,在被测的方法里插入System.gc()很可能会带来干扰,使被测方法的调用次数始终达不到编译的条件,导致其不被JIT编译。要禁用计数器衰减的话,启动VM的时候要给参数-XX:-UseCounterDecay。要确认某个方法有没有被JIT编译请使用-XX:+PrintCompilation。
靠microbenchmark来观察某个对象有没有被GC回收多半是不准确的。可以具体情况具体分析。
另外值得注意的是,System.gc()不一定是触发所谓的“full GC”或者叫“major GC”。
在Sun JDK6与OpenJDK 6的HotSpot里,"GCCause是_java_lang_system_gc"的时候,如果VM启动参数DisableExplicitGC为false,则会触发一次full GC,如果该参数为true则完全不触发任何GC。要将这个参数设置为true,启动的时候写上-XX:+DisableExplicitGC就行。
HotSpot对System.gc()有特别处理,最主要的地方体现在一次System.gc()是否与普通GC一样会触发GC的统计/阈值数据的更新——HotSpot里的许多GC算法都带有自适应的功能,会根据先前收集的效率来决定接下来的GC中使用的参数,但System.gc()默认不更新这些统计数据,避免用户强行调用GC对这些自适应功能的干扰。除此之外,在HotSpot里,System.gc()所触发的full GC跟普通的full GC没啥大差别。
------------------
在Oracle JRockit里,System.gc()触发的是nursery GC(如果选择了分代GC的话;如果选择的不是分代式GC算法则谈不上nursery还是old)。与HotSpot相同,可以通过一个参数禁用System.gc():-XXnoSystemGC。也可以通过另一个参数来强制System.gc()做full GC:-XXfullSystemGC。
JRockit R28里,禁用System.gc()的推荐参数是-XX:AllowSystemGC=false,而设定System.gc()触发full GC的参数是-XX:FullSystemGC=true。
------------------
在IBM JDK的JVM里,System.gc()同样可以禁用——使用-Xdisableexplicitgc参数。另外也有一些可以调节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
它们之间的关系请参考:Jon Masamitsu: Our Collectors
Jon Masamitsu 写道
Sun(=> Oracle)的产品版JDK 6里的HotSpot同上。
------------------
JRockit R28的话,GC算法的基本设定可以用下面几个参数:
-Xgc:singlecon
-Xgc:gencon
-Xgc:singlepar
-Xgc:genpar
不过更推荐并且也更简单的是设定优化的目标,例如这几个参数:
-XgcPrio:throughput
-XgcPrio:pausetime
-XgcPrio:deterministic
------------------
IBM J9有诸如下面几种设定GC算法的VM参数:
-Xgcpolicy:optthruput
-Xgcpolicy:optavgpause
-Xgcpolicy:gencon
-Xgcpolicy:subpool
真心感谢RednaxelaFX大哥的解答,又学到了很多东西,对于JVM的迷雾又可以清晰点了。
对于JAVA内存的适合细粒度观察,有没有一些建议?
真是大牛,研究细致到这个级别了,膜拜一下^_^ , 到这个级别都研究源码去了吧
二楼:
IcyFenix 写道
kingkan 写道
真心感谢RednaxelaFX大哥的解答,又学到了很多东西,对于JVM的迷雾又可以清晰点了。
对于JAVA内存的适合细粒度观察,有没有一些建议?
撒加应该是把你的问题想复杂了,你说的设置为null才会回收应该是指下面的情形:这3段代码加-verbose:gc运行。
public static void main(String[] args)() { byte[] placeholder = new byte[64 * 1024 * 1024]; System.gc(); }
结果:placeholder没有被干掉,这是天经地义的,因为执行System.gc()的时候PC计数器还没越过placeholder的作用域。
引用
[GC 66846K->65824K(125632K), 0.0032678 secs]
[Full GC 65824K->65746K(125632K), 0.0064131 secs]
[Full GC 65824K->65746K(125632K), 0.0064131 secs]
public static void main(String[] args)() { { byte[] placeholder = new byte[64 * 1024 * 1024]; } System.gc(); }
结果:placeholder还是没有被干掉,这时候PC计数器越过了它的作用域,但是它在本地变量表中的Slot还没有被清除,也没有被其他变量复用。
引用
[GC 66846K->65888K(125632K), 0.0009397 secs]
[Full GC 65888K->65746K(125632K), 0.0051574 secs]
[Full GC 65888K->65746K(125632K), 0.0051574 secs]
public static void main(String[] args)() { { byte[] placeholder = new byte[64 * 1024 * 1024]; placeholder = null; } // 或者上面那句=null的不写,在这里写个int a = 0;也可以 System.gc(); }
结果:这次placeholder完蛋了。虽然执行System.gc()的时候这个方法的栈帧没有被回收,但本地变量表那个slot被填null了,使用int a= 0的话就被变量a复用了,这时候GC Roots才没有placeholder。
引用
[GC 66401K->65778K(125632K), 0.0035471 secs]
[Full GC 65778K->218K(125632K), 0.0140596 secs]
[Full GC 65778K->218K(125632K), 0.0140596 secs]
这时候设null也不能说一点意义都没有,意义真的不大就是了,等jit编译了就会削除掉。
另外,要在程序中精确得知某个变量有没有挂掉,如撒加所说,靠Runtime.getRuntime().freeMemory()是不靠谱的,毕竟正常人不会和上面例子一样没事搞个64M的对象来耍,变量挂掉内存变化就会很明显看出来。要做到这点可以考虑用PhantomReference,回收后在ReferenceQueue中会收到消息。
三楼:
IcyFenix 写道
试了一下下面代码,无论是c1还是c2,jit后placeholder = null都没有被干掉。
撒加用-XX:+PrintCompilation -XX:+PrintAssembly跑一下看看?
public static void fillHeap(boolean go, int n) throws Exception { { byte[] placeholder = new byte[n]; placeholder = null; } if (go) System.gc(); } public static void main(String[] args) throws Exception { for (int i = 0; i < 100000; i++) { fillHeap(false, 1); } fillHeap(true, 64 * 1024 * 1024); }
撒加用-XX:+PrintCompilation -XX:+PrintAssembly跑一下看看?
四楼:
bugmenot 写道
IcyFenix 写道
试了一下下面代码,无论是c1还是c2,jit后placeholder = null都没有被干掉。
撒加用-XX:+PrintCompilation -XX:+PrintAssembly跑一下看看?
public static void fillHeap(boolean go, int n) throws Exception { { byte[] placeholder = new byte[n]; placeholder = null; } if (go) System.gc(); } public static void main(String[] args) throws Exception { for (int i = 0; i < 100000; i++) { fillHeap(false, 1); } fillHeap(true, 64 * 1024 * 1024); }
撒加用-XX:+PrintCompilation -XX:+PrintAssembly跑一下看看?
请问楼上的大大,测试的环境和启动参数是什么?
小的也看了下,发现fillHeap在两种条件下都编译了
小的测试的环境是XP SP2,JDK 1.6.0_14 fastdebug
D:\test>java -version java version "1.6.0_14-ea-fastdebug" Java(TM) SE Runtime Environment (build 1.6.0_14-ea-fastdebug-b04) Java HotSpot(TM) Client VM (build 14.0-b13-fastdebug, mixed mode) D:\test>java -Xms128m -Xmx128m -XX:+PrintCompilation -XX:+PrintInlining Test VM option '+PrintCompilation' VM option '+PrintInlining' 1 java.lang.String::hashCode (60 bytes) 2 java.lang.String::charAt (33 bytes) 3 Test::fillHeap (14 bytes) 1% Test::main @ 2 (26 bytes) @ 10 Test::fillHeap (14 bytes) @ 22 Test::fillHeap (14 bytes) D:\test>java -server -Xms128m -Xmx128m -XX:+PrintCompilation -XX:+PrintInlining Test VM option '+PrintCompilation' VM option '+PrintInlining' 1 java.lang.String::charAt (33 bytes) 2 Test::fillHeap (14 bytes) 1% Test::main @ 2 (26 bytes) @ 10 Test::fillHeap inline (hot) @ 22 Test::fillHeap inline (hot) @ 10 java.lang.System::gc executed < MinInliningThreshold times
client编译的fillHeap:
Decoding compiled method 0x00bb1cc8: Code: [Disassembling for mach='i386'] [Entry Point] [Verified Entry Point] ;; block B3 [0, 0] 0x00bb1db0: mov %eax,-0x4000(%esp) 0x00bb1db7: push %ebp 0x00bb1db8: mov %esp,%ebp 0x00bb1dba: sub $0x18,%esp ;*iload_1 ; - Test::fillHeap@0 (line 4) 0x00bb1dbd: mov %ecx,0x10(%esp) ;; block B0 [0, 7] 0x00bb1dc1: mov %edx,%ebx 0x00bb1dc3: mov $0x18010850,%edx ; {oop({type array byte})} 0x00bb1dc8: mov %ebx,%edi 0x00bb1dca: cmp $0xffffff,%ebx 0x00bb1dd0: ja 0x00bb1e6e 0x00bb1dd6: mov $0x13,%esi 0x00bb1ddb: lea (%esi,%ebx,1),%esi 0x00bb1dde: and $0xfffffff8,%esi 0x00bb1de1: mov %fs:0x0(,%eiz,1),%ecx 0x00bb1de9: mov -0xc(%ecx),%ecx 0x00bb1dec: mov 0x44(%ecx),%eax 0x00bb1def: lea (%eax,%esi,1),%esi 0x00bb1df2: cmp 0x4c(%ecx),%esi 0x00bb1df5: ja 0x00bb1e6e 0x00bb1dfb: mov %esi,0x44(%ecx) 0x00bb1dfe: sub %eax,%esi 0x00bb1e00: movl $0x1,(%eax) 0x00bb1e06: mov %edx,0x4(%eax) 0x00bb1e09: mov %ebx,0x8(%eax) 0x00bb1e0c: sub $0xc,%esi 0x00bb1e0f: je 0x00bb1e52 0x00bb1e15: test $0x3,%esi 0x00bb1e1b: je 0x00bb1e32 0x00bb1e21: push $0x838451c ; {external_word} 0x00bb1e26: call 0x00bb1e2b 0x00bb1e2b: pusha 0x00bb1e2c: call 0x0801b6a0 ; {runtime_call} 0x00bb1e31: hlt 0x00bb1e32: xor %ebx,%ebx 0x00bb1e34: shr $0x3,%esi 0x00bb1e37: jae 0x00bb1e47 0x00bb1e3d: mov %ebx,0xc(%eax,%esi,8) 0x00bb1e41: je 0x00bb1e52 0x00bb1e47: mov %ebx,0x8(%eax,%esi,8) 0x00bb1e4b: mov %ebx,0x4(%eax,%esi,8) 0x00bb1e4f: dec %esi 0x00bb1e50: jne 0x00bb1e47 ;*newarray ; - Test::fillHeap@1 (line 4) 0x00bb1e52: mov 0x10(%esp),%ecx 0x00bb1e56: cmp $0x0,%ecx ;; 22 branch [EQ] [B2] 0x00bb1e59: je 0x00bb1e64 ;*ifeq ; - Test::fillHeap@7 (line 7) ;; block B1 [10, 10] 0x00bb1e5f: call 0x00b6b3d0 ; OopMap{off=180} ;*invokestatic gc ; - Test::fillHeap@10 (line 8) ; {static_call} ;; block B2 [13, 13] 0x00bb1e64: mov %ebp,%esp 0x00bb1e66: pop %ebp 0x00bb1e67: test %eax,0x960100 ; {poll_return} 0x00bb1e6d: ret ;; NewTypeArrayStub slow case 0x00bb1e6e: call 0x00baef50 ; OopMap{off=195} ;*newarray ; - Test::fillHeap@1 (line 4) ; {runtime_call} 0x00bb1e73: jmp 0x00bb1e52 0x00bb1e75: nop 0x00bb1e76: nop 0x00bb1e77: hlt 0x00bb1e78: hlt 0x00bb1e79: hlt 0x00bb1e7a: hlt 0x00bb1e7b: hlt 0x00bb1e7c: hlt 0x00bb1e7d: hlt 0x00bb1e7e: hlt 0x00bb1e7f: hlt [Stub Code] 0x00bb1e80: nop ; {no_reloc} 0x00bb1e81: nop 0x00bb1e82: mov $0x0,%ebx ; {static_stub} 0x00bb1e87: jmp 0x00bb1e87 ; {runtime_call} [Exception Handler] 0x00bb1e8c: mov $0xdead,%ebx 0x00bb1e91: mov $0xdead,%ecx 0x00bb1e96: mov $0xdead,%edx 0x00bb1e9b: mov $0xdead,%esi 0x00bb1ea0: mov $0xdead,%edi 0x00bb1ea5: jmp 0x00bad920 ; {runtime_call} 0x00bb1eaa: push $0xbb1eaa ; {section_word} 0x00bb1eaf: jmp 0x00b6ba40 ; {runtime_call}
server编译的fillHeap:
Decoding compiled method 0x00bdad48: Code: [Disassembling for mach='i386'] [Entry Point] [Verified Entry Point] 0x00bdae20: mov %eax,-0x4000(%esp) 0x00bdae27: push %ebp 0x00bdae28: sub $0x8,%esp ;*synchronization entry ; - Test::fillHeap@-1 (line 4) 0x00bdae2e: mov %ecx,%ebp 0x00bdae30: cmp $0x100000,%edx 0x00bdae36: ja 0x00bdaea8 0x00bdae38: mov %edx,%ecx 0x00bdae3a: add $0x13,%ecx 0x00bdae3d: mov %ecx,%esi 0x00bdae3f: and $0xfffffff8,%esi 0x00bdae42: mov %fs:0x0,%edi 0x00bdae49: mov -0xc(%edi),%ebx 0x00bdae4f: mov 0x44(%ebx),%eax 0x00bdae52: mov %eax,%edi 0x00bdae54: add %esi,%edi 0x00bdae56: cmp 0x4c(%ebx),%edi 0x00bdae59: jae 0x00bdaea8 0x00bdae5b: mov %edi,0x44(%ebx) 0x00bdae5e: prefetchnta 0x100(%edi) 0x00bdae65: movl $0x1,(%eax) 0x00bdae6b: prefetchnta 0x140(%edi) 0x00bdae72: movl $0x18010850,0x4(%eax) ; {oop({type array byte})} 0x00bdae79: mov %edx,0x8(%eax) 0x00bdae7c: prefetchnta 0x180(%edi) 0x00bdae83: movl $0x0,0xc(%eax) 0x00bdae8a: lea 0x10(%eax),%edi 0x00bdae8d: shr $0x3,%ecx 0x00bdae90: add $0xfffffffe,%ecx 0x00bdae93: shl %ecx 0x00bdae95: xor %eax,%eax 0x00bdae97: rep stos %eax,%es:(%edi) ;*newarray ; - Test::fillHeap@1 (line 4) 0x00bdae99: test %ebp,%ebp 0x00bdae9b: jne 0x00bdaeb6 ;*ifeq ; - Test::fillHeap@7 (line 7) 0x00bdae9d: add $0x8,%esp 0x00bdaea0: pop %ebp 0x00bdaea1: test %eax,0x9b0000 ; {poll_return} 0x00bdaea7: ret 0x00bdaea8: mov $0x18010850,%ecx ; {oop({type array byte})} 0x00bdaead: nop 0x00bdaeae: nop 0x00bdaeaf: call 0x00bda920 ; OopMap{off=148} ;*newarray ; - Test::fillHeap@1 (line 4) ; {runtime_call} 0x00bdaeb4: jmp 0x00bdae99 ;*synchronization entry ; - Test::fillHeap@-1 (line 4) 0x00bdaeb6: mov $0x16,%ecx 0x00bdaebb: call 0x00bba680 ; OopMap{off=160} ;*invokestatic gc ; - Test::fillHeap@10 (line 8) ; {runtime_call} 0x00bdaec0: int3 ;*newarray ; - Test::fillHeap@1 (line 4) 0x00bdaec1: mov %eax,%ecx 0x00bdaec3: add $0x8,%esp 0x00bdaec6: pop %ebp 0x00bdaec7: jmp 0x00bdbee0 ; {runtime_call} 0x00bdaecc: hlt 0x00bdaecd: hlt 0x00bdaece: hlt 0x00bdaecf: hlt 0x00bdaed0: hlt 0x00bdaed1: hlt 0x00bdaed2: hlt 0x00bdaed3: hlt 0x00bdaed4: hlt 0x00bdaed5: hlt 0x00bdaed6: hlt 0x00bdaed7: hlt 0x00bdaed8: hlt 0x00bdaed9: hlt 0x00bdaeda: hlt 0x00bdaedb: hlt 0x00bdaedc: hlt 0x00bdaedd: hlt 0x00bdaede: hlt 0x00bdaedf: hlt [Exception Handler] [Stub Code] 0x00bdaee0: jmp 0x00bdab00 ; {no_reloc} 0x00bdaee5: push $0xbdaee5 ; {section_word} 0x00bdaeea: jmp 0x00bbbb40 ; {runtime_call} [Constants] 0x00bdaeef: int3
两个版本的placeholder = null都被干掉了。编译的代码结构差不多,快速路径中,
·先检查是否将有栈溢出(mov %eax,-0x4000(%esp)),
·然后保存上一栈帧(push %ebp)并建立新栈帧(sub $0x8,%esp),
·然后尝试从TLAB申请空间,
·然后初始化数组的对象头,并对数组内容清零,
·(注意这里没有了placeholder = null的动作),
·然后判断go的真假,真的时候调用System.gc(),
·然后撤销栈帧(mov %ebp,%esp)并恢复上一栈帧(pop %ebp),
·然后临返回前检查safepoint(test %eax,0x9b0000),
·最后返回(ret)。
client代码中,指向新建数组实例的指针存在%eax里,也就是代码中placeholder变量被分配到了%eax上。但给数组分配了空间并将数组内容清零后没有再出现对%eax赋值的指令,可准确判断placeholder = null的代码被消除了。
server代码中,0x00bdae95: xor %eax,%eax用于数组内容清零。在这之前,指向新建数组实例的指针就存在%eax,也就是placeholder变量分配在前半部分代码里分配到%eax上。但执行了异或指令后%eax的值就被破坏掉了,虽然实际效果跟placeholder = null一样,但因为后面%eax转为用作数组清零所以这个赋值并不能看作是placeholder = null。
五楼:
IcyFenix 写道
bugmenot 写道
两个版本的placeholder = null都被干掉了。编译的代码结构差不多,快速路径中,
·先检查是否将有栈溢出(mov %eax,-0x4000(%esp)),
·然后保存上一栈帧(push %ebp)并建立新栈帧(sub $0x8,%esp),
·然后尝试从TLAB申请空间,
·然后初始化数组的对象头,并对数组内容清零,
·(注意这里没有了placeholder = null的动作),
·然后判断go的真假,真的时候调用System.gc(),
·然后撤销栈帧(mov %ebp,%esp)并恢复上一栈帧(pop %ebp),
·然后临返回前检查safepoint(test %eax,0x9b0000),
·最后返回(ret)。
client代码中,指向新建数组实例的指针存在%eax里,也就是代码中placeholder变量被分配到了%eax上。但给数组分配了空间并将数组内容清零后没有再出现对%eax赋值的指令,可准确判断placeholder = null的代码被消除了。
server代码中,0x00bdae95: xor %eax,%eax用于数组内容清零。在这之前,指向新建数组实例的指针就存在%eax,也就是placeholder变量分配在前半部分代码里分配到%eax上。但执行了异或指令后%eax的值就被破坏掉了,虽然实际效果跟placeholder = null一样,但因为后面%eax转为用作数组清零所以这个赋值并不能看作是placeholder = null。
·先检查是否将有栈溢出(mov %eax,-0x4000(%esp)),
·然后保存上一栈帧(push %ebp)并建立新栈帧(sub $0x8,%esp),
·然后尝试从TLAB申请空间,
·然后初始化数组的对象头,并对数组内容清零,
·(注意这里没有了placeholder = null的动作),
·然后判断go的真假,真的时候调用System.gc(),
·然后撤销栈帧(mov %ebp,%esp)并恢复上一栈帧(pop %ebp),
·然后临返回前检查safepoint(test %eax,0x9b0000),
·最后返回(ret)。
client代码中,指向新建数组实例的指针存在%eax里,也就是代码中placeholder变量被分配到了%eax上。但给数组分配了空间并将数组内容清零后没有再出现对%eax赋值的指令,可准确判断placeholder = null的代码被消除了。
server代码中,0x00bdae95: xor %eax,%eax用于数组内容清零。在这之前,指向新建数组实例的指针就存在%eax,也就是placeholder变量分配在前半部分代码里分配到%eax上。但执行了异或指令后%eax的值就被破坏掉了,虽然实际效果跟placeholder = null一样,但因为后面%eax转为用作数组清零所以这个赋值并不能看作是placeholder = null。
那把前面的话换一个说法:“placeholder = null并不是无意义的,是否存在placeholder = null会直接影响jit后的编译结果”。兄弟认同否?
六楼:
bugmenot 写道
IcyFenix 写道
那把前面的话换一个说法:“placeholder = null并不是无意义的,是否存在placeholder = null会直接影响jit后的编译结果”。兄弟认同否?
小的不敢认同,对不对还等大大们自己定夺
小的把placeholder = null注释掉再跑了一次,
public class Test { public static void fillHeap(boolean go, int n) throws Exception { { byte[] placeholder = new byte[n]; // placeholder = null; } if (go) System.gc(); } public static void main(String[] args) throws Exception { for (int i = 0; i < 100000; i++) { fillHeap(false, 1); } fillHeap(true, 64 * 1024 * 1024); } }
client编译出来的fillHeap:
Decoding compiled method 0x00bb1cc8: Code: [Disassembling for mach='i386'] [Entry Point] [Verified Entry Point] ;; block B3 [0, 0] 0x00bb1db0: mov %eax,-0x4000(%esp) 0x00bb1db7: push %ebp 0x00bb1db8: mov %esp,%ebp 0x00bb1dba: sub $0x18,%esp ;*iload_1 ; - Test::fillHeap@0 (line 4) 0x00bb1dbd: mov %ecx,0x10(%esp) ;; block B0 [0, 5] 0x00bb1dc1: mov %edx,%ebx 0x00bb1dc3: mov $0x18010850,%edx ; {oop({type array byte})} 0x00bb1dc8: mov %ebx,%edi 0x00bb1dca: cmp $0xffffff,%ebx 0x00bb1dd0: ja 0x00bb1e6e 0x00bb1dd6: mov $0x13,%esi 0x00bb1ddb: lea (%esi,%ebx,1),%esi 0x00bb1dde: and $0xfffffff8,%esi 0x00bb1de1: mov %fs:0x0(,%eiz,1),%ecx 0x00bb1de9: mov -0xc(%ecx),%ecx 0x00bb1dec: mov 0x44(%ecx),%eax 0x00bb1def: lea (%eax,%esi,1),%esi 0x00bb1df2: cmp 0x4c(%ecx),%esi 0x00bb1df5: ja 0x00bb1e6e 0x00bb1dfb: mov %esi,0x44(%ecx) 0x00bb1dfe: sub %eax,%esi 0x00bb1e00: movl $0x1,(%eax) 0x00bb1e06: mov %edx,0x4(%eax) 0x00bb1e09: mov %ebx,0x8(%eax) 0x00bb1e0c: sub $0xc,%esi 0x00bb1e0f: je 0x00bb1e52 0x00bb1e15: test $0x3,%esi 0x00bb1e1b: je 0x00bb1e32 0x00bb1e21: push $0x838451c ; {external_word} 0x00bb1e26: call 0x00bb1e2b 0x00bb1e2b: pusha 0x00bb1e2c: call 0x0801b6a0 ; {runtime_call} 0x00bb1e31: hlt 0x00bb1e32: xor %ebx,%ebx 0x00bb1e34: shr $0x3,%esi 0x00bb1e37: jae 0x00bb1e47 0x00bb1e3d: mov %ebx,0xc(%eax,%esi,8) 0x00bb1e41: je 0x00bb1e52 0x00bb1e47: mov %ebx,0x8(%eax,%esi,8) 0x00bb1e4b: mov %ebx,0x4(%eax,%esi,8) 0x00bb1e4f: dec %esi 0x00bb1e50: jne 0x00bb1e47 ;*newarray ; - Test::fillHeap@1 (line 4) 0x00bb1e52: mov 0x10(%esp),%ecx 0x00bb1e56: cmp $0x0,%ecx ;; 22 branch [EQ] [B2] 0x00bb1e59: je 0x00bb1e64 ;*ifeq ; - Test::fillHeap@5 (line 7) ;; block B1 [8, 8] 0x00bb1e5f: call 0x00b6b3d0 ; OopMap{off=180} ;*invokestatic gc ; - Test::fillHeap@8 (line 8) ; {static_call} ;; block B2 [11, 11] 0x00bb1e64: mov %ebp,%esp 0x00bb1e66: pop %ebp 0x00bb1e67: test %eax,0x960100 ; {poll_return} 0x00bb1e6d: ret ;; NewTypeArrayStub slow case 0x00bb1e6e: call 0x00baef50 ; OopMap{off=195} ;*newarray ; - Test::fillHeap@1 (line 4) ; {runtime_call} 0x00bb1e73: jmp 0x00bb1e52 0x00bb1e75: nop 0x00bb1e76: nop 0x00bb1e77: hlt 0x00bb1e78: hlt 0x00bb1e79: hlt 0x00bb1e7a: hlt 0x00bb1e7b: hlt 0x00bb1e7c: hlt 0x00bb1e7d: hlt 0x00bb1e7e: hlt 0x00bb1e7f: hlt [Stub Code] 0x00bb1e80: nop ; {no_reloc} 0x00bb1e81: nop 0x00bb1e82: mov $0x0,%ebx ; {static_stub} 0x00bb1e87: jmp 0x00bb1e87 ; {runtime_call} [Exception Handler] 0x00bb1e8c: mov $0xdead,%ebx 0x00bb1e91: mov $0xdead,%ecx 0x00bb1e96: mov $0xdead,%edx 0x00bb1e9b: mov $0xdead,%esi 0x00bb1ea0: mov $0xdead,%edi 0x00bb1ea5: jmp 0x00bad920 ; {runtime_call} 0x00bb1eaa: push $0xbb1eaa ; {section_word} 0x00bb1eaf: jmp 0x00b6ba40 ; {runtime_call}
代码与带有placeholder = null时编译出来的一模一样。唯有的差异是原始字节码短了2字节,所以PrintAssembly出来的注释的基本块大小有点差异。
server编译出来的fillHeap:
Decoding compiled method 0x00bdb608: Code: [Disassembling for mach='i386'] [Entry Point] [Verified Entry Point] 0x00bdb6e0: mov %eax,-0x4000(%esp) 0x00bdb6e7: push %ebp 0x00bdb6e8: sub $0x8,%esp ;*synchronization entry ; - Test::fillHeap@-1 (line 4) 0x00bdb6ee: mov %ecx,%ebp 0x00bdb6f0: cmp $0x100000,%edx 0x00bdb6f6: ja 0x00bdb768 0x00bdb6f8: mov %edx,%ecx 0x00bdb6fa: add $0x13,%ecx 0x00bdb6fd: mov %ecx,%esi 0x00bdb6ff: and $0xfffffff8,%esi 0x00bdb702: mov %fs:0x0,%edi 0x00bdb709: mov -0xc(%edi),%ebx 0x00bdb70f: mov 0x44(%ebx),%eax 0x00bdb712: mov %eax,%edi 0x00bdb714: add %esi,%edi 0x00bdb716: cmp 0x4c(%ebx),%edi 0x00bdb719: jae 0x00bdb768 0x00bdb71b: mov %edi,0x44(%ebx) 0x00bdb71e: prefetchnta 0x100(%edi) 0x00bdb725: movl $0x1,(%eax) 0x00bdb72b: prefetchnta 0x140(%edi) 0x00bdb732: movl $0x18010850,0x4(%eax) ; {oop({type array byte})} 0x00bdb739: mov %edx,0x8(%eax) 0x00bdb73c: prefetchnta 0x180(%edi) 0x00bdb743: movl $0x0,0xc(%eax) 0x00bdb74a: lea 0x10(%eax),%edi 0x00bdb74d: shr $0x3,%ecx 0x00bdb750: add $0xfffffffe,%ecx 0x00bdb753: shl %ecx 0x00bdb755: xor %eax,%eax 0x00bdb757: rep stos %eax,%es:(%edi) ;*newarray ; - Test::fillHeap@1 (line 4) 0x00bdb759: test %ebp,%ebp 0x00bdb75b: jne 0x00bdb776 ;*ifeq ; - Test::fillHeap@5 (line 7) 0x00bdb75d: add $0x8,%esp 0x00bdb760: pop %ebp 0x00bdb761: test %eax,0x9b0000 ; {poll_return} 0x00bdb767: ret 0x00bdb768: mov $0x18010850,%ecx ; {oop({type array byte})} 0x00bdb76d: nop 0x00bdb76e: nop 0x00bdb76f: call 0x00bda7e0 ; OopMap{off=148} ;*newarray ; - Test::fillHeap@1 (line 4) ; {runtime_call} 0x00bdb774: jmp 0x00bdb759 ;*synchronization entry ; - Test::fillHeap@-1 (line 4) 0x00bdb776: mov $0x16,%ecx 0x00bdb77b: call 0x00bba680 ; OopMap{off=160} ;*invokestatic gc ; - Test::fillHeap@8 (line 8) ; {runtime_call} 0x00bdb780: int3 ;*newarray ; - Test::fillHeap@1 (line 4) 0x00bdb781: mov %eax,%ecx 0x00bdb783: add $0x8,%esp 0x00bdb786: pop %ebp 0x00bdb787: jmp 0x00bdbfe0 ; {runtime_call} 0x00bdb78c: hlt 0x00bdb78d: hlt 0x00bdb78e: hlt 0x00bdb78f: hlt 0x00bdb790: hlt 0x00bdb791: hlt 0x00bdb792: hlt 0x00bdb793: hlt 0x00bdb794: hlt 0x00bdb795: hlt 0x00bdb796: hlt 0x00bdb797: hlt 0x00bdb798: hlt 0x00bdb799: hlt 0x00bdb79a: hlt 0x00bdb79b: hlt 0x00bdb79c: hlt 0x00bdb79d: hlt 0x00bdb79e: hlt 0x00bdb79f: hlt [Exception Handler] [Stub Code] 0x00bdb7a0: jmp 0x00bdab00 ; {no_reloc} 0x00bdb7a5: push $0xbdb7a5 ; {section_word} 0x00bdb7aa: jmp 0x00bbbb40 ; {runtime_call} [Constants] 0x00bdb7af: int3
与client的情况一样,去掉placeholder = null之后编译出来的代码没有变化。直接diff能看到的都是地址上的差异,因为多次运行编译出来的代码不一定在同一地址上,这是正常的;除地址之外,操作指令都一模一样。
IcyFenix大大,小的也请教了问题,大大还没回答呢
bugmenot 写道
请问楼上的大大,测试的环境和启动参数是什么?
七楼:
IcyFenix 写道
bugmenot 写道
小的把placeholder = null注释掉再跑了一次,
与client的情况一样,去掉placeholder = null之后编译出来的代码没有变化。直接diff能看到的都是地址上的差异,因为多次运行编译出来的代码不一定在同一地址上,这是正常的;除地址之外,操作指令都一模一样。
IcyFenix大大,小的也请教了问题,大大还没回答呢
请问楼上的大大,测试的环境和启动参数是什么?
公司里测试的是6u21,现在在家里用笔记本,下面测试用的是刚刚下的openjdk7-b127-fastdebug
D:\>java -version
java version "1.7.0-ea-fastdebug"
Java(TM) SE Runtime Environment (build 1.7.0-ea-fastdebug-b127)
Java HotSpot(TM) Client VM (build 20.0-b06-fastdebug, mixed mode)
参数是就2个:-verbose:gc -XX:+PrintCompilation,由于没有梯子,下载不到墙外的hsdis-i386.dll,所以没加PrintAssembly,如果打印出来的asm都一样的话,对于下面这两次测试的结果,黑体字部分我觉得很疑惑,请兄弟不吝指教。
不带placeholder = null的版本
225 1 java.lang.String::hashCode (67 bytes)
VM option '+PrintCompilation'
228 2 java.lang.String::charAt (33 bytes)
229 3 java.lang.String::indexOf (87 bytes)
230 4 java.lang.Object::<init> (1 bytes)
230 5 java.lang.String::indexOf (166 bytes)
242 6 org.fenixsoft.test.Test::fillHeap (12 bytes)
243 1% org.fenixsoft.test.Test::main @ 5 (26 bytes)
[GC 2080K->267K(15872K), 0.0042400 secs]
[Full GC 267K->267K(15872K), 0.0117231 secs]
[Full GC 65893K->65803K(81476K), 0.0140037 secs]
带placeholder = null的版本
253 1 java.lang.String::hashCode (67 bytes)
VM option '+PrintCompilation'
258 2 java.lang.String::charAt (33 bytes)
259 3 java.lang.String::indexOf (87 bytes)
261 4 java.lang.Object::<init> (1 bytes)
261 5 java.lang.String::indexOf (166 bytes)
274 6 org.fenixsoft.test.Test::fillHeap (17 bytes)
275 1% org.fenixsoft.test.Test::main @ 5 (26 bytes)
[GC 2080K->267K(15872K), 0.0043529 secs]
[Full GC 267K->267K(15872K), 0.0124955 secs]
[Full GC 65893K->267K(81476K), 0.0235428 secs]
八楼:
bugmenot 写道
IcyFenix 写道
由于没有梯子,下载不到墙外的hsdis-i386.dll,所以没加PrintAssembly
小的也没用梯子呀,这里就有:http://hllvm.group.iteye.com/group/share
IcyFenix 写道
242 6 org.fenixsoft.test.Test::fillHeap (12 bytes)
这数字是被编译的方法原本字节码的大小。用javap能看到。
小的跑不带placeholder = null测试用的完整代码是这样:
public class Test { public static void fillHeap(boolean go, int n) throws Exception { { byte[] placeholder = new byte[n]; // placeholder = null; } if (go) System.gc(); } public static void main(String[] args) throws Exception { for (int i = 0; i < 100000; i++) { fillHeap(false, 1); } fillHeap(true, 64 * 1024 * 1024); } }
用javap看fillHeap
public static void fillHeap(boolean, int) throws java.lang.Exception; Code: Stack=1, Locals=3, Args_size=2 0: iload_1 1: newarray byte 3: astore_2 4: iload_0 5: ifeq 11 8: invokestatic #2; //Method java/lang/System.gc:()V 11: return LineNumberTable: line 4: 0 line 7: 4 line 8: 8 line 9: 11 LocalVariableTable: Start Length Slot Name Signature 4 0 2 placeholder [B 0 12 0 go Z 0 12 1 n I StackMapTable: number_of_entries = 1 frame_type = 11 /* same */ Exceptions: throws java.lang.Exception
字节码就12字节,能对上。
把注释去掉,带上placeholder = null的fillHeap,用javap看
public static void fillHeap(boolean, int) throws java.lang.Exception; Code: Stack=1, Locals=3, Args_size=2 0: iload_1 1: newarray byte 3: astore_2 4: aconst_null 5: astore_2 6: iload_0 7: ifeq 13 10: invokestatic #2; //Method java/lang/System.gc:()V 13: return LineNumberTable: line 4: 0 line 5: 4 line 7: 6 line 8: 10 line 9: 13 LocalVariableTable: Start Length Slot Name Signature 4 2 2 placeholder [B 0 14 0 go Z 0 14 1 n I StackMapTable: number_of_entries = 1 frame_type = 13 /* same */ Exceptions: throws java.lang.Exception
就比不带placeholder = null多两条指令,字节码大小增加到14字节。IcyFenix大大的测试里带上placeholder = null是17字节是因为大大用的测试代码与小的的不一样。所以才有必要把完整的测试代码,运行环境,启动参数都拉出来晒晒,不然讨论的就不是同一个东西。
IcyFenix 写道
[Full GC 65893K->65803K(81476K), 0.0140037 secs]
不知大大是如何跑测试的。小的继续在XP SP2,JDK 1.6.0_14 fastdebug上跑,不带placeholder = null跑出来几组结果
D:\test>java -Xmx128m -Xms128m -XX:+PrintCompilation -XX:+PrintGC -XX:+PrintGCDetails Test
VM option '+PrintCompilation'
VM option '+PrintGC'
VM option '+PrintGCDetails'
1 java.lang.String::hashCode (60 bytes)
2 java.lang.String::charAt (33 bytes)
3 Test::fillHeap (12 bytes)
1% Test::main @ 2 (26 bytes)
[Full GC (System) [Tenured: 65536K->137K(121024K), 0.0829719 secs] 67324K->137K(130112K), [Perm : 2284K->2284K(12288K)], 0.0835611 secs] [Times: user=0.08 sys=0.00, real=0.08 secs]
Heap
def new generation total 9088K, used 162K [0x10010000, 0x109e0000, 0x109e0000)
eden space 8128K, 2% used [0x10010000, 0x10038aa8, 0x10800000)
from space 960K, 0% used [0x10800000, 0x10800000, 0x108f0000)
to space 960K, 0% used [0x108f0000, 0x108f0000, 0x109e0000)
tenured generation total 121024K, used 137K [0x109e0000, 0x18010000, 0x18010000)
the space 121024K, 0% used [0x109e0000, 0x10a02530, 0x10a02600, 0x18010000)
compacting perm gen total 12288K, used 2313K [0x18010000, 0x18c10000, 0x1c010000)
the space 12288K, 18% used [0x18010000, 0x18252710, 0x18252800, 0x18c10000)
No shared spaces configured.
D:\test>java -server -Xmx128m -Xms128m -XX:+PrintCompilation -XX:+PrintGC -XX:+PrintGCDetails -XX:+UseParallelGC Test
VM option '+PrintCompilation'
VM option '+PrintGC'
VM option '+PrintGCDetails'
VM option '+UseParallelGC'
1 java.lang.String::charAt (33 bytes)
2 Test::fillHeap (12 bytes)
1% Test::main @ 2 (26 bytes)
2 made not entrant (2) Test::fillHeap (12 bytes)
[GC [PSYoungGen: 1970K->168K(12736K)] 67506K->65704K(129280K), 0.3141070 secs] [Times: user=0.63 sys=0.00, real=0.31 secs]
[Full GC (System) [PSYoungGen: 168K->0K(12736K)] [PSOldGen: 65536K->137K(116544K)] 65704K->137K(129280K) [PSPermGen: 2286K->2286K(16384K)], 0.0629148 secs] [Times: user=0.06 sys=0.00, real=0.06 secs]
Heap
PSYoungGen total 12736K, used 218K [0x1b1e0000, 0x1c010000, 0x1c010000)
eden space 10944K, 2% used [0x1b1e0000,0x1b216bf0,0x1bc90000)
from space 1792K, 0% used [0x1bc90000,0x1bc90000,0x1be50000)
to space 1792K, 0% used [0x1be50000,0x1be50000,0x1c010000)
PSOldGen total 116544K, used 137K [0x14010000, 0x1b1e0000, 0x1b1e0000)
object space 116544K, 0% used [0x14010000,0x140325e8,0x1b1e0000)
PSPermGen total 16384K, used 2315K [0x10010000, 0x11010000, 0x14010000)
object space 16384K, 14% used [0x10010000,0x10252f90,0x11010000)
D:\test>java -server -Xmx128m -Xms128m -XX:+PrintCompilation -XX:+PrintGC -XX:+PrintGCDetails -XX:+UseSerialGC Test
VM option '+PrintCompilation'
VM option '+PrintGC'
VM option '+PrintGCDetails'
VM option '+UseSerialGC'
1 java.lang.String::charAt (33 bytes)
2 Test::fillHeap (12 bytes)
1% Test::main @ 2 (26 bytes)
2 made not entrant (2) Test::fillHeap (12 bytes)
[Full GC (System) [Tenured: 65536K->137K(116544K), 0.0755938 secs] 67410K->137K(129664K), [Perm : 2286K->2286K(16384K)], 0.0761244 secs] [Times: user=0.08 sys=0.00, real=0.08 secs]
Heap
def new generation total 13120K, used 234K [0x10010000, 0x10e40000, 0x10e40000)
eden space 11712K, 2% used [0x10010000, 0x1004a960, 0x10b80000)
from space 1408K, 0% used [0x10b80000, 0x10b80000, 0x10ce0000)
to space 1408K, 0% used [0x10ce0000, 0x10ce0000, 0x10e40000)
tenured generation total 116544K, used 137K [0x10e40000, 0x18010000, 0x18010000)
the space 116544K, 0% used [0x10e40000, 0x10e625e8, 0x10e62600, 0x18010000)
compacting perm gen total 16384K, used 2315K [0x18010000, 0x19010000, 0x1c010000)
the space 16384K, 14% used [0x18010000, 0x18252f90, 0x18253000, 0x19010000)
No shared spaces configured.
看到的结果跟大大的不一样。那个64M的byte数组能看到被收集了。打上-XX:+PrintGCDetails看到Full GC (System)的是System.gc()的日志。
九楼:
IcyFenix 写道
hsdis-i386.dll我找了一圈,没想到JE就有 @_@
加上-XX:+PrintAssembly可以看到jit之后赋null的操作确实消除了,那64m内存在jit之后没有被收集掉,是因为挂着jdwp agent的关系,不挂这个agentlib就可以正常回收掉。用eclipse debug环境跑确实是个低级错误。
在命令行重新测试了一次,对于没有进入jit的代码,那64m如果没有赋null的话不会被回收,进入了jit之后,赋不赋null都会被那句System.gc()回收。
感谢楼上的bugmenot同学。
加上-XX:+PrintAssembly可以看到jit之后赋null的操作确实消除了,那64m内存在jit之后没有被收集掉,是因为挂着jdwp agent的关系,不挂这个agentlib就可以正常回收掉。用eclipse debug环境跑确实是个低级错误。
在命令行重新测试了一次,对于没有进入jit的代码,那64m如果没有赋null的话不会被回收,进入了jit之后,赋不赋null都会被那句System.gc()回收。
感谢楼上的bugmenot同学。
悲剧完毕,over。
评论
4 楼
littleJava
2011-04-11
膜拜啊,都是牛人
3 楼
RednaxelaFX
2011-02-02
今天不是才年三十么?新年快乐^_^
2 楼
IcyFenix
2011-02-02
RednaxelaFX 写道
其实人家还没把话说完吧。你看帖里的日志里有:
bugmenot 写道
1 java.lang.String::charAt (33 bytes)
2 Test::fillHeap (12 bytes)
1% Test::main @ 2 (26 bytes)
2 made not entrant (2) Test::fillHeap (12 bytes)
2 Test::fillHeap (12 bytes)
1% Test::main @ 2 (26 bytes)
2 made not entrant (2) Test::fillHeap (12 bytes)
这个made not entrant表示日志中compile_id为2的fillHeap()的编译结果已经报废了。要调查具体原因需要别的信息。
而上面一行main()的日志则说明main()已经被OSR方式编译,更前面带有-XX:+PrintInlining的日志则说明main()中两处对fillHeap()的调用都已经被内联到main()里了。所以真正到那个fillHeap(true, 64 * 1000 * 1000)的调用的时候,执行的代码是已经被内联到main()里的代码。真的要精确分析的话,应该拿main()被编译后的代码来看更准确。
当然,直接看fillHeap()的编译结果也足够回答楼主提出的疑问就是了,关于placeholder = null是否被消除了的问题。于是我就不用费力回答了,哈哈
另外简单说说,HotSpot在GC中做root enumeration的时候,对解释执行中的Java方法与已编译后的Java方法是用不同方式来做的。解释执行比较好办,本身不会对Java代码做什么优化所以可以很直观的获取root信息。而JIT编译器则会为每个编译出来的代码(nmethod对象)带上一组OopMap来记录一些特定位置上的root信息。之前分享的演示稿里有提到这点,没详细说。啥时候我再把它说得详细点更新到演示稿里去。
i see~~thx~~
现在大年初一了,新年快乐。
1 楼
RednaxelaFX
2011-02-02
其实人家还没把话说完吧。你看帖里的日志里有:
bugmenot 写道
1 java.lang.String::charAt (33 bytes)
2 Test::fillHeap (12 bytes)
1% Test::main @ 2 (26 bytes)
2 made not entrant (2) Test::fillHeap (12 bytes)
2 Test::fillHeap (12 bytes)
1% Test::main @ 2 (26 bytes)
2 made not entrant (2) Test::fillHeap (12 bytes)
这个made not entrant表示日志中compile_id为2的fillHeap()的编译结果已经报废了。要调查具体原因需要别的信息。
而上面一行main()的日志则说明main()已经被OSR方式编译,更前面带有-XX:+PrintInlining的日志则说明main()中两处对fillHeap()的调用都已经被内联到main()里了。所以真正到那个fillHeap(true, 64 * 1000 * 1000)的调用的时候,执行的代码是已经被内联到main()里的代码。真的要精确分析的话,应该拿main()被编译后的代码来看更准确。
当然,直接看fillHeap()的编译结果也足够回答楼主提出的疑问就是了,关于placeholder = null是否被消除了的问题。于是我就不用费力回答了,哈哈
另外简单说说,HotSpot在GC中做root enumeration的时候,对解释执行中的Java方法与已编译后的Java方法是用不同方式来做的。解释执行比较好办,本身不会对Java代码做什么优化所以可以很直观的获取root信息。而JIT编译器则会为每个编译出来的代码(nmethod对象)带上一组OopMap来记录一些特定位置上的root信息。之前分享的演示稿里有提到这点,没详细说。啥时候我再把它说得详细点更新到演示稿里去。
相关推荐
"JE分词 1.4+1.5"是一个针对中文文本处理的工具,主要用于将汉字字符串分割成有意义的词语,这是自然语言处理中的关键步骤。这个版本的JE分词在之前的基础上进行了多方面的改进和增强,使得其在处理中文文本时更为...
Oracle BerkeleyDB-JE je-6.0.11
赠送jar包:je-5.0.73.jar; 赠送原API文档:je-5.0.73-javadoc.jar; 赠送源代码:je-5.0.73-sources.jar; 赠送Maven依赖信息文件:je-5.0.73.pom; 包含翻译后的API文档:je-5.0.73-javadoc-API文档-中文(简体)版...
这一系列产品的目标市场是那些寻求降低设备成本但又不希望牺牲太多性能的用户。JE系列产品能够满足基本的伺服控制需求,且易于选型和设计集成,非常适合在成本敏感型的系统中使用。 标签“三菱伺服电机,JE系列”...
**Berkeley DB je3.3版详解** Berkeley DB(简称BDB)是Oracle公司提供的一款开源、轻量级、高性能的嵌入式数据库系统。它最初由Sleepycat Software开发,后被Oracle收购。Berkeley DB je是其增强版,专为Java环境...
"je-analysis-1.5.1"是一款专用于中文分词的开源工具,它在中文信息处理领域扮演着重要的角色。这款工具集成了高效的分词算法,为开发者提供了便捷的接口,使得在Java环境中进行文本分析变得更加简单。"JE分词器"是...
"je-analysis.jar" 是一个Java Archive (JAR) 文件,它是Java编程语言中用于封装多个类文件和其他资源的容器。这种格式通常用于分发可执行的Java应用程序或库。在这个特定的情况下,"je-analysis-1.5.3.jar" 版本...
三菱MR-JE-C伺服电机FB功能块(适用Q系列PLC) 流水线项目,16个MR-JE-C电机,为了加快编程速度,特意做的一个FB功能块,内部采用局部变量+全局缓冲区的方式进行编程,多次调用不冲突! 适用于Q系列PLC和MR-JE-C的运动...
根据提供的文件信息,以下是对“丝印HX-JE芯片资料”的详细知识点阐述: 标题“丝印HX-JE芯片资料”指出了我们讨论的焦点是关于一款特定的芯片,而“丝印”这个词通常用在半导体制造工艺中,涉及在芯片表面印刷用于...
### 三菱伺服MR-JE使用手册关键知识点解析 #### 一、概述 三菱伺服MR-JE系列是一款高性能的交流伺服系统,适用于各种自动化控制领域。本手册旨在提供关于该系列产品安装、配置、维护等方面的技术指导,确保用户能够...
三菱伺服MELSERVO-JE系列是三菱电机生产的一款高性能的交流伺服系统,它提供了多种伺服电机和伺服放大器型号,适用于广泛的工业应用。在使用MR-JE伺服样本之前,用户应仔细阅读相关使用说明书和技术资料集以确保安全...
《深入解析je-analysis-1.5.3:Java Lucene中的中文全文检索与分词组件》 在现代信息处理领域,全文检索与精准的文本分析是至关重要的技术。Je-analysis,作为一款基于Java的开源全文检索框架Lucene的中文分词组件...
赠送jar包:je-5.0.73.jar; 赠送原API文档:je-5.0.73-javadoc.jar; 赠送源代码:je-5.0.73-sources.jar; 赠送Maven依赖信息文件:je-5.0.73.pom; 包含翻译后的API文档:je-5.0.73-javadoc-API文档-中文(简体)-...
本文将深入探讨"je-analysis-1.5.3"和"lucene-core-2.4.1"这两个分词组件,以及它们在相关场景中的应用。 首先,让我们了解什么是分词。分词,即词语切分,是自然语言处理(NLP)中的基本任务之一,它的目标是将...
JE-C伺服控制要点,方便plc对伺服关键寄存器读写
【标题】"je分词jar文件1.5+1.4l两版本"涉及的核心知识点是中文分词技术,以及两个不同版本的Java Archive (JAR) 文件——JE-Analysis1.5.1.jar和JE-Analysis1.4.0.jar。 中文分词是自然语言处理(NLP)中的关键...
je-analysis-1.5.1.jar 中科院的分词器,用的人很多,需要Lucene1.9-2.4版本才能使用
《深入解析Je-5.0.73.jar.zip:核心功能与应用实践》 Je-5.0.73.jar.zip是一个包含Je-5.0.73版本的核心库文件,该文件是Java编程语言中的一种归档格式,主要用于封装Java类和其他相关资源,便于在各种Java应用程序...
《GX Works3与MR-JE-C的CC-LINK IE Field Basic通信详解》 在工业自动化领域,GX Works3是一款广泛使用的编程软件,主要用于三菱电机的PLC(可编程逻辑控制器)编程。而MR-JE-C系列伺服驱动器是三菱电机推出的一款...