Be alaways javaing...

Loving Java
posts - 43, comments - 5, trackbacks - 0, articles - 0
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

2008年12月16日


posted @ 2008-12-16 09:37 追风舞者 阅读(95) | 评论 (0)编辑 收藏

2008年12月11日

转载自:www.blogjava.net/zhaobin
来自BlueDavy的博客

    1. 为什么学习OSGi
    2. OSGi成为JSR291以及OSGi对Spring产生的影响
    3. OSGi应用于企业应用Step by step之持久篇
    4. SCA:企业应用开发的利器
    5. OSGi和SCA
    6. 基于OSGi实现可扩展的模块的设计
    7. OSGi in action online演讲的资料【推荐】
    8. 基于OSGi搭建动态化的系统
    9. OSGi Extender Model启示录
    10. Play OSGi
    11. Bnd - Bundle Tool中文使用手册
    12. 基于Eclipse Equinox的插件框架:TPF
    13. TPF插件管理框架功能、实现以及下载【推荐】
    14. 发布《OSGi实战》正式版【特别推荐】
    15. 发布《OSGi进阶》正式版【特别推荐】
    16. OSGi PPT For Newer【推荐】

来自 勤劳的蜜蜂的博客

    1. OSGi介绍(一)什么是OSGi
    2. OSGi介绍(二)一个假想的实例
    3. OSGi介绍(三)OSGi service platform的体系结构
    4. OSGi介绍(四)第一个bundle
    5. OSGi介绍(五)两个bundle
    6. OSGi介绍(六)OSGi的service
    7. OSGi介绍(七)bundle和service(续)

来自Phrancol的博客

    1. 构建模块化的动态Web应用(演示版)
    2. 开发基于Equinox/OSGI 的 Web Application (一) [译]
    3. 开发基于Equinox/OSGI 的 Web Application (二) [译]
    4. Spring-OSGI 1.0 M3 中文手册(Spring Dynamic Modules Reference Guide for OSGi(tm) Service Platforms)
    5. Developing Equinox/Spring-osgi/Spring Framework Web Application Part 1 - 显示首页【推荐】
    6. Developing Equinox/Spring-osgi/Spring Framework Web Application Part 2 - 使用Spring-OSGI
    7. Developing Equinox/Spring-osgi/Spring Framework Web Application Part 3 - 找到我的Bean

来自八进制的博客

    1. Equinox OSGi服务器应用程序的配置步骤
    2. 利用OSGi DS实现可配置Web应用程序初探

来自罗明的博客

    1. 让OSGi支持JSF Web开发
    2. [OSGi]为什么我们需要Bnd?

来自yangbutao的博客

    1. OSGI 服务层探究
    2. OSGI Module & lifecycle

来自erylee的博客

    1. OpenCore: OSGi上部署Hibernate的四种方式
    2. OpenCore:OSGi上部署Apache Common Log
    3. OpenCore:基于OSGi开发纯插件体系结构的WEB应用程序

来自任成's Blog

    1. Equinox OSGi系列之一 Equinox入门
    2. Equinox OSGi系列之二 搭建Equinox OSGi核心环境
    3. Equinox OSGi系列之三 Equinox配置参数详解
    4. Equinox OSGi系列之四 创建自己的OSGi应用项目 

来自IBM 的developerworks

    1. 利用 Eclipse 开发基于 OSGi 的 Bundle 应用(2006 年 7 月 17 日)
    2. 基于 OSGi 的面向服务的组件编程(2007 年 8 月 31 日)

来自其他的博客或帖子:

    1. SOA应用系统总体框架及相关概念
    2. SCA与OSGi真的具有可比性吗?【推荐】
    3. Eclipse(3.1) Plugin Framework(基于OSGI的Plugin Architecture)
    4. 谈谈osgi中的事件机制
    5. 基于Web的OSGi框架构想
    6. From JavaWorld  "Hello, OSGi, Part 1: Bundles for beginners" "Hello, OSGi, Part 3: Take it to the server side - JavaWorld"

posted @ 2008-12-11 14:40 追风舞者 阅读(162) | 评论 (0)编辑 收藏

2008年12月3日

刚刚找到的面试题目。自己做了一下,反正挺惨不人睹的。贴出来就想帮帮有需要的人
并且问问为什么是这个结果呢?有的题的答案真的想不到啊~想不到~

一、判断题(30分)
1.Java程序里,创建新的类对象用关键字new,回收无用的类对象使用关键字free。
2.对象可以赋值,只要使用赋值号(等号)即可,相当于生成了一个各属性与赋值对象相同的新对象。
3.有的类定义时可以不定义构造函数,所以构造函数不是必需的。
4.类及其属性、方法可以同时有一个以上的修饰符来修饰。
5.Java的屏幕坐标是以像素为单位,容器的左下角被确定为坐标的起点
6.抽象方法必须在抽象类中,所以抽象类中的方法都必须是抽象方法。
7.Final类中的属性和方法都必须被final修饰符修饰。
8.最终类不能派生子类,最终方法不能被覆盖。
9.子类要调用父类的方法,必须使用super关键字。
10.一个Java类可以有多个父类。
11.如果p是父类Parent的对象,而c是子类Child的对象,则语句c = p是正确的。
12.在java集合中,Vector和HashMap是线程安全的。
13.当一个方法在运行过程中产生一个异常,则这个方法会终止,但是整个程序不一定终止运行。
14.接口是特殊的类,所以接口也可以继承,子接口将继承父接口的所有常量和抽象方法。
15.用“+”可以实现字符串的拼接,用- 可以从一个字符串中去除一个字符子串。

