Skip to content

java oom hprof文件生成时机

Posted on:September 22, 2023 at 01:18 PM

背景

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文件,再抛异常

相关阅读