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方法一般不需要调用。