e.printStackTrace() 原理的分析
e.printStackTrace();
先查看下源码
如图片中1所示,使用的是 PrintStreamOrWriter
public void printStackTrace() { printStackTrace(System.err); } /** * Prints this throwable and its backtrace to the specified print stream. * * @param s {@code PrintStream} to use for output */ public void printStackTrace(PrintStream s) { printStackTrace(new WrappedPrintStream(s)); } private void printStackTrace(PrintStreamOrWriter s) { // Guard against malicious overrides of Throwable.equals by // using a Set with identity equality semantics. Set<Throwable> dejaVu = Collections.newSetFromMap(new IdentityHashMap<Throwable, Boolean>()); dejaVu.add(this); synchronized (s.lock()) { // Print our stack trace s.println(this); StackTraceElement[] trace = getOurStackTrace(); for (StackTraceElement traceElement : trace) s.println("\tat " + traceElement); // Print suppressed exceptions, if any for (Throwable se : getSuppressed()) se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION, "\t", dejaVu); // Print cause, if any Throwable ourCause = getCause(); if (ourCause != null) ourCause.printEnclosedStackTrace(s, trace, CAUSE_CAPTION, "", dejaVu); } }
,而这来源于 PrintStream,而 PrintStream 又继承 FilterOutputStream ,是文件输出流,会肯定会影响内存的变动
ublic class PrintStream extends FilterOutputStream implements Appendable, Closeable { private final boolean autoFlush; private boolean trouble = false; private Formatter formatter; /** * Track both the text- and character-output streams, so that their buffers * can be flushed without flushing the entire stream. */ private BufferedWriter textOut; private OutputStreamWriter charOut; /** * requireNonNull is explicitly declared here so as not to create an extra * dependency on java.util.Objects.requireNonNull. PrintStream is loaded * early during system initialization. */ private static <T> T requireNonNull(T obj, String message) { if (obj == null) throw new NullPointerException(message); return obj; } }
而图片中二所示的lock锁,锁住这个流对象,就是占用住了内存不让进行gc回收,先输出打印,
跟踪s.println(this) 也就是PrintStream的方法,发现是会使用bufferedwriter和outputstream
/** * Prints an Object and then terminate the line. This method calls * at first String.valueOf(x) to get the printed object's string value, * then behaves as * though it invokes <code>{@link #print(String)}</code> and then * <code>{@link #println()}</code>. * * @param x The <code>Object</code> to be printed. */ public void println(Object x) { String s = String.valueOf(x); synchronized (this) { print(s); newLine(); } }
/**
* Prints a string. If the argument is <code>null</code> then the string
* <code>"null"</code> is printed. Otherwise, the string's characters are
* converted into bytes according to the platform's default character
* encoding, and these bytes are written in exactly the manner of the
* <code>{@link #write(int)}</code> method.
*
* @param s The <code>String</code> to be printed
*/
public void print(String s) {
if (s == null) {
s = "null";
}
write(s);
}
private void write(String s) {
try {
synchronized (this) {
ensureOpen();
textOut.write(s);
textOut.flushBuffer();
charOut.flushBuffer();
if (autoFlush && (s.indexOf('\n') >= 0))
out.flush();
}
}
catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
}
catch (IOException x) {
trouble = true;
}
}
后又获取 StackTraceElement ,也就是获取方法调用者的具体信息 的。 循环打印
然后又获取Throwable报错的堆栈跟踪异常数据 的数组, 循环打印。
特意说下,在 printEnclosedStackTrace的方法中使用
assert Thread.holdsLock(s.lock());
这个是断言,判断当前线程是是否获得当前流的锁,判断是否成功。
专业一点的词叫:为了确保该代码所在的线程正持有所在类的对象作为锁。
意思执行的过程中,这个流对象只能被当前流锁占用不能被切换到其它线程。如果报错多的话,那么这些线程都会在统计这些报错信息,而不能去执行其它东西了
来源
java中e.printStackTrace()不要使用,请使用logger记录
Thread.holdsLock(Object)方法、assert断言、宏