stacktraces
我们公司致力于使软件错误的原因对开发人员和运营透明。 与替代解决方案相反, 我们在您发现源代码中恶意代码的地方指出了问题的所在 。 即使目前我们以检测内存泄漏的能力而闻名,但我们也正在扩展到其他领域。 为了给您一些有关我们研究方向的提示,我们决定通过三个示例进行分享。
这些示例归结为JVM功能,以提供有意义的堆栈跟踪。 在许多情况下,stacktrace确实包含解决问题所需的所有信息。 在其他情况下,它只是浮出水面,不知道可能是什么引起了潜在的问题。 让我用触发以下臭名昭著的错误消息的三个示例进行说明:
- java.lang.OutOfMemoryError:无法创建新的本机线程
- java.io.IOException:系统中打开的文件过多
- java.lang.OutOfMemoryError:Java堆空间
所有示例都使用简单的代码片段进行了说明,从而使潜在问题更易于理解。
线程太多
static void createTooManyThreads() {
try {
for (int i = 0; i < TOO_MANY; i++) {
Thread t = new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
}
} catch (OutOfMemoryError e) {
e.printStackTrace();
}
}
在上面的代码中,我们一直在启动线程,直到达到系统限制并遇到“ java.lang.OutOfMemoryError:无法创建新的本机线程”消息。 理解问题与线程限制耗尽有关,这有什么问题? 让我们仔细看看stacktrace:
java.lang.OutOfMemoryError: unable to create new native thread
at java.lang.Thread.start0(Native Method)
at java.lang.Thread.start(Thread.java:693)
at eu.plumbr.demo.MisleadingStacktrace.createTooManyThreads(MisleadingStacktrace.java:34)
at eu.plumbr.demo.MisleadingStacktrace.main(MisleadingStacktrace.java:16)
问题就一直盯着我们的脸-有人告诉我们添加最后一根稻草将骆驼背折断了。 同时,我们没有一个线索是,骆驼已经被完全装载了。 如果错误消息还包含一种方法,以查看在我们最后一次尝试启动最后一个调用导致上述堆栈跟踪之前,哪个调用堆栈正在消耗线程的不同跟踪,那么这将使开发人员的工作更加轻松。
但是让我们看一下相同的问题–从另一个角度来看资源消耗:
打开的文件太多
再次,让我们从示例代码开始:
static void createTooManyFiles() {
try {
for (int i = 0; i < TOO_MANY; i++) {
File f = new File(PATH_TO_FILE + i + ".txt");
f.createNewFile();
OutputStream s = new FileOutputStream(f);
s.write(1);
}
} catch (IOException e) {
e.printStackTrace();
}
}
该示例尝试创建许多文件,并且仅在每个文件中写入一个整数而不关闭前一个文件。 再次,运行上面的代码会导致堆栈跟踪不太有用:
java.io.IOException: Too many open files in system
at java.io.UnixFileSystem.createFileExclusively(Native Method)
at java.io.File.createNewFile(File.java:947)
at eu.plumbr.demo.MisleadingStacktrace.createTooManyFiles(MisleadingStacktrace.java:45)
at eu.plumbr.demo.MisleadingStacktrace.main(MisleadingStacktrace.java:17)
现在,以不同的方式掩盖了相同的问题-我们确实得到了这样的消息:我现在尝试过多地打开一个文件,但是-谁打开了其他文件以在无法完成运行的程度上向JVM施加压力?
如果您仍然不确定,请看第三个示例,即我们目前的面包和黄油:
消耗太多内存
代码示例再次很简单–我们采用数据结构并不断增加结构,直到可用堆耗尽为止:
static void createTooLargeDataStructure() {
try {
List l = new ArrayList();
for (int i = 0; i < TOO_MANY; i++) {
l.add(i);
}
} catch (OutOfMemoryError e) {
e.printStackTrace();
}
}
运行代码会给您带来臭名昭著的java.lang.OutOfMemoryError:Java堆空间消息。 同样,如果所讨论的数据结构是通过源代码中的不同可能位置填充的,则很难解释。
我知道所有C语言开发人员现在都无奈地耸了耸肩,因为在他们的世界中,以上所有内容都只是采取段错误的形式,但是–如果我们走得那么远,为什么我们不能做得更好? 我相信我们可以做到,这就是我们要做的– 找到适合您的性能瓶颈 。
如果您对完整的代码示例感兴趣,可以在此处下载。 该代码在JDK 7u25上的OS X 10.9的Macbook Pro上运行。 之后,请确保您不会错过未来有趣内容的更新,并订阅我们的Twitter feed 。
翻译自: https://www.javacodegeeks.com/2013/11/stacktraces-are-telling-the-truth-but-not-the-whole-truth.html
stacktraces