Java的资源管理

Overview

Java程序中的常见的资源有:文件,Socket,数据库连接。在使用这些资源时候要分外小心,因为操作系统可同时操作的资源是有限的,比如默认情况下系统允许同时打开的文件数为1024个,Mysql服务器默认允许的最大连接数是100,所以操作这些资源时候要注意即使在遇到错误时也要让系统能正确回收资源。如果发生错误时候,打开的文件描述符没关闭或数据库连接没关闭,积累到一定程度后,应用将会变得不可用,只能重启。

try-catch-finally

Java提供了try-catch-finally来保证程序遇到异常时总是有机会可以处理资源的关闭 – 调用资源对象的close()方法。但经验不足的Java程序员还是会错误的管理资源,而造成资源的泄露,静态代码分析工具,如FindBugs可以帮助发现此类问题。

首先我们来看一段文件操作代码:

private void copy(String from, String to) throws IOException {
    FileInputStream in = null;  
    FileOutputStream out = null;  
    in = new FileInputStream(from);  
    out = new FileOutputStream(to);  
    int c;  
    while ((c = in.read()) != -1)
        out.write(c);  
    in.close();
    out.close();
}

一眼看上去,代码挺整齐的,逻辑也容易理解。但其中有一个很大的问题是,如果out.write调用失败(比如磁盘空间满了)方法异常退出,in.close()和out.close()就不会被调用,而in和out对象内部都引用了系统资源-文件描述符,这样会导致文件描述符没有关闭,不能被重新使用而直到整个Java进程退出。

File descriptor

Linux的每个进程(如:Java进程)都有一个文件描述符表管理当前进程访问的所有的文件,文件描述符关联了系统文件表中的file entry,系统能容纳多少file entry是有限制的,如果超过限制系统会拒绝访问,抛出Too many opened files错误。

较为正确的代码应该是:

private void copy(String src, String dest) throws IOException {
    FileInputStream in = null;  
    FileOutputStream out = null;  
    try {
        in = new FileInputStream(src);  
        out = new FileOutputStream(dest);  
        int c;  
        while ((c = in.read()) != -1)
            out.write(c);
    } finally {
         try {
             if (in!=null) {
                in.close();
             }
         } finally {
             if (out!=null) {
                out.close();
             }
         }
    }
}

但是这样的代码写起来是不是让人有点沮丧?这样写代码犯错的可能性确实比较大。 改良过后的代码阅读性好一些:

[Read More]

Java程序的日志

Overview

一个在生产环境里运行的程序如果没有日志是很让维护者提心吊胆的,有太多杂乱又无意义的日志也是令人伤神。程序出现问题时候,从日志里如果发现不了问题可能的原因是很令人受挫的。本文想讨论的是如何在Java程序里写好日志。大多数的Web服务器(如Apache,Nginx)都有access日志和error日志,分别记录在不同的文件内;我们使用的服务器操作系统Linux有Syslog日志, /var/log目录下也有很多基础应用和服务的日志文件;桌面Windows有事件查看器, Mac有Console应用可以查看和管理日志;这些成熟的系统及工具方法都值得我们学习并在自己的项目中应用。

一般来说日志分为两种:业务日志和异常日志。使用日志我们希望能达到以下目标:

  1. 对程序运行情况的记录和监控;
  2. 在必要时可详细了解程序内部的运行状态;
  3. 对系统性能的影响尽量小;

日志规范

程序框架应该提供统一的日志记录接口,日志格式也需要有一定的规范,方便利用日志工具来分析日志。 首先我们有必要了解一下Linux普遍使用的Syslog标准协议,协议规定日志中应包含产生日志的模块(Facility),严重性(Severity Level),时间,主机名或IP,进程名,进程ID和日志内容,根据模块和严重性可以配置相应的动作:是否需要记录,日志存储路径(文件或网络)。

下面是部分常见的Syslog模块类型:

模块ID关键词描述
0kern内核消息
1user用户级别消息
2mail邮件系统
3daemon系统后台守护程序
4auth安全/鉴权消息
5syslogsyslogd内部产生的日志消息

以及Syslog严重程度划分:

代码严重程度关键词描述
0Emergencyemerg(panic)紧急,系统已经不稳定了
1Alertalert需要立刻采取措施
2Criticalcrit严重情况
3Errorerr (error)系统出错
4Warningwarning(warn)系统警告
5Noticenotice系统仍然正常,但值得注意
6Informationalinfo正常系统通告
7Debugdebug系统调试信息

在你的Java程序里日志也可以参考Syslog的设计,根据业务对程序的模块和日志级别做一定的规划和设计。

[Read More]