精华帖 (1) :: 良好帖 (6) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2011-01-31
@bugmenot同学
在这里,没几个人能担当得起你“大大”、“小的”称呼 @_@ |
|
返回顶楼 | |
发表时间:2011-01-31
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()的日志。 |
|
返回顶楼 | |
发表时间:2011-01-31
hsdis-i386.dll我找了一圈,没想到JE就有 @_@
加上-XX:+PrintAssembly可以看到jit之后赋null的操作确实消除了,那64m内存在jit之后没有被收集掉,是因为挂着jdwp agent的关系,不挂这个agentlib就可以正常回收掉。用eclipse debug环境跑确实是个低级错误。 在命令行重新测试了一次,对于没有进入jit的代码,那64m如果没有赋null的话不会被回收,进入了jit之后,赋不赋null都会被那句System.gc()回收。 感谢楼上的bugmenot同学。 |
|
返回顶楼 | |
发表时间:2011-12-12
最后修改:2011-12-12
GC的基本回收算法是“标记-清除”。为了解决它的效率低下和清除后产生的内存碎片问题,提出了“标记-复制”的算法。应用了“标记-复制”GC算法下的JVM不会把全部的内存用于给对象分配,而会预留一部分内存不使用。当GC回收被触发后,仍然存活的对象会被复制到预留的那部分内存中,而已经被使用的那部分将会被清空。这种算法的好处是效率高并且不容易产生内存碎片;短处是预留的那部分内存由于不能“直接”分配给对象使用而被“浪费”了。
如果每次GC后,能存活的对象数目很少,则只要预留很少的内存就可以了,那么“标记-复制”算法的这点代价对于它的好处来说是值得付出的。 IBM的一项研究指出,位于新生代的对象中,每次GC后存活的大约只有2%。这意味着如果采用“标记-复制”算法,那么预留的内存只要能装下这大约2%的对象即可。这样的话,浪费的内存是可以承受的。事实上,SUN的虚拟机的新生代采用的都是这种“标记-复制”算法。并且将新生代的内存布局按照8:1:1的比例设成eden区、survivorFrom区、survivorTo区(比例可以通过参数配置)。给对象分配内存时使用eden区和其中的一个survivor区,另外一个survivor区作为预留的内存用来保存GC后幸存的对象。 这就是为什么要有平行的两个survivor区的原因。 以上是拜读了《深入理解java虚拟机》一书的收获。 那位问“为什么要有平行的两个survivor区”的朋友可以看一下。 |
|
返回顶楼 | |