2008年11月12日星期三

使用Unlocker和Autoruns清除Bonjour mDNSResponder.exe 进程

mDNSResponder.exe
进程文件:mDNSResponder 或者 mDNSResponder.exe
进程名称: Bonjour for Windows Component

描述:
mDNSResponder.exe是一款名为Bonjour的音乐分享软件相关程序。

出品者: Apple
属于: Bonjour for Windows

系统进程: 否
后台程序: 否
使用网络: 否
硬件相关: 否
常见错误: 未知N/A
内存使用: 未知N/A

安全等级 (0-5): 2
间谍软件: 否
广告软件: 否
病毒: 否
木马: 否

安装了ADOBE CS3 ,发现进程中出现这个东西。 在windows服务里面加了一个服务项。进程为mDNSResponder.exe。 禁用这个服务后又会自动启动。

1,Ctrl+Alt+Del打开Windows任务管理器,找到mDNSResponder.exe,单击右键结束进程;
2,选择 C:\Program Files\Bonjour ,右键菜单中选择unlocker,解除所有进程锁定并删除 ;
3,启动Autoruns,从服务中删除该服务项。
到此清理完毕。

该方法比重命名的方法更加方便可靠、彻底。

参考: http://www.starfox.cn/Studio/article.asp?id=96

 

Technorati 标签: ,,

HttpClient中用POST方式提交中文数据时的解决方案

在用HttpClient编程时,如果用PostMethod提交中文数据,则在服务器端不能得到正确的数据,可以通过下面的方法解决:

1、从PostMethod派生一个类,并改写getRequestCharSet()方法

import org.apache.commons.httpclient.methods.PostMethod;

class UTF8PostMethod extends PostMethod {
    public UTF8PostMethod(String url) {
        super(url);
    }

    @Override
    public String getRequestCharSet() {
        // return super.getRequestCharSet();
        return "gb2312";
    }
}

2、在相关的地方,用UTF8PostMethod替换原来的PostMethod类

2008年10月17日星期五

在EmEditor录制了两个宏

最近搞试卷库,程序设计的,很多题目的选项,分号,引号等都不对,于是在EmEditor中进行替换

1. 程序分析和填空

document.selection.Replace(")",")",eeFindNext | eeReplaceAll);
document.selection.Replace("(","(",eeFindNext | eeReplaceAll);
document.selection.Replace("’","\x27",eeFindNext | eeReplaceAll);
document.selection.Replace("‘","\x27",eeFindNext | eeReplaceAll);
document.selection.Replace("”","\x22",eeFindNext | eeReplaceAll);
document.selection.Replace("“","\x22",eeFindNext | eeReplaceAll);
document.selection.Replace(",",",",eeFindNext | eeReplaceAll);
document.selection.Replace(";",";",eeFindNext | eeReplaceAll);
document.selection.Replace("_{2,}","____________________________",eeFindNext | eeReplaceAll | eeFindReplaceRegExp);
document.selection.Replace("\\s{2,}"," ",eeFindNext | eeReplaceAll | eeFindReplaceRegExp);
document.selection.Replace("\\t+"," ",eeFindNext | eeReplaceAll | eeFindReplaceRegExp);

2. 选择

document.selection.Replace("_{2,}","(  )",eeFindNext | eeReplaceAll | eeFindReplaceRegExp);
document.selection.Replace("_{2,}","(  )",eeFindNext | eeReplaceAll | eeFindReplaceRegExp);
document.selection.Replace(")",")",eeFindNext | eeReplaceAll);
document.selection.Replace("(","(",eeFindNext | eeReplaceAll);
document.selection.Replace("’","\x27",eeFindNext | eeReplaceAll);
document.selection.Replace("‘","\x27",eeFindNext | eeReplaceAll);
document.selection.Replace("”","\x22",eeFindNext | eeReplaceAll);
document.selection.Replace("“","\x22",eeFindNext | eeReplaceAll);
document.selection.Replace(",",",",eeFindNext | eeReplaceAll);
document.selection.Replace(";",";",eeFindNext | eeReplaceAll);
document.selection.Replace("([A-D])、","\\1. ",eeFindNext | eeFindReplaceCase | eeReplaceAll | eeFindReplaceRegExp);
document.selection.Replace("([A-D])\\)","\\1.",eeFindNext | eeFindReplaceCase | eeReplaceAll | eeFindReplaceRegExp);

2008年10月10日星期五

锐捷S2026G交换机常用配置命令

Enable 进入特权模式

conf t 进入配置模式

vlan n  配置一个VLAN

int fa0/n 配置快速以太网端口n

int range fa0/n-m 配置一批快速以太网端口

sw mode trunk 将interface配置为trunk,这个和S1926里边的Tag Vlan不太一样,设置为trunk的默认是所有的VLAN的数据都可以通过,不像1926需要逐个设置

sw access vlan n 将一个端口划分到制定VLAN

write 将配置写入交换机的Flash Memory

2008年10月4日星期六

Eclipse下C/C++开发环境CDT的安装配置

1、从http://www.mingw.org/下载并安装MinGW,假设安装在D:\MinGW;安装完成之后将bin子目录中的mingw32-make.exe复制一份在同一目录,更名为make.exe,并从主页再下载gdb调试程序也放在bin目录下。

2、设置系统PATH环境变量,将D:\MinGW\bin加入到其中,其他环境变量不需要做设置。

3、从http://www.eclipse.org/cdt/下载CDT,并添加到Eclipse中。

4、启动Eclipse,新建C/C++工程,会根据环境变量自动设别出LIB和INCLUDE的位置。在工程中添加一个C源文件,输入代码并保存。

5、在编译之前,在工程上点击鼠标右键,选择Build Configurations,在Build下为其添加要编译的目标版本是Release还是Debug版本,或者全选。

6、编译工程,如果没有错误,会生成可执行的二进制文件。

鼠标点击Project,然后在Project->Build Project
最后找到exe文件,右键点击exe文件->Run As->Run Local C/C++ Application

7、也可以和Java一样,为C程序添加断点,用Debug功能进行调试。

参考文章:

How to setup the MinGW gcc tools for your Managed Make C Project in CDT 3.1 and Eclipse 3.2
HOW TO: Use CDT and MinGW for Eclipse (i.e. develop C/C++ applications in windows)
http://hi.baidu.com/billyboy/blog/item/e989b2a1eb231b8a471064d8.html
http://blog.csdn.net/goodfunman/archive/2005/08/31/468319.aspx

 

Technorati 标签: ,,

Eclipse下的反编译方案

方案一:JAD

http://www.kpdus.com/jad.html下载JAD,将可执行文件解压缩到Windows目录下(也可以是环境变量PATH所指定搜索路径的任意目录下);从http://jadclipse.sourceforge.net下载net.sf.jadclipse_3.3.0.jar,解压缩到Eclipse安装目录下的对应位置。

方案二:JD-GUI

http://java.decompiler.free.fr/下载jd-gui-0.2.5.windows.zip,将可执行文件解压缩到Windows目录下;感谢CCF论坛的Hanzac会员对上面的jadclipse插件做了修改,使它能和JD-GUI配合使用,下载地址:http://www.newdisk.cn/mypane.aspx?down=ok&filepath=hanzac/net.sf.hnindev.jdadaptor_3.3.1.jar。将其解压缩到Eclipse安装目录下的对应位置。

注意:上述两套方案中的Eclipse插件只能安装一个。

安装完成之后做如下配置:

选择菜单项 Window | Preferences .... ,定位到如下图所示位置,在文件类型中选择*.class

000

按下方的Add...按钮,打开对话框,选择JadClipse Class File Viewer,并按OK确认。

001

在对话框中按“default”按钮将刚才选择的Viewer设置为Class文件的默认查看程序。

004

从项目中选择一个Class双击,可看到其源代码。

005

【推荐】优秀的Java反编译软件 - JD-GUI

只有一个小小的EXE,运行后直接打开class file或者jar file就能反编译,效果也很不错。UI有Eclipse的风格,Find和Firefox的Find操作风格一致。
现在还在开发中,功能不是特别完善,应该会不断完善下去。

软件主页:http://java.decompiler.free.fr/

Main features

  • JD-Core and JD-GUI are written in C++. This allows an extremely fast decompilation and a display.
  • JD-Core does not require the Java runtime environment for its functioning, therefore no special setup is required.
  • JD-Core works with most current compilers including the following:
    • jdk1.1.8
    • jdk1.3.1
    • jdk1.4.2
    • jdk1.5.0
    • jdk1.6.0
    • jikes-1.22
    • harmony-jdk-r533500
    • Eclipse Java Compiler v_677_R32x, 3.2.1 release
    • jrockit90_150_06
  • JD-Core supports the following new features of Java 5:
    • Annotations
    • Generics
    • Type “enum”
  • JD-GUI supports Drag and Drop.
  • JD-GUI supports JAR files.
  • JD-GUI displays color coded Java source code.
  • JD-GUI allows you to browse the “class” files hierarchy.
  • JD-Core and JD-GUI use the excellent cross-platform wxWidgets toolkit.

 

Technorati 标签: ,

Aptana-JavaScript开发利器及其安装

一个JS的编辑调试工具 Aptana,功能不错,可以在Firefox下调试JS代码,具有很强的代码辅助功能,可以在代码辅助中添加第三方的类库。

它的特性包括:
*JavaScript,JavaScript函数,HTML,CSS语言的Code Assist功能。
*Outliner(大纲):显示JavaScript,HTML和CSS的代码结构。
*支持JavaScript,HTML,CSS代码提示,包括JavaScript自定函数。
*代码语法错误提示。
*支持Aptana UI自定义和扩展。
*支持跨平台。
*支持FTP/SFTP。
*调试JavaScript。
*支持流行AJAX框架的Code Assist功能:AFLAX,Dojo,JQuery,MochiKit,Prototype,Rico,script.aculo.us,Yahoo UI,Ext。
*Adobe AIR与iPhone开发工具

一般我使用的是Eclipse插件版本,安装的方法有两种。在 http://www.aptana.com/docs/index.php/Plugging_Aptana_into_an_existing_Eclipse_configuration#Introduction 上有详细的说明。

以前在用下载后安装的方法时出错,所以一直使用的是在线安装的方式,但是这个网站速度期满无比,安装过程需要重试n次才能完成,非常麻烦。这次重新安装机器,偶尔使用了下载再安装的方法,一点问题都没有。

下载的地址是 http://update.aptana.com/update/studio/3.2/aptana_update_015414.zip
推荐使用迅雷下载。

在IBM DeveloperWorks网站上还看到了一篇利用Aptana和AIR来开发桌面应用程序的文章:使用 Ext、Aptana 和 AIR 构建桌面应用程序

 

Technorati 标签: ,,

记性差了,还是要在这里多写写

最近计算机系统运行很慢,决定重新安装一次。也难怪,这个系统有2年半没有重新安装了。安装的过程中,发现一些开发工具,一些插件的安装方式都忘记了,决定还是在这里多记录一些东西。

年纪慢慢大了,记性就是不行了啊。

2008年9月28日星期日

车被人擦了,第一次事故

9/26傍晚5点多,我朋友在我家帮我安装设备后回家,我开车送的,刚好我家旁边是一个中学和一个小学,接孩子的车很多,一路慢慢爬,快开出这条路的时候,我在三条车道的中间一条,两条白线的正中间好好开着,旁边一辆金杯硬挤进我的车道把我擦了。下车后这家伙还说你怎么不刹停,我说你超我也应该远一点再超,现在我叫警察的话肯定你全责。
结果警察在附近已经发现了,过来就训了他一通,指挥我们把车停到路边,处理,他全责,我无责。
期间通知了朋友派人过来,看了一下说没事。今天早上对方通知我去定损,让我朋友去了,然后直接在他那边钣金补漆,傍晚就把修好的车给我送过来了,告诉我定损是350元。
xxxx,现在开车野蛮的太多了。。。。

第一次事故,以前最严重的也就是和一棵树亲密接触了一下

2008年9月25日星期四

文明4主题曲《Baba yetu》

  非洲Swahili(斯瓦西里语)土语,意思是《天父之歌》 (our father )。由斯坦福大学一个团体"A Cappella "演唱。
听起来荡气回肠。尤其搭配文明4片头动画,更让人深感人类历史长河之深邃。

[audio:http://cse.cslg.cn/audio/babayetu.mp3]

【Swahili土语歌词】
【CHORUS】
Baba yetu yetu, uliye
Mbinguni yetu yetu, amina!
Baba yetu yetu, uliye
Jina lako litukuzwe.
【x2】
Utupe leo chakula chetu
Tunachohitaji utusamehe
Makosa yetu, hey!
Kama nasi tunavyowasamehe
Waliotukosea usitutie
Katika majaribu, lakini
Utuokoe, na yule, milelea milele!
【CHORUS 】
Ufalme wako ufike utakalo
Lifanyike duniani kama mbinguni. (Amina)
【CHORUS 】
Utupe leo chakula chetu
Tunachohitaji utusamehe
Makosa yetu, hey!
Kama nasi tunavyowasamehe
Waliotukosea usitutie
Katika majaribu, lakini
Utuokoe, na yule, simama mwehu
Baba yetu yetu uliye
Jina lako litukuzwe.

【中英文对照歌词】
【CHORUS 合唱】
Our Father, who art in Heaven. Amen!
天父,在天之父。阿门!
Our Father, Hallowed be thy name.
天父,愿世人皆颂圣名。
【x2】
Give us this day our daily bread,
赐我今日之食 天天皆然。
Forgive us of our trespasses
免我之罪,
As we forgive others who tresspass against us
若我之于他人。
Lead us not into temptation, but deliver us from Evil,
指引我远离诱惑,救赎我于邪恶,
and you are forever and ever!
愿祢不朽永存!
【CHORUS 合唱 】
Thy kingdom come,
愿祢的天国降临,
thy will be done on Earth as it is in Heaven. (Amen)
愿你的旨意行于世间。(阿门)
【CHORUS 合唱】
Give us this day our daily bread,
赐我今日之食 天天皆然。
Forgive us of our trespasses
免我之罪,
As we forgive others who tresspass against us
若我之于他人。
Lead us not into temptation, but deliver us from Evil,
指引我远离诱惑,救赎我于邪恶,
and you wake the dead
愿您唤醒俗世
Our Father, who art...
天父,在天之父…
Hallowed be thy name.
愿世人皆颂圣名…

2008年8月8日星期五

On the Future of Computers

 

I think there is a world market for may be five computers...
--Thomas Watson, Chairman of IBM, 1943

There is no reason anyone would want a computer in their home.
--Ken Olson, President, Chairman and Founder of Digital Equipment Corp., 1977

640K ought to be enough for anybody.
--Bill Gates, in 1981

Computers are incredibly fast, accurate, and stupid; humans are incredibly slow, inaccurate and brilliant; together they are powerful beyond imagination.
--Albert Einstein

2008年7月26日星期六

QQ清理工具更新

更新内容:

  1. 可选择需要清理的项目
  2. 记录上次选择的QQ路径,方便使用

qq

点击下载QQ清理工具

2008年7月23日星期三

五月阳光论坛签名图片过滤脚本

FireFox下的greasemonkey脚本

// ==UserScript==
// @name           bbs.cslg.cn
// @namespace      bbs.cslg.cn
// @description    signatures filter
// @include        http://bbs.cslg.cn/*
// ==/UserScript==

(function(){
    var el;
    var xpath = "//td[@class='signed']";
    var els = document.evaluate(xpath, document, null,XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
    for (var i = 0; i < els.snapshotLength; i++) {
      el = els.snapshotItem(i);
      el.parentNode.removeChild(el);
    }
}) ();

用于FireFox的 www.096.cc 广告、头像和签名图片过滤greasemonkey脚本

// ==UserScript==
// @name           096.cc
// @namespace      096.cc
// @description    096.cc image filter
// @include        http://www.096.cc/*
// ==/UserScript==

(function(){
    var href = window.location.href;
    if(href.indexOf("ShowForum.asp")!=-1)
        hideForum();
    else if(href.indexOf("ShowPost.asp")!=-1)
        hideThread();
    else if(href.indexOf("Default.asp")!=-1 || href=="http://www.096.cc/" )
        hideMain();
    HideCommon();
    function HideCommon(){
        var xpath;
        xpath="/html/body/table[2]";
        remove(xpath);
        xpath="/html/body/table[1]";
        remove(xpath);
        xpath="//table[@bgcolor='#333333']";
        remove(xpath);
    }
    function hideMain(){
        var xpath;
        xpath="/html/body/table[10]";
        remove(xpath);
        xpath="/html/body/table[5]";
        remove(xpath);
    }
    function hideForum(){
        var xpath;
        xpath="/html/body/table[9]";
        remove(xpath);
    }
    function hideThread(){
        var xpath="//img";
        var el;
        var els = document.evaluate(xpath, document, null,XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
        for (var i = 0; i < els.snapshotLength; i++) {
          el = els.snapshotItem(i);
          if(el.src.indexOf("UpFile/UpAttachment")!=-1 || el.src.indexOf("images/upface")!=-1 || el.src.indexOf("images/face")!=-1 || el.src.indexOf("UpFile/UpFace")!=-1)
              el.parentNode.removeChild(el);
        }
    }

    function remove(xpath){
        var el;
        var els = document.evaluate(xpath, document, null,XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
        for (var i = 0; i < els.snapshotLength; i++) {
          el = els.snapshotItem(i);
          el.parentNode.removeChild(el);
        }
    }
}) ();

为常熟零距离论坛写了一个greasemonkey脚本

写了一个 greasemonkey 的脚本,在FireFox中安装greasemonkey插件后,使用下面的这段脚本,可以过滤所有的广告和签名,世界很清静
// ==UserScript==
// @name              090广告签名图片过滤
// @version           0.1.0
// @description       Hidden sign imges in Dvbbs
// @namespace         http://bbs.wm090.com/
// @include           http://bbs.wm090.com/*
// ==/UserScript==
(function(){
    var xpath = "//div[@class='post']/div[@id]";
    remove(xpath);
    xpath = "//*[@id='topbar_mid_m']";
    remove(xpath);
    xpath = "/html/body/table";
    remove(xpath);
    xpath= "//div[@style='margin: 2px auto 0px;']";
    remove(xpath);
    function remove(xpath){
        var el;
        var els = document.evaluate(xpath, document, null,XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
        for (var i = 0; i < els.snapshotLength; i++) {
          el = els.snapshotItem(i);
          el.parentNode.removeChild(el);
        }
    }
}) ();

2008年6月18日星期三

哇哈哈!最强大的空前绝后的在线编辑器

在IE中打开一个网页,然后在浏览器地址栏中输入:

javascript:document.body.contentEditable='true'; document.designMode='on';!msn1dai

用鼠标中点页面中的元素,看到什么了?

2008年6月14日星期六

EclipseTidy插件

在Web应用程序开发中,特别是别人拿来的一些代码,难免要碰到一些格式混乱,又存在很多错误的HTML页面。比如说标记不匹配,不正确的属性设置,嵌套混乱等等。但正因为浏览器都有一定的容错性,而致使问题不容易解决,即使想解决也非常困难。

HTML Tidy是一个免费的HTML语言检查工具,检查代码并指出一些与已发布的W3C标准不一致的地方。它可以用来解析包含HTML标记的HTML文件或字符串,并可以自动改正错误,使其与相关标准完全一致。

和HTML Tidy相关的工具有:

  • jtidy,用于java程序的进行HTML格式化和清理的API http://jtidy.sourceforge.net
  • EclipseTidy,eclipse的插件,可在eclipse中对页面进行格式化和清理,使之符合W3C标准

EclipseTidy的下载地址: http://eclipsetidy.sourceforge.net

将下载好的eclipsetidy解压到eclipse安装目录,重新启动eclipse即可生效。

为了能对JSP进行处理,需要在配置中将JSP扩展名加进去。

Orangevolt EclipseXSLT 插件

构建在Eclipse WTP之上的XSLT支持插件,包括一个可定制的XML目录大纲,一个xpath查询视图等等。

安装:

在Eclipse中选择 Help > Software Updates >  Find and Install ,添加下面的URL进行安装

http://eclipsexslt.sourceforge.net/update-site

6

除了XML编辑、XSLT转换功能之外,EclipseXSLT还提供了一个很好的XPATH Navigator视图。

XPath Navigator 视图是一个出色的特性。可以使用它从 XML 源文件创建 XPath 表达式。插件将针对 XML 文件的 DOM 表示执行这个 XPath 表达式。

选择 Window > Show View > Other...

1

展开 Orangevolt 来显示 XPath Navigator 并单击 OK

2

现在应该会看到 XPath Navigator 视图。

3

从 XML Source Document 下拉框中选择要处理的xml文件(该XML应该首先用Orangevolt XML编辑器打开,否则不能选择),我打开的XML文件内容如下。

4

在 XPath Expression 文本框中输入XPATH表达式。单击 Navigator 中的 Run 图标。 可以修改 XPath 表达式来生成不同的结果。

5

FireFox3将于2008年6月17日推出正式版,先贴一下FireFox2里边现在用的扩展

生成: Fri Jun 13 2008 19:36:56 GMT+0800
User Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.8.1.14) Gecko/20080404 Firefox/2.0.0.14
Build ID: 2008040413

启用扩展: [38]
- Adblock Filterset.G Updater 0.3.1.3: http://www.pierceive.com/
- Adblock Plus 0.7.5.4: http://adblockplus.org/
- AI Roboform Toolbar for Firefox 6.9.89: http://www.roboform.com
- All-in-One Gestures 0.18.0: http://perso.wanadoo.fr/marc.boullet/ext/extensions-en.html
- All-in-One Sidebar 0.7.5: http://firefox.exxile.net/aios/
- BatchDownload 1.1.9: http://blog.csdn.net/linuxpgy/archive/2008/05/04/2375033.aspx
- Controle de Scripts 1.0.3: http://controledescripts.mozdev.org/
- CuteMenus - Crystal SVG 1.9.2: http://www.cutemenuproject.com/
- DOM Inspector 1.8.1.14: http://www.mozilla.org/projects/inspector/
- Download Embedded 0.5: http://extensions.aeruder.net
- Fasterfox 2.0.0: http://fasterfox.mozdev.org/
- Firebug 1.05: http://www.getfirebug.com/
- Flagfox 3.2.6: http://flagfox.servehttp.com/
- Flashblock 1.5.6: http://flashblock.mozdev.org/
- FlashGot 1.0.3: http://flashgot.net
- FoxyProxy 2.7.4: http://foxyproxy.mozdev.org
- gladder 2.0.2.1: http://gladder.gneheix.com
- Gmail Manager 0.5.5: http://www.longfocus.com/firefox/gmanager/
- Google Browser Sync 1.3.20070523.0: http://www.google.com/tools/firefox/browsersync/
- Google Toolbar for Firefox 3.0.20070525W: http://www.google.com/
- Google 笔记本 1.0.0.20: http://www.google.com/notebook
- Html Validator 0.8.4.0: http://users.skynet.be/mgueury/mozilla/
- IE Tab 1.3.3.20070528: http://ietab.mozdev.org/
- Image Toolbar 0.6.4: http://www.cusser.net
- JavaScript Debugger 0.9.87.3: http://www.hacksrus.com/~ginda/venkman/
- JSView 2.0.4: http://forum.softwareblaze.com
- MR Tech Local Install 5.3.2.6: http://www.mrtech.com/extensions/local_install/
- NoScript 1.6.9: http://noscript.net
- Quick Locale Switcher 1.6.5.6: http://www.captaincaveman.nl
- ScribeFire 2.2.6: http://www.scribefire.com/
- StumbleUpon 3.18: http://www.stumbleupon.com/
- Super DragAndGo 0.2.6: http://morphis.eu.org/
- Tab Mix Plus 0.3.49.061028: http://tmp.garyr.net
- Text Link 2.0.2008052801: http://piro.sakura.ne.jp/xul/_textlink.html.en

已安装主题: [2]
- Firefox (default): http://www.mozilla.org/
- Mostly Crystal 2.0.0.17: http://www.tom-cat.com/mozilla

已安装插件: (4)
- IE Tab Plug-in
- Microsoft Office 2003
- Mozilla Default Plug-in
- Shockwave Flash

写了一个从虞城热线上抽取天气预报的程序

写了一个从虞城热线上抽取天气预报的程序,生成了javascript调用代码,调用形式:

<script language="javascript" type="text/javascript" src="http://cse.cslg.cn:8888/weather/weather.js"></javascript>

2008年6月10日星期二

[简单小玩意]QQ文件清理程序

QQ文件清理工具下载

自己机器上的QQ越来越慢,搜索了一下网上的相关资料,知道速度变慢的主要原因是接收的图片、用户自定义头像等太多。删除这些文件后试验了一下,启动果然是瞬间完成了,为了方便大家,花几分钟用Delphi写了一个小程序来完成这个工作。程序运行之后只要先选择QQ安装目录中你的号码子目录,然后点击清理即可。

程序非常简陋,请包涵。

2008年6月7日星期六

从IBM developerWorks上一篇关于Java内存泄漏的文章引起的讨论,内存泄漏产生 的原因和解决途径

在研究Java内存泄漏时,看到了这篇文章 http://www.ibm.com/developerworks/cn/java/l-JavaMemoryLeak/index.html

觉得其中的例子是有问题的,至少如果v(Vector对象)是局部变量的话就不会引起内存泄漏。

下面给出了一个简单的内存泄露的例子。在这个例子中,我们循环申请Object对象,并将所申请的对象放入一个Vector中,如果我们仅仅释放引用本身,那么Vector仍然引用该对象,所以这个对象对GC来说是不可回收的。因此,如果对象加入到Vector后,还必须从Vector中删除,最简单的方法就是将Vector对象设置为null。

Vector v=new Vector(10);
for (int i=1;i<100; i++)
{
Object o=new Object();
v.add(o);
o=null;
}

//此时,所有的Object对象都没有被释放,因为变量v引用这些对象。


同时,在www.matrix.org.cn上也找到了这篇文章,看到支持的批评的都有。


下面是一些有价值的一些观点:



1、这个根本不是计算机科学中所谓的“内存泄漏”,而是程序员心理上认为的“内存泄漏”,那个例子只能说是程序员有意想保存那些object在vector里,GC当然不会回收。


2、这不是内存泄漏的问题, java中不存在全局变量,因此上述的Vector肯定在一个Class之中,而当这个Class的对象不再被引用时Vector也就被释放了


3、我觉得这篇文章的例子不会存在内存泄漏啊,只要Vector不再被引用,不是就会释放所有的资源了吗?只不过是何时释放内存的问题罢了。作者是不是把没有及时释放内存也定义成了内存泄漏啊!这些内存最终都会释放的。不过作者的说法也不是没有借鉴意义,如果想及时的释放内存的话,还是需要考虑的。


4、“只要Vector不使用,GC还是会回收的,只是时间的问题。 ” ---------  如果是一个站点,同时在线人数多了,那些未释放的内存还不是让你的服务器完蛋。有内存在浪费而不懂得释放就像你花10块钱买别人1块钱能买到的东西一样浪费。总之一句话,编写程序时,发现能释放的,短时间内没用的马上就释放。


但是,当这个对象v是作为类的属性,特别是静态属性时,会引起很大的麻烦。


在《Effective Java 》中,第五条



Eliminate obsolete object references
When you switch from a language with manual memory management, such as C or C++, to a garbage-collected language, your job as a programmer is made much easier by the fact that your objects are automatically reclaimed when you're through with them. It seems almost like magic when you first experience it. It can easily lead to the impression that you don't have to think about memory management, but this isn't quite true.
Consider the following simple stack implementation:
Code View: Scroll / Show All
// Can you spot the "memory leak"?
public class Stack {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Stack() {
elements = new Object[DEFAULT_INITIAL_CAPACITY];
}
public void push(Object e) {
ensureCapacity();
elements[size++] = e;
}
public Object pop() {
if (size == 0)
throw new EmptyStackException();
return elements[--size];
}
/**
* Ensure space for at least one more element, roughly
* doubling the capacity each time the array needs to grow.
*/
private void ensureCapacity() {
if (elements.length == size)
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
There's nothing obviously wrong with this program (but see Item 26 for a generic version). You could test it exhaustively, and it would pass every test with flying colors, but there's a problem lurking. Loosely speaking, the program has a "memory leak," which can silently manifest itself as reduced performance due to increased garbage collector activity or increased memory footprint. In extreme cases, such memory leaks can cause disk paging and even program failure with an OutOfMemoryError, but such failures are relatively rare.
So where is the memory leak? If a stack grows and then shrinks, the objects that were popped off the stack will not be garbage collected, even if the program using the stack has no more references to them. This is because the stack maintains obsolete references to these objects. An obsolete reference is simply a reference that will never be dereferenced again. In this case, any references outside of the "active portion" of the element array are obsolete. The active portion consists of the elements whose index is less than size.
Memory leaks in garbage-collected languages (more properly known as unintentional object retentions) are insidious. If an object reference is unintentionally retained, not only is that object excluded from garbage collection, but so too are any objects referenced by that object, and so on. Even if only a few object references are unintentionally retained, many, many objects may be prevented from being garbage collected, with potentially large effects on performance.
The fix for this sort of problem is simple: null out references once they become obsolete. In the case of our Stack class, the reference to an item becomes obsolete as soon as it's popped off the stack. The corrected version of the pop method looks like this:
public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; // Eliminate obsolete reference
return result;
}
An added benefit of nulling out obsolete references is that, if they are subsequently dereferenced by mistake, the program will immediately fail with a NullPointerException, rather than quietly doing the wrong thing. It is always beneficial to detect programming errors as quickly as possible.
When programmers are first stung by this problem, they may overcompensate by nulling out every object reference as soon as the program is finished using it. This is neither necessary nor desirable, as it clutters up the program unnecessarily. Nulling out object references should be the exception rather than the norm. The best way to eliminate an obsolete reference is to let the variable that contained the reference fall out of scope. This occurs naturally if you define each variable in the narrowest possible scope (Item 45).
So when should you null out a reference? What aspect of the Stack class makes it susceptible to memory leaks? Simply put, it manages its own memory. The storage pool consists of the elements of the elements array (the object reference cells, not the objects themselves). The elements in the active portion of the array (as defined earlier) are allocated, and those in the remainder of the array are free. The garbage collector has no way of knowing this; to the garbage collector, all of the object references in the elements array are equally valid. Only the programmer knows that the inactive portion of the array is unimportant. The programmer effectively communicates this fact to the garbage collector by manually nulling out array elements as soon as they become part of the inactive portion.
Generally speaking, whenever a class manages its own memory, the programmer should be alert for memory leaks. Whenever an element is freed, any object references contained in the element should be nulled out.
Another common source of memory leaks is caches. Once you put an object reference into a cache, it's easy to forget that it's there and leave it in the cache long after it becomes irrelevant. There are several solutions to this problem. If you're lucky enough to implement a cache for which an entry is relevant exactly so long as there are references to its key outside of the cache, represent the cache as a WeakHashMap; entries will be removed automatically after they become obsolete. Remember that WeakHashMap is useful only if the desired lifetime of cache entries is determined by external references to the key, not the value.
More commonly, the useful lifetime of a cache entry is less well defined, with entries becoming less valuable over time. Under these circumstances, the cache should occasionally be cleansed of entries that have fallen into disuse. This can be done by a background thread (perhaps a Timer or ScheduledThreadPoolExecutor) or as a side effect of adding new entries to the cache. The LinkedHashMap class facilitates the latter approach with its removeEldestEntry method. For more sophisticated caches, you may need to use java.lang.ref directly.
A third common source of memory leaks is listeners and other callbacks. If you implement an API where clients register callbacks but don't deregister them explicitly, they will accumulate unless you take some action. The best way to ensure that callbacks are garbage collected promptly is to store only weak references to them, for instance, by storing them only as keys in a WeakHashMap.
Because memory leaks typically do not manifest themselves as obvious failures, they may remain present in a system for years. They are typically discovered only as a result of careful code inspection or with the aid of a debugging tool known as a heap profiler. Therefore, it is very desirable to learn to anticipate problems like this before they occur and prevent them from happening.


我的理解是,这个并不是真正意义上的内存泄漏,而是由于JVM GC的不可控和不确定性引起的。Java里全是句柄,内存是托管的;我们不可能造成内存丢失,有可能犯的错误是仍旧把持着句柄不放,造成内存无法回收。


主要是针对生命周期比较长的对象。例如,对Effective Java Item 5中的这个数组进行pop操作之后,有很长时间未操作,那个所引用的对象在所在单元被覆盖之前就不会被回收,造成所谓的“内存泄漏”。


当然,当这个对象消亡时,所引用的对象最终会释放。但是,如果有成千上万的用户访问时,集合一直保持着对实际已经不再使用的对象的引用,最终会导致OutOfMemory错误,这就是这儿“内存泄漏”的实质。


在编写程序时,应该尽量使用生命周期短的小对象。不要让生命周期很长的对象一直把持其他对象的引用不释放。


当然,如果上面例子中的属性是static的,那么问题就更严重了,因为一旦类被加载,那么该静态属性就一直存在了。


引用CCF会员太阳公公的话



在我看来Java内存泄漏主要存在两种地方:
1.静态变量:这个简单就不说了。
2.资源:涉及到数据库、网络、消息、文件等资源访问的地方。比如:曾经遇见Oracle JDBC的bug,一个查询结束了,却不释放连接。于是相关的资源统统不会释放。
3.死锁:例如线程死锁,资源是不会释放的。
通常情况下,写J2EE的Java程序,并不需要特别关注内存泄漏问题。
当然还有其他的情况造成JVM out of memory,并不一定是泄漏。
比如,曾经在一个高并发的Java程序中解析>50M的XML,造成内存溢出,即使那台服务器的内存有的是。我理解为JVM无法快速申请大量内存,配置JVM启动参数就可以解决了。
另外需要注意的是长时间的操作,系统不会释放这个操作中利用到的内存。
比如,通过一系列复杂查询统计后生成文件,这个过程如果比较长,这期间GC发生了也不会释放内存。
除此之外,JVM都有很多启动参数,是值得研究的。SUN JVM、BEA JRocket、IBM JVM的参数还有些不一样。


解决内存泄漏除了注意及时释放对象引用之外,还可以使用弱引用对象。IBM developerWorks上有两篇文章提到了这个问题:

http://www.ibm.com/developerworks/cn/java/j-refs/index.html

http://www.ibm.com/developerworks/cn/java/j-jtp11225/

根据上述两篇文章写了一小段测试代码:


import java.lang.ref.WeakReference;

public class WeakTest {
    public static void main(String[] args){
        String s=new String("Hello");
        WeakReference<String> wr=new WeakReference<String>(s);
        s=null;                                                                                //1
        System.gc();
        System.out.println(wr.get());
    }
}


其中s是强引用,wr是弱引用。当编号为1的语句存在时,最后输出的是null,说明gc之后弱引用的对象被回收了。

在Java中提供了现成的采用弱引用的集合类WeakHashMap。

2008年6月1日星期日

[转]Oracle中的日期操作

一个月的第一天
SELECT to_date(to_char(SYSDATE,'yyyy-mm')||'-01','yyyy-mm-dd')
FROM dual

sysdate 为数据库服务器的当前系统时间。
to_char 是将日期型转为字符型的函数。
to_date 是将字符型转为日期型的函数,一般使用 yyyy-mm-dd hh24:mi:ss格式,当没有指定时间部分时,则默认时间为 00:00:00
dual 表为sys用户的表,这个表仅有一条记录,可以用于计算一些表达式,如果有好事者用 sys 用户登录系统,然后在 dual 表增加了记录的话,那么系统99.999%不能使用了。为什么使用的时候不用 sys.dual 格式呢,因为 sys 已经为 dual 表建立了所有用户均可使用的别名。

一年的第一天
SELECT to_date(
        to_char(SYSDATE,'yyyy')||'-01-01', 'yyyy-mm-dd'
        )
FROM dual

季度的第一天
SELECT to_date(
               to_char(SYSDATE,'yyyy-')||
               lpad(floor(to_number(to_char(SYSDATE,'mm'))/3)*3+1,2,'0')||
               '-01',
               'yyyy-mm-dd')
FROM dual

floor 为向下取整
lpad 为向左使用指定的字符扩充字符串,这个扩充字符串至2位,不足的补'0'。

当天的半夜
SELECT trunc(SYSDATE)+1-1/24/60/60
FROM dual

trunc 是将 sysdate 的时间部分截掉,即时间部分变成 00:00:00
Oracle中日期加减是按照天数进行的,所以 +1-1/24/60/60 使时间部分变成了 23:59:59。
Oracle 8i 中仅支持时间到秒,9i以上则支持到 1/100000000 秒。

上个月的最后一天
SELECT trunc(last_day(add_months(SYSDATE,-1)))+1-1/24/60/60
FROM dual

add_months 是月份加减函数。
last_day 是求该月份的最后一天的函数。

本年的最后一天
SELECT trunc(
      last_day(to_date(to_char(SYSDATE,'yyyy')||'-12-01','yyyy-mm-dd'))
     )+1-1/24/60/60
FROM dual

本月的最后一天
select trunc(last_day(sysdate))+1-1/24/60/60
from dual

本月的第一个星期一
SELECT next_day(
to_date(to_char(SYSDATE,'yyyy-mm')||'-01','yyyy-mm-dd'),
         '星期一'
         )
FROM dual

next_day 为计算从指定日期开始的第一个符合要求的日期,这里的'星期一'将根据NLS_DATE_LANGUAGE的设置稍有不同。

去掉时分秒
select trunc(sysdate)
from dual

显示星期几
SELECT to_char(SYSDATE,'Day')
FROM dual

取得某个月的天数
SELECT trunc(last_day(SYSDATE))-
       to_date(to_char(SYSDATE,'yyyy-mm')||'-01','yyyy-mm-dd')+
       1
FROM dual

判断是否闰年
SELECT decode(
       to_char(last_day(to_date(to_char(SYSDATE,'yyyy')||'-02-01','yyyy-mm-dd')),'dd'),
               '28','平年','闰年'
       )
FROM dual

一个季度多少天
SELECT last_day(to_date(
                       to_char(SYSDATE,'yyyy-')||
                       lpad(floor(to_number(to_char(SYSDATE,'mm'))/3)*3+3,2,'0')||
                       '-01','yyyy-mm-dd'
                       )
               )
       - 
       to_date(
               to_char(SYSDATE,'yyyy-')||
               lpad(floor(to_number(to_char(SYSDATE,'mm'))/3)*3+1,2,'0')||
               '-01','yyyy-mm-dd')
       +1
FROM dual

一个月的第一天
select trunc(sysdate, 'mm') from dual

一年的第一天
select trunc(sysdate, 'yy') from dual

[转帖] SQL Server各种日期计算方法(收藏)

通常,你需要获得当前日期和计算一些其他的日期,例如,你的程序可能需要判断一个月的第一天或者最后一天。你们大部分人大概都知道怎样把日期进行分割(年、月、日等),然后仅仅用分割出来的年、月、日等放在几个函数中计算出自己所需要的日期!在这篇文 章里,我将告诉你如何使用DATEADD和DATEDIFF函数来计算出在你的程序中可能你要用到的一些不同日期。
  在使用本文中的例子之前,你必须注意以下的问题。大部分可能不是所有例子在不同的机器上执行的结果可能不一样,这完全由哪一天是一个星期的第一天这个设置决定。第一天(DATEFIRST)设定决定了你的系统使用哪一天作为一周的第一天。所有以下的例 子都是以星期天作为一周的第一天来建立,也就是第一天设置为7。假如你的第一天设置不一样,你可能需要调整这些例子,使它和不同的第一天设置相符合。你可以通过@@DATEFIRST函数来检查第一天设置。
  为了理解这些例子,我们先复习一下DATEDIFF和DATEADD函数。DATEDIFF函数计算两个日期之间的小时、天、周、月、年等时间间隔总数。DATEADD函数计算一个日期通过给时间间隔加减来获得一个新的日期。要了解更多的DATEDI FF和DATEADD函数以及时间间隔可以阅读微软联机帮助。
  使用DATEDIFF和DATEADD函数来计算日期,和本来从当前日期转换到你需要的日期的考虑方法有点不同。你必须从时间间隔这个方面来考虑。比如,从当前日期到你要得到的日期之间有多少时间间隔,或者,从今天到某一天(比如1900-1-1)之间有多少时间间隔,等等。理解怎样着眼于时间间隔有助于你轻松的理解我的不同的日期计算例子。
一个月的第一天
  第一个例子,我将告诉你如何从当前日期去这个月的最后一天。请注意:这个例子以及这篇文章中的其他例子都将只使用DATEDIFF和DATEADD函数来计算我们想要的日期。每一个例子都将通过计算但前的时间间隔,然后进行加减来得到想要计算的日期。
  这是计算一个月第一天的SQL 脚本:
  SELECT DATEADD(mm, DATEDIFF(mm,0,getdate()), 0)
  我们把这个语句分开来看看它是如何工作的。最核心的函数是getdate(),大部分人都知道这个是返回当前的日期和时间的函数。下一个执行的函数DATEDIFF(mm,0,getdate())是计算当前日期和“1900-01-01 00:00:00.000”这个日期之间的月数。记住:时期和时间变量和毫秒一样是从“1900-01-01 00:00:00.000”开始计算的。这就是为什么你可以在DATEDIFF函数中指定第一个时间表达式为“0”。下一个函数是DATEADD,增加当前日期到“1900-01-01”的月数。通过增加预定义的日期“1900-01-01”和当前日期的月数,我们可以获得这个月的第一天。另外,计算出来的日期的时间部分将会是“00:00:00.000”。
  这个计算的技巧是先计算当前日期到“1900-01-01”的时间间隔数,然后把它加到“1900-01-01”上来获得特殊的日期,这个技巧可以用来计算很多不同的日期。下一个例子也是用这个技巧从当前日期来产生不同的日期。
本周的星期一
  这里我是用周(wk)的时间间隔来计算哪一天是本周的星期一。
  SELECT DATEADD(wk, DATEDIFF(wk,0,getdate()), 0)
一年的第一天
  现在用年(yy)的时间间隔来显示这一年的第一天。
  SELECT DATEADD(yy, DATEDIFF(yy,0,getdate()), 0)
季度的第一天
  假如你要计算这个季度的第一天,这个例子告诉你该如何做。
  SELECT DATEADD(qq, DATEDIFF(qq,0,getdate()), 0)
当天的半夜
  曾经需要通过getdate()函数为了返回时间值截掉时间部分,就会考虑到当前日期是不是在半夜。假如这样,这个例子使用DATEDIFF和DATEADD函数来获得半夜的时间点。
  SELECT DATEADD(dd, DATEDIFF(dd,0,getdate()), 0)
  深入DATEDIFF和DATEADD函数计算
  你可以明白,通过使用简单的DATEDIFF和DATEADD函数计算,你可以发现很多不同的可能有意义的日期。
  目前为止的所有例子只是仅仅计算当前的时间和“1900-01-01”之间的时间间隔数量,然后把它加到“1900-01-01”的时间间隔上来计算出日期。假定你修改时间间隔的数量,或者使用不同的时间间隔来调用DATEADD函数,或者减去时间间隔而不是增加,那么通过这些小的调整你可以发现和多不同的日期。
  这里有四个例子使用另外一个DATEADD函数来计算最后一天来分别替换DATEADD函数前后两个时间间隔。
上个月的最后一天
  这是一个计算上个月最后一天的例子。它通过从一个月的最后一天这个例子上减去3毫秒来获得。有一点要记住,在Sql Server中时间是精确到3毫秒。这就是为什么我需要减去3毫秒来获得我要的日期和时间。
  SELECT dateadd(ms,-3,DATEADD(mm, DATEDIFF(mm,0,getdate()), 0))
  计算出来的日期的时间部分包含了一个Sql Server可以记录的一天的最后时刻(“23:59:59:997”)的时间。
去年的最后一天
  连接上面的例子,为了要得到去年的最后一天,你需要在今年的第一天上减去3毫秒。
  SELECT dateadd(ms,-3,DATEADD(yy, DATEDIFF(yy,0,getdate()), 0))
本月的最后一天
  现在,为了获得本月的最后一天,我需要稍微修改一下获得上个月的最后一天的语句。修改需要给用DATEDIFF比较当前日期和“1900-01-01”返回的时间间隔上加1。通过加1个月,我计算出下个月的第一天,然后减去3毫秒,这样就计算出了这个月的最后一天。这是计算本月最后一天的SQL脚本。
  SELECT dateadd(ms,-3,DATEADD(mm, DATEDIFF(m,0,getdate())+1, 0))
本年的最后一天
  你现在应该掌握这个的做法,这是计算本年最后一天脚本
  SELECT dateadd(ms,-3,DATEADD(yy, DATEDIFF(yy,0,getdate())+1, 0))。
本月的第一个星期一
  好了,现在是最后一个例子。这里我要计算这个月的第一个星期一。这是计算的脚本。
  select DATEADD(wk, DATEDIFF(wk,0,
  dateadd(dd,6-datepart(day,getdate()),getdate())
  ), 0)
  在这个例子里,我使用了“本周的星期一”的脚本,并作了一点点修改。修改的部分是把原来脚本中“getdate()”部分替换成计算本月的第6天,在计算中用本月的第6天来替换当前日期使得计算可以获得这个月的第一个星期一。
  总结
  我希望这些例子可以在你用DATEADD和DATEDIFF函数计算日期时给你一点启发。通过使用这个计算日期的时间间隔的数学方法,我发现为了显示两个日期之间间隔的有用历法是有价值的。注意,这只是计算出这些日期的一种方法。要牢记,还有很多方法 可以得到相同的计算结果。假如你有其他的方法,那很不错,要是你没有,我希望这些例子可以给你一些启发,当你要用DATEADD和DATEDIFF函数计算你程序可能要用到的日期时。
附录,其他日期处理方法
  1)去掉时分秒
  declare @ datetime
  set @ = getdate() --'2003-7-1 10:00:00'
  SELECT @,DATEADD(day, DATEDIFF(day,0,@), 0)
  2)显示星期几
  select datename(weekday,getdate())
  3)如何取得某个月的天数
  declare @m int
  set @m=2 --月份
  select datediff(day,'2003-'+cast(@m as varchar)+'-15' ,'2003-'+cast(@m+1 as varchar)+'-15')
  另外,取得本月天数
  select datediff(day,cast(month(GetDate()) as varchar)+'-'+cast(month(GetDate()) as varchar)+'-15' ,cast(month(GetDate()) as varchar)+'-'+cast(month(GetDate())+1 as varchar)+'-15')
  或者使用计算本月的最后一天的脚本,然后用DAY函数区最后一天
  SELECT Day(dateadd(ms,-3,DATEADD(mm, DATEDIFF(m,0,getdate())+1, 0)))
  4)判断是否闰年:
  SELECT case day(dateadd(mm, 2, dateadd(ms,-3,DATEADD(yy, DATEDIFF(yy,0,getdate()), 0)))) when 28 then '平年' else '闰年' end
  或者
  select case datediff(day,datename(year,getdate())+'-02-01',dateadd(mm,1,datename(year,getdate())+'-02-01'))
  when 28 then '平年' else '闰年' end
  5)一个季度多少天
  declare @m tinyint,@time smalldatetime
  select @m=month(getdate())
  select @m=case when @m between 1 and 3 then 1
  when @m between 4 and 6 then 4
  when @m between 7 and 9 then 7
  else 10 end
  select @time=datename(year,getdate())+'-'+convert(varchar(10),@m)+'-01'
  select datediff(day,@time,dateadd(mm,3,@time))

Java的自动装箱与拆箱

在JDK 1.5之前,只能往集合类中存放对象。基本类型的数据只能先包装成包装类对象才能放进去。在JDK 1.5中引入了自动装箱和拆箱的功能。

Integer i = 100;

相当于编译器自动为您作以下的语法编译:

Integer i = new Integer(100);

所以自动装箱与拆箱的功能是所谓的“编译器蜜糖”(Compiler Sugar),虽然使用这个功能很方便,但在程序运行阶段您得了解Java的语义。例如下面的程序是可以通过编译的:

Integer i = null;
int j = i;

这样的语法在编译时期是合法的,但是在运行时期会有错误,因为这种写法相当于:

Integer i = null;
int j = i.intValue();

null表示i没有参考至任何的对象实体,它可以合法地指定给对象参考名称。由于实际上i并没有参考至任何的对象,所以也就不可能操作intValue()方法,这样上面的写法在运行时会出现NullPointerException错误。

自动装箱、拆箱的功能提供了方便性,但隐藏了一些细节,所以必须小心。

那么自动装箱的原理是什么呢?

《使用JProfiler剖析Java Application中对象的分配和回收(gc)》一文中,原先的代码是:

package my;

import java.io.IOException;
import java.util.ArrayList;

public class Test {

    public static void main(String[] args) throws IOException {
        char ch=' ';
        while(ch!='n')
            ch=(char)System.in.read();
        for(int i=0;i<1000;i++)
            test();
        ch=' ';
        while(ch!='n')
            ch=(char)System.in.read();
    }

    public static void test(){
        ArrayList<Integer> arr=new ArrayList<Integer>();
        for(int i=0;i<10000;i++)
            arr.add((int)(Math.random()*100));
        //arr.clear();
        //arr=null;
    }
}

后来发现,程序运行结束以后总有256个Integer对象不能回收,百思不得其解。后来反编译了代码,看到实际上是编译器自动完成了将基本类型数据变成包装类对象的操作。下面是反编译的结果,注意红色的部分是编译器做的工作。

package my;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;

public class Test
{
  public static void main(String[] args)
    throws IOException
  {
    char ch = ' ';
    while (ch != 'n')
      ch = (char)System.in.read();
    for (int i = 0; i < 1000; ++i)
      test();
    ch = ' ';
    while (ch != 'n')
      ch = (char)System.in.read();
  }

  public static void test()
  {
    ArrayList arr = new ArrayList();
    for (int i = 0; i < 10000; ++i)
      arr.add(Integer.valueOf((int)(Math.random() * 100.0D)));
  }
}

而这个valueOf()方法的源代码如下:

/**
     * Returns a <tt>Integer</tt> instance representing the specified
     * <tt>int</tt> value.
     * If a new <tt>Integer</tt> instance is not required, this method
     * should generally be used in preference to the constructor
     * {@link #Integer(int)}, as this method is likely to yield
     * significantly better space and time performance by caching
     * frequently requested values.
     *
     * @param  i an <code>int</code> value.
     * @return a <tt>Integer</tt> instance representing <tt>i</tt>.
     * @since  1.5
     */
    public static Integer valueOf(int i) {
    final int offset = 128;
    if (i >= -128 && i <= 127) { // must cache
        return IntegerCache.cache[i + offset];
    }
        return new Integer(i);
    }

IntegerCache是Integer类的内部类

private static class IntegerCache {
    private IntegerCache(){}

    static final Integer cache[] = new Integer[-(-128) + 127 + 1];

    static {
        for(int i = 0; i < cache.length; i++)
        cache[i] = new Integer(i - 128);
    }
    }

当要包装的数值在-128-+127之间时,就会产生256个Integer对象,并返回其中的一个。如果在此范围之外,则直接返回一个Integer对象。这样就不难理解为什么最后有256个不能回收的Integer对象了。

带来的问题:

下面的三个程序输出各是什么?

public class AutoBoxDemo {
    public static void main(String[] args) {
        Integer i1 = 100;
        Integer i2 = 100;
        if (i1 == i2)
            System.out.println("i1 == i2");
        else
            System.out.println("i1 != i2");
    }
}

答案:i1==i2  (数值在-128到127之间,自动装箱返回的是IntegerCache中的引用,因此是同一个对象)

public class AutoBoxDemo {
    public static void main(String[] args) {
        Integer i1 = 200;
        Integer i2 = 200;
        if (i1 == i2)
            System.out.println("i1 == i2");
        else
            System.out.println("i1 != i2");
    }
}

答案:i1!=i2 (数值超出-128到127这个范围,直接new一个对象返回,数值相等,但是对象不是同一个)

public class AutoBoxDemo {
    public static void main(String[] args) {
        Integer i1 = 200;
        Integer i2 = 200;
        if (i1.equals(i2))
            System.out.println("i1 == i2");
        else
            System.out.println("i1 != i2");
    }
}

答案:i1==i2 (数值超出-128到127这个范围,直接new一个对象返回,数值相等,用equals比较当然是相等的)

再次提醒:对象的相等性比较应该用equals,==进行的是同一性比较。

使用JProfiler剖析Java Application中对象的分配和回收(gc)

JProfiler是一款Java的性能监控工具。可以查看当前应用的对象、对象引用、内存、CPU使用情况、线程、线程运行情况(阻塞、等待等),同时可以查找应用内存使用得热点,即:哪个对象占用的内存比较多;或者CPU热点,即:哪儿方法占用的较大的CPU资源。

下面结合一个简单的例子说明Jprofiler如何使用

package my;

import java.io.IOException;
import java.util.ArrayList;

public class Test {

    public static void main(String[] args) throws IOException {
       char ch=' ';
        while(ch!='n')
            ch=(char)System.in.read();

        for(int i=0;i<1000;i++)
            test();
        ch=' ';
        while(ch!='n')
            ch=(char)System.in.read();

    }

    public static void test(){
        ArrayList<Integer> arr=new ArrayList<Integer>();
        for(int i=0;i<10000;i++)
            arr.add(new Integer((int)(Math.random()*100)));
        //arr.clear();
        //arr=null;

    }
}

代码中红色的部分是为了停下来对JProfiler进行操作和观察结果而设置的。

经过实验,发现蓝色部分加上和不加结果是一样的。因为arr是局部变量,test()运行结束时其生命周期也结束了,arr所引用的对象也被回收。因此也没必要设置为null。

1、启动JProfiler,选择local application

1

2、进入下一步,设置参数

2

3、确认,在下面的对话框中也点OK确认

3

Profiling Setting

5

系统中已存在的对象

4、设置要记录的数据

6

记录对象分配情况

7

记录对类的跟踪数据

8

9

在类跟踪选项中添加要跟踪的类

5、开始测试

10

在Concole窗口中按n(代码里边要求的),进入下一步对象的分配和释放阶段

6、测试程序段结束,观察结果

11

运行结束时,有42470个Integer对象尚未回收

12

所关注的类的实例数量变化情况,下降的地方是gc起作用了

13

GC在运行过程中,每一时刻回收的对象数量。(GC不是一直运行的,要在JVM的内存不够时才运行)

14

所记录的对象的数量变化情况

15

堆内存分配变化情况

7、按F4执行gc,观察结果

在第6步看到,运行结束时,有42470个Integer对象尚未回收。现在按F4执行gc

16

Integer对象全部被回收

17

18

19

20

图中的小的尖峰就是对之前未回收的Integer对象进行回收时产生的数据。

[转]java垃圾收集算法(http://www.javaeye.com/topic/144859)

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.   Garbage() {    
  5.   count++;    
  6.   System.out.println("object "+count+" construct");    
  7.   setID(count);    
  8.   }    
  9. void setID(int id) {    
  10.   index=id;    
  11.   }    
  12. protected void finalize() //重写finalize方法 
  13.   {    
  14.   System.out.println("object "+index+" is reclaimed");    
  15.   }    
  16. public static void main(String[] args)    
  17.   {    
  18. new Garbage();    
  19. new Garbage();    
  20. new Garbage();    
  21. new Garbage();    
  22.   System.gc(); //请求运行垃圾收集器 
  23.   }    
  24.  } 

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

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

java 代码

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

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的活动,确定内存使用量是否随着时间增加。如果确实如此,就可能发生了内存泄漏。

Java内存泄漏初探(2)

第二个内存泄漏的例子是:没有正确实现pop方法的ObjStack

class ObjStack
{
private Object[] stack;
private int index;
public void push(Object o)
{
stack[index] = o;
index++;
}
public Object pop()
{
index-;
return stack[index];
}
//...
}


现在创建一个容量为10的对象,然后调用8次push方法向它添加对象,那么此时索引值为8。现在考虑三次调用pop方法后发生什么?此时的索引值为5,但是请注意,除了这个索引值发生变化外堆栈其实没有其它任何变化!虽然pop方法减小了索引值,但是实际上堆栈仍然保持着对那些对象的引用。调用pop方法往往意味着那些对象应该被收集(大多情况是如此的,即使不是马上,也是在稍后使用完该对象后)。然而由于堆栈仍然保留有对该对象的引用,它就不能被收集。这些对象只能在调用push后被替换才可能被收集

正确的pop的实现如下:


public Object pop()
{
index-;
Object o = stack[index];
stack[index] = null;
return o;
}


在这个版本的pop方法中,当引用被返回后,堆栈删除对他们的引用因此垃圾收集器在以后可以回收他们。

同样地,对于不正确的pop()方法,我在JProfiler中进行了测试

public class Pushpop {
    public static void main(String[] args) {
        int i = 0;
        Object tempobj;
        // new一个ObjStack对象,并调用有参构造函数。分配stack Obj数组的空间大小为64,可以存64个对象,从0开始存储
        ObjStack stack1 = new ObjStack(64);
        // 循环new Obj对象,把每次循环的对象一一存放在stack Obj数组中。
        while (i < 64) {
            tempobj = new Object();
            stack1.push(tempobj);
            i++;
            System.out.println("第" + i + "次进栈" + "\t");
        }
        //==========A===================
        while (i > 32) {
            tempobj = stack1.pop();// 这里造成了空间的浪费。
            i--;
            System.out.println("第" + (64 - i) + "次出栈" + "\t");
        }
        //==========B===================
        stack1=null;
        //==========C===================
    }
}

在A点和B点,Obejct对象数量都是64个,原因就是数组仍然维持着对于对象的引用。当执行stack1=null之后,在C点,在Jprofiler中按F4进行gc,会发现Object数量降为0,对象均被回收。

Java内存泄漏初探(1)

进来编写的一个程序在长时间运行后似乎会停止响应,想到了是否是内存泄漏的缘故。之前没有好好研究过Java内存泄漏的问题,所以首先搜索了一下网络,结合Jprofiler工具,想先对Java Application的内存泄漏问题作一下研究。

通过搜索,网上所举的关于内存泄漏的问题其中一个为:

释放不用的对象:参见《java内存泄漏》,了解JVM的工作原理及机制规范;
ArrayList al = new ArrayList();
for(int i = 0;i<200;i++){
Object o = new Object();
o.name = 1;
o.id = 1;
al.add(o);
}
以上代码中o仍被al引用,没有释放,不会被回收。因此需修正:
Arraylist al = new Arraylist();
for(int i = 0;i<200;i++){
Object o = new Object();
o.name = 1;
o.id = 1;
al.add(o);
o = null;
}

注意上面红色字体部分是错误的。而修正中的 o=null; 更是画蛇添足。因为o本来就是一个局部变量,每次循环时都会去引用一个新的对象,而且再离开复合语句时o就不存在了,但是由于对象被加入了集合ArrayList中,因此对象还是被引用的,并不会被回收。

为了在JProfiler中查看对象分配和回收的情况,我写了下面的两个类

package my;

public class MyObject {
    public int name;
    public int id;
}

package my;

import java.io.IOException;
import java.util.ArrayList;

public class Test2 {
    public static void main(String[] args) throws IOException {
        char ch = ' ';
        while (ch != 'y')
            ch = (char) System.in.read();
        ArrayList<MyObject> al = new ArrayList<MyObject>();
        for (int i = 0; i < 200; i++) {
            MyObject o = new MyObject();
            o.name = 1;
            o.id = 1;
            al.add(o);
           o = null;
        }
        ch = ' ';
        while (ch != 'n')
            ch = (char) System.in.read();
        al=null;
        ch = ' ';
        while (ch != 'n')
            ch = (char) System.in.read();
    }
}

经过分析,程序在运行时,上述代码中蓝色部分 o=null; 加与不加,对对象的分配和回收是没有任何影响的。而红色的 al=null; 如果不加的话,那么在程序中在这一点明显可以看到内存是不能回收的,但是,由于al本身是一个局部变量,因此在main()运行结束时,al的生命周期结束,它所引用的对象都会被释放。

江南白衣所写的《编写对GC友好,又不泄漏的代码》 (最新版链接:http://blog.csdn.net/calvinxiu/archive/2007/05/22/1621051.aspx)中,提到了这样几条原则:

1.使用更多生命周期短的、小的、不改变指向(immutable)的对象,编写清晰的代码。

    出于懒惰也好,朴素的节俭意识也好,我们都习惯对一个变量重用再重用。但是....

  • Java的垃圾收集器喜欢短生命周期的对象,对象如果在新生代内,在垃圾收集发生前就死掉了,垃圾收集器就什么都不用做了。
  • 现代JVM构建一个新对象只需要10个本地CPU指令,并不弱于C/C++。 (但垃圾收集没有压缩算法时会稍慢,更频繁的New对象也导致更频繁的GC)。
  • 大对象的分配效率更低,而且对非压缩算法的垃圾收集器,更容易造成碎片。
  • 对象重用增加了代码的复杂度,降低了可读性。

   所以有标题的呼吁,比如不要害怕为中间结果分配小对象。但编程习惯的改变也不是一朝一夕的事情。

2.将用完的对象设为NULL其实没什么作用。

    貌似很酷的把对象主动设为Null 的"好习惯"其实没什么用,JIT Compiler会自动分析local变量的生命周期。
    只有一个例外情况,就是String[1024] foo 这种赤裸裸的数组,你需要主动的foo[100]=null释放第100号元素,所以最好还是直接用ArrayList这些标准库算了。

3.避免显式GC--System.gc()。

    大家都知道System.gc()不好,full-gc浪费巨大,gc的时机把握不一定对等等,甚至有-XX:+DisableExplicitGC的JVM参数来禁止它。

    哈哈,但我还不会用System.gc()呢,不怕不怕。真的不怕吗?

  • 先用FindBugs 查一下所用到的全部第三方类库吧...
  • 至少RMI 就会老实不客气的执行System.gc()来实现分布式GC算法。但我也不会用RMI啊。那EJB呢,EJB可是建在RMI上的....

    如果无可避免,用-Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000 (单位为微妙) 增大大GC的间隔(原默认值为1分钟),-XX:+ExplicitGCInvokesConcurrent 让System.gc() 也CMS并发执行。

4.继续千夫所指的finalize()

    大家也都知道finalize()不好,分配代价昂贵,释放代价更昂贵(要多走一个循环,而且他们死得慢,和他们相关联的对象也跟着死得慢了),又不确定能否被调用(JVM开始关闭时,就不会再进行垃圾收集),又不确定何时被调用(GC时间不定,即使system.gc()也只是提醒而不是强迫GC,又不确定以什么样的顺序调用,所以finalize不是C++的析构函数,也不像C++的析构函数。

   我们都知道啊,所以我从来都没使用。都是在显式的维护那些外部资源,比如在finally{}里释放。

在上面的例子中,确实也如白衣的文章所述,一般情况下没有必要将对象设置为null。

另外,一些文章中提到了使用ArrayList的方法clear()来清除ArrayList对对象的引用,该方法的源代码如下,实际上就是将其中的每个引用变量设置为null,断开对对象的引用。

    /**
     * Removes all of the elements from this list.  The list will
     * be empty after this call returns.
     */
    public void clear() {
    modCount++;

    // Let gc do its work
    for (int i = 0; i < size; i++)
        elementData[i] = null;

    size = 0;
    }

而前面的代码已经说明,对ArrayList对象消亡的时候,所引用的对象也会被回收,所以这个clear方法一般不需要调用。

2008年5月30日星期五

【转帖】五种提高SQL Server性能的方法

FROM: http://searchdatebase.techtarget.co...7/1923316.shtml
有时,为了让应用程序运行得更快,所做的全部工作就是在这里或那里做一些很小调整。啊,但关键在于确定如何进行调整!迟早您会遇到这种情况:应用程序中的 SQL 查询不能按照您想要的方式进行响应。它要么不返回数据,要么耗费的时间长得出奇。如果它降低了报告或您的企业应用程序的速度,用户必须等待的时间过长,他们就会很不满意。就像您的父母不想听您解释为什么在深更半夜才回来一样,用户也不会听你解释为什么查询 耗费这么长时间。(“对不起,妈妈,我使用了太多的 LEFT JOIN。”)用户希望应用程序响应迅速,他们的报告能够在瞬间之内返回分析数据。就我自己而言,如果在 Web 上冲浪时某个页面要耗费十多秒才能加载(好吧,五秒更实际一些),我也会很不耐烦。
为了解决这些问题,重要的是找到问题的根源。那么,从哪里开始呢?根本原因通常在于数据库设计和访问它的查询。在本月的专栏中,我将讲述四项技术,这些技术可用于提高基于 SQL Server? 的应用程序的性能或改善其可伸缩性。我将仔细说明 LEFT JOIN、CROSS JOIN 的使用以及 IDENTITY 值的检索。请记住,根本没有神奇的解决方案。调整您的数据库及其查询需要占用时间、进行分析,还需要大量的测试。这些技术都已被证明行之有效,但对您的应用程序而言,可能其中一些技术比另一些技术更适用。
从 INSERT 返回 IDENTITY
我决定从遇到许多问题的内容入手:如何在执行 SQL INSERT 后检索 IDENTITY 值。通常,问题不在于如何编写检索值的查询,而在于在哪里以及何时进行检索。在 SQL Server 中,下面的语句可用于检索由最新在活动数据库连接上运行的 SQL 语句所创建的 IDENTITY 值:
SELECT @@IDENTITY
这个 SQL 语句并不复杂,但需要记住的一点是:如果这个最新的 SQL 语句不是 INSERT,或者您针对非 INSERT SQL 的其他连接运行了此 SQL,则不会获得期望的值。您必须运行下列代码才能检索紧跟在 INSERT SQL 之后且位于同一连接上的 IDENTITY,如下所示:
INSERT INTO Products (ProductName) VALUES ('Chalk')
SELECT @@IDENTITY
在一个连接上针对 Northwind 数据库运行这些查询将返回一个名称为 Chalk 的新产品的 IDENTITY 值。所以,在使用 ADO 的 Visual Basic? 应用程序中,可以运行以下语句:
Set oRs = oCn.Execute("SET NOCOUNT ON;INSERT INTO Products _
(ProductName) VALUES ('Chalk');SELECT @@IDENTITY")
lProductID = oRs(0)
  此代码告诉 SQL Server 不要返回查询的行计数,然后执行 INSERT 语句,并返回刚刚为这个新行创建的 IDENTITY 值。SET NOCOUNT ON 语句表示返回的记录集有一行和一列,其中包含了这个新的 IDENTITY 值。如果没有此语句,则会首先返回一个空的记录集(因为 INSERT 语句不返回任何数据),然后会返回第二个记录集,第二个记录集中包含 IDENTITY 值。这可能有些令人困惑,尤其是因为您从来就没有希望过 INSERT 会返回记录集。之所以会发生此情况,是因为 SQL Server 看到了这个行计数(即一行受到影响)并将其解释为表示一个记录集。因此,真正的数据被推回到了第二个记录集。当然您可以使用 ADO 中的 NextRecordset 方法获取此第二个记录集,但如果总能够首先返回该记录集且只返回该记录集,则会更方便,也更有效率。
此方法虽然有效,但需要在 SQL 语句中额外添加一些代码。获得相同结果的另一方法是在 INSERT 之前使用 SET NOCOUNT ON 语句,并将 SELECT @@IDENTITY 语句放在表中的 FOR INSERT 触发器中,如下面的代码片段所示。这样,任何进入该表的 INSERT 语句都将自动返回 IDENTITY 值。
CREATE TRIGGER trProducts_Insert ON Products FOR INSERT AS
SELECT @@IDENTITY
GO
触发器只在 Products 表上发生 INSERT 时启动,所以它总是会在成功 INSERT 之后返回一个 IDENTITY。使用此技术,您可以始终以相同的方式在应用程序中检索 IDENTITY 值。
内嵌视图与临时表
某些时候,查询需要将数据与其他一些可能只能通过执行 GROUP BY 然后执行标准查询才能收集的数据进行联接。例如,如果要查询最新五个定单的有关信息,您首先需要知道是哪些定单。这可以使用返回定单 ID 的 SQL 查询来检索。此数据就会存储在临时表(这是一个常用技术)中,然后与 Products 表进行联接,以返回这些定单售出的产品数量:
CREATE TABLE #Temp1 (OrderID INT NOT NULL, _
OrderDate DATETIME NOT NULL)
INSERT INTO #Temp1 (OrderID, OrderDate)
SELECT TOP 5 o.OrderID, o.OrderDate
FROM Orders o ORDER BY o.OrderDate DESC
SELECT p.ProductName, SUM(od.Quantity) AS ProductQuantity
FROM #Temp1 t
INNER JOIN [Order Details] od ON t.OrderID = od.OrderID
INNER JOIN Products p ON od.ProductID = p.ProductID
GROUP BY p.ProductName
ORDER BY p.ProductName
DROP TABLE #Temp1
这些 SQL 语句会创建一个临时表,将数据插入该表中,将其他数据与该表进行联接,然后除去该临时表。这会导致此查询进行大量 I/O 操作,因此,可以重新编写查询,使用内嵌视图取代临时表。内嵌视图只是一个可以联接到 FROM 子句中的查询。所以,您不用在 tempdb 中的临时表上耗费大量 I/O 和磁盘访问,而可以使用内嵌视图得到同样的结果:
SELECT p.ProductName,
SUM(od.Quantity) AS ProductQuantity
FROM (
SELECT TOP 5 o.OrderID, o.OrderDate
FROM Orders o
ORDER BY o.OrderDate DESC
) t
INNER JOIN [Order Details] od ON t.OrderID = od.OrderID
INNER JOIN Products p ON od.ProductID = p.ProductID
GROUP BY
p.ProductName
ORDER BY
p.ProductName
此查询不仅比前面的查询效率更高,而且长度更短。临时表会消耗大量资源。如果只需要将数据联接到其他查询,则可以试试使用内嵌视图,以节省资源。
避免 LEFT JOIN 和 NULL
当然,有很多时候您需要执行 LEFT JOIN 和使用 NULL 值。但是,它们并不适用于所有情况。改变 SQL 查询的构建方式可能会产生将一个花几分钟运行的报告缩短到只花几秒钟这样的天壤之别的效果。有时,必须在查询中调整数据的形态,使之适应应用程序所要求的显示方式。虽然 TABLE 数据类型会减少大量占用资源的情况,但在查询中还有许多区域可以进行优化。SQL 的一个有价值的常用功能是 LEFT JOIN。它可以用于检索第一个表中的所有行、第二个表中所有匹配的行、以及第二个表中与第一个表不匹配的所有行。例如,如果希望返回每个客户及其定单,使用 LEFT JOIN 则可以显示有定单和没有定单的客户。
此工具可能会被过度使用。LEFT JOIN 消耗的资源非常之多,因为它们包含与 NULL(不存在)数据匹配的数据。在某些情况下,这是不可避免的,但是代价可能非常高。LEFT JOIN 比 INNER JOIN 消耗资源更多,所以如果您可以重新编写查询以使得该查询不使用任何 LEFT JOIN,则会得到非常可观的回报。
加快使用 LEFT JOIN 的查询速度的一项技术涉及创建一个 TABLE 数据类型,插入第一个表(LEFT JOIN 左侧的表)中的所有行,然后使用第二个表中的值更新 TABLE 数据类型。此技术是一个两步的过程,但与标准的 LEFT JOIN 相比,可以节省大量时间。一个很好的规则是尝试各种不同的技术并记录每种技术所需的时间,直到获得用于您的应用程序的执行性能最佳的查询。
测试查询的速度时,有必要多次运行此查询,然后取一个平均值。因为查询(或存储过程)可能会存储在 SQL Server 内存中的过程缓存中,因此第一次尝试耗费的时间好像稍长一些,而所有后续尝试耗费的时间都较短。另外,运行您的查询时,可能正在针对相同的表运行其他查询。当其他查询锁定和解锁这些表时,可能会导致您的查询要排队等待。例如,如果您进行查询时某人正在更新 此表中的数据,则在更新提交时您的查询可能需要耗费更长时间来执行。
避免使用 LEFT JOIN 时速度降低的最简单方法是尽可能多地围绕它们设计数据库。例如,假设某一产品可能具有类别也可能没有类别。如果 Products 表存储了其类别的 ID,而没有用于某个特定产品的类别,则您可以在字段中存储 NULL 值。然后您必须执行 LEFT JOIN 来获取所有产品及其类别。您可以创建一个值为“No Category”的类别,从而指定外键关系不允许 NULL 值。通过执行上述操作,现在您就可以使用 INNER JOIN 检索所有产品及其类别了。虽然这看起来好像是一个带有多余数据的变通方法,但可能是一个很有价值的技术,因为它可以消除 SQL 批处理语句中消耗资源较多的 LEFT JOIN。在数据库中全部使用此概念可以为您节省大量的处理时间。请记住,对于您的用户而言,即使几秒钟的时间也非常重要,因为当您有许多用户正在访问同一个联机数据库应用程序时,这几秒钟实际上的意义会非常重大。
灵活使用笛卡尔乘积
对于此技巧,我将进行非常详细的介绍,并提倡在某些情况下使用笛卡尔乘积。出于某些原因,笛卡尔乘积 (CROSS JOIN) 遭到了很多谴责,开发人员通常会被警告根本就不要使用它们。在许多情况下,它们消耗的资源太多,从而无法高效使用。但是像 SQL 中的任何工具一样,如果正确使用,它们也会很有价值。例如,如果您想运行一个返回每月数据(即使某一特定月份客户没有定单也要返回)的查询,您就可以很方便地使用笛卡尔乘积。
虽然这看起来好像没什么神奇的,但是请考虑一下,如果您从客户到定单(这些定单按月份进行分组并对销售额进行小计)进行了标准的 INNER JOIN,则只会获得客户有定单的月份。因此,对于客户未订购任何产品的月份,您不会获得 0 值。如果您想为每个客户都绘制一个图,以显示每个月和该月销售额,则可能希望此图包括月销售额为 0 的月份,以便直观标识出这些月份。如果使用 Figure 2(最后一页) 中的 SQL,数据则会跳过销售额为 0 美元的月份,因为在定单表中对于零销售额不会包含任何行(假设您只存储发生的事件)。
Figure 3(最后一页)中的代码虽然较长,但是可以达到获取所有销售数据(甚至包括没有销售额的月份)的目标。首先,它会提取去年所有月份的列表,然后将它们放入第一个 TABLE 数据类型表 (@tblMonths) 中。下一步,此代码会获取在该时间段内有销售额的所有客户公司的名称列表,然后将它们放入另一个 TABLE 数据类型表 (@tblCus-tomers) 中。这两个表存储了创建结果集所必需的所有基本数据,但实际销售数量除外。 第一个表中列出了所有月份(12 行),第二个表中列出了这个时间段内有销售额的所有客户(对于我是 81 个)。并非每个客户在过去 12 个月中的每个月都购买了产品,所以,执行 INNER JOIN 或 LEFT JOIN 不会返回每个月的每个客户。这些操作只会返回购买产品的客户和月份。
笛卡尔乘积则可以返回所有月份的所有客户。笛卡尔乘积基本上是将第一个表与第二个表相乘,生成一个行集合,其中包含第一个表中的行数与第二个表中的行数相乘的结果。因此,笛卡尔乘积会向表 @tblFinal 返回 972 行。最后的步骤是使用此日期范围内每个客户的月销售额总计更新 @tblFinal 表,以及选择最终的行集。
如果由于笛卡尔乘积占用的资源可能会很多,而不需要真正的笛卡尔乘积,则可以谨慎地使用 CROSS JOIN。例如,如果对产品和类别执行了 CROSS JOIN,然后使用 WHERE 子句、DISTINCT 或 GROUP BY 来筛选出大多数行,那么使用 INNER JOIN 会获得同样的结果,而且效率高得多。如果需要为所有的可能性都返回数据(例如在您希望使用每月销售日期填充一个图表时),则笛卡尔乘积可能会非常有帮助。但是,您不应该将它们用于其他用途,因为在大多数方案中 INNER JOIN 的效率要高得多。
拾遗补零
这里介绍其他一些可帮助提高 SQL 查询效率的常用技术。假设您将按区域对所有销售人员进行分组并将他们的销售额进行小计,但是您只想要那些数据库中标记为处于活动状态的销售人员。您可以按区域对销售人员分组,并使用 HAVING 子句消除那些未处于活动状态的销售人员,也可以在 WHERE 子句中执行此操作。在 WHERE 子句中执行此操作会减少需要分组的行数,所以比在 HAVING 子句中执行此操作效率更高。HAVING 子句中基于行的条件的筛选会强制查询对那些在 WHERE 子句中会被去除的数据进行分组。
另一个提高效率的技巧是使用 DISTINCT 关键字查找数据行的单独报表,来代替使用 GROUP BY 子句。在这种情况下,使用 DISTINCT 关键字的 SQL 效率更高。请在需要计算聚合函数(SUM、COUNT、MAX 等)的情况下再使用 GROUP BY。另外,如果您的查询总是自己返回一个唯一的行,则不要使用 DISTINCT 关键字。在这种情况下,DISTINCT 关键字只会增加系统开销。
您已经看到了,有大量技术都可用于优化查询和实现特定的业务规则,技巧就是进行一些尝试,然后比较它们的性能。最重要的是要测试、测试、再测试。在此专栏的将来各期内容中,我将继续深入讲述 SQL Server 概念,包括数据库设计、好的索引实践以及 SQL Server 安全范例。
Figure 2 Returning All Customers and Their Sales
set nocount on
DECLARE @dtStartDate DATETIME,
@dtEndDate DATETIME,
@dtDate DATETIME
SET @dtEndDate = '5/5/1997'
SET @dtEndDate = DATEADD(DD, -1, CAST(CAST((MONTH(@dtEndDate) + 1)
AS VARCHAR(2)) + '/01/' + CAST(YEAR(@dtEndDate) AS VARCHAR(4)) + '
23:59:59' AS DATETIME))
SET @dtStartDate = DATEADD(MM, -1 * 12, @dtEndDate)
SELECT CAST(YEAR(o.OrderDate) AS VARCHAR(4)) + '-' +
CASE
WHEN MONTH(o.OrderDate) < 10
THEN '0' + CAST(MONTH(o.OrderDate) AS VARCHAR(2))
ELSE CAST(MONTH(o.OrderDate) AS VARCHAR(2))
END AS sMonth,
c.CustomerID,
c.CompanyName,
c.ContactName,
SUM(od.Quantity * od.UnitPrice) AS mSales
FROM Customers c
INNER JOIN Orders o ON c.CustomerID = o.CustomerID
INNER JOIN [Order Details] od ON o.OrderID = od.OrderID
WHERE o.OrderDate BETWEEN @dtStartDate AND @dtEndDate
GROUP BY
CAST(YEAR(o.OrderDate) AS VARCHAR(4)) + '-' +
CASE
WHEN MONTH(o.OrderDate) < 10
THEN '0' + CAST(MONTH(o.OrderDate) AS VARCHAR(2))
ELSE CAST(MONTH(o.OrderDate) AS VARCHAR(2))
END,
c.CustomerID,
c.CompanyName,
c.ContactName
ORDER BY
c.CompanyName,
sMonth
___________________________________________________________________________
Figure 3 Cartesian Product at Work
DECLARE @tblMonths TABLE (sMonth VARCHAR(7))
DECLARE @tblCustomers TABLE ( CustomerID CHAR(10),
CompanyName VARCHAR(50),
ContactName VARCHAR(50))
DECLARE @tblFinal TABLE ( sMonth VARCHAR(7),
CustomerID CHAR(10),
CompanyName VARCHAR(50),
ContactName VARCHAR(50),
mSales MONEY)
DECLARE @dtStartDate DATETIME,
@dtEndDate DATETIME,
@dtDate DATETIME,
@i INTEGER
SET @dtEndDate = '5/5/1997'
SET @dtEndDate = DATEADD(DD, -1, CAST(CAST((MONTH(@dtEndDate) + 1) AS
VARCHAR(2)) + '/01/' + CAST(YEAR(@dtEndDate) AS VARCHAR(4)) + '
23:59:59' AS DATETIME))
SET @dtStartDate = DATEADD(MM, -1 * 12, @dtEndDate)
— Get all months into the first table
SET @i = 0
WHILE (@i < 12)
BEGIN
SET @dtDate = DATEADD(mm, -1 * @i, @dtEndDate)
INSERT INTO @tblMonths SELECT CAST(YEAR(@dtDate) AS VARCHAR(4)) + '-' +
CASE
WHEN MONTH(@dtDate) < 10
THEN '0' + CAST(MONTH(@dtDate) AS VARCHAR(2))
ELSE CAST(MONTH(@dtDate) AS VARCHAR(2))
END AS sMonth
SET @i = @i + 1
END
— Get all clients who had sales during that period into the "y" table
INSERT INTO @tblCustomers
SELECT DISTINCT
c.CustomerID,
c.CompanyName,
c.ContactName
FROM Customers c
INNER JOIN Orders o ON c.CustomerID = o.CustomerID
WHERE o.OrderDate BETWEEN @dtStartDate AND @dtEndDate
INSERT INTO @tblFinal
SELECT m.sMonth,
c.CustomerID,
c.CompanyName,
c.ContactName,
0
FROM @tblMonths m CROSS JOIN @tblCustomers c
UPDATE @tblFinal SET
mSales = mydata.mSales
FROM @tblFinal f INNER JOIN
(
SELECT c.CustomerID,
CAST(YEAR(o.OrderDate) AS VARCHAR(4)) + '-' +
CASE WHEN MONTH(o.OrderDate) < 10
THEN '0' + CAST(MONTH(o.OrderDate) AS VARCHAR(2))
ELSE CAST(MONTH(o.OrderDate) AS VARCHAR(2))
END AS sMonth,
SUM(od.Quantity * od.UnitPrice) AS mSales
FROM Customers c
INNER JOIN Orders o ON c.CustomerID = o.CustomerID
INNER JOIN [Order Details] od ON o.OrderID = od.OrderID
WHERE o.OrderDate BETWEEN @dtStartDate AND @dtEndDate
GROUP BY
c.CustomerID,
CAST(YEAR(o.OrderDate) AS VARCHAR(4)) + '-' +
CASE WHEN MONTH(o.OrderDate) < 10
THEN '0' + CAST(MONTH(o.OrderDate) AS VARCHAR(2))
ELSE CAST(MONTH(o.OrderDate) AS VARCHAR(2))
END
) mydata on f.CustomerID = mydata.CustomerID AND f.sMonth =
mydata.sMonth
SELECT f.sMonth,
f.CustomerID,
f.CompanyName,
f.ContactName,
f.mSales
FROM @tblFinal f
ORDER BY
f.CompanyName,
f.sMonth

2008年5月29日星期四

转:致工作者的一封信(阅读后必有收获)

     一天,一只兔子在山洞前写文章,一只狼走了过来, 问:“兔子啊,你在干什么?”
  答曰:“写文章。”
  问:“什么题目?”
  答曰:“《浅谈兔子是怎样吃掉狼的》。”
  狼哈哈大笑,表示不信,于是兔子把狼领进山洞。
  过了一会,兔子独自走出山洞,继续写文章。
  一只野猪走了过来,问:“兔子你在写什么?”
  答:“文 章。”
  问:“题目是什么?”
  答:“《浅谈兔子是如何把野猪吃掉的》。”
  野猪不信,于是同样的事情发生。
  最后,在山洞里,一只狮子在一堆白骨之间,满意的剔着牙读着兔子交给它的文章, 题目:“《一只动物,能力大小关键要看你的老板是谁》。”
  这只兔子有次不小心告诉了他的一个兔子朋友,这消息逐渐在森林中传播; 狮子知道后非常生气,他告诉兔子:“如果这个星期没有食物进洞,我就吃你。”
  于是兔子继续在洞口写文章
  一只小鹿走过来,
  “兔子,你在干什么啊?”
  “写文章”
  “什么题目”
  “《浅谈兔子是怎样吃掉狼的》”
  “哈哈,这个事情全森林都知道啊,你别胡弄我了,
  我是不会进洞的”
  “我马上要退休了,狮子说要找个人顶替我,难道你不想这篇文章的兔子变成小鹿么”
  小鹿想了想,终于忍不住诱惑,跟随兔子走进洞里。
  过了一会,兔子独自走出山洞,继续写文章
  一只小马走过来,同样是事情发生了。
  最后,在山洞里,一只狮子在一堆白骨之间,满意的剔着牙读着兔子交给它的文章
  题目是:《如何发展下线动物为老板提供食物》
  随着时间的推移,狮子越长越大,兔子的食物已远远不能填饱肚子。
  一日,他告诉兔子:“我的食物量要加倍,
  例如:原来4天一只小鹿,现在要2天一 只,如果一周之内改变不了局面 我就吃你。
  于是,兔子离开洞口,跑进森林深处,他见到一只狼
  “你相信兔子能轻松吃掉狼吗”
  狼哈哈大笑,表示不信,于是兔子把狼领进山洞。
  过了一会,兔子独自走出山洞,继续进入森林深处
  这回他碰到一只野猪----“你相信兔子能轻松吃掉野猪吗”
  野猪不信,于是同样的事情发生了。
  原来森林深处的动物并不知道兔子和狮子的故事
  最后,在山洞里,一只狮子在一堆白骨之间,满意的剔着牙读着兔子交给它的文章
  题目是:《如何实现由坐商到行商的转型为老板提供更多的食物》
  时间飞快,转眼之间,兔子在森林里的名气越来越大
  因为大家都知道它有一个很历害的老板
  这只小兔开始横行霸道,欺上欺下,没有动物敢惹
  它时时想起和乌龟赛跑的羞辱
  它找到乌龟说:“三天之内,见我老板!”扬长而去
  乌龟难过的哭了
  这时却碰到了一位猎人
  乌龟把这事告诉了他
  猎人哈哈大笑
  于是森林里发生了一件重大事情
  猎人披着狮子皮和乌龟一起在吃兔子火锅
  地下丢了半张纸片歪歪扭扭的写着:山外青山楼外楼,强中还有强中手啊!!
  在很长一段时间里森林里恢复了往日的宁静,兔子吃狼的故事似乎快要被大家忘记了
  不过一只年轻的老虎在听说了这个故事后,被激发了灵感
  于是他抓住了一只羚羊,对羚羊说,如果你可以象以前的兔子那样为我带来食物那我
  就不吃你。
  于是,羚羊无奈的答应了老虎,而老虎也悠然自得的进了山洞。
  可是三天过去了,也没有见羚羊领一只动物进洞。他实在憋不住了,想出来看看情
  况。
  羚羊早已不在了,他异常愤怒。正在他暴跳如雷的时候突然发现了羚羊写的一篇文章
  题目是:《想要做好老板先要懂得怎样留住员工》

【转帖】老外对T-sql的研究:一个问题多种方法.太精彩了!

原文地址:
http://www.winmag.com.cn/forum/itemdisplay.asp?boardid=11&id=503269
感谢这篇文章的作者带我们如此棒的文章
T-SQL允许你使用不同的方法解决一个问题.有的时候,尽管选择不是那么明显,但是却可以让你得到令人满意的和快乐的惊奇.下边让我们解读Dr. Tom Moreau对同一问题不同的可能性的探索.可能我们可以在那些不同的方法之中发现一些珍贵的东西.
让我们以我们的老朋友Northwind数据库为例,这里我们用到的是[order details]表,这个表是一个定单的明细表,和order表是多对一的关系.也就是一个定单对应多个订购的产品.假设你想得到每个定单订购的总价值,但是不包括59号产品.Listing 1给了我们第一种解法:
select
OrderID,sum (Quantity * UnitPrice) value
from
[Order Details] o1
where
ProductID <> 59
group by
OrderID
上边的语句很简单,它排除掉了59号产品的定单明细条目,然后进行分组统计.但是如果我们需要忽略掉订购59号产品的定单呢?也就是说我们要统计没有包含59号产品的定单的价值.你想到了WHERE, NOT EXIST(S)关键词了吗?Listing 2给了我们第二种方法:
select
o1.OrderID,sum (o1.Quantity * o1.UnitPrice) value
from
[Order Details] o1
where not exists
(
select
*
from
[Order Details] o2
where
o2.OrderID = o1.OrderID
and o2.ProductID = 59
)
group by
o1.OrderID
如果你不喜欢用exist的话,你可以转化成使用not in:
Listing 3
select
o1.OrderID,sum (o1.Quantity * o1.UnitPrice) value
from
[Order Details] o1
where 59 not in
(
select
ProductID
from
[Order Details] o2
where
o2.OrderID = o1.OrderID
)
group by
o1.OrderID
尽管Listing 1不满足我们现在的查询条件.但是从性能发面考虑,Listing 1还是最好的,因为它只用到了一次表的扫描.而后边的两个查询都是用到了相关子查询,如果你查看查询计划就回看到,他们都涉及到了两次表的扫描.如果你曾经在 T-SQL用过交叉表查询的话,你就不会对聚集函数里边的case结构陌生.现在我们就把这个非常有趣的方法应用到我们的问题中来:
Listing 4
select
OrderID,sum (Quantity * UnitPrice) value
from
[Order Details] o1
group by
OrderID
having
sum (case when ProductID = 59 then 1 else 0 end) = 0
HAVING子句起到了对分组的结果进行过滤的作用.如果没有包含59号产品,就会出现0=0,显然这是满足条件的.如果包含了59号产品的订购,就会出现n=0(n<>0),这样的定单就回被过滤掉.查看执行计划你就回发现是一次表的扫描,非常棒!
再来举一个例子:我们这回用到的表是order表,假设我们要统计只通过一个雇员雇员下定单的顾客.你可以想到用子查询not exist来实现:
select distinct
o1.CustomerID
from
Orders o1
where not exists
(
select
*
from
Orders o2
where
o2.CustomerID = o1.CustomerID
and o2.EmployeeID <> o1.EmployeeID
)
同样的,这个语句可以通过带有HAVING子句的分组来实现.
Listing 6
select
CustomerID
from
Orders
group by
CustomerID
having
min (EmployeeID) = max (EmployeeID)
另一种方法:
Listing 7
select
CustomerID
from
Orders
group by
CustomerID
having
count (distinct EmployeeID) = 1
Listing 6和Listing 7查询消耗都要小于Listing 5.相比Listing 5的两次表扫描,他们只进行一次表的扫描.而Listing 6的损耗还要稍微小于Listing 7.但是,Listing 7的一个显著的特点就是它可以适应到一个顾客对应两个雇员,三个雇员......
其实大家可能现在明白了这篇文章将的是什么?它就是教我们怎么用having 子句来达到过滤组的目的.可以达到避免两次表扫描的目的.可以达到更高的性能.我从这篇文章学到了很多的方法,你呢?

2008年5月28日星期三

[转]巧记 connect string

作者: DRL mikewolf

使用ADO的时候会需要创建ADOConnect,用来链接数据库
而创建一个Connect,必须要配置相关的参数
比如说:
Provide,server,database,uid/pwd...
不同以上参数不同,链接字符串不尽相同
比如说
[sqlserver]
Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=hrms;Data Source=(local)
[oracle]
Provider=MSDAORA.1;Password=fff;User ID=fff;Data Source=orcalserver;Persist Security Info=True
虽然对于一个应用,数据库连接部分写一次就可以了,也不同太记
但有时如果手边没有资料,也无法上google,就很难写对了
这里提供一个非常简单的,获得connect string的方法
1、创建一个txt
2、将扩展名改为.udl
3、双击这个改名后的文件,会出现一个连接配置向导
4、选择provide,以及其他参数
5、测试连接成功
6、确认
用notepad 打开这个文件
这时候就可以看到自动生成的connect string了。

2008年5月27日星期二

得到刚刚插入的记录的自动编号值一例

对于access和Sql server 数据库,下面方法可以得到刚刚插入记录的自动编号值。

'得到刚刚插入的记录的自动编号值

dim conn,rs
set conn=Server.CreateObject("ADODB.Connection")
conn.Open "Provider=Microsoft.Jet.OLEDB.4.0;Jet OLEDB:Database Password=;Data Source=" & Server.MapPath("51windows.mdb")
set rs=Server.CreateObject("ADODB.Recordset")
sql="select * from [table] where id is null" '自动编号字段为id
rs.open sql,conn,1,3
rs.addnew
rs("Name") = Request.form("Name")
rs("Name2") = Request.form("Name2")
rs.update
dim NewDbid
NewDbid = rs("id") '得刚刚插入记录的自动编号的值
rs.close
set rs = nothing
conn.close
set conn = nothing

更多方法和其它数据库请参考:How do I get the ID number of a just-inserted record

2008年5月26日星期一

[转]Java集合框架使用方法

前言:
    本文是对Java集合框架做了一个概括性的解说,目的是对Java集合框架体系有个总体认识,如果你想学习具体的接口和类的使用方法,请参看Java API文档。

    一、概述
    数据结构对程序设计有着深远的影响,在面向过程的C语言中,数据库结构用struct来描述,而在面向对象的编程中,数据结构是用类来描述的,并且包含有对该数据结构操作的方法。
    在Java语言中,Java语言的设计者对常用的数据结构和算法做了一些规范(接口)和实现(具体实现接口的类)。所有抽象出来的数据结构和操作(算法)统称为Java集合框架(Java Collection Framework)。
    Java程序员在具体应用时,不必考虑数据结构和算法实现细节,只需要用这些类创建出来一些对象,然后直接应用就可以了。这样就大大提高了编程效率。

    二、集合框架的层次结构
    Collection是集合接口
    |————Set子接口:无序,不允许重复。
    |————List子接口:有序,可以有重复元素。

    区别:Collections是集合类

    Set和List对比:
    Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。
    List:和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变。

    Set和List具体子类:
    Set
     |————HashSet:以哈希表的形式存放元素,插入删除速度很快。

    List
     |————ArrayList:动态数组
     |————LinkedList:链表、队列、堆栈。

    Array和java.util.Vector
    Vector是一种老的动态数组,是线程同步的,效率很低,一般不赞成使用。

    三、Iterator迭代器(接口)
    Iterator是获取集合中元素的过程,实际上帮助获取集合中的元素。
    迭代器代替了 Java Collections Framework 中的 Enumeration。迭代器与枚举有两点不同:
    迭代器允许调用方利用定义良好的语义在迭代期间从迭代器所指向的集合移除元素。
    方法名称得到了改进。

    Iterator仅有一个子接口ListIterator,是列表迭代器,允许程序员按任一方向遍历列表、迭代期间修改列表,并获得迭代器在列表中的当前位置。ListIterator 没有当前元素;它的光标位置 始终位于调用 previous() 所返回的元素和调用 next() 所返回的元素之间。在长度为 n 的列表中,有 n+1 个有效的索引值,从 0 到 n(包含)。

    四、集合框架之外的Map接口
    Map将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射一个值。
    Map接口是Dictionary(字典)抽象类的替代品。
    Map 接口提供三种collection 视图,允许以键集、值集合或键-值映射关系集的形式查看某个映射的内容。映射的顺序 定义为迭代器在映射的 collection 视图中返回其元素的顺序。某些映射实现可明确保证其顺序,如 TreeMap 类;某些映射实现则不保证顺序,如 HashMap 类。

    有两个常见的已实现的子类:
    HashMap:基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。(除了不同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。)此类不保证映射的顺序,特别是它不保证该顺序恒久不变。

    TreeMap:它实现SortedMap 接口的基于红黑树的实现。此类保证了映射按照升序顺序排列关键字,根据使用的构造方法不同,可能会按照键的类的自然顺序 进行排序(参见 Comparable),或者按照创建时所提供的比较器进行排序。

    Hashtable:此类实现一个哈希表,该哈希表将键映射到相应的值。任何非 null 对象都可以用作键或值。

    五、线程安全
    在集合框架中,有些类是线程安全的,这些都是JDK1.1中的出现的。在JDK1.2之后,就出现许许多多非线程安全的类。
    下面是这些线程安全的同步的类:
    Vector:就比ArrayList多了个同步化机制(线程安全)。
    Statck:堆栈类,先进后出。
    Hashtable:就比HashMap多了个线程安全。
    Enumeration:枚举,相当于迭代器。
    除了这些之外,其他的都是非线程安全的类和接口。
    线程安全的类其方法是同步的,每次只能一个访问。是重量级对象,效率较低。对于非线程安全的类和接口,在多线程中需要程序员自己处理线程安全问题。

    六、其他一些接口和类介绍
    Dictionary和Hashtable类:
    Dictionary提供键值映射的功能,是个抽象类。一般使用它的子类HashTable类。遍历Hashtable类要用到枚举。

    Properties类
    Properties 继承于 Hashtable,Properties 类表示了一个持久的属性集。Properties 可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。一般可以通过读取properties配置文件来填充Properties对象。