记录完整堆栈跟踪性能问题

时间:2015-01-25 07:15:40

标签: java logging apache-commons

我正在研究Apache commons库中ExceptionUtils getFullStackTrace的代码,它如下:

public static String getFullStackTrace(Throwable throwable) {
            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw, true);
            Throwable[] ts = getThrowables(throwable);
            for (int i = 0; i < ts.length; i++) {
                ts[i].printStackTrace(pw);
                if (isNestedThrowable(ts[i])) {
                    break;
                }
           }
            return sw.getBuffer().toString();
        }

我注意到它没有关闭PrintWriter并只进行自动刷新。

不关闭PrintWriter会导致内存泄漏吗?

我使用FileWriter登录文件,发生了所有异常,我想使用上面的方法记录完整的堆栈跟踪。

但后来我用以下内容替换它,因为我怀疑它可能会造成内存泄漏:

public static String stackTraceToString(Throwable e) throws Exception {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw,true);
        e.printStackTrace(pw);
        StringBuilder sb = new StringBuilder();
        sb.append(sw.toString());
        sw.flush();
        sw.close();
        pw.close();
        return sb.toString();
    }

您如何看待这两种方法的CPU和内存性能,哪种方法表现更好?

编辑:自定义记录器代码

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;
import javax.faces.context.FacesContext;


public class AppLogger
{
    public static String MESSAGE_TYPE_ERROR = "ERROR";
    public static String MESSAGE_TYPE_INFO = "INFO";
    public static boolean DISABLE_LOG = false;

    private static final String _logFilePath = FacesContext.getCurrentInstance().getExternalContext().getInitParameter("LogFilePath");
    private static FileWriter _fw;
    private AppLogger() 
    {
    }


    public static String stackTraceToString(Throwable e) throws Exception {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw,true);
        e.printStackTrace(pw);
        StringBuilder sb = new StringBuilder();
        sb.append(sw.toString());
        sw.flush();
        sw.close();
        pw.close();
        return sb.toString();
    }



    public static void Log(Exception e)
    {
        if(DISABLE_LOG)
            return;
        try
        {             
            _fw = new FileWriter(_logFilePath + getFileName(), true);
            _fw.write(formatMessage(stackTraceToString(e), AppLogger.MESSAGE_TYPE_ERROR,null));
            _fw.close();
        }
        catch(IOException ex)
        {
            ex.printStackTrace();
        }
        catch(Exception ex)
        {
            ex.printStackTrace();
        }
        finally
        {
            releaseResources(); 
        }
    }

    private static void releaseResources()
    {
        if(_fw != null)
        {
            try
            {
                _fw.close();
                _fw = null;
            }
            catch(Exception e)
            {
               e.printStackTrace(); 
            }
        }
    }


    private static String getFileName()
    {
        GregorianCalendar gc = new GregorianCalendar();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        StringBuilder sb = new StringBuilder();
        sb.append("logFile.").append(sdf.format(gc.getTime())).append(".log");  
        return sb.toString();
    }

    private static String formatMessage(String message, String messageType,String className)
    {
        GregorianCalendar gc = new GregorianCalendar();
        SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss a");
        StringBuilder sb = new StringBuilder();
        if(className != null)
          sb.append(sdf.format(gc.getTime())).append(" ").append(messageType).append(" ").append(className).append(" : ").append(message).append("\n");
        else
          sb.append(sdf.format(gc.getTime())).append(" ").append(messageType).append(" ").append(" : ").append(message).append("\n");
        return sb.toString();
    }
}

2 个答案:

答案 0 :(得分:1)

在这种情况下,它不会。以下是PrintWriter.close的代码 - 正如您所看到的,它所做的只是关闭它包装的Writer(out),还有一些额外的同步和#34;错误处理&#34;:

public void close() {
    try {
        synchronized (lock) {
            if (out == null)
                return;
            out.close();
            out = null;
        }
    }
    catch (IOException x) {
        trouble = true;
    }
}

在这种情况下,它会关闭StringWriter。以下是StringWriter.close的代码:

/**
 * Closing a <tt>StringWriter</tt> has no effect. The methods in this
 * class can be called after the stream has been closed without generating
 * an <tt>IOException</tt>.
 */
public void close() throws IOException {
}

正如您所看到的,它完全没有 - 这在文档中有详细说明,而不仅仅是实现细节。

关闭流对于文件之类的东西很重要(在这种情况下它会阻止其他进程写入文件),网络连接(在这种情况下它会断开连接),以及其他几种类型的流和写入器;但是,StringWriter(以及等效的流ByteArrayOutputStream和输入等效的StringReaderByteArrayInputStream)并不重要。

答案 1 :(得分:1)

  

不会关闭PrintWriter会造成内存问题吗?

简短回答:不。

更长的答案:

建议关闭读者,编写者,输入和输出流......以及类似套接字等类似事物的原因是这些对象是由操作系统管理的有限资源的包装器。如果应用程序未显式关闭它们或使用try-with-resource,则存在资源(池)将耗尽的风险。这可能导致操作系统拒绝打开文件,套接字,数据库连接等。

但是对于StringWriter和类似的,没有这样的OS资源。该对象仅仅是在堆内缓冲区中捕获数据。该缓冲区将由垃圾收集器回收...假设它不再被引用。关闭它没有任何区别。

(@ immibis对代码的阐述证实了这一点......)


  

但后来我用以下内容替换它,因为我怀疑它可能会造成内存泄漏

不会。但是......

  

您如何看待这两种方法的CPU和内存性能,哪种方法表现更好?

较慢版本所需的额外时间可能非常小,以至于没有实际意义。 (但如果你真的,真的想知道......替补它。)