精確的字符串截取方法 java

发开项目需要,在网上找了很久都没有打到.于是自己动手写了一下.贡献出来给有需要的人.如果有什么问题可以加我MSN:strongkill@yahoo.com.hk 讨论讨论


源代码:


 /**
  * 截取字符串的前targetCount个字符
  * @param str 被处理字符串
  * @param targetCount 截取长度
  * @version 1.1
  * @author Strong Yuan
  * @return String
  */
 public static String subContentStringOrialBytes(String str,int targetCount){
  return subContentStringOrialBytes(str,targetCount,”…”);
 }
 /**
  * 获取指定长度字符串的字节长
  * @param str 被处理字符串
  * @param maxlength 截取长度
  * @author Strong Yuan
  * @version 1.1
  * @return String
  */
 private static long getStringByteLength(String str,int maxlength){
  if(str==null)
   return 0;
  int tmp_len = maxlength;
  
  if(str.length()<maxlength)
   tmp_len = str.length();
  else if(str.length()>maxlength*2)
   tmp_len = maxlength*2;
  
  char[] tempchar = str.substring(0, tmp_len).toCharArray();


  int intVariable = 0;
  String s1 = null;
  for(int i=0;i<tempchar.length && intVariable<=maxlength;i++){
   s1 = String.valueOf(tempchar[i]);
   intVariable += s1.getBytes().length;
  }
  s1= null;
  tempchar = null;
  return intVariable;
 }
 /**
  * 截取指定长度的字符串,基于bytes,即是中文的长度为2,英文为1
  * @param str 被处理字符串
  * @param targetCount 截取长度
  * @param more 后缀字符串
  * @author Strong Yuan
  * @version 1.1
  * @return
  */
 public static String subContentStringOrialBytes(String str, int targetCount,String more)
 {
  if (str == null)
   return “”;
  int initVariable = 0;
  StringBuffer restr = new StringBuffer();
  if (getStringByteLength(str,targetCount) <= targetCount)
   return str;


  String s1=null;
  byte[] b;
  char[] tempchar = str.toCharArray();
  for (int i = 0; (i < tempchar.length && targetCount > initVariable); i++) {
   s1 = String.valueOf(tempchar[i]);
   b = s1.getBytes();
   initVariable += b.length;
   restr.append(tempchar[i]);
  }


  if (targetCount == initVariable || (targetCount == initVariable – 1)) {
   restr.append(more);
  }
  return restr.toString();
 }
 /**
  * 截取指定长度的字符串,存在问题,但效率会高一点点.just a little
  * @param str 被处理字符串
  * @param targetCount 截取长度
  * @param more 后缀字符串
  * @version 1.1
  * @author Strong Yuan
  * @return String
  */
 public static String subContentStringOrial(String str,int targetCount){
  return subContentStringOrial(str,targetCount,”…”);
 } 
 /**
  * 截取指定长度的字符串,存在问题,但效率会高一点点.just a little
  * @param str 被处理字符串
  * @param targetCount 截取长度
  * @param more 后缀字符串
  * @author Strong Yuan
  * @return String
  */
 public static String subContentStringOrial(String str, int targetCount,String more)
 {
  if (str == null)
   return “”;
  int initVariable = 0;
  StringBuffer restr = new StringBuffer();
  if (str.length() <= targetCount)
   return str;


  String s1=null;
  byte[] b;
  char[] tempchar = str.toCharArray();
  for (int i = 0; (i < tempchar.length && targetCount > initVariable); i++) {
   s1 = String.valueOf(tempchar[i]);
   b = s1.getBytes();
   initVariable += b.length;
   restr.append(tempchar[i]);
  }


  if (targetCount == initVariable || (targetCount == initVariable – 1)) {
   restr.append(more);
  }
  return restr.toString();
 }


下面是性能测试:

 

public static void main(String[] args){

  int maxLength = 28;  
  String str =”当奥运圣火格式化字符串长度,超出部分显示省略号,区分汉字跟字母。汉字2个字节,字母数字一个字节格式化字符串长度,超出部分显示省略号,区分汉字跟字母。汉字2个字节,字母数字一个字节格式化字符串长度,超出部分显示省略号,区分汉字跟字母。汉字2个字节,字母数字一个字节格式化字符串长度,超出部分显示省略号,区分汉字跟字母。汉字2个字节,字母数字一个字节格式化字符串长度,超出部分显示省略号,区分汉字跟字母。汉字2个字节,字母数字一个字节格式化字符串长度,超出部分显示省略号,区分汉字跟字母。汉字2个字节,字母数字一个字节格式化字符串长度,超出部分显示省略号,区分汉字跟字母。汉字2个字节,字母数字一个字节格式化字符串长度,超出部分显示省略号,区分汉字跟字母。汉字2个字节,字母数字一个字节格式化字符串长度,超出部分显示省略号,区分汉字跟字母。汉字2个字节,字母数字一个字节格式化字符串长度,超出部分显示省略号,区分汉字跟字母。汉字2个字节,字母数字一个字节格式化字符串长度,超出部分显示省略号,区分汉字跟字母。汉字2个字节,字母数字一个字节格式化字符串长度,超出部分显示省略号,区分汉字跟字母。汉字2个字节,字母数字一个字节格式化字符串长度,超出部分显示省略号,区分汉字跟字母。汉字2个字节,字母数字一个字节格式化字符串长度,超出部分显示省略号,区分汉字跟字母。汉字2个字节,字母数字一个字节格式化字符串长度,超出部分显示省略号,区分汉字跟字母。汉字2个字节,字母数字一个字节格式化字符串长度,超出部分显示省略号,区分汉字跟字母。汉字2个字节,字母数字一个字节格式化字符串长度,超出部分显示省略号,区分汉字跟字母。汉字2个字节,字母数字一个字节格式化字符串长度,超出部分显示省略号,区分汉字跟字母。汉字2个字节,字母数字一个字节格式化字符串长度,超出部分显示省略号,区分汉字跟字母。汉字2个字节,字母数字一个字节格式化字符串长度,超出部分显示省略号,区分汉字跟字母。汉字2个字节,字母数字一个字节格式化字符串长度,超出部分显示省略号,区分汉字跟字母。汉字2个字节,字母数字一个字节格式化字符串长度,超出部分显示省略号,区分汉字跟字母。汉字2个字节,字母数字一个字节”;

  long curr = System.currentTimeMillis();
  String aa = null;
  for(int i =0;i<=100000;i++){
   aa = htmlFilter.subContentStringOrial(str, maxLength);
  }
  System.out.println(“结果: “+aa);
  System.out.println(“耗时: ” + (System.currentTimeMillis()-curr)+” ms”);
 
  curr = System.currentTimeMillis();
  for(int i =0;i<=100000;i++){
   aa = htmlFilter.subContentStringOrialBytes(str, maxLength);
  }
  System.out.println(“结果: “+aa);  
  System.out.println(“耗时: ” + (System.currentTimeMillis()-curr)+” ms”);
  

  System.out.println(htmlFilter.subContentStringOrial(“[原创]山西临汾矿难3主犯被判无期dfasdfasdfasdf万人听判”, maxLength));
  System.out.println(htmlFilter.subContentStringOrialBytes(“[原创]山西临汾矿难3主犯被判无期dfasdfasdfasdf万人听判”, maxLength,”…”));
  //存在问题,当str.length()小于maxLength时,就不进行截取
  System.out.println(htmlFilter.subContentStringOrial(“[原创]山西临汾矿难3主犯被判无期万人听判”, maxLength));
  System.out.println(htmlFilter.subContentStringOrialBytes(“[原创]山西临汾矿难3主犯被判无期万人听判”, maxLength,”…”));

}

 

