论坛首页 Java企业应用论坛

HotSpot VM 内存堆的两个Servivor区

浏览 21927 次
精华帖 (1) :: 良好帖 (6) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2011-01-31  
@bugmenot同学

在这里,没几个人能担当得起你“大大”、“小的”称呼 @_@
0 请登录后投票
   发表时间: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()的日志。
0 请登录后投票
   发表时间: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同学。
0 请登录后投票
   发表时间: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区”的朋友可以看一下。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics