Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions src/hotspot/share/runtime/globals.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,38 @@ const int ObjectAlignmentInBytes = 8;
"compression. Otherwise the level must be between 1 and 9.") \
range(0, 9) \
\
/* SapMachine 2026-05-06: Allow to overwrite the heap dump file. */ \
product(bool, HeapDumpOverwrite, false, MANAGEABLE, \
"If enabled, the heap dump on out of memory error can " \
"overwrite an already existing file.") \
\
/* SapMachine 2026-05-06: Sets the parallelism of the heap dump. */ \
product(uint, HeapDumpParallelism, 0, MANAGEABLE, \
"Sets the parallelism of the heap dump creation. 0 means to let "\
"the VM decide.") \
\
/* SapMachine 2026-05-06: Allow to skip content of arrays in dumps.*/ \
product(bool, LimitPrimitiveArrayContentInHeapDump, false, MANAGEABLE, \
"If enabled, the content of primitive arrays is not completely " \
"written to a heap dump for large arrays. Note that this only " \
"really saves space, if the compression of the heap dump is " \
"enabled too, since the skipped elements are written as " \
"0 or false.") \
\
/* SapMachine 2026-05-06: Allow to skip content of arrays in dumps.*/ \
product(int, StringLikeContentSizeLimitInHeapDump, 120, MANAGEABLE, \
"The number of entries in primitive char and byte arrays to " \
"not skip in a heap dump when " \
"LimitPrimitiveArrayContentInHeapDump is enabled.") \
range(0, 100000) \
\
/* SapMachine 2026-05-06: Allow to skip contents of arrays in dumps.*/ \
product(int, ArrayContentSizeLimitInHeapDump, 50, MANAGEABLE, \
"The number of entries in a primitive array other than char and " \
"byte arrays to not skip in a heap dump when " \
"LimitPrimitiveArrayContentInHeapDump is enabled.") \
range(0, 100000) \
\
Comment thread
schmelter-sap marked this conversation as resolved.
product(ccstr, NativeMemoryTracking, DEBUG_ONLY("summary") NOT_DEBUG("off"), \
"Native memory tracking options") \
\
Expand Down
77 changes: 73 additions & 4 deletions src/hotspot/share/services/heapDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,8 @@ class AbstractDumpWriter : public CHeapObj<mtInternal> {
void write_symbolID(Symbol* o);
void write_classID(Klass* k);
void write_id(u4 x);
// SapMachine 2026-05-06: Writes zeros to the buffer.
void write_zero(size_t len);

// Start a new sub-record. Starts a new heap dump segment if needed.
void start_sub_record(u1 tag, u4 len);
Expand Down Expand Up @@ -539,6 +541,26 @@ void AbstractDumpWriter::write_id(u4 x) {
#endif
}

// SapMachine 2026-05-06: Writes zeros to the buffer.
void AbstractDumpWriter::write_zero(size_t len) {
assert(!_in_dump_segment || (_sub_record_left >= len), "sub-record too large");
DEBUG_ONLY(_sub_record_left -= len);

// flush buffer to make room.
while (len > buffer_size() - position()) {
assert(!_in_dump_segment || _is_huge_sub_record,
"Cannot overflow in non-huge sub-record.");
size_t to_write = buffer_size() - position();
memset(buffer() + position(), 0, to_write);
len -= to_write;
set_position(position() + to_write);
flush();
}

memset(buffer() + position(), 0, len);
set_position(position() + len);
}

// We use java mirror as the class ID
void AbstractDumpWriter::write_classID(Klass* k) {
write_objectID(k->java_mirror());
Expand Down Expand Up @@ -1357,6 +1379,8 @@ void DumperSupport::dump_prim_array(AbstractDumpWriter* writer, typeArrayOop arr

int length = calculate_array_max_length(writer, array, header_size);
int type_size = type2aelembytes(type);
// SapMachine 2026-05-06
int fill_with_zero = 0;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could move that to the next hunk (after line 1399), so we do not need this lonely comment, right?

u4 length_in_bytes = (u4)length * type_size;
u4 size = header_size + length_in_bytes;

Expand All @@ -1372,6 +1396,21 @@ void DumperSupport::dump_prim_array(AbstractDumpWriter* writer, typeArrayOop arr
return;
}

// SapMachine 2026-05-06: If enabled, we don't dump the whole content of large arrays, but just the start.
if (LimitPrimitiveArrayContentInHeapDump) {
int limit = ArrayContentSizeLimitInHeapDump;

if (type == T_BYTE || type == T_CHAR) {
limit = StringLikeContentSizeLimitInHeapDump;
}

if (length > limit) {
fill_with_zero = length - limit;
length = limit;
length_in_bytes = (u4) length * type_size;
}
}

// If the byte ordering is big endian then we can copy most types directly

switch (type) {
Expand Down Expand Up @@ -1439,6 +1478,11 @@ void DumperSupport::dump_prim_array(AbstractDumpWriter* writer, typeArrayOop arr
default : ShouldNotReachHere();
}

// SapMachine 2026-05-06: Fill with zeros, if we don't dump the whole content of the array.
if (fill_with_zero > 0) {
writer->write_zero((u4) fill_with_zero * type_size);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be size_t like it is used in the declaration of write_zero ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The length in bytes in this function always uses u4. So for consistency with other code I kept it that way.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @MBaesken . This can overflow and the write_zero function takes size_t anyways. So why do this cast to u4 here?

}

writer->end_sub_record();
}

Expand Down Expand Up @@ -2200,6 +2244,11 @@ void DumpMerger::merge_file(const char* path) {
#endif

void DumpMerger::do_merge() {
// SapMachine 2026-05-06: No need to merge a non-parallel heap dump.
if (_dump_seq <= 1) {
return;
}

TraceTime timer("Merge heap files complete", TRACETIME_LOG(Info, heapdump));

// Since contents in segmented heap file were already zipped, we don't need to zip
Expand Down Expand Up @@ -2475,8 +2524,11 @@ void VM_HeapDumper::work(uint worker_id) {

ResourceMark rm;
// share global compressor, local DumpWriter is not responsible for its life cycle
DumpWriter segment_writer(DumpMerger::get_writer_path(writer()->get_file_path(), dumper_id),
writer()->is_overwrite(), writer()->compressor());
// SapMachine 2026-05-06: Don't use segments if the dump is not parallel. This makes it
// possible to not use any disk space if dumping to a names pipe or a tty.
DumpWriter* parallel_writer = is_parallel_dump() ? new DumpWriter(DumpMerger::get_writer_path(writer()->get_file_path(), dumper_id),
writer()->is_overwrite(), writer()->compressor()) : nullptr;
DumpWriter& segment_writer = parallel_writer == nullptr ? *writer() : *parallel_writer;
if (!segment_writer.has_error()) {
if (is_vm_dumper(dumper_id)) {
// dump some non-heap subrecords to heap dump segment
Expand Down Expand Up @@ -2533,6 +2585,8 @@ void VM_HeapDumper::work(uint worker_id) {
// At this point, all fragments of the heapdump have been written to separate files.
// We need to merge them into a complete heapdump and write HPROF_HEAP_DUMP_END at that time.
}
// SapMachine 2026-05-06
delete parallel_writer;
}

void VM_HeapDumper::dump_stack_traces(AbstractDumpWriter* writer) {
Expand Down Expand Up @@ -2584,6 +2638,17 @@ void VM_HeapDumper::dump_vthread(oop vt, AbstractDumpWriter* segment_writer) {
ThreadDumper thread_dumper(ThreadDumper::ThreadType::UnmountedVirtual, nullptr, vt);
thread_dumper.init_serial_nums(&_thread_serial_num, &_frame_serial_num);

// SapMachine 2026-05-06: If we don't do a parallel dump, we don't need the lock
// but have to end the current heap dump segment.
if (!is_parallel_dump()) {
segment_writer->finish_dump_segment();
thread_dumper.dump_stack_traces(writer(), _klass_map);
thread_dumper.dump_thread_obj(segment_writer);
thread_dumper.dump_stack_refs(segment_writer);

return;
}

// write HPROF_TRACE/HPROF_FRAME records to global writer
_dumper_controller->lock_global_writer();
thread_dumper.dump_stack_traces(writer(), _klass_map);
Expand Down Expand Up @@ -2734,7 +2799,9 @@ void HeapDumper::set_error(char const* error) {
// outside of a JVM safepoint
void HeapDumper::dump_heap_from_oome() {
// SapMachine 2024-05-10: HeapDumpPath for jcmd
HeapDumper::dump_heap(false, true);
// SapMachine 2026-05-06: Handle HeapDumpOverwrite and HeapDumpParallelism.
HeapDumper::dump_heap(false, true, tty, -1, HeapDumpOverwrite, HeapDumpParallelism == 0 ?
HeapDumper::default_num_of_dump_threads(): HeapDumpParallelism);
}

// Called by error reporting by a single Java thread outside of a JVM safepoint,
Expand All @@ -2744,7 +2811,9 @@ void HeapDumper::dump_heap_from_oome() {
// inteference when updating the static variables base_path and dump_file_seq below.
void HeapDumper::dump_heap() {
// SapMachine 2024-05-10: HeapDumpPath for jcmd
HeapDumper::dump_heap(false, false);
// SapMachine 2026-05-06: Handle HeapDumpOverwrite and HeapDumpParallelism.
HeapDumper::dump_heap(false, false, tty, -1, HeapDumpOverwrite, HeapDumpParallelism == 0 ?
HeapDumper::default_num_of_dump_threads() : HeapDumpParallelism);
}

// SapMachine 2024-05-10: HeapDumpPath for jcmd
Expand Down
Loading