背景
1 有次排查oom问题,发现没有对应的目录,oom后会不生成hprof的dump文件 2 oom后被try catch 后依然可以生成dump的prof文件,所以不是在退出生成hprof文件的,而是在生成这个异常的时候生成dump的hprof文件的
代码
java -Xms50m -Xmx50m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/admin/logs/jvmlogs/java.hprof Main.java
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Object> tem = new ArrayList<>();
boolean test = true;
try {
while (test) {
tem.add(new int[10000000]);
}
}catch (Throwable table){
System.out.println("oom test");
}
System.out.println("afasdfadsf");
}
}
堆栈
Thread 2 "java" hit Breakpoint 1, HeapDumper::dump (this=this@entry=0x7ffff7bfe090, path=path@entry=0x7ffff0803c20 "/home/ubuntu/fasdfd/fadf", out=0x7ffff0000b80, compression=0, overwrite=overwrite@entry=false, num_dump_threads=num_dump_threads@entry=1) at /home/ubuntu/jdk/src/hotspot/share/services/heapDumper.cpp:2383
2383 int HeapDumper::dump(const char* path, outputStream* out, int compression, bool overwrite, uint num_dump_threads) {
(gdb) bt
#0 HeapDumper::dump (this=this@entry=0x7ffff7bfe090, path=path@entry=0x7ffff0803c20 "/home/ubuntu/fasdfd/fadf", out=0x7ffff0000b80, compression=0, overwrite=overwrite@entry=false, num_dump_threads=num_dump_threads@entry=1)
at /home/ubuntu/jdk/src/hotspot/share/services/heapDumper.cpp:2383
#1 0x00007ffff65473a8 in HeapDumper::dump_heap (oome=oome@entry=true) at /home/ubuntu/jdk/src/hotspot/share/services/heapDumper.cpp:2573
#2 0x00007ffff654750e in HeapDumper::dump_heap_from_oome () at /home/ubuntu/jdk/src/hotspot/share/services/heapDumper.cpp:2487
#3 0x00007ffff61e9c78 in report_java_out_of_memory (message=message@entry=0x7ffff75a0d5e "Java heap space") at /home/ubuntu/jdk/src/hotspot/share/utilities/debug.cpp:356
#4 0x00007ffff6c3760d in MemAllocator::Allocation::check_out_of_memory (this=this@entry=0x7ffff7bfe1b0) at /home/ubuntu/jdk/src/hotspot/share/gc/shared/memAllocator.cpp:126
#5 0x00007ffff6c3aac6 in MemAllocator::Allocation::~Allocation (this=0x7ffff7bfe1b0, __in_chrg=<optimized out>) at /home/ubuntu/jdk/src/hotspot/share/gc/shared/memAllocator.cpp:83
#6 MemAllocator::allocate (this=this@entry=0x7ffff7bfe280) at /home/ubuntu/jdk/src/hotspot/share/gc/shared/memAllocator.cpp:375
#7 0x00007ffff72658e7 in CollectedHeap::array_allocate (__the_thread__=0x7ffff0029850, do_zero=true, length=10000000, size=<optimized out>, klass=0x100040d90, this=<optimized out>) at /home/ubuntu/jdk/src/hotspot/share/gc/shared/collectedHeap.inline.hpp:41
#8 TypeArrayKlass::allocate_common (this=this@entry=0x100040d90, length=length@entry=10000000, do_zero=do_zero@entry=true, __the_thread__=__the_thread__@entry=0x7ffff0029850) at /home/ubuntu/jdk/src/hotspot/share/oops/typeArrayKlass.cpp:93
#9 0x00007ffff6d861d9 in TypeArrayKlass::allocate (__the_thread__=0x7ffff0029850, length=10000000, this=<optimized out>) at /home/ubuntu/jdk/src/hotspot/share/oops/typeArrayKlass.hpp:68
#10 oopFactory::new_typeArray (type=type@entry=T_INT, length=length@entry=10000000, __the_thread__=__the_thread__@entry=0x7ffff0029850) at /home/ubuntu/jdk/src/hotspot/share/memory/oopFactory.cpp:93
#11 0x00007ffff662b51a in InterpreterRuntime::newarray (current=0x7ffff0029850, type=T_INT, size=10000000) at /home/ubuntu/jdk/src/hotspot/share/interpreter/interpreterRuntime.cpp:248
#12 0x00007fffe856be1a in ?? ()
#13 0x00007fffe856bd91 in ?? ()
#14 0x00000000fcf98230 in ?? ()
#15 0x00007ffff7bfe3e0 in ?? ()
#16 0x00007fffc9014349 in ?? ()
#17 0x00007ffff7bfe450 in ?? ()
#18 0x00007fffc9014408 in ?? ()
#19 0x0000000000000000 in ?? ()
核心函数是
void report_java_out_of_memory(const char* message) {
static int out_of_memory_reported = 0;
if (Atomic::cmpxchg(&out_of_memory_reported, 0, 1) == 0) {
// create heap dump before OnOutOfMemoryError commands are executed
if (HeapDumpOnOutOfMemoryError) {
tty->print_cr("java.lang.OutOfMemoryError: %s", message);
HeapDumper::dump_heap_from_oome(); // 生成hprof 文件
}
if (OnOutOfMemoryError && OnOutOfMemoryError[0]) {
VMError::report_java_out_of_memory(message); // 生成错误信息
}
if (CrashOnOutOfMemoryError) {
tty->print_cr("Aborting due to java.lang.OutOfMemoryError: %s", message);
report_fatal(OOM_JAVA_HEAP_FATAL, __FILE__, __LINE__, "OutOfMemory encountered: %s", message); // catch 导致的
}
if (ExitOnOutOfMemoryError) {
tty->print_cr("Terminating due to java.lang.OutOfMemoryError: %s", message);
os::_exit(3); // quick exit with no cleanup hooks run
}
}
}
如何打开dump文件的
char const* FileWriter::open_writer() {
assert(_fd < 0, "Must not already be open");
_fd = os::create_binary_file(_path, _overwrite);
if (_fd < 0) {
return os::strerror(errno);
}
return NULL;
}
最后调用的是linux 的库函数open64
// jdk/src/hotspot/os/linux/os_linux.cpp
// create binary file, rewriting existing file if required
int os::create_binary_file(const char* path, bool rewrite_existing) {
int oflags = O_WRONLY | O_CREAT;
oflags |= rewrite_existing ? O_TRUNC : O_EXCL;
return ::open64(path, oflags, S_IREAD | S_IWRITE);
}
dump的目录一定要存在,不存在也不会检查
生成hprof文件和exception的时机
bool MemAllocator::Allocation::check_out_of_memory() {
JavaThread* THREAD = _thread; // For exception macros.
assert(!HAS_PENDING_EXCEPTION, "Unexpected exception, will result in uninitialized storage");
if (obj() != NULL) {
return false;
}
const char* message = _overhead_limit_exceeded ? "GC overhead limit exceeded" : "Java heap space";
if (!_thread->in_retryable_allocation()) {
// -XX:+HeapDumpOnOutOfMemoryError and -XX:OnOutOfMemoryError support
report_java_out_of_memory(message); ////////////// 生成hprof 文件 , 里面就是上面的一对内容
if (JvmtiExport::should_post_resource_exhausted()) {
JvmtiExport::post_resource_exhausted(
JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_JAVA_HEAP,
message);
}
oop exception = _overhead_limit_exceeded ?
Universe::out_of_memory_error_gc_overhead_limit() : // gc 超过limit 导致的oom的异常
Universe::out_of_memory_error_java_heap(); // 我们平常说的堆内存不足导致oom
THROW_OOP_(exception, true);
} else {
THROW_OOP_(Universe::out_of_memory_error_retry(), true);
}
}
所以是先生成dump文件,再抛异常