最后输出结果:

 

结果: 当奥运圣火格式化字符
耗时: 1183 ms
结果: 当奥运圣火格式化字符
耗时: 1834 ms
[原创]dfasdfasdfasdf
[原创]dfasdfasdfasdf
[原创]山西临汾矿难3主
[原创]山西临汾矿难3主
[原创]山西临汾矿难3主犯被判无期万人听判判无期万人听判
[原创]山西临汾矿难3主


resin 3.1使用总结.

  使用resin已经有四、五年了,但以前都是做一些小系统,resin的压力并不大,近段时间做一个大系统,日平均ip上10万,resin的压力非常的大,除了对程序做优化以外,resin 的优化也小不了。

 

 

一、优化配置

  修改 conf/resin.conf 文章中的 JVM参数

 

<jvm-arg>-Xms512m</jvm-arg>
<jvm-arg>-Xss128k</jvm-arg>
<jvm-arg>-Xmn184m</jvm-arg>
<jvm-arg>-XX:ParallelGCThreads=20</jvm-arg>
<jvm-arg>-XX:+UseConcMarkSweepGC</jvm-arg>
<jvm-arg>-XX:+UseParNewGC</jvm-arg>
<jvm-arg>-Xdebug</jvm-arg>
<jvm-arg>-Xloggc:gc.log</jvm-arg>

 

  修改 最大thread-max为2500

      <!– Maximum number of threads. –>
      <thread-max>2500</thread-max>

      <!– Configures the socket timeout –>
      <socket-timeout>65s</socket-timeout>
        
      <!– Configures the keepalive –>
      <keepalive-max>10240</keepalive-max>
      <keepalive-timeout>30s</keepalive-timeout>

 

二、利用resin-admin监控resin运行情况。


 

第一行是Thread pool情况,如果发现Peak大于thread max,就应该修改conf/resin.conf 中的thread-max,相应的增大thread-max。

第二行是Threads,如果长期出现在这里而又不是SUN的方法,或者resin的方法的话,就要对这些方法进行测试、优化。

JVM调优总结 -Xms -Xmx -Xmn -Xss

三、常见配置举例


  1. 堆大小设置
    JVM 中最大堆大小有三方面限制:相关操作系统的数据模型(32-bt还是64-bit)限制;系统的可用虚拟内存限制;系统的可用物理内存限制。32位系统下,一般限制在1.5G~2G;64为操作系统对内存无限制。我在Windows Server 2003 系统,3.5G物理内存,JDK5.0下测试,最大可设置为1478m。
    典型设置:

    • java -Xmx3550m -Xms3550m -Xmn2g -Xss128k
      Xmx3550m:设置JVM最大可用内存为3550M。
      -Xms3550m
      :设置JVM促使内存为3550m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
      -Xmn2g
      :设置年轻代大小为2G。整个堆大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
      -Xss128k
      :设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。

    • java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0
      -XX:NewRatio=4
      :设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5
      -XX:SurvivorRatio=4
      :设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6
      -XX:MaxPermSize=16m:设置持久代大小为16m。
      -XX:MaxTenuringThreshold=0:设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。

  2. 回收器选择
    JVM给了三种选择:串行收集器、并行收集器、并发收集器,但是串行收集器只适用于小数据量的情况,所以这里的选择主要针对并行收集器和并发收集器。默认情况下,JDK5.0以前都是使用串行收集器,如果想使用其他收集器需要在启动时加入相应参数。JDK5.0以后,JVM会根据当前系统配置进行判断。

    1. 吞吐量优先的并行收集器
      如上文所述,并行收集器主要以到达一定的吞吐量为目标,适用于科学技术和后台处理等。
      典型配置

      • java -Xmx3800m -Xms3800m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20
        -XX:+UseParallelGC
        :选择垃圾收集器为并行收集器。此配置仅对年轻代有效。即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集。
        -XX:ParallelGCThreads=20:配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。

      • java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC
        -XX:+UseParallelOldGC:配置年老代垃圾收集方式为并行收集。JDK6.0支持对年老代并行收集。

      • java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC  -XX:MaxGCPauseMillis=100
        -XX:MaxGCPauseMillis=100:设置每次年轻代垃圾回收的最长时间,如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值。

      • java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC  -XX:MaxGCPauseMillis=100 -XX:+UseAdaptiveSizePolicy
        -XX:+UseAdaptiveSizePolicy
        :设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开。

    2. 响应时间优先的并发收集器
      如上文所述,并发收集器主要是保证系统的响应时间,减少垃圾收集时的停顿时间。适用于应用服务器、电信领域等。
      典型配置

      • java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC
        -XX:+UseConcMarkSweepGC:设置年老代为并发收集。测试中配置这个以后,-XX:NewRatio=4的配置失效了,原因不明。所以,此时年轻代大小最好用-Xmn设置。
        -XX:+UseParNewGC:设置年轻代为并行收集。可与CMS收集同时使用。JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此值。

      • java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseConcMarkSweepGC -XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection
        -XX:CMSFullGCsBeforeCompaction:由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理。
        -XX:+UseCMSCompactAtFullCollection:打开对年老代的压缩。可能会影响性能,但是可以消除碎片

  3. 辅助信息
    JVM提供了大量命令行参数,打印信息,供调试使用。主要有以下一些:

    • -XX:+PrintGC
      输出形式:[GC 118250K->113543K(130112K), 0.0094143 secs]

                      [Full GC 121376K->10414K(130112K), 0.0650971 secs]


    • -XX:+PrintGCDetails
      输出形式:[GC [DefNew: 8614K->781K(9088K), 0.0123035 secs] 118250K->113543K(130112K), 0.0124633 secs]

                      [GC [DefNew: 8614K->8614K(9088K), 0.0000665 secs][Tenured: 112761K->10414K(121024K), 0.0433488 secs] 121376K->10414K(130112K), 0.0436268 secs]


    • -XX:+PrintGCTimeStamps -XX:+PrintGC:PrintGCTimeStamps可与上面两个混合使用
      输出形式:11.851: [GC 98328K->93620K(130112K), 0.0082960 secs]

    • -XX:+PrintGCApplicationConcurrentTime:打印每次垃圾回收前,程序未中断的执行时间。可与上面混合使用
      输出形式:Application time: 0.5291524 seconds

    • -XX:+PrintGCApplicationStoppedTime:打印垃圾回收期间程序暂停的时间。可与上面混合使用
      输出形式:Total time for which application threads were stopped: 0.0468229 seconds

    • -XX:PrintHeapAtGC:打印GC前后的详细堆栈信息
      输出形式:
      34.702: [GC {Heap before gc invocations=7:
       def new generation   total 55296K, used 52568K [0x1ebd0000, 0x227d0000, 0x227d0000)
      eden space 49152K,  99% used [0x1ebd0000, 0x21bce430, 0x21bd0000)
      from space 6144K,  55% used [0x221d0000, 0x22527e10, 0x227d0000)
        to   space 6144K,   0% used [0x21bd0000, 0x21bd0000, 0x221d0000)
       tenured generation   total 69632K, used 2696K [0x227d0000, 0x26bd0000, 0x26bd0000)
      the space 69632K,   3% used [0x227d0000, 0x22a720f8, 0x22a72200, 0x26bd0000)
       compacting perm gen  total 8192K, used 2898K [0x26bd0000, 0x273d0000, 0x2abd0000)
         the space 8192K,  35% used [0x26bd0000, 0x26ea4ba8, 0x26ea4c00, 0x273d0000)
          ro space 8192K,  66% used [0x2abd0000, 0x2b12bcc0, 0x2b12be00, 0x2b3d0000)
          rw space 12288K,  46% used [0x2b3d0000, 0x2b972060, 0x2b972200, 0x2bfd0000)
      34.735: [DefNew: 52568K->3433K(55296K), 0.0072126 secs] 55264K->6615K(124928K)Heap after gc invocations=8:
       def new generation   total 55296K, used 3433K [0x1ebd0000, 0x227d0000, 0x227d0000)
      eden space 49152K,   0% used [0x1ebd0000, 0x1ebd0000, 0x21bd0000)
        from space 6144K,  55% used [0x21bd0000, 0x21f2a5e8, 0x221d0000)
        to   space 6144K,   0% used [0x221d0000, 0x221d0000, 0x227d0000)
       tenured generation   total 69632K, used 3182K [0x227d0000, 0x26bd0000, 0x26bd0000)
      the space 69632K,   4% used [0x227d0000, 0x22aeb958, 0x22aeba00, 0x26bd0000)
       compacting perm gen  total 8192K, used 2898K [0x26bd0000, 0x273d0000, 0x2abd0000)
         the space 8192K,  35% used [0x26bd0000, 0x26ea4ba8, 0x26ea4c00, 0x273d0000)
          ro space 8192K,  66% used [0x2abd0000, 0x2b12bcc0, 0x2b12be00, 0x2b3d0000)
          rw space 12288K,  46% used [0x2b3d0000, 0x2b972060, 0x2b972200, 0x2bfd0000)
      }
      , 0.0757599 secs]

    • -Xloggc:filename:与上面几个配合使用,把相关日志信息记录到文件以便分析。

  4. 常见配置汇总

    1. 堆设置

      • -Xms:初始堆大小
      • -Xmx:最大堆大小
      • -XX:NewSize=n:设置年轻代大小
      • -XX:NewRatio=n:设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
      • -XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5
      • -XX:MaxPermSize=n:设置持久代大小

    2. 收集器设置

      • -XX:+UseSerialGC:设置串行收集器
      • -XX:+UseParallelGC:设置并行收集器
      • -XX:+UseParalledlOldGC:设置并行年老代收集器
      • -XX:+UseConcMarkSweepGC:设置并发收集器

    3. 垃圾回收统计信息

      • -XX:+PrintGC
      • -XX:+PrintGCDetails
      • -XX:+PrintGCTimeStamps
      • -Xloggc:filename

    4. 并行收集器设置

      • -XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。
      • -XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间
      • -XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)

    5. 并发收集器设置

      • -XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。
      • -XX:ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。

四、调优总结

  1. 年轻代大小选择

    • 响应时间优先的应用尽可能设大,直到接近系统的最低响应时间限制(根据实际情况选择)。在此种情况下,年轻代收集发生的频率也是最小的。同时,减少到达年老代的对象。
    • 吞吐量优先的应用:尽可能的设置大,可能到达Gbit的程度。因为对响应时间没有要求,垃圾收集可以并行进行,一般适合8CPU以上的应用。

  2. 年老代大小选择

    • 响应时间优先的应用:年老代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话率会话持续时间等一些参数。如果堆设置小了,可以会造成内存碎片、高回收频率以及应用暂停而使用传统的标记清除方式;如果堆大了,则需要较长的收集时间。最优化的方案,一般需要参考以下数据获得:

      • 并发垃圾收集信息
      • 持久代并发收集次数
      • 传统GC信息
      • 花在年轻代和年老代回收上的时间比例
      减少年轻代和年老代花费的时间,一般会提高应用的效率

    • 吞吐量优先的应用:一般吞吐量优先的应用都有一个很大的年轻代和一个较小的年老代。原因是,这样可以尽可能回收掉大部分短期对象,减少中期的对象,而年老代尽存放长期存活对象。

  3. 较小堆引起的碎片问题
    因为年老代的并发收集器使用标记、清除算法,所以不会对堆进行压缩。当收集器回收时,他会把相邻的空间进行合并,这样可以分配给较大的对象。但是,当堆空间较小时,运行一段时间以后,就会出现“碎片”,如果并发收集器找不到足够的空间,那么并发收集器将会停止,然后使用传统的标记、清除方式进行回收。如果出现“碎片”,可能需要进行如下配置:

    • -XX:+UseCMSCompactAtFullCollection:使用并发收集器时,开启对年老代的压缩。
    • -XX:CMSFullGCsBeforeCompaction=0:上面配置开启的情况下,这里设置多少次Full GC后,对年老代进行压缩

五、参考文献

JVM详解之Java垃圾回收机制详解和调优

1.JVM的gc概述

  gc即垃圾收集机制是指jvm用于释放那些不再使用的对象所占用的内存。java语言并不要求jvm有gc,也没有规定gc如何工作。不过常用的jvm都有gc,而且大多数gc都使用类似的算法管理内存和执行收集操作。

  在充分理解了垃圾收集算法和执行过程后,才能有效的优化它的性能。有些垃圾收集专用于特殊的应用程序。比如,实时应用程序主要是为了避免垃圾收集中断,而大多数OLTP应用程序则注重整体效率。理解了应用程序的工作负荷和jvm支持的垃圾收集算法,便可以进行优化配置垃圾收集器。

  垃圾收集的目的在于清除不再使用的对象。gc通过确定对象是否被活动对象引用来确定是否收集该对象。gc首先要判断该对象是否是时候可以收集。两种常用的方法是引用计数和对象引用遍历。

  1.1.引用计数

  引用计数存储对特定对象的所有引用数,也就是说,当应用程序创建引用以及引用超出范围时,jvm必须适当增减引用数。当某对象的引用数为0时,便可以进行垃圾收集。

  1.2.对象引用遍历

  早期的jvm使用引用计数,现在大多数jvm采用对象引用遍历。对象引用遍历从一组对象开始,沿着整个对象图上的每条链接,递归确定可到达(reachable)的对象。如果某对象不能从这些根对象的一个(至少一个)到达,则将它作为垃圾收集。在对象遍历阶段,gc必须记住哪些对象可以到达,以便删除不可到达的对象,这称为标记(marking)对象。

  下一步,gc要删除不可到达的对象。删除时,有些gc只是简单的扫描堆栈,删除未标记的未标记的对象,并释放它们的内存以生成新的对象,这叫做清除(sweeping)。这种方法的问题在于内存会分成好多小段,而它们不足以用于新的对象,但是组合起来却很大。因此,许多gc可以重新组织内存中的对象,并进行压缩(compact),形成可利用的空间。

  为此,gc需要停止其他的活动活动。这种方法意味着所有与应用程序相关的工作停止,只有gc运行。结果,在响应期间增减了许多混杂请求。另外,更复杂的 gc不断增加或同时运行以减少或者清除应用程序的中断。有的gc使用单线程完成这项工作,有的则采用多线程以增加效率。

2.几种垃圾回收机制

  2.1.标记-清除收集器

  这种收集器首先遍历对象图并标记可到达的对象,然后扫描堆栈以寻找未标记对象并释放它们的内存。这种收集器一般使用单线程工作并停止其他操作。

  2.2.标记-压缩收集器

  有时也叫标记-清除-压缩收集器,与标记-清除收集器有相同的标记阶段。在第二阶段,则把标记对象复制到堆栈的新域中以便压缩堆栈。这种收集器也停止其他操作。

  2.3.复制收集器

  这种收集器将堆栈分为两个域,常称为半空间。每次仅使用一半的空间,jvm生成的新对象则放在另一半空间中。gc运行时,它把可到达对象复制到另一半空间,从而压缩了堆栈。这种方法适用于短生存期的对象,持续复制长生存期的对象则导致效率降低。

  2.4.增量收集器

  增量收集器把堆栈分为多个域,每次仅从一个域收集垃圾。这会造成较小的应用程序中断。

  2.5.分代收集器

  这种收集器把堆栈分为两个或多个域,用以存放不同寿命的对象。jvm生成的新对象一般放在其中的某个域中。过一段时间,继续存在的对象将获得使用期并转入更长寿命的域中。分代收集器对不同的域使用不同的算法以优化性能。

  2.6.并发收集器

  并发收集器与应用程序同时运行。这些收集器在某点上(比如压缩时)一般都不得不停止其他操作以完成特定的任务,但是因为其他应用程序可进行其他的后台操作,所以中断其他处理的实际时间大大降低。

  2.7.并行收集器

  并行收集器使用某种传统的算法并使用多线程并行的执行它们的工作。在多cpu机器上使用多线程技术可以显著的提高java应用程序的可扩展性。



3.Sun HotSpot

  1.4.1 JVM堆大小的调整

  Sun HotSpot 1.4.1使用分代收集器,它把堆分为三个主要的域:新域、旧域以及永久域。Jvm生成的所有新对象放在新域中。一旦对象经历了一定数量的垃圾收集循环后,便获得使用期并进入旧域。在永久域中jvm则存储class和method对象。就配置而言,永久域是一个独立域并且不认为是堆的一部分。

  下面介绍如何控制这些域的大小。可使用-Xms和-Xmx 控制整个堆的原始大小或最大值。

  下面的命令是把初始大小设置为128M:

  java –Xms128m

  –Xmx256m为控制新域的大小,可使用-XX:NewRatio设置新域在堆中所占的比例。

  下面的命令把整个堆设置成128m,新域比率设置成3,即新域与旧域比例为1:3,新域为堆的1/4或32M:

java –Xms128m –Xmx128m
–XX:NewRatio =3可使用-XX:NewSize和-XX:MaxNewsize设置新域的初始值和最大值。

  下面的命令把新域的初始值和最大值设置成64m:

java –Xms256m –Xmx256m –Xmn64m

  永久域默认大小为4m。运行程序时,jvm会调整永久域的大小以满足需要。每次调整时,jvm会对堆进行一次完全的垃圾收集。

  使用-XX:MaxPerSize标志来增加永久域搭大小。在WebLogic Server应用程序加载较多类时,经常需要增加永久域的最大值。当jvm加载类时,永久域中的对象急剧增加,从而使jvm不断调整永久域大小。为了避免调整,可使用-XX:PerSize标志设置初始值。

  下面把永久域初始值设置成32m,最大值设置成64m。

java -Xms512m -Xmx512m -Xmn128m -XX:PermSize=32m -XX:MaxPermSize=64m

  默认状态下,HotSpot在新域中使用复制收集器。该域一般分为三个部分。第一部分为Eden,用于生成新的对象。另两部分称为救助空间,当Eden 充满时,收集器停止应用程序,把所有可到达对象复制到当前的from救助空间,一旦当前的from救助空间充满,收集器则把可到达对象复制到当前的to救助空间。From和to救助空间互换角色。维持活动的对象将在救助空间不断复制,直到它们获得使用期并转入旧域。使用-XX:SurvivorRatio 可控制新域子空间的大小。

  同NewRation一样,SurvivorRation规定某救助域与Eden空间的比值。比如,以下命令把新域设置成64m,Eden占32m,每个救助域各占16m:

java -Xms256m -Xmx256m -Xmn64m -XX:SurvivorRation =2

  如前所述,默认状态下HotSpot对新域使用复制收集器,对旧域使用标记-清除-压缩收集器。在新域中使用复制收集器有很多意义,因为应用程序生成的大部分对象是短寿命的。理想状态下,所有过渡对象在移出Eden空间时将被收集。如果能够这样的话,并且移出Eden空间的对象是长寿命的,那么理论上可以立即把它们移进旧域,避免在救助空间反复复制。但是,应用程序不能适合这种理想状态,因为它们有一小部分中长寿命的对象。最好是保持这些中长寿命的对象并放在新域中,因为复制小部分的对象总比压缩旧域廉价。为控制新域中对象的复制,可用-XX:TargetSurvivorRatio控制救助空间的比例(该值是设置救助空间的使用比例。如救助空间位1M,该值50表示可用500K)。该值是一个百分比,默认值是50。当较大的堆栈使用较低的 sruvivorratio时,应增加该值到80至90,以更好利用救助空间。用-XX:maxtenuring threshold可控制上限。

  为放置所有的复制全部发生以及希望对象从eden扩展到旧域,可以把MaxTenuring Threshold设置成0。设置完成后,实际上就不再使用救助空间了,因此应把SurvivorRatio设成最大值以最大化Eden空间,设置如下:

java … -XX:MaxTenuringThreshold=0 –XX:SurvivorRatio=50000 …

4.BEA JRockit JVM的使用

  Bea WebLogic 8.1使用的新的JVM用于Intel平台。在Bea安装完毕的目录下可以看到有一个类似于jrockit81sp1_141_03的文件夹。这就是 Bea新JVM所在目录。不同于HotSpot把Java字节码编译成本地码,它预先编译成类。JRockit还提供了更细致的功能用以观察JVM的运行状态,主要是独立的GUI控制台(只能适用于使用Jrockit才能使用jrockit81sp1_141_03自带的console监控一些cpu及 memory参数)或者WebLogic Server控制台。

  Bea JRockit JVM支持4种垃圾收集器:

  4.1.1.分代复制收集器

  它与默认的分代收集器工作策略类似。对象在新域中分配,即JRockit文档中的nursery。这种收集器最适合单cpu机上小型堆操作。

  4.1.2.单空间并发收集器

  该收集器使用完整堆,并与背景线程共同工作。尽管这种收集器可以消除中断,但是收集器需花费较长的时间寻找死对象,而且处理应用程序时收集器经常运行。如果处理器不能应付应用程序产生的垃圾,它会中断应用程序并关闭收集。

  分代并发收集器这种收集器在护理域使用排它复制收集器,在旧域中则使用并发收集器。由于它比单空间共同发生收集器中断频繁,因此它需要较少的内存,应用程序的运行效率也较高,注意,过小的护理域可以导致大量的临时对象被扩展到旧域中。这会造成收集器超负荷运作,甚至采用排它性工作方式完成收集。

  4.1.3.并行收集器

  该收集器也停止其他进程的工作,但使用多线程以加速收集进程。尽管它比其他的收集器易于引起长时间的中断,但一般能更好的利用内存,程序效率也较高。

  默认状态下,JRockit使用分代并发收集器。要改变收集器,可使用-Xgc:,对应四个收集器分别为 gencopy,singlecon,gencon以及parallel。可使用-Xms和-Xmx设置堆的初始大小和最大值。要设置护理域,则使用- Xns:java –jrockit –Xms512m –Xmx512m –Xgc:gencon –Xns128m…尽管JRockit支持-verbose:gc开关,但它输出的信息会因收集器的不同而异。JRockit还支持memory、 load和codegen的输出。

  注意 :如果 使用JRockit JVM的话还可以使用WLS自带的console(C:\bea\jrockit81sp1_141_03\bin下)来监控一些数据,如cpu, memery等。要想能构监控必须在启动服务时startWeblogic.cmd中加入-Xmanagement参数。


5.如何从JVM中获取信息来进行调整

  -verbose.gc开关可显示gc的操作内容。打开它,可以显示最忙和最空闲收集行为发生的时间、收集前后的内存大小、收集需要的时间等。打开- xx:+ printgcdetails开关,可以详细了解gc中的变化。打开-XX: + PrintGCTimeStamps开关,可以了解这些垃圾收集发生的时间,自jvm启动以后以秒计量。最后,通过-xx: + PrintHeapAtGC开关了解堆的更详细的信息。为了了解新域的情况,可以通过-XX:=PrintTenuringDistribution开关了解获得使用期的对象权。

6.Pdm系统JVM调整

  6.1.服务器:前提内存1G 单CPU

  可通过如下参数进行调整:-server 启用服务器模式(如果CPU多,服务器机建议使用此项)

  -Xms,-Xmx一般设为同样大小。 800m

  -Xmn 是将NewSize与MaxNewSize设为一致。320m

  -XX:PerSize 64m

  -XX:NewSize 320m 此值设大可调大新对象区,减少Full GC次数

  -XX:MaxNewSize 320m

  -XX:NewRato NewSize设了可不设。

  -XX: SurvivorRatio

  -XX:userParNewGC 可用来设置并行收集

  -XX:ParallelGCThreads 可用来增加并行度

  -XXUseParallelGC 设置后可以使用并行清除收集器

  -XX:UseAdaptiveSizePolicy 与上面一个联合使用效果更好,利用它可以自动优化新域大小以及救助空间比值

  6.2.客户机:通过在JNLP文件中设置参数来调整客户端JVM

  JNLP中参数:initial-heap-size和max-heap-size

  这可以在framework的RequestManager中生成JNLP文件时加入上述参数,但是这些值是要求根据客户机的硬件状态变化的(如客户机的内存大小等)。建议这两个参数值设为客户机可用内存的60%(有待测试)。为了在动态生成JNLP时以上两个参数值能够随客户机不同而不同,可靠虑获得客户机系统信息并将这些嵌到首页index.jsp中作为连接请求的参数。

  在设置了上述参数后可以通过Visualgc 来观察垃圾回收的一些参数状态,再做相应的调整来改善性能。一般的标准是减少fullgc的次数,最好硬件支持使用并行垃圾回收(要求多CPU)。

Resin 3 pro高并发,响应性与稳定性方案

以下方案我是在Intel xeon(至强) 3.2G 2个双核物理CPU+2G内存(Ecc)上进行:

resin版本为resin-pro-3.0.21,JVM为Jrockit 1.5_06, resin java 启动参数 -Xms256m -Xmx512m

1. 以下为resin.conf配置
<!–

– Resin 3.0 configuration file.

–>

<resin xmlns=“http://caucho.com/ns/resin”
xmlns:resin=“http://caucho.com/ns/resin/core”>

<!–

– Logging configuration for the JDK logging API.

–>

<log name=“” level=“all” path=“stdout:” timestamp=“[%H:%M:%S.%s] “/>



<logger name=“com.caucho.java” level=“config”/>

<logger name=“com.caucho.loader” level=“config”/>



<dependency-check-interval>600s</dependency-check-interval>



<javac compiler=“internal” args=“”/>



<thread-pool>

<thread-max>10240</thread-max>

<spare-thread-min>50</spare-thread-min>

</thread-pool>



<min-free-memory>5M</min-free-memory>



<server>

<class-loader>

<tree-loader path=“${resin.home}/lib”/>

<tree-loader path=“${server.root}/lib”/>

</class-loader>



<keepalive-max>1024</keepalive-max>

<keepalive-timeout>60s</keepalive-timeout>



<resin:if test=“${resin.isProfessional()}”>

<select-manager enable=“true”/>

</resin:if>



<bind-ports-after-start/>



<http server-id=“” host=“*” port=“80”/>



<cluster>

<srun server-id=“” host=“127.0.0.1” port=“6802”/>

</cluster>



<resin:if test=“${resin.isProfessional()}”>

<persistent-store type=“cluster”>

<init path=“session”/>

</persistent-store>

</resin:if>



<ignore-client-disconnect>true</ignore-client-disconnect>



<resin:if test=“${isResinProfessional}”>

<cache path=“cache” memory-size=“20M”/>

</resin:if>



<web-app-default>

<class-loader>

<tree-loader path=“${server.root}/ext-webapp”/>

</class-loader>



<cache-mapping url-pattern=“/” expires=“60s”/>

<cache-mapping url-pattern=“*.gif” expires=“600s”/>

<cache-mapping url-pattern=“*.jpg” expires=“600s”/>



<servlet servlet-name=“directory”
servlet-class=“com.caucho.servlets.DirectoryServlet”>

<init enable=“false”/>

</servlet>



<allow-servlet-el/>



<session-config>

<enable-url-rewriting>false</enable-url-rewriting>

</session-config>



</web-app-default>



<host-default>

<class-loader>

<compiling-loader path=“webapps/WEB-INF/classes”/>

<library-loader path=“webapps/WEB-INF/lib”/>

</class-loader>



<!–access-log path=“logs/access.log”

format=‘%h %l %u %t “%r” %s %b “%{Referer}i” “%{User-Agent}i”‘
rollover-period=“1W”/–>



<web-app-deploy path=“webapps”/>



<ear-deploy path=“deploy”>

<ear-default>

<!– Configure this for the ejb server



– <ejb-server>

– <config-directory>WEB-INF</config-directory>

– <data-source>jdbc/test</data-source>

– </ejb-server>

–>

</ear-default>

</ear-deploy>



<resource-deploy path=“deploy”/>



<web-app-deploy path=“deploy”/>

</host-default>



<resin:import path=“${resin.home}/conf/app-default.xml”/>



<host-deploy path=“hosts”>

<host-default>

<resin:import path=“host.xml” optional=“true”/>

</host-default>

</host-deploy>



<host id=“” root-directory=“.”>

<web-app id=“/” document-directory=“d:\website\chat”>

</web-app>

</host>



</server>

</resin>





2. 在应用的web.xml中加入resin status查看servlet映射

      <servlet-mapping servlet-class=‘com.caucho.servlets.ResinStatusServlet’>

<url-pattern>/resin-status</url-pattern>

<init enable=“read”/>

</servlet-mapping>



3. 启动resin,确认应用正常启动。

4. 写访问测试程序

import java.io.InputStream;

import java.net.URL;


public class TestURL

{
public static void main(String[] args) throws Exception

{
long a = System.currentTimeMillis();

System.out.println(“Starting request url:”);

for(int i = 0; i < 10000; i++){
URL url = new URL(“http://192.168.1.200/main.jsp”);



InputStream is = url.openStream();

is.close();

System.out.println(“Starting request url:”+i);

}
System.out.println(“request url end.take “(System.currentTimeMillis()-a)“ms”);

}


}



5. 在Jbuilder中执行TestURL

在执行过程中,一边刷新http://192.168.1.200/resin-status,查看resin状态,在http://*:80 中的 Active Threads 和 Total,会一直增长,当长到512的时候不再增长,这时再刷新resin-status页面时,会发现打开很慢。原因是服务器已经达到最大连接数,在等待前面连接的释放而不能接受新的连接。

于是下载Resin 3.0.21源码,搜索 512,发现com.caucho.server.port.Port类中有以下代码:
// default timeout


private long _timeout = 65000L;



private int _connectionMax = 512;//就是这行,查找resin所有源码后,发现没有对这个值进行设置

private int _minSpareConnection = 16;



private int _keepaliveMax = -1;



private int _minSpareListen = 5;

private int _maxSpareListen = 10;


将_connectionMax 改为 20480,然后重新编译并替换resin.jar中的Port类。

6. 重新启动Resin,再次运行TestURL进行测试,这次你会发现Threads Active 和 Total 一直变大,且可以超过512一直增大,在测试程序运行过程中刷新页面,页面响应性能还是不错的.

另,测试过程中Resin会打印出 1-3次 强制执行GC的信息,属于正常。

7.待测试完毕,Threads Active 和 Total 马上降为1.Idle为9,总内存为536.87Meg 空闲内存为480.33M

再经多次测试,结果一致,内存回收正常,表明当前 resin 稳定性和响应性可靠。

出自 JAVA开发者(http://www.chinajavaworld.com)

破解resin-pro-3.1

在网上查看了很多关于resin性能的文章,比较公认的说法是resin比tomcat的性能要高很多。


但最近在csdn上看了一篇文章,说resin 3.x的免费版与tomcat性能已经不相上下了。原因有两个:


1、tomcat从5.0后就开始在性能上不断改进,以达到生产环境的要求;


2、resin从3.0开始分为免费版与pro版,更多的性能改进被放到了pro版中了,所以免费版的性能就下降了。


基于这样的一些原因,更由于resin的pro版支持session持久化存储、负载均衡及cache等高端特性,于是对破解resin pro版产生了兴趣。


经过N个夜晚的奋战,终于把这个pro版给破解了。


网上也有人说破解得不好的resin pro版可能会使性能下降,个人感觉不存在这样的问题,只要破解了即可享用pro版的各种特性,性能也有大幅提高。


以下的resin启动的部分输出:


Resin Professional 3.1.4 (built Tue, 04 Dec 2007 11:27:12 PST)
Copyright(c) 1998-2007 Caucho Technology.  All rights reserved.


Starting Resin on Mon, 31 Dec 2007 17:18:52 +0800 (CST)


[17:18:53.640] {main} Proxy Cache disk-size=1024M memory-size=64M
[17:18:53.656] {main} PingThread[] starting, checking []
[17:18:54.109] {main}
[17:18:54.234] {main} Windows XP 5.1 x86
[17:18:54.343] {main} Java 1.5.0_11-b03, 32, mixed mode, GBK, zh, Sun Microsystems Inc.
[17:18:54.468] {main} user.name: dingl
[17:18:54.593] {main} resin.home = C:\resin-pro-3.1.4
[17:18:54.718] {main} resin.root = C:\resin-pro-3.1.4
[17:18:54.828] {main} resin.conf = /D:/workspace/project/resin-3.1.conf
[17:18:54.828] {main}
[17:18:55.734] {main} WebApp[http://localhost:80] Set web app root system property: ‘webapp.root’ = [D:\workspace\project\WebContent\]
[17:18:59.515] {main} WebApp[http://localhost:80] active
[17:19:00.171] {main} WebApp[http://localhost:80/resin-doc] active
[17:19:00.390] {main} WebApp[http://localhost:80/resin-admin] active
[17:19:00.500] {main} Host[] active
[17:19:00.640] {main} Socket JNI library is not available.
[17:19:00.640] {main} Resin will still run but performance will be slower.
[17:19:00.640] {main} To compile the Socket JNI library on Unix, use ./configure; make; make install.
[17:19:00.796] {main} hmux listening to localhost:6800
[17:19:00.921] {main} http listening to *:80
[17:19:01.140] {main} Server[id=,cluster=app-tier] active
[17:19:01.281] {main} Resin started in 9547ms


 可以看到已经没有关于license的提示了,并且增加了Proxy Cache disk-size=1024M memory-size=64M的输出,说明已经使用了cache来提高性能。


 值得一提的是,要想获得更好的性能,请使用linux/unix系统,因为在windows下,默认安装的resin是不能使用JNI库的,需要手工编译。而在linux/unix下,make源码后就已经将JNI库编译好了。这个特性可以从输出中看出来:


[17:19:00.640] {main} Socket JNI library is not available.
[17:19:00.640] {main} Resin will still run but performance will be slower.


linux/unix系统中没有这样的提示。


目前本blog提供resin-pro-3.1.3版的下载,地址为:


resin-pro-3.1.3 for windows 下载


resin-pro-3.1.3 for linux/unix 下载


resin-pro-3.1.4由于目前标记为dev,暂不破解此版本。3.1.5目前正在开发,尚未发布,目前已经可以看到3.1.5的change log了。


使用ping功能使resin更健壮

由于种种原因,resin有时会遇到resin down机的情况,比如:java.lang.OutOfMemoryError。这样的问题如果从代码角度来解决问题,也不是一时半会能解决的,应急解决方案是启用resin-pro的ping功能。
    <resin:if test=”${isResinProfessional}”>
      <ping>
        <url>http://localhost:8080/ping.jsp</url>
        <sleep-time>1m</sleep-time>
        <try-count>5</try-count>
        <socket-timeout>30s</socket-timeout>
      </ping>
    </resin:if>


ping.jsp可随意写一些代码,纯html代码也行,只能服务器能响应返回200。


如果访问resin访问http://localhost:8080/ping.jsp返回不是200 code,然后再重试try-count次,如果依然不响应200,则将自动重启resin服务。


如果在resin tag下配置了:


  <system-property mail.smtp.host=”127.0.0.1″/>
  <system-property mail.smtp.port=”25″/>


还可以让resin自动给你发个邮件通知你它自动重启过了:


    <ping resin:type=”com.caucho.server.admin.PingMailer”>
      <url>http://localhost:8080/ping.jsp</url>
      <mail-to>fred@hogwarts.com</mail-to>
      <mail-from>resin@hogwarts.com</mail-from>
      <mail-subject>Resin ping has failed for server ${’${’}server.name}</mail-subject>
    </ping>


这样配置好了,不管resin出现什么故障,只要resin进程不死,就可以放心睡大觉了。


如果还启用了resin的session持久化存储,用户基本感觉不到服务已经重启过了。


如果启用了resin的cluster,那么请修改httpd.sh,将-server参数加上,不然重启resin后是没有cluster的。


resin服务器突然变慢问题解决办法.

  本人负责的一个网站不知道为什么,由周一开始,突然间变得很慢,有时则很快.搞来搞去,搞了差不多一个星期(部份时间开发新功能去了).终于找到原因了.

 

服务器情况:

linux as4

Intel(R) Xeon(R) CPU            5130  @ 2.00GHz

4G内存,

300Gscsi+320GSATA硬盘.

resin 3.0.23+ lighttpd

 

resin启动参数

-server -verbose:gc -Xdebug -Xloggc:gc.log -Djava.awt.headless=true -XX:PermSize=128M -XX:NewRatio=4 -XX:+UseConcMarkSweepGC -XX:MaxPermSize=200m -XX:NewSize=100 -XX:MaxNewSize=128m -Xmn256m -Xms512m -Xmx512m

 

  服务器运行一直很正常,到了周一,网站变慢,CPU占用50%以上,但内存点使用50%,其中大部分是mysql用去了.load average: 10.02, 10.03, 10.00 .

 

  全部程序都测试过.底层代码也查过.JDK换过,RESIN版本换过,mysql配置也换过.问题依然存在.

 

  利用resin-admin查看情况.

 









Total memory: 501.44Meg
Free memory:

350.37Meg


  

  看上去也正常.不过free memory 变化的很快,由100多M,突然变到300多M.一开始还以为是程序释放了内存.就不去管他了.

 

  经过好多晚的调度,发现一个规律.当启动resin时,一切正常.证明跟jdk版本无关.

 

  今天早上更新服务器程序时,发现resin目录有个gc.log文件.打开一看.全部是gc的执行记录.很多.于是使用tail命令一下.

 

        tail -f gc.log

 

  吓了我一跳,平均每秒就会执行一次gc.问题就在这里.是gc把服务器拖慢了.

 

  于是就把resin的启动参数改一下,把内存改大,gc就不会这么容易触发.

 

        -server -verbose:gc -Xdebug -Xloggc:gc.log -Djava.awt.headless=true -XX:PermSize=128M -XX:NewRatio=4 -XX:+UseConcMarkSweepGC -XX:MaxPermSize=200m -XX:NewSize=100 -XX:MaxNewSize=128m -Xmn256m -Xms1024m -Xmx1024m

 

 

  现在好了,CPU占用只有0.1%左右,有时上到30%多,但很快降下来.load average: 0.05, 0.01, 0.00 <—服务器很健康.

 

  利用resin-admin查看情况.









Total memory: 998.44Meg
Free memory: 607.76Meg

      resin平均使用400M左右的内存,一开始只配置了512给她,怪不得老是gc.呵.