二、选择题(30分)
1、关于垃圾收集的哪些叙述是正确的(   ):
A.程序开发者必须自己创建一个线程进行内存释放的工作
B.垃圾收集允许程序开发者明确指定并立即释放该内存
C.垃圾收集将检查并释放不再使用的内存
D.垃圾收集能够在期望的时间释放被java对象使用的内存
2、下面的哪些赋值语句是不正确的(   ):
A.float f=11.1;
B.double d=5.3E12;
C.double d=3.14159;
D.double d=3.14D;
3、下面关于变量及其范围的陈述哪些是不正确的(   ):
A.实例变量是类的成员变量
B.实例变量用关键字static声明
C.在方法中定义的局部变量在该方法被执行时创建
D.局部变量在使用前必须被初始化
4、下列关于修饰符混用的说法,错误的是(  ):
A.abstract不能与final并列修饰同一个类
B.abstract类中不可以有private的成员
C.abstract方法必须在abstract类中
D.static方法中能处理非static的属性
5、容器Panel和Applet缺省使用的布局编辑策略是(    ):
A、BorderLayout  B、FlowLayout      C、GridLayout      D、CardLayout
6、以下标识符中哪项是不合法的(    ):
A、 BigMeaninglessName                     B、$int
C、1 st                                    D、$1
7、main方法是Java  Application程序执行的入口点,关于main方法的方法头以下哪项是合法的(    ):
A、    public  static  void  main()   
B、    public  static  void   main(String[ ]  args)
C、    public  static int  main(String[ ]  arg)
D、    public  void  main(String  arg[ ])
8、执行完以下代码int [ ]  x = new  int[25];后,以下哪项说明是正确的(    ):
A、    x[24]为0
B、    x[24]未定义
C、    x[25]为0
D、    x[0]为空
9、以下代码段执行后的输出结果为(     ):
       int  x=3; int  y=10;
       System.out.println(y%x);
A、0
B、1
C、2
D、3
10、以下哪个表达式是不合法的(    ):
A、String  x=”Hello”;   int  y=9;   x+=y;
B、String  x=”Hello”;   int  y=9;  if(x= =y)  { }
C、String  x=”Hello”;  int  y=9;  x=x+y;
D、String  x=null;  int  y=(x!=null)&&(x.length()>0) ? x.length : 0
11、编译运行以下程序后,关于输出结果的说明正确的是 (    ):
       public  class   Conditional{
              public  static  void  main(String  args[  ]){
                     int  x=4;
                     System.out.println(“value  is  “+ ((x>4) ? 99.9 :9));
}
}
A、    输出结果为:value  is  99.99
B、    输出结果为:value  is  9
C、    输出结果为:value  is  9.0
D、    编译错误
12、以下声明合法的是(     ):
A、    default  String  s;
B、    public  final  static  native  int  w( )
C、    abstract  double  d;
D、    abstract  final  double  hyperbolicCosine( )
13、关于以下application的说明,正确的是(    ):
1.  class   StaticStuff
2. {
3.                  static  int  x=10;
4.                  static  { x+=5;}
5.                  public  static  void  main(String  args[ ])
6.                  {
7.                       System.out.println(“x=” + x);
8.                  }
9.                  static  { x/=3;}
10.   }
A、 4行与9行不能通过编译,因为缺少方法名和返回类型 
B、 9行不能通过编译,因为只能有一个静态初始化器
C、 编译通过,执行结果为:x=5
D、编译通过,执行结果为:x=3
14、关于以下程序代码的说明正确的是(   ):
1.class  HasStatic{
2.    private  static  int  x=100;
3.    public  static  void  main(String  args[  ]){
4.        HasStatic  hs1=new  HasStatic(  );
5.        hs1.x++;
6.        HasStatic  hs2=new  HasStatic(  );
7.        hs2.x++;
8.        hs1=new  HasStatic( );
9.        hs1.x++;
10.       HasStatic.x- -;
11.       System.out.println(“x=”+x);
12.   }
13.}
A、5行不能通过编译,因为引用了私有静态变量
B、10行不能通过编译,因为x是私有静态变量
C、程序通过编译,输出结果为:x=103
D、程序通过编译,输出结果为:x=102
15、以下选项中循环结构合法的是(    ):
A、while (int  i<7){
     i++;
     System.out.println(“i is “+i);
}
B、int  j=3;
while(j){
   System.out.println(“ j  is “+j);
}
C、int  j=0;
for(int  k=0; j + k !=10; j++,k++){
    System.out.println(“ j  is “+ j + “k  is”+ k);
}
D、int  j=0;
do{
        System.out.println( “j  is “+j++);
        if (j = = 3) {continue  loop;}
}while  (j<10);

三、简答题(40分)
1.    写出下列程序的运行结果
public class Cat
{  
  void mi( ) throws NullPointerException
  {
    System.out.println( “Cat mi mi .. “ );
  }
}
public class SmallCat extends Cat
{int i=8;
  void mi( ) throws Exception
  {
    System.out.println( “SmallCat mi mi .. “ );
  }
  public static void main( String[] a ) throws Exception
  {
    Cat cat = new SmallCat();
    cat.mi();
  }
}


写出下列程序的运行结果
interface Playable {
    void play();
}
interface Bounceable {
    void play();
}
interface Rollable extends Playable, Bounceable {
    Ball ball = new Ball("PingPang");
}
class Ball implements Rollable {
    private String name;
    public String getName() {
        return name;
    }
    public Ball(String name) {
        this.name = name;       
    }
   public void play() {
        ball = new Ball("Football");
        System.out.println(ball.getName());
    }
}

写出下列程序的运行结果
class Value{
public int i = 15;
}
public class Test{
public static void main(String argv[]){
       Test t = new Test();
    t.first();
   }
public void first(){
       int i = 5;
       Value v = new Value();
      v.i = 25;
      second(v, i);
      System.out.println(v.i);
   }
public void second(Value v, int i){
      i = 0;
       v.i = 20;
     Value val = new Value();
        v = val;
        System.out.println(v.i + " " + i);
      }
}


写出下列程序的运行结果
class MyThread extends Thread{
public void run(){
System.out.println("MyThread: run()");
}
public void start(){
System.out.println("MyThread: start()");
    }
}
class MyRunnable implements Runnable{
public void run(){
System.out.println("MyRunnable: run()");
    }
public void start(){
System.out.println("MyRunnable: start()");
   }
}
public class MyTest {
public static void main(String args[]){
MyThread myThread  =  new MyThread();
MyRunnable myRunnable = new MyRunnable();
Thread thread  =  new Thread(myRunnable);
myThread.start();
thread.start();
}
}

posted @ 2008-12-03 09:57 追风舞者 阅读(318) | 评论 (0)编辑 收藏

一、参考资料:

  1. Tuning Garbage Collection with the 5.0 Java Virtual Machine 官方指南。
  2. Hotspot memory management whitepaper 官方白皮书。
  3. Java Tuning White Paper 官方文档。
  4. FAQ about Garbage Collection in the Hotspot  官方FAQ,JVM1.4.2。
  5. Java HotSpot 虚拟机中的垃圾收集 JavaOne2004上的中文ppt
  6. A Collection of JVM Options JVM选项的超完整收集。

二、基本概念

1、堆(Heap)

JVM管理的内存叫堆。在32Bit操作系统上有1.5G-2G的限制,而64Bit的就没有。

JVM初始分配的内存由-Xms指定,默认是物理内存的1/64但小于1G。

JVM最大分配的内存由-Xmx指定,默认是物理内存的1/4但小于1G。

默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制,可以由-XX:MinHeapFreeRatio=指定。
默认空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制,可以由-XX:MaxHeapFreeRatio=指定。

服务器一般设置-Xms、-Xmx相等以避免在每次GC 后调整堆的大小,所以上面的两个参数没啥用。 

2.基本收集算法

  1. 复制:将堆内分成两个相同空间,从根(ThreadLocal的对象,静态对象)开始访问每一个关联的活跃对象,将空间A的活跃对象全部复制到空间B,然后一次性回收整个空间A。
    因为只访问活跃对象,将所有活动对象复制走之后就清空整个空间,不用去访问死对象,所以遍历空间的成本较小,但需要巨大的复制成本和较多的内存。
  2. 标记清除(mark-sweep):收集器先从根开始访问所有活跃对象,标记为活跃对象。然后再遍历一次整个内存区域,把所有没有标记活跃的对象进行回收处理。该算法遍历整个空间的成本较大暂停时间随空间大小线性增大,而且整理后堆里的碎片很多。
  3. 标记整理(mark-sweep-compact):综合了上述两者的做法和优点,先标记活跃对象,然后将其合并成较大的内存块。

    可见,没有免费的午餐,无论采用复制还是标记清除算法,自动的东西都要付出很大的性能代价。

3.分代

    分代是Java垃圾收集的一大亮点,根据对象的生命周期长短,把堆分为3个代:Young,Old和Permanent,根据不同代的特点采用不同的收集算法,扬长避短也。

Young(Nursery),年轻代。研究表明大部分对象都是朝生暮死,随生随灭的。因此所有收集器都为年轻代选择了复制算法。
    复制算法优点是只访问活跃对象,缺点是复制成本高。因为年轻代只有少量的对象能熬到垃圾收集,因此只需少量的复制成本。而且复制收集器只访问活跃对象,对那些占了最大比率的死对象视而不见,充分发挥了它遍历空间成本低的优点。

    Young的默认值为4M,随堆内存增大,约为1/15,JVM会根据情况动态管理其大小变化。
    -XX:NewRatio= 参数可以设置Young与Old的大小比例,-server时默认为1:2,但实际上young启动时远低于这个比率?如果信不过JVM,也可以用-Xmn硬性规定其大小,有文档推荐设为Heap总大小的1/4。

    Young的大小非常非常重要,见“后面暂停时间优先收集器”的论述。

    Young里面又分为3个区域,一个Eden,所有新建对象都会存在于该区,两个Survivor区,用来实施复制算法。每次复制就是将Eden和第一块Survior的活对象复制到第2块,然后清空Eden与第一块Survior。Eden与Survivor的比例由-XX:SurvivorRatio=设置,默认为32。Survivio大了会浪费,小了的话,会使一些年轻对象潜逃到老人区,引起老人区的不安,但这个参数对性能并不重要。 

Old(Tenured),年老代。年轻代的对象如果能够挺过数次收集,就会进入老人区。老人区使用标记整理算法。因为老人区的对象都没那么容易死的,采用复制算法就要反复的复制对象,很不合算,只好采用标记清理算法,但标记清理算法其实也不轻松,每次都要遍历区域内所有对象,所以还是没有免费的午餐啊。

-XX:MaxTenuringThreshold=设置熬过年轻代多少次收集后移入老人区,CMS中默认为0,熬过第一次GC就转入,可以用-XX:+PrintTenuringDistribution查看。

Permanent,持久代。装载Class信息等基础数据,默认64M,如果是类很多很多的服务程序,需要加大其设置-XX:MaxPermSize=,否则它满了之后会引起fullgc()或Out of Memory。 注意Spring,Hibernate这类喜欢AOP动态生成类的框架需要更多的持久代内存。

4.minor/major collection

    每个代满了之后都会促发collection,(另外Concurrent Low Pause Collector默认在老人区68%的时候促发)。GC用较高的频率对young进行扫描和回收,这种叫做minor collection
而因为成本关系对Old的检查回收频率要低很多,同时对Young和Old的收集称为major collection。
    System.gc()会引发major collection,使用-XX:+DisableExplicitGC禁止它,或设为CMS并发-XX:+ExplicitGCInvokesConcurrent。

5.小结

Young -- minor collection -- 复制算法

Old(Tenured) -- major colletion -- 标记清除/标记整理算法

三、收集器

1.古老的串行收集器(Serial Collector)

    使用 -XX:+UseSerialGC,策略为年轻代串行复制,年老代串行标记整理。

2.吞吐量优先的并行收集器(Throughput Collector)

    使用 -XX:+UseParallelGC ,也是JDK5 -server的默认值。策略为:
    1.年轻代暂停应用程序,多个垃圾收集线程并行的复制收集,线程数默认为CPU个数,CPU很多时,可用–XX:ParallelGCThreads=减少线程数。
    2.年老代暂停应用程序,与串行收集器一样,单垃圾收集线程标记整理。

    所以需要2+的CPU时才会优于串行收集器,适用于后台处理,科学计算。

    可以使用-XX:MaxGCPauseMillis= 和 -XX:GCTimeRatio 来调整GC的时间。

3.暂停时间优先的并发收集器(Concurrent Low Pause Collector-CMS)

    前面说了这么多,都是为了这节做铺垫......

    使用-XX:+UseConcMarkSweepGC,策略为:
    1.年轻代同样是暂停应用程序,多个垃圾收集线程并行的复制收集。
    2.年老代则只有两次短暂停,其他时间应用程序与收集线程并发的清除。

3.1 年老代详述

    并行(Parallel)与并发(Concurrent)仅一字之差,并行指多条垃圾收集线程并行,并发指用户线程与垃圾收集线程并发,程序在继续运行,而垃圾收集程序运行于另一个个CPU上。

    并发收集一开始会很短暂的停止一次所有线程来开始初始标记根对象,然后标记线程与应用线程一起并发运行,最后又很短的暂停一次,多线程并行的重新标记之前可能因为并发而漏掉的对象,然后就开始与应用程序并发的清除过程。可见,最长的两个遍历过程都是与应用程序并发执行的,比以前的串行算法改进太多太多了!!!

    串行标记清除是等年老代满了再开始收集的,而并发收集因为要与应用程序一起运行,如果满了才收集,应用程序就无内存可用,所以系统默认68%满的时候就开始收集。内存已设得较大,吃内存又没有这么快的时候,可以用-XX:CMSInitiatingOccupancyFraction=恰当增大该比率。

3.2 年轻代详述

   可惜对年轻代的复制收集,依然必须停止所有应用程序线程,原理如此,只能靠多CPU,多收集线程并发来提高收集速度,但除非你的Server独占整台服务器,否则如果服务器上本身还有很多其他线程时,切换起来速度就..... 所以,搞到最后,暂停时间的瓶颈就落在了年轻代的复制算法上。

    因此Young的大小设置挺重要的,大点就不用频繁GC,而且增大GC的间隔后,可以让多点对象自己死掉而不用复制了。但Young增大时,GC造成的停顿时间攀升得非常恐怖,比如在我的机器上,默认8M的Young,只需要几毫秒的时间,64M就升到90毫秒,而升到256M时,就要到300毫秒了,峰值还会攀到恐怖的800ms。谁叫复制算法,要等Young满了才开始收集,开始收集就要停止所有线程呢。

3.3 持久代

可设置-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled,使CMS收集持久代的类,而不是fullgc,netbeans5.5 performance文档的推荐。

4.增量(train算法)收集器(Incremental Collector)

已停止维护,–Xincgc选项默认转为并发收集器。

四、暂停时间显示

 加入下列参数 (请将PrintGC和Details中间的空格去掉,CSDN很怪的认为是禁止字句) 

-verbose:gc -XX:+PrintGC Details  -XX:+PrintGCTimeStamps

会程序运行过程中将显示如下输出

 9.211: [GC 9.211: [ParNew: 7994K->0K(8128K), 0.0123935 secs] 427172K->419977K(524224K), 0.0125728 secs]

 显示在程序运行的9.211秒发生了Minor的垃圾收集,前一段数据针对新生区,从7994k整理为0k,新生区总大小为8128k,程序暂停了12ms,而后一段数据针对整个堆。

对于年老代的收集,暂停发生在下面两个阶段,CMS-remark的中断是17毫秒:

[GC [1 CMS-initial-mark: 80168K(196608K)] 81144K(261184K), 0.0059036 secs] 

[1 CMS-remark: 80168K(196608K)] 82493K(261184K),0.0168943 secs]

再加两个参数 -XX:+PrintGCApplicationConcurrentTime -XX:+PrintGCApplicationStoppedTime对暂停时间看得更清晰。

五、真正不停的BEA JRockit 与Sun RTS2.0

   Bea的JRockit 5.0 R27 的特色之一是动态决定的垃圾收集策略,用户可以决定自己关心的是吞吐量,暂停时间还是确定的暂停时间,再由JVM在运行时动态决定、改变改变垃圾收集策略。
   
   它的Deterministic GC的选项是-Xgcprio: deterministic,号称可以把暂停可以控制在10-30毫秒,非常的牛,一句Deterministic道尽了RealTime的真谛。 不过细看一下文档,30ms的测试环境是1 GB heap 和 平均  30% 的活跃对象(也就是300M)活动对象,2 个 Xeon 3.6 GHz  4G内存 ,或者是4 个Xeon 2.0 GHz,8G内存。

  最可惜JRockt的license很奇怪,虽然平时使用免费,但这个30ms的选项就需要购买整个Weblogic Real Time Server的license。 

  其他免费选项,有:

  • -Xgcprio:pausetime -Xpausetarget=210ms 
      因为免费,所以最低只能设置到200ms pause target。 200ms是Sun认为Real-Time的分界线。
  • -Xgc:gencon
    普通的并发做法,效率也不错。

  JavaOne2007上有Sun的 Java Real-Time System 2.0 的介绍,RTS2.0基于JDK1.5,在Real-Time  Garbage Collctor上又有改进,但还在beta版状态,只供给OEM,更怪。

六、JDK 6.0的改进

因为JDK5.0在Young较大时的表现还是不够让人满意,又继续看JDK6.0的改进,结果稍稍失望,不涉及我最头痛的年轻代复制收集改良。

1.年老代的标识-清除收集,并行执行标识
  JDK5.0只开了一条收集进程与应用线程并发标识,而6.0可以开多条收集线程来做标识,缩短标识老人区所有活动对象的时间。

2.加大了Young区的默认大小
默认大小从4M加到16M,从堆内存的1/15增加到1/7

3.System.gc()可以与应用程序并发执行
使用-XX:+ExplicitGCInvokesConcurrent 设置

七、小结

1. JDK5.0/6.0

对于服务器应用,我们使用Concurrent Low Pause Collector,对年轻代,暂停时多线程并行复制收集;对年老代,收集器与应用程序并行标记--整理收集,以达到尽量短的垃圾收集时间。

本着没有深刻测试前不要胡乱优化的宗旨,命令行属性只需简单写为:

-server -Xms<heapsize>M -Xmx<heapsize>M -XX:+UseConcMarkSweepGC  -XX:+PrintGC Details  -XX:+PrintGCTimeStamps

然后要根据应用的情况,在测试软件辅助可以下看看有没有JVM的默认值和自动管理做的不够的地方可以调整,如-xmn 设Young的大小,-XX:MaxPermSize设持久代大小等。

2. JRockit 6.0 R27.2

但因为JDK5的测试结果实在不能满意,后来又尝试了JRockit,总体效果要好些。
 JRockit的特点是动态垃圾收集器是根据用户关心的特征动态决定收集算法的,参数如下

 -Xms<heapsize>M -Xmx<heapsize>M -Xgcprio:pausetime -Xpausetarget=200ms -XgcReport -XgcPause -Xverbose:memory

posted @ 2008-12-03 09:51 追风舞者 阅读(134) | 评论 (0)编辑 收藏

1.垃圾收集算法的核心思想

  Java语言建立了垃圾收集机制,用以跟踪正在使用的对象和发现并回收不再使用(引用)的对象。该机制可以有效防范动态内存分配中可能发生的两个危险:因内存垃圾过多而引发的内存耗尽,以及不恰当的内存释放所造成的内存非法引用。

  垃圾收集算法的核心思想是:对虚拟机可用内存空间,即堆空间中的对象进行识别,如果对象正在被引用,那么称其为存活对象,反之,如果对象不再被引用,则为垃圾对象,可以回收其占据的空间,用于再分配。垃圾收集算法的选择和垃圾收集系统参数的合理调节直接影响着系统性能,因此需要开发人员做比较深入的了解。

2.触发主GC(Garbage Collector)的条件

  JVM进行次GC的频率很高,但因为这种GC占用时间极短,所以对系统产生的影响不大。更值得关注的是主GC的触发条件,因为它对系统影响很明显。总的来说,有两个条件会触发主GC:
 

  ①当应用程序空闲时,即没有应用线程在运行时,GC会被调用。因为GC在优先级最低的线程中进行,所以当应用忙时,GC线程就不会被调用,但以下条件除外。

  ②Java堆内存不足时,GC会被调用。当应用线程在运行,并在运行过程中创建新对象,若这时内存空间不足,JVM就会强制地调用GC线程,以便回收内存用于新的分配。若GC一次之后仍不能满足内存分配的要求,JVM会再进行两次GC作进一步的尝试,若仍无法满足要求,则 JVM将报“out of memory”的错误,Java应用将停止。

  由于是否进行主GC由JVM根据系统环境决定,而系统环境在不断的变化当中,所以主GC的运行具有不确定性,无法预计它何时必然出现,但可以确定的是对一个长期运行的应用来说,其主GC是反复进行的。

3.减少GC开销的措施

  根据上述GC的机制,程序的运行会直接影响系统环境的变化,从而影响GC的触发。若不针对GC的特点进行设计和编码,就会出现内存驻留等一系列负面影响。为了避免这些影响,基本的原则就是尽可能地减少垃圾和减少GC过程中的开销。具体措施包括以下几个方面:

  (1)不要显式调用System.gc()

  此函数建议JVM进行主GC,虽然只是建议而非一定,但很多情况下它会触发主GC,从而增加主GC的频率,也即增加了间歇性停顿的次数。

  (2)尽量减少临时对象的使用

  临时对象在跳出函数调用后,会成为垃圾,少用临时变量就相当于减少了垃圾的产生,从而延长了出现上述第二个触发条件出现的时间,减少了主GC的机会。

  (3)对象不用时最好显式置为Null

  一般而言,为Null的对象都会被作为垃圾处理,所以将不用的对象显式地设为Null,有利于GC收集器判定垃圾,从而提高了GC的效率。

  (4)尽量使用StringBuffer,而不用String来累加字符串(详见blog另一篇文章JAVA中String与StringBuffer)

  由于String是固定长的字符串对象,累加String对象时,并非在一个String对象中扩增,而是重新创建新的String对象,如Str5=Str1+Str2+Str3+Str4,这条语句执行过程中会产生多个垃圾对象,因为对次作“+”操作时都必须创建新的String对象,但这些过渡对象对系统来说是没有实际意义的,只会增加更多的垃圾。避免这种情况可以改用StringBuffer来累加字符串,因StringBuffer是可变长的,它在原有基础上进行扩增,不会产生中间对象。

  (5)能用基本类型如Int,Long,就不用Integer,Long对象

  基本类型变量占用的内存资源比相应对象占用的少得多,如果没有必要,最好使用基本变量。

  (6)尽量少用静态对象变量

  静态变量属于全局变量,不会被GC回收,它们会一直占用内存。

  (7)分散对象创建或删除的时间

  集中在短时间内大量创建新对象,特别是大对象,会导致突然需要大量内存,JVM在面临这种情况时,只能进行主GC,以回收内存或整合内存碎片,从而增加主GC的频率。集中删除对象,道理也是一样的。它使得突然出现了大量的垃圾对象,空闲空间必然减少,从而大大增加了下一次创建新对象时强制主GC的机会。

4.gc与finalize方法

  ⑴gc方法请求垃圾回收

  使用System.gc()可以不管JVM使用的是哪一种垃圾回收的算法,都可以请求Java的垃圾回收。需要注意的是,调用System.gc()也仅仅是一个请求。JVM接受这个消息后,并不是立即做垃圾回收,而只是对几个垃圾回收算法做了加权,使垃圾回收操作容易发生,或提早发生,或回收较多而已。

  ⑵finalize方法透视垃圾收集器的运行

  在JVM垃圾收集器收集一个对象之前 ,一般要求程序调用适当的方法释放资源,但在没有明确释放资源的情况下,Java提供了缺省机制来终止化该对象释放资源,这个方法就是finalize()。它的原型为:

  protected void finalize() throws Throwable

  在finalize()方法返回之后,对象消失,垃圾收集开始执行。原型中的throws Throwable表示它可以抛出任何类型的异常。

  因此,当对象即将被销毁时,有时需要做一些善后工作。可以把这些操作写在finalize()方法里。

 

java 代码
  1. protected void finalize()    
  2.    {    
  3.    // finalization code here    
  4.    }  

 

⑶代码示例

java 代码
  1. class Garbage{    
  2.    int index;    
  3.    static int count;    
  4.   
  5.    Garbage() {    
  6.    count++;    
  7.    System.out.println("object "+count+" construct");    
  8.    setID(count);    
  9.    }    
  10.   
  11.    void setID(int id) {    
  12.    index=id;    
  13.    }    
  14.   
  15.    protected void finalize() //重写finalize方法    
  16.    {    
  17.    System.out.println("object "+index+" is reclaimed");    
  18.    }    
  19.   
  20.    public static void main(String[] args)    
  21.    {    
  22.    new Garbage();    
  23.    new Garbage();    
  24.    new Garbage();    
  25.    new Garbage();    
  26.    System.gc(); //请求运行垃圾收集器    
  27.    }    
  28.   
  29.  }  

5.Java 内存泄漏
  由于采用了垃圾回收机制,任何不可达对象(对象不再被引用)都可以由垃圾收集线程回收。因此通常说的Java 内存泄漏其实是指无意识的、非故意的对象引用,或者无意识的对象保持。无意识的对象引用是指代码的开发人员本来已经对对象使用完毕,却因为编码的错误而意外地保存了对该对象的引用(这个引用的存在并不是编码人员的主观意愿),从而使得该对象一直无法被垃圾回收器回收掉,这种本来以为可以释放掉的却最终未能被释放的空间可以认为是被“泄漏了”。

  考虑下面的程序,在ObjStack类中,使用push和pop方法来管理堆栈中的对象。两个方法中的索引(index)用于指示堆栈中下一个可用位置。push方法存储对新对象的引用并增加索引值,而pop方法减小索引值并返回堆栈最上面的元素。在main方法中,创建了容量为64的栈,并64次调用push方法向它添加对象,此时index的值为64,随后又32次调用pop方法,则index的值变为32,出栈意味着在堆栈中的空间应该被收集。但事实上,pop方法只是减小了索引值,堆栈仍然保持着对那些对象的引用。故32个无用对象不会被GC回收,造成了内存渗漏。

 

java 代码
public class ObjStack {    
  1.    private Object[] stack;    
  2.    private int index;    
  3.    ObjStack(int indexcount) {    
  4.    stack = new Object[indexcount];    
  5.    index = 0;    
  6.    }    
  7.    public void push(Object obj) {    
  8.    stack[index] = obj;    
  9.    index++;    
  10.    }    
  11.    public Object pop() {    
  12.    index--;    
  13.    return stack[index];    
  14.    }    
  15.    }    
  16.    public class Pushpop {    
  17.    public static void main(String[] args) {    
  18.    int i = 0;    
  19.    Object tempobj;    
  20.   
  21. //new一个ObjStack对象,并调用有参构造函数。分配stack Obj数组的空间大小为64,可以存64个对象,从0开始存储   
  22.    ObjStack stack1 = new ObjStack(64);   
  23.   
  24.    while (i < 64)    
  25.    {    
  26.    tempobj = new Object();//循环new Obj对象,把每次循环的对象一一存放在stack Obj数组中。    
  27.    stack1.push(tempobj);    
  28.    i++;    
  29.    System.out.println("第" + i + "次进栈" + "\t");    
  30.    }    
  31.   
  32.    while (i > 32)    
  33.    {    
  34.    tempobj = stack1.pop();//这里造成了空间的浪费。    
  35.    //正确的pop方法可改成如下所指示,当引用被返回后,堆栈删除对他们的引用,因此垃圾收集器在以后可以回收他们。    
  36.    /*   
  37.    * public Object pop() {index - -;Object temp = stack [index];stack [index]=null;return temp;}   
  38.    */    
  39.    i--;    
  40.    System.out.println("第" + (64 - i) + "次出栈" + "\t");    
  41.    }    
  42.    }    
  43.    }  

 

6.如何消除内存泄漏

  虽然Java虚拟机(JVM)及其垃圾收集器(garbage collector,GC)负责管理大多数的内存任务,Java软件程序中还是有可能出现内存泄漏。实际上,这在大型项目中是一个常见的问题。避免内存泄漏的第一步是要弄清楚它是如何发生的。本文介绍了编写Java代码的一些常见的内存泄漏陷阱,以及编写不泄漏代码的一些最佳实践。一旦发生了内存泄漏,要指出造成泄漏的代码是非常困难的。因此本文还介绍了一种新工具,用来诊断泄漏并指出根本原因。该工具的开销非常小,因此可以使用它来寻找处于生产中的系统的内存泄漏。

  垃圾收集器的作用

  虽然垃圾收集器处理了大多数内存管理问题,从而使编程人员的生活变得更轻松了,但是编程人员还是可能犯错而导致出现内存问题。简单地说,GC循环地跟踪所有来自“根”对象(堆栈对象、静态对象、JNI句柄指向的对象,诸如此类)的引用,并将所有它所能到达的对象标记为活动的。程序只可以操纵这些对象;其他的对象都被删除了。因为GC使程序不可能到达已被删除的对象,这么做就是安全的。

  虽然内存管理可以说是自动化的,但是这并不能使编程人员免受思考内存管理问题之苦。例如,分配(以及释放)内存总会有开销,虽然这种开销对编程人员来说是不可见的。创建了太多对象的程序将会比完成同样的功能而创建的对象却比较少的程序更慢一些(在其他条件相同的情况下)。

  而且,与本文更为密切相关的是,如果忘记“释放”先前分配的内存,就可能造成内存泄漏。如果程序保留对永远不再使用的对象的引用,这些对象将会占用并耗尽内存,这是因为自动化的垃圾收集器无法证明这些对象将不再使用。正如我们先前所说的,如果存在一个对对象的引用,对象就被定义为活动的,因此不能删除。为了确保能回收对象占用的内存,编程人员必须确保该对象不能到达。这通常是通过将对象字段设置为null或者从集合(collection)中移除对象而完成的。但是,注意,当局部变量不再使用时,没有必要将其显式地设置为null。对这些变量的引用将随着方法的退出而自动清除。

  概括地说,这就是内存托管语言中的内存泄漏产生的主要原因:保留下来却永远不再使用的对象引用。

  典型泄漏

  既然我们知道了在Java中确实有可能发生内存泄漏,就让我们来看一些典型的内存泄漏及其原因。

  全局集合

  在大的应用程序中有某种全局的数据储存库是很常见的,例如一个JNDI树或一个会话表。在这些情况下,必须注意管理储存库的大小。必须有某种机制从储存库中移除不再需要的数据。

  这可能有多种方法,但是最常见的一种是周期性运行的某种清除任务。该任务将验证储存库中的数据,并移除任何不再需要的数据。

  另一种管理储存库的方法是使用反向链接(referrer)计数。然后集合负责统计集合中每个入口的反向链接的数目。这要求反向链接告诉集合何时会退出入口。当反向链接数目为零时,该元素就可以从集合中移除了。

  缓存

  缓存是一种数据结构,用于快速查找已经执行的操作的结果。因此,如果一个操作执行起来很慢,对于常用的输入数据,就可以将操作的结果缓存,并在下次调用该操作时使用缓存的数据。

  缓存通常都是以动态方式实现的,其中新的结果是在执行时添加到缓存中的。典型的算法是:

  检查结果是否在缓存中,如果在,就返回结果。

  如果结果不在缓存中,就进行计算。

  将计算出来的结果添加到缓存中,以便以后对该操作的调用可以使用。

  该算法的问题(或者说是潜在的内存泄漏)出在最后一步。如果调用该操作时有相当多的不同输入,就将有相当多的结果存储在缓存中。很明显这不是正确的方法。

  为了预防这种具有潜在破坏性的设计,程序必须确保对于缓存所使用的内存容量有一个上限。因此,更好的算法是:

  检查结果是否在缓存中,如果在,就返回结果。

  如果结果不在缓存中,就进行计算。

  如果缓存所占的空间过大,就移除缓存最久的结果。

  将计算出来的结果添加到缓存中,以便以后对该操作的调用可以使用。

  通过始终移除缓存最久的结果,我们实际上进行了这样的假设:在将来,比起缓存最久的数据,最近输入的数据更有可能用到。这通常是一个不错的假设。

  新算法将确保缓存的容量处于预定义的内存范围之内。确切的范围可能很难计算,因为缓存中的对象在不断变化,而且它们的引用包罗万象。为缓存设置正确的大小是一项非常复杂的任务,需要将所使用的内存容量与检索数据的速度加以平衡。

  解决这个问题的另一种方法是使用java.lang.ref.SoftReference类跟踪缓存中的对象。这种方法保证这些引用能够被移除,如果虚拟机的内存用尽而需要更多堆的话。

  ClassLoader

  Java ClassLoader结构的使用为内存泄漏提供了许多可乘之机。正是该结构本身的复杂性使ClassLoader在内存泄漏方面存在如此多的问题。ClassLoader的特别之处在于它不仅涉及“常规”的对象引用,还涉及元对象引用,比如:字段、方法和类。这意味着只要有对字段、方法、类或ClassLoader的对象的引用,ClassLoader就会驻留在JVM中。因为ClassLoader本身可以关联许多类及其静态字段,所以就有许多内存被泄漏了。

  确定泄漏的位置

  通常发生内存泄漏的第一个迹象是:在应用程序中出现了OutOfMemoryError。这通常发生在您最不愿意它发生的生产环境中,此时几乎不能进行调试。有可能是因为测试环境运行应用程序的方式与生产系统不完全相同,因而导致泄漏只出现在生产中。在这种情况下,需要使用一些开销较低的工具来监控和查找内存泄漏。还需要能够无需重启系统或修改代码就可以将这些工具连接到正在运行的系统上。可能最重要的是,当进行分析时,需要能够断开工具而保持系统不受干扰。

  虽然OutOfMemoryError通常都是内存泄漏的信号,但是也有可能应用程序确实正在使用这么多的内存;对于后者,或者必须增加JVM可用的堆的数量,或者对应用程序进行某种更改,使它使用较少的内存。但是,在许多情况下,OutOfMemoryError都是内存泄漏的信号。一种查明方法是不间断地监控GC的活动,确定内存使用量是否随着时间增加。如果确实如此,就可能发生了内存泄漏。

posted @ 2008-12-03 09:41 追风舞者 阅读(1119) | 评论 (1)编辑 收藏

2008年10月10日

public class Search {

    
/**
     * 前提条件array数组已排序
     
*/
    
public static boolean binarySearch(int[] array, int target) {
        
boolean result = false;
        
int bottom = 0;
        
int top = array.length-1;
        
while (bottom <= top) {
            
int mid = (top + bottom) / 2;
            
if (target == array[mid]) {
                result 
= true;
                
break;
            } 
else if (target < array[mid]) {
                top 
= mid - 1;
            } 
else if (target > array[mid]) {
                bottom 
= mid + 1;
            }
        }

        
return result;
    }

    
public static void main(String[] args) {
        
int [] array = {1,3,5,7,9,10};
        
boolean result = binarySearch(array, 10);
        System.out.println(result);
    }
}

posted @ 2008-10-10 16:19 追风舞者 阅读(135) | 评论 (0)编辑 收藏

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Hanoi {
    
public static void main(String[] args) throws NumberFormatException,
            IOException {
        System.out.print(
"请输入盘数:");
        BufferedReader br 
= new BufferedReader(new InputStreamReader(System.in));
        
int n = Integer.parseInt(br.readLine());
        move(n, 
'A''B''C');
    }

    
/**
     * 将n个盘子借助b,从 a 移到 c
     
*/
    
public static void move(int n, char a, char b, char c) {
        
if (n == 1)
            System.out.println(
"盘 " + n + " 由 " + a + " 移至 " + c);
        
else {
            move(n 
- 1, a, c, b);
            System.out.println(
"盘 " + n + " 由 " + a + " 移至 " + c);
            move(n 
- 1, b, a, c);
        }
    }
}

posted @ 2008-10-10 15:56 追风舞者 阅读(108) | 评论 (0)编辑 收藏

public class Sort {

    
public static void quickSort(int[] array) {
        quickSort(array, 
0, array.length - 1);
    }

    
private static void quickSort(int[] array, int low, int high) {
        
if (low < high) {
            
int p = partition(array, low, high);
            quickSort(array, low, p 
- 1);
            quickSort(array, p 
+ 1, high);
        }

    }

    
private static int partition(int[] array, int low, int high) {
        
int s = array[high];
        
int i = low - 1;
        
for (int j = low; j < high; j++) {
            
if (array[j] < s) {
                i
++;
                swap(array, i, j);
            }
        }
        swap(array, 
++i, high);
        
return i;
    }

    
private static void swap(int[] array, int i, int j) {
        
int temp;
        temp 
= array[i];
        array[i] 
= array[j];
        array[j] 
= temp;
    }
    
    
public static void main(String[] args) {
        
int [] array = {2,5,3,7,4};
        quickSort(array);
        
for(int i = 0;i<array.length;i++){
            System.out.println(array[i]);
        }
    }

}

posted @ 2008-10-10 15:39 追风舞者 阅读(1830) | 评论 (4)编辑 收藏

2008年9月19日

在Ubuntu 8.04中查看显卡是否装好
运行glxinfo | grep rendering
如果显示”direct rendering: Yes”,则已安装

1、下载驱动

我下载的就是NVIDIA-Linux-x86_64-173.14.12-pkg2.run这个文件,下载后放到home目录下吧。

 

2、删除原驱动包
sudo apt-get -–purge remove nvidia-glx nvidia-glx-new
然后删除 /lib/linux-restricted-modules/2.6.22-14-generic/文件夹下面带有nvidia字样的内容,注意里面有一个隐藏文件.nvidia-new-installer也要删掉

这里是内核自带的驱动

再检查一下这两个文件是否存在,/etc/init.d/nvidia-glx /etc/init.d/nvidia-kernel 如果存在,删除掉。

3、安装要用到的软件
sudo apt-get install build-essential pkg-config xserver-xorg-dev linux-headers-`uname -r`

安装编译包。

我在安装的时候提示说没有lib,驱动安装时候要安装Kernel Interface,如果采用自己编译的方式则要求系统中有libc的源码。我想一般人都是没有的,呵呵。这个时候我们可以在安装驱动之前先自己把这个源码给安装好,问题就解决了。如何安装?呵呵,更简单,强大的 apt install阿, 运行:
sudo apt-get install libc6-dev
一行命令搞定。

4、备份
备份文件是一个好习惯。
sudo cp /etc/default/linux-restricted-modules-common ~/linux-restricted-modules-common.backup
sudo cp /etc/X11/xorg.conf ~/xorg.conf.backup

5、禁止系统使用默认的驱动
sudo gedit /etc/default/linux-restricted-modules-common
在最后的双引号中添加nv nvidia_new,即把文件中的“”,改成“nv nvidia_new”
如果前面第二步删除完整了其实也可以不用执行这一步。

6、将后面的操作记录在纸上,因为后面会完全在终端字符界面下操作。

7、停止GDM进程
sudo /etc/init.d/gdm stop
按Ctrl+Alt+F1,登录后进入第7步。

8、安装驱动
进入下好的驱动所在目录
执行:sudo sh ./NVIDIA-Linux-x86_64-173.14.12-pkg2.run

安装过程中

如果提示有旧驱动,询问是否删除旧驱动,选Yes;
如果提示缺少某某模块(modules),询问是否上网下载,选no;
如果提示编译模块,询问是否进行编译,选ok;
如果提示将要修改Xorg.conf,询问是否允许,选Yes;

接下来就是等待安装完成。

9、安装完成就回到终端,重启GDM
sudo /etc/init.d/gdm restart

如果失败,就重启机子:sudo shutdown -r now

好了,当看到NV的logo后,才表示安装成功。

如果不想看NVIDIA的LOGO,可以修改 /etc/X11/xorg.conf
在Section “Device”中添加Option “NoLogo” “True”

如:
Section “Device”
Identifier “通用显示卡”
Driver “nvidia”
Option “NoLogo” “True”
EndSection

10.显示高级设置
如果想进行显示方面的高级设置,在终端下输入:nvidia-settings命令。

左边第二项是设置分辨率(server display configuration),从右边的resolution中选择分辨率,再点击apply,

预览一下,不好取消就行了。

至此,安装完毕。

如果御载的话就用这个命令

sh NVIDIA-Linux-x86_64-173.14.12-pkg2.run –uninstall

安装成功,就这么简单,一般nvidia 会自动配置好所有~`

重新配置x server:
sudo apt-get install pkg-config xserver-xorg-dev3
sudo dpkg-reconfigure xserver-xorg



posted @ 2008-09-19 13:23 追风舞者 阅读(426) | 评论 (0)编辑 收藏

2008年9月18日

安装MySQL

sudo apt-get install mysql-server

这个应该很简单了,而且我觉得大家在安装方面也没什么太大问题,所以也就不多说了,下面我们来讲讲配置。

配置MySQL

注意,在Ubuntu下MySQL缺省是只允许本地访问的,如果你要其他机器也能够访问的话,那么需要改变/etc/mysql/my.cnf配置文件了!下面我们一步步地来:

默认的MySQL安装之后根用户是没有密码的,所以首先用根用户进入:

$mysql -u root

在这里之所以用-u root是因为我现在是一般用户(firehare),如果不加-u root的话,mysql会以为是firehare在登录。注意,我在这里没有进入根用户模式,因为没必要。一般来说,对mysql中的数据库进行操作, 根本没必要进入根用户模式,只有在设置时才有这种可能。

进入mysql之后,最要紧的就是要设置Mysql中的root用户密码了,否则,Mysql服务无安全可言了。

mysql> GRANT ALL PRIVILEGES ON *.* TO root@localhost IDENTIFIED BY "123456";

注意,我这儿用的是123456做为root用户的密码,但是该密码是不安全的,请大家最好使用大小写字母与数字混合的密码,且不少于8位。

这样的话,就设置好了MySQL中的root用户密码了,然后就用root用户建立你所需要的数据库。我这里就以xoops为例:

mysql>CREATE DATABASE xoops;

mysql>GRANT ALL PRIVILEGES ON xoops.* TO xoops_root@localhost IDENTIFIED BY "654321";

这样就建立了一个xoops_roots的用户,它对数据库xoops有着全部权限。以后就用xoops_root来对xoops数据库进行管理,而无需要再用root用户了,而该用户的权限也只被限定在xoops数据库中。

如果你想进行远程访问或控制,那么你要做两件事:

其一:

mysql>GRANT ALL PRIVILEGES ON xoops.* TO xoops_root@"%" IDENTIFIED BY "654321";

允许xoops_root用户可以从任意机器上登入MySQL。

其二:

$sudo gedit /etc/mysql/my.cnf

老的版本中

>skip-networking => # skip-networking

新的版本中

>bind-address=127.0.0.1 => bind-address= 你机器的IP

这样就可以允许其他机器访问MySQL了。

posted @ 2008-09-18 17:53 追风舞者 阅读(112) | 评论 (0)编辑 收藏