8046598: Scalable Native memory tracking development
Summary: Enhance scalability of native memory tracking
Reviewed-by: coleenp, ctornqvi, gtriantafill
--- a/hotspot/make/excludeSrc.make Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/make/excludeSrc.make Thu Aug 07 12:18:58 2014 -0700
@@ -119,8 +119,8 @@
CFLAGS += -DINCLUDE_NMT=0
Src_Files_EXCLUDE += \
- memBaseline.cpp memPtr.cpp memRecorder.cpp memReporter.cpp memSnapshot.cpp memTrackWorker.cpp \
- memTracker.cpp nmtDCmd.cpp
+ memBaseline.cpp memReporter.cpp mallocTracker.cpp virtualMemoryTracker.cpp nmtCommon.cpp \
+ memTracker.cpp nmtDCmd.cpp mallocSiteTable.cpp
endif
-include $(HS_ALT_MAKE)/excludeSrc.make
--- a/hotspot/src/os/bsd/vm/os_bsd.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/os/bsd/vm/os_bsd.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -2439,23 +2439,25 @@
}
// The memory is committed
- MemTracker::record_virtual_memory_reserve_and_commit((address)addr, bytes, mtNone, CALLER_PC);
+ MemTracker::record_virtual_memory_reserve_and_commit((address)addr, bytes, CALLER_PC);
return addr;
}
bool os::release_memory_special(char* base, size_t bytes) {
- MemTracker::Tracker tkr = MemTracker::get_virtual_memory_release_tracker();
- // detaching the SHM segment will also delete it, see reserve_memory_special()
- int rslt = shmdt(base);
- if (rslt == 0) {
- tkr.record((address)base, bytes);
- return true;
+ if (MemTracker::tracking_level() > NMT_minimal) {
+ Tracker tkr = MemTracker::get_virtual_memory_release_tracker();
+ // detaching the SHM segment will also delete it, see reserve_memory_special()
+ int rslt = shmdt(base);
+ if (rslt == 0) {
+ tkr.record((address)base, bytes);
+ return true;
+ } else {
+ return false;
+ }
} else {
- tkr.discard();
- return false;
+ return shmdt(base) == 0;
}
-
}
size_t os::large_page_size() {
--- a/hotspot/src/os/bsd/vm/perfMemory_bsd.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/os/bsd/vm/perfMemory_bsd.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -753,7 +753,7 @@
(void)::memset((void*) mapAddress, 0, size);
// it does not go through os api, the operation has to record from here
- MemTracker::record_virtual_memory_reserve((address)mapAddress, size, mtInternal, CURRENT_PC);
+ MemTracker::record_virtual_memory_reserve_and_commit((address)mapAddress, size, CURRENT_PC, mtInternal);
return mapAddress;
}
@@ -918,7 +918,7 @@
}
// it does not go through os api, the operation has to record from here
- MemTracker::record_virtual_memory_reserve((address)mapAddress, size, mtInternal, CURRENT_PC);
+ MemTracker::record_virtual_memory_reserve_and_commit((address)mapAddress, size, CURRENT_PC, mtInternal);
*addr = mapAddress;
*sizep = size;
--- a/hotspot/src/os/linux/vm/os_linux.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/os/linux/vm/os_linux.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -3504,9 +3504,12 @@
assert(is_ptr_aligned(start, alignment), "Must be");
- // os::reserve_memory_special will record this memory area.
- // Need to release it here to prevent overlapping reservations.
- MemTracker::record_virtual_memory_release((address)start, bytes);
+ if (MemTracker::tracking_level() > NMT_minimal) {
+ // os::reserve_memory_special will record this memory area.
+ // Need to release it here to prevent overlapping reservations.
+ Tracker tkr = MemTracker::get_virtual_memory_release_tracker();
+ tkr.record((address)start, bytes);
+ }
char* end = start + bytes;
@@ -3601,7 +3604,7 @@
}
// The memory is committed
- MemTracker::record_virtual_memory_reserve_and_commit((address)addr, bytes, mtNone, CALLER_PC);
+ MemTracker::record_virtual_memory_reserve_and_commit((address)addr, bytes, CALLER_PC);
}
return addr;
@@ -3617,24 +3620,30 @@
}
bool os::release_memory_special(char* base, size_t bytes) {
+ bool res;
+ if (MemTracker::tracking_level() > NMT_minimal) {
+ Tracker tkr = MemTracker::get_virtual_memory_release_tracker();
+ res = os::Linux::release_memory_special_impl(base, bytes);
+ if (res) {
+ tkr.record((address)base, bytes);
+ }
+
+ } else {
+ res = os::Linux::release_memory_special_impl(base, bytes);
+ }
+ return res;
+}
+
+bool os::Linux::release_memory_special_impl(char* base, size_t bytes) {
assert(UseLargePages, "only for large pages");
-
- MemTracker::Tracker tkr = MemTracker::get_virtual_memory_release_tracker();
-
bool res;
+
if (UseSHM) {
res = os::Linux::release_memory_special_shm(base, bytes);
} else {
assert(UseHugeTLBFS, "must be");
res = os::Linux::release_memory_special_huge_tlbfs(base, bytes);
}
-
- if (res) {
- tkr.record((address)base, bytes);
- } else {
- tkr.discard();
- }
-
return res;
}
--- a/hotspot/src/os/linux/vm/os_linux.hpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/os/linux/vm/os_linux.hpp Thu Aug 07 12:18:58 2014 -0700
@@ -108,6 +108,7 @@
static char* reserve_memory_special_huge_tlbfs_only(size_t bytes, char* req_addr, bool exec);
static char* reserve_memory_special_huge_tlbfs_mixed(size_t bytes, size_t alignment, char* req_addr, bool exec);
+ static bool release_memory_special_impl(char* base, size_t bytes);
static bool release_memory_special_shm(char* base, size_t bytes);
static bool release_memory_special_huge_tlbfs(char* base, size_t bytes);
--- a/hotspot/src/os/linux/vm/perfMemory_linux.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/os/linux/vm/perfMemory_linux.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -753,7 +753,7 @@
(void)::memset((void*) mapAddress, 0, size);
// it does not go through os api, the operation has to record from here
- MemTracker::record_virtual_memory_reserve((address)mapAddress, size, mtInternal, CURRENT_PC);
+ MemTracker::record_virtual_memory_reserve_and_commit((address)mapAddress, size, CURRENT_PC, mtInternal);
return mapAddress;
}
@@ -924,7 +924,7 @@
}
// it does not go through os api, the operation has to record from here
- MemTracker::record_virtual_memory_reserve((address)mapAddress, size, mtInternal, CURRENT_PC);
+ MemTracker::record_virtual_memory_reserve_and_commit((address)mapAddress, size, CURRENT_PC, mtInternal);
*addr = mapAddress;
*sizep = size;
--- a/hotspot/src/os/posix/vm/os_posix.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/os/posix/vm/os_posix.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -75,21 +75,41 @@
VMError::report_coredump_status(buffer, success);
}
-address os::get_caller_pc(int n) {
+int os::get_native_stack(address* stack, int frames, int toSkip) {
#ifdef _NMT_NOINLINE_
- n ++;
+ toSkip++;
#endif
+
+ int frame_idx = 0;
+ int num_of_frames; // number of frames captured
frame fr = os::current_frame();
- while (n > 0 && fr.pc() &&
- !os::is_first_C_frame(&fr) && fr.sender_pc()) {
- fr = os::get_sender_for_C_frame(&fr);
- n --;
+ while (fr.pc() && frame_idx < frames) {
+ if (toSkip > 0) {
+ toSkip --;
+ } else {
+ stack[frame_idx ++] = fr.pc();
+ }
+ if (fr.fp() == NULL || os::is_first_C_frame(&fr)
+ ||fr.sender_pc() == NULL || fr.cb() != NULL) break;
+
+ if (fr.sender_pc() && !os::is_first_C_frame(&fr)) {
+ fr = os::get_sender_for_C_frame(&fr);
+ } else {
+ break;
+ }
}
- if (n == 0) {
- return fr.pc();
- } else {
- return NULL;
+ num_of_frames = frame_idx;
+ for (; frame_idx < frames; frame_idx ++) {
+ stack[frame_idx] = NULL;
}
+
+ return num_of_frames;
+}
+
+
+bool os::unsetenv(const char* name) {
+ assert(name != NULL, "Null pointer");
+ return (::unsetenv(name) == 0);
}
int os::get_last_error() {
--- a/hotspot/src/os/solaris/vm/perfMemory_solaris.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/os/solaris/vm/perfMemory_solaris.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -770,7 +770,8 @@
(void)::memset((void*) mapAddress, 0, size);
// it does not go through os api, the operation has to record from here
- MemTracker::record_virtual_memory_reserve((address)mapAddress, size, mtInternal, CURRENT_PC);
+ MemTracker::record_virtual_memory_reserve_and_commit((address)mapAddress,
+ size, CURRENT_PC, mtInternal);
return mapAddress;
}
@@ -941,7 +942,8 @@
}
// it does not go through os api, the operation has to record from here
- MemTracker::record_virtual_memory_reserve((address)mapAddress, size, mtInternal, CURRENT_PC);
+ MemTracker::record_virtual_memory_reserve_and_commit((address)mapAddress,
+ size, CURRENT_PC, mtInternal);
*addr = mapAddress;
*sizep = size;
--- a/hotspot/src/os/windows/vm/os_windows.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/os/windows/vm/os_windows.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -138,9 +138,8 @@
// Workaround for issue when a custom launcher doesn't call
// DestroyJavaVM and NMT is trying to track memory when free is
// called from a static destructor
- if (MemTracker::is_on()) {
- MemTracker::shutdown(MemTracker::NMT_normal);
- }
+ MemTracker::shutdown();
+
break;
default:
break;
@@ -163,6 +162,10 @@
return result > 0 && result < len;
}
+bool os::unsetenv(const char* name) {
+ assert(name != NULL, "Null pointer");
+ return (SetEnvironmentVariable(name, NULL) == TRUE);
+}
// No setuid programs under Windows.
bool os::have_special_privileges() {
@@ -319,15 +322,16 @@
* So far, this method is only used by Native Memory Tracking, which is
* only supported on Windows XP or later.
*/
-address os::get_caller_pc(int n) {
+int os::get_native_stack(address* stack, int frames, int toSkip) {
#ifdef _NMT_NOINLINE_
- n++;
+ toSkip ++;
#endif
- address pc;
- if (os::Kernel32Dll::RtlCaptureStackBackTrace(n + 1, 1, (PVOID*)&pc, NULL) == 1) {
- return pc;
- }
- return NULL;
+ int captured = Kernel32Dll::RtlCaptureStackBackTrace(toSkip + 1, frames,
+ (PVOID*)stack, NULL);
+ for (int index = captured; index < frames; index ++) {
+ stack[index] = NULL;
+ }
+ return captured;
}
@@ -2901,7 +2905,7 @@
PAGE_READWRITE);
// If reservation failed, return NULL
if (p_buf == NULL) return NULL;
- MemTracker::record_virtual_memory_reserve((address)p_buf, size_of_reserve, mtNone, CALLER_PC);
+ MemTracker::record_virtual_memory_reserve((address)p_buf, size_of_reserve, CALLER_PC);
os::release_memory(p_buf, bytes + chunk_size);
// we still need to round up to a page boundary (in case we are using large pages)
@@ -2967,7 +2971,7 @@
// need to create a dummy 'reserve' record to match
// the release.
MemTracker::record_virtual_memory_reserve((address)p_buf,
- bytes_to_release, mtNone, CALLER_PC);
+ bytes_to_release, CALLER_PC);
os::release_memory(p_buf, bytes_to_release);
}
#ifdef ASSERT
@@ -2986,11 +2990,10 @@
}
// Although the memory is allocated individually, it is returned as one.
// NMT records it as one block.
- address pc = CALLER_PC;
if ((flags & MEM_COMMIT) != 0) {
- MemTracker::record_virtual_memory_reserve_and_commit((address)p_buf, bytes, mtNone, pc);
+ MemTracker::record_virtual_memory_reserve_and_commit((address)p_buf, bytes, CALLER_PC);
} else {
- MemTracker::record_virtual_memory_reserve((address)p_buf, bytes, mtNone, pc);
+ MemTracker::record_virtual_memory_reserve((address)p_buf, bytes, CALLER_PC);
}
// made it this far, success
@@ -3188,8 +3191,7 @@
DWORD flag = MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES;
char * res = (char *)VirtualAlloc(addr, bytes, flag, prot);
if (res != NULL) {
- address pc = CALLER_PC;
- MemTracker::record_virtual_memory_reserve_and_commit((address)res, bytes, mtNone, pc);
+ MemTracker::record_virtual_memory_reserve_and_commit((address)res, bytes, CALLER_PC);
}
return res;
--- a/hotspot/src/os/windows/vm/perfMemory_windows.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/os/windows/vm/perfMemory_windows.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -1498,7 +1498,8 @@
(void)memset(mapAddress, '\0', size);
// it does not go through os api, the operation has to record from here
- MemTracker::record_virtual_memory_reserve((address)mapAddress, size, mtInternal, CURRENT_PC);
+ MemTracker::record_virtual_memory_reserve_and_commit((address)mapAddress,
+ size, CURRENT_PC, mtInternal);
return (char*) mapAddress;
}
@@ -1680,7 +1681,8 @@
}
// it does not go through os api, the operation has to record from here
- MemTracker::record_virtual_memory_reserve((address)mapAddress, size, mtInternal, CURRENT_PC);
+ MemTracker::record_virtual_memory_reserve_and_commit((address)mapAddress, size,
+ CURRENT_PC, mtInternal);
*addrp = (char*)mapAddress;
@@ -1834,10 +1836,14 @@
return;
}
- MemTracker::Tracker tkr = MemTracker::get_virtual_memory_release_tracker();
- remove_file_mapping(addr);
- // it does not go through os api, the operation has to record from here
- tkr.record((address)addr, bytes);
+ if (MemTracker::tracking_level() > NMT_minimal) {
+ // it does not go through os api, the operation has to record from here
+ Tracker tkr = MemTracker::get_virtual_memory_release_tracker();
+ remove_file_mapping(addr);
+ tkr.record((address)addr, bytes);
+ } else {
+ remove_file_mapping(addr);
+ }
}
char* PerfMemory::backing_store_filename() {
--- a/hotspot/src/share/vm/asm/codeBuffer.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/asm/codeBuffer.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -269,7 +269,7 @@
GrowableArray<int>* CodeBuffer::create_patch_overflow() {
if (_overflow_arena == NULL) {
- _overflow_arena = new (mtCode) Arena();
+ _overflow_arena = new (mtCode) Arena(mtCode);
}
return new (_overflow_arena) GrowableArray<int>(_overflow_arena, 8, 0, 0);
}
--- a/hotspot/src/share/vm/c1/c1_Compiler.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/c1/c1_Compiler.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -48,7 +48,7 @@
void Compiler::init_c1_runtime() {
BufferBlob* buffer_blob = CompilerThread::current()->get_buffer_blob();
- Arena* arena = new (mtCompiler) Arena();
+ Arena* arena = new (mtCompiler) Arena(mtCompiler);
Runtime1::initialize(buffer_blob);
FrameMap::initialize();
// initialize data structures
--- a/hotspot/src/share/vm/ci/ciEnv.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/ci/ciEnv.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -86,7 +86,8 @@
// ------------------------------------------------------------------
// ciEnv::ciEnv
-ciEnv::ciEnv(CompileTask* task, int system_dictionary_modification_counter) {
+ciEnv::ciEnv(CompileTask* task, int system_dictionary_modification_counter)
+ : _ciEnv_arena(mtCompiler) {
VM_ENTRY_MARK;
// Set up ciEnv::current immediately, for the sake of ciObjectFactory, etc.
@@ -144,7 +145,7 @@
_jvmti_can_pop_frame = false;
}
-ciEnv::ciEnv(Arena* arena) {
+ciEnv::ciEnv(Arena* arena) : _ciEnv_arena(mtCompiler) {
ASSERT_IN_VM;
// Set up ciEnv::current immediately, for the sake of ciObjectFactory, etc.
--- a/hotspot/src/share/vm/ci/ciObjectFactory.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/ci/ciObjectFactory.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -112,7 +112,7 @@
// This Arena is long lived and exists in the resource mark of the
// compiler thread that initializes the initial ciObjectFactory which
// creates the shared ciObjects that all later ciObjectFactories use.
- Arena* arena = new (mtCompiler) Arena();
+ Arena* arena = new (mtCompiler) Arena(mtCompiler);
ciEnv initial(arena);
ciEnv* env = ciEnv::current();
env->_factory->init_shared_objects();
--- a/hotspot/src/share/vm/classfile/symbolTable.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/classfile/symbolTable.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -70,9 +70,9 @@
void SymbolTable::initialize_symbols(int arena_alloc_size) {
// Initialize the arena for global symbols, size passed in depends on CDS.
if (arena_alloc_size == 0) {
- _arena = new (mtSymbol) Arena();
+ _arena = new (mtSymbol) Arena(mtSymbol);
} else {
- _arena = new (mtSymbol) Arena(arena_alloc_size);
+ _arena = new (mtSymbol) Arena(mtSymbol, arena_alloc_size);
}
}
--- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsCollectorPolicy.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsCollectorPolicy.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -52,7 +52,8 @@
}
void ConcurrentMarkSweepPolicy::initialize_generations() {
- _generations = NEW_C_HEAP_ARRAY3(GenerationSpecPtr, number_of_generations(), mtGC, 0, AllocFailStrategy::RETURN_NULL);
+ _generations = NEW_C_HEAP_ARRAY3(GenerationSpecPtr, number_of_generations(), mtGC,
+ CURRENT_PC, AllocFailStrategy::RETURN_NULL);
if (_generations == NULL)
vm_exit_during_initialization("Unable to allocate gen spec");
--- a/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -289,7 +289,7 @@
}
_fine_grain_regions = NEW_C_HEAP_ARRAY3(PerRegionTablePtr, _max_fine_entries,
- mtGC, 0, AllocFailStrategy::RETURN_NULL);
+ mtGC, CURRENT_PC, AllocFailStrategy::RETURN_NULL);
if (_fine_grain_regions == NULL) {
vm_exit_out_of_memory(sizeof(void*)*_max_fine_entries, OOM_MALLOC_ERROR,
--- a/hotspot/src/share/vm/memory/allocation.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/memory/allocation.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -422,26 +422,23 @@
}
//------------------------------Arena------------------------------------------
-NOT_PRODUCT(volatile jint Arena::_instance_count = 0;)
-Arena::Arena(size_t init_size) {
+Arena::Arena(MEMFLAGS flag, size_t init_size) : _flags(flag), _size_in_bytes(0) {
size_t round_size = (sizeof (char *)) - 1;
init_size = (init_size+round_size) & ~round_size;
_first = _chunk = new (AllocFailStrategy::EXIT_OOM, init_size) Chunk(init_size);
_hwm = _chunk->bottom(); // Save the cached hwm, max
_max = _chunk->top();
- _size_in_bytes = 0;
+ MemTracker::record_new_arena(flag);
set_size_in_bytes(init_size);
- NOT_PRODUCT(Atomic::inc(&_instance_count);)
}
-Arena::Arena() {
+Arena::Arena(MEMFLAGS flag) : _flags(flag), _size_in_bytes(0) {
_first = _chunk = new (AllocFailStrategy::EXIT_OOM, Chunk::init_size) Chunk(Chunk::init_size);
_hwm = _chunk->bottom(); // Save the cached hwm, max
_max = _chunk->top();
- _size_in_bytes = 0;
+ MemTracker::record_new_arena(flag);
set_size_in_bytes(Chunk::init_size);
- NOT_PRODUCT(Atomic::inc(&_instance_count);)
}
Arena *Arena::move_contents(Arena *copy) {
@@ -463,7 +460,7 @@
Arena::~Arena() {
destruct_contents();
- NOT_PRODUCT(Atomic::dec(&_instance_count);)
+ MemTracker::record_arena_free(_flags);
}
void* Arena::operator new(size_t size) throw() {
@@ -479,21 +476,21 @@
// dynamic memory type binding
void* Arena::operator new(size_t size, MEMFLAGS flags) throw() {
#ifdef ASSERT
- void* p = (void*)AllocateHeap(size, flags|otArena, CALLER_PC);
+ void* p = (void*)AllocateHeap(size, flags, CALLER_PC);
if (PrintMallocFree) trace_heap_malloc(size, "Arena-new", p);
return p;
#else
- return (void *) AllocateHeap(size, flags|otArena, CALLER_PC);
+ return (void *) AllocateHeap(size, flags, CALLER_PC);
#endif
}
void* Arena::operator new(size_t size, const std::nothrow_t& nothrow_constant, MEMFLAGS flags) throw() {
#ifdef ASSERT
- void* p = os::malloc(size, flags|otArena, CALLER_PC);
+ void* p = os::malloc(size, flags, CALLER_PC);
if (PrintMallocFree) trace_heap_malloc(size, "Arena-new", p);
return p;
#else
- return os::malloc(size, flags|otArena, CALLER_PC);
+ return os::malloc(size, flags, CALLER_PC);
#endif
}
@@ -518,8 +515,9 @@
// change the size
void Arena::set_size_in_bytes(size_t size) {
if (_size_in_bytes != size) {
+ long delta = (long)(size - size_in_bytes());
_size_in_bytes = size;
- MemTracker::record_arena_size((address)this, size);
+ MemTracker::record_arena_size_change(delta, _flags);
}
}
--- a/hotspot/src/share/vm/memory/allocation.hpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/memory/allocation.hpp Thu Aug 07 12:18:58 2014 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -133,51 +133,34 @@
/*
- * MemoryType bitmap layout:
- * | 16 15 14 13 12 11 10 09 | 08 07 06 05 | 04 03 02 01 |
- * | memory type | object | reserved |
- * | | type | |
+ * Memory types
*/
enum MemoryType {
// Memory type by sub systems. It occupies lower byte.
- mtNone = 0x0000, // undefined
- mtClass = 0x0100, // memory class for Java classes
- mtThread = 0x0200, // memory for thread objects
- mtThreadStack = 0x0300,
- mtCode = 0x0400, // memory for generated code
- mtGC = 0x0500, // memory for GC
- mtCompiler = 0x0600, // memory for compiler
- mtInternal = 0x0700, // memory used by VM, but does not belong to
+ mtJavaHeap = 0x00, // Java heap
+ mtClass = 0x01, // memory class for Java classes
+ mtThread = 0x02, // memory for thread objects
+ mtThreadStack = 0x03,
+ mtCode = 0x04, // memory for generated code
+ mtGC = 0x05, // memory for GC
+ mtCompiler = 0x06, // memory for compiler
+ mtInternal = 0x07, // memory used by VM, but does not belong to
// any of above categories, and not used for
// native memory tracking
- mtOther = 0x0800, // memory not used by VM
- mtSymbol = 0x0900, // symbol
- mtNMT = 0x0A00, // memory used by native memory tracking
- mtChunk = 0x0B00, // chunk that holds content of arenas
- mtJavaHeap = 0x0C00, // Java heap
- mtClassShared = 0x0D00, // class data sharing
- mtTest = 0x0E00, // Test type for verifying NMT
- mtTracing = 0x0F00, // memory used for Tracing
- mt_number_of_types = 0x000F, // number of memory types (mtDontTrack
+ mtOther = 0x08, // memory not used by VM
+ mtSymbol = 0x09, // symbol
+ mtNMT = 0x0A, // memory used by native memory tracking
+ mtClassShared = 0x0B, // class data sharing
+ mtChunk = 0x0C, // chunk that holds content of arenas
+ mtTest = 0x0D, // Test type for verifying NMT
+ mtTracing = 0x0E, // memory used for Tracing
+ mtNone = 0x0F, // undefined
+ mt_number_of_types = 0x10 // number of memory types (mtDontTrack
// is not included as validate type)
- mtDontTrack = 0x0F00, // memory we do not or cannot track
- mt_masks = 0x7F00,
-
- // object type mask
- otArena = 0x0010, // an arena object
- otNMTRecorder = 0x0020, // memory recorder object
- ot_masks = 0x00F0
};
-#define IS_MEMORY_TYPE(flags, type) ((flags & mt_masks) == type)
-#define HAS_VALID_MEMORY_TYPE(flags)((flags & mt_masks) != mtNone)
-#define FLAGS_TO_MEMORY_TYPE(flags) (flags & mt_masks)
+typedef MemoryType MEMFLAGS;
-#define IS_ARENA_OBJ(flags) ((flags & ot_masks) == otArena)
-#define IS_NMT_RECORDER(flags) ((flags & ot_masks) == otNMTRecorder)
-#define NMT_CAN_TRACK(flags) (!IS_NMT_RECORDER(flags) && !(IS_MEMORY_TYPE(flags, mtDontTrack)))
-
-typedef unsigned short MEMFLAGS;
#if INCLUDE_NMT
@@ -189,27 +172,23 @@
#endif // INCLUDE_NMT
-// debug build does not inline
-#if defined(_NMT_NOINLINE_)
- #define CURRENT_PC (NMT_track_callsite ? os::get_caller_pc(1) : 0)
- #define CALLER_PC (NMT_track_callsite ? os::get_caller_pc(2) : 0)
- #define CALLER_CALLER_PC (NMT_track_callsite ? os::get_caller_pc(3) : 0)
-#else
- #define CURRENT_PC (NMT_track_callsite? os::get_caller_pc(0) : 0)
- #define CALLER_PC (NMT_track_callsite ? os::get_caller_pc(1) : 0)
- #define CALLER_CALLER_PC (NMT_track_callsite ? os::get_caller_pc(2) : 0)
-#endif
-
+class NativeCallStack;
template <MEMFLAGS F> class CHeapObj ALLOCATION_SUPER_CLASS_SPEC {
public:
- _NOINLINE_ void* operator new(size_t size, address caller_pc = 0) throw();
+ _NOINLINE_ void* operator new(size_t size, const NativeCallStack& stack) throw();
+ _NOINLINE_ void* operator new(size_t size) throw();
_NOINLINE_ void* operator new (size_t size, const std::nothrow_t& nothrow_constant,
- address caller_pc = 0) throw();
- _NOINLINE_ void* operator new [](size_t size, address caller_pc = 0) throw();
+ const NativeCallStack& stack) throw();
+ _NOINLINE_ void* operator new (size_t size, const std::nothrow_t& nothrow_constant)
+ throw();
+ _NOINLINE_ void* operator new [](size_t size, const NativeCallStack& stack) throw();
+ _NOINLINE_ void* operator new [](size_t size) throw();
_NOINLINE_ void* operator new [](size_t size, const std::nothrow_t& nothrow_constant,
- address caller_pc = 0) throw();
+ const NativeCallStack& stack) throw();
+ _NOINLINE_ void* operator new [](size_t size, const std::nothrow_t& nothrow_constant)
+ throw();
void operator delete(void* p);
void operator delete [] (void* p);
};
@@ -384,13 +363,15 @@
//------------------------------Arena------------------------------------------
// Fast allocation of memory
-class Arena : public CHeapObj<mtNone|otArena> {
+class Arena : public CHeapObj<mtNone> {
protected:
friend class ResourceMark;
friend class HandleMark;
friend class NoHandleMark;
friend class VMStructs;
+ MEMFLAGS _flags; // Memory tracking flags
+
Chunk *_first; // First chunk
Chunk *_chunk; // current chunk
char *_hwm, *_max; // High water mark and max in current chunk
@@ -418,8 +399,8 @@
}
public:
- Arena();
- Arena(size_t init_size);
+ Arena(MEMFLAGS memflag);
+ Arena(MEMFLAGS memflag, size_t init_size);
~Arena();
void destruct_contents();
char* hwm() const { return _hwm; }
@@ -518,8 +499,6 @@
static void free_malloced_objects(Chunk* chunk, char* hwm, char* max, char* hwm2) PRODUCT_RETURN;
static void free_all(char** start, char** end) PRODUCT_RETURN;
- // how many arena instances
- NOT_PRODUCT(static volatile jint _instance_count;)
private:
// Reset this Arena to empty, access will trigger grow if necessary
void reset(void) {
@@ -681,7 +660,7 @@
NEW_C_HEAP_ARRAY3(type, (size), memflags, pc, AllocFailStrategy::RETURN_NULL)
#define NEW_C_HEAP_ARRAY_RETURN_NULL(type, size, memflags)\
- NEW_C_HEAP_ARRAY3(type, (size), memflags, (address)0, AllocFailStrategy::RETURN_NULL)
+ NEW_C_HEAP_ARRAY3(type, (size), memflags, CURRENT_PC, AllocFailStrategy::RETURN_NULL)
#define REALLOC_C_HEAP_ARRAY(type, old, size, memflags)\
(type*) (ReallocateHeap((char*)(old), (size) * sizeof(type), memflags))
--- a/hotspot/src/share/vm/memory/allocation.inline.hpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/memory/allocation.inline.hpp Thu Aug 07 12:18:58 2014 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,6 +27,7 @@
#include "runtime/atomic.inline.hpp"
#include "runtime/os.hpp"
+#include "services/memTracker.hpp"
// Explicit C-heap memory management
@@ -49,12 +50,10 @@
#endif
// allocate using malloc; will fail if no memory available
-inline char* AllocateHeap(size_t size, MEMFLAGS flags, address pc = 0,
+inline char* AllocateHeap(size_t size, MEMFLAGS flags,
+ const NativeCallStack& stack,
AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM) {
- if (pc == 0) {
- pc = CURRENT_PC;
- }
- char* p = (char*) os::malloc(size, flags, pc);
+ char* p = (char*) os::malloc(size, flags, stack);
#ifdef ASSERT
if (PrintMallocFree) trace_heap_malloc(size, "AllocateHeap", p);
#endif
@@ -63,10 +62,14 @@
}
return p;
}
+inline char* AllocateHeap(size_t size, MEMFLAGS flags,
+ AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM) {
+ return AllocateHeap(size, flags, CURRENT_PC, alloc_failmode);
+}
-inline char* ReallocateHeap(char *old, size_t size, MEMFLAGS flags,
+inline char* ReallocateHeap(char *old, size_t size, MEMFLAGS flag,
AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM) {
- char* p = (char*) os::realloc(old, size, flags, CURRENT_PC);
+ char* p = (char*) os::realloc(old, size, flag, CURRENT_PC);
#ifdef ASSERT
if (PrintMallocFree) trace_heap_malloc(size, "ReallocateHeap", p);
#endif
@@ -85,8 +88,22 @@
template <MEMFLAGS F> void* CHeapObj<F>::operator new(size_t size,
- address caller_pc) throw() {
- void* p = (void*)AllocateHeap(size, F, (caller_pc != 0 ? caller_pc : CALLER_PC));
+ const NativeCallStack& stack) throw() {
+ void* p = (void*)AllocateHeap(size, F, stack);
+#ifdef ASSERT
+ if (PrintMallocFree) trace_heap_malloc(size, "CHeapObj-new", p);
+#endif
+ return p;
+}
+
+template <MEMFLAGS F> void* CHeapObj<F>::operator new(size_t size) throw() {
+ return CHeapObj<F>::operator new(size, CALLER_PC);
+}
+
+template <MEMFLAGS F> void* CHeapObj<F>::operator new (size_t size,
+ const std::nothrow_t& nothrow_constant, const NativeCallStack& stack) throw() {
+ void* p = (void*)AllocateHeap(size, F, stack,
+ AllocFailStrategy::RETURN_NULL);
#ifdef ASSERT
if (PrintMallocFree) trace_heap_malloc(size, "CHeapObj-new", p);
#endif
@@ -94,23 +111,28 @@
}
template <MEMFLAGS F> void* CHeapObj<F>::operator new (size_t size,
- const std::nothrow_t& nothrow_constant, address caller_pc) throw() {
- void* p = (void*)AllocateHeap(size, F, (caller_pc != 0 ? caller_pc : CALLER_PC),
- AllocFailStrategy::RETURN_NULL);
-#ifdef ASSERT
- if (PrintMallocFree) trace_heap_malloc(size, "CHeapObj-new", p);
-#endif
- return p;
+ const std::nothrow_t& nothrow_constant) throw() {
+ return CHeapObj<F>::operator new(size, nothrow_constant, CALLER_PC);
}
template <MEMFLAGS F> void* CHeapObj<F>::operator new [](size_t size,
- address caller_pc) throw() {
- return CHeapObj<F>::operator new(size, caller_pc);
+ const NativeCallStack& stack) throw() {
+ return CHeapObj<F>::operator new(size, stack);
+}
+
+template <MEMFLAGS F> void* CHeapObj<F>::operator new [](size_t size)
+ throw() {
+ return CHeapObj<F>::operator new(size, CALLER_PC);
}
template <MEMFLAGS F> void* CHeapObj<F>::operator new [](size_t size,
- const std::nothrow_t& nothrow_constant, address caller_pc) throw() {
- return CHeapObj<F>::operator new(size, nothrow_constant, caller_pc);
+ const std::nothrow_t& nothrow_constant, const NativeCallStack& stack) throw() {
+ return CHeapObj<F>::operator new(size, nothrow_constant, stack);
+}
+
+template <MEMFLAGS F> void* CHeapObj<F>::operator new [](size_t size,
+ const std::nothrow_t& nothrow_constant) throw() {
+ return CHeapObj<F>::operator new(size, nothrow_constant, CALLER_PC);
}
template <MEMFLAGS F> void CHeapObj<F>::operator delete(void* p){
--- a/hotspot/src/share/vm/memory/cardTableRS.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/memory/cardTableRS.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -56,7 +56,7 @@
#endif
set_bs(_ct_bs);
_last_cur_val_in_gen = NEW_C_HEAP_ARRAY3(jbyte, GenCollectedHeap::max_gens + 1,
- mtGC, 0, AllocFailStrategy::RETURN_NULL);
+ mtGC, CURRENT_PC, AllocFailStrategy::RETURN_NULL);
if (_last_cur_val_in_gen == NULL) {
vm_exit_during_initialization("Could not create last_cur_val_in_gen array.");
}
--- a/hotspot/src/share/vm/memory/collectorPolicy.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/memory/collectorPolicy.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -909,7 +909,8 @@
}
void MarkSweepPolicy::initialize_generations() {
- _generations = NEW_C_HEAP_ARRAY3(GenerationSpecPtr, number_of_generations(), mtGC, 0, AllocFailStrategy::RETURN_NULL);
+ _generations = NEW_C_HEAP_ARRAY3(GenerationSpecPtr, number_of_generations(), mtGC, CURRENT_PC,
+ AllocFailStrategy::RETURN_NULL);
if (_generations == NULL) {
vm_exit_during_initialization("Unable to allocate gen spec");
}
--- a/hotspot/src/share/vm/memory/heapInspection.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/memory/heapInspection.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -135,7 +135,7 @@
_ref = (HeapWord*) Universe::boolArrayKlassObj();
_buckets =
(KlassInfoBucket*) AllocateHeap(sizeof(KlassInfoBucket) * _num_buckets,
- mtInternal, 0, AllocFailStrategy::RETURN_NULL);
+ mtInternal, CURRENT_PC, AllocFailStrategy::RETURN_NULL);
if (_buckets != NULL) {
_size = _num_buckets;
for (int index = 0; index < _size; index++) {
--- a/hotspot/src/share/vm/memory/memRegion.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/memory/memRegion.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -103,11 +103,13 @@
}
void* MemRegion::operator new(size_t size) throw() {
- return (address)AllocateHeap(size, mtGC, 0, AllocFailStrategy::RETURN_NULL);
+ return (address)AllocateHeap(size, mtGC, CURRENT_PC,
+ AllocFailStrategy::RETURN_NULL);
}
void* MemRegion::operator new [](size_t size) throw() {
- return (address)AllocateHeap(size, mtGC, 0, AllocFailStrategy::RETURN_NULL);
+ return (address)AllocateHeap(size, mtGC, CURRENT_PC,
+ AllocFailStrategy::RETURN_NULL);
}
void MemRegion::operator delete(void* p) {
FreeHeap(p, mtGC);
--- a/hotspot/src/share/vm/memory/resourceArea.hpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/memory/resourceArea.hpp Thu Aug 07 12:18:58 2014 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -49,11 +49,11 @@
debug_only(static int _warned;) // to suppress multiple warnings
public:
- ResourceArea() {
+ ResourceArea() : Arena(mtThread) {
debug_only(_nesting = 0;)
}
- ResourceArea(size_t init_size) : Arena(init_size) {
+ ResourceArea(size_t init_size) : Arena(mtThread, init_size) {
debug_only(_nesting = 0;);
}
@@ -64,7 +64,7 @@
if (UseMallocOnly) {
// use malloc, but save pointer in res. area for later freeing
char** save = (char**)internal_malloc_4(sizeof(char*));
- return (*save = (char*)os::malloc(size, mtThread));
+ return (*save = (char*)os::malloc(size, mtThread, CURRENT_PC));
}
#endif
return (char*)Amalloc(size, alloc_failmode);
--- a/hotspot/src/share/vm/opto/compile.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/opto/compile.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -647,6 +647,10 @@
_printer(IdealGraphPrinter::printer()),
#endif
_congraph(NULL),
+ _comp_arena(mtCompiler),
+ _node_arena(mtCompiler),
+ _old_arena(mtCompiler),
+ _Compile_types(mtCompiler),
_replay_inline_data(NULL),
_late_inlines(comp_arena(), 2, 0, NULL),
_string_late_inlines(comp_arena(), 2, 0, NULL),
@@ -954,6 +958,10 @@
_in_dump_cnt(0),
_printer(NULL),
#endif
+ _comp_arena(mtCompiler),
+ _node_arena(mtCompiler),
+ _old_arena(mtCompiler),
+ _Compile_types(mtCompiler),
_dead_node_list(comp_arena()),
_dead_node_count(0),
_congraph(NULL),
--- a/hotspot/src/share/vm/opto/type.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/opto/type.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -265,7 +265,7 @@
// locking.
Arena* save = current->type_arena();
- Arena* shared_type_arena = new (mtCompiler)Arena();
+ Arena* shared_type_arena = new (mtCompiler)Arena(mtCompiler);
current->set_type_arena(shared_type_arena);
_shared_type_dict =
--- a/hotspot/src/share/vm/precompiled/precompiled.hpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/precompiled/precompiled.hpp Thu Aug 07 12:18:58 2014 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -222,10 +222,17 @@
# include "runtime/vmThread.hpp"
# include "runtime/vm_operations.hpp"
# include "runtime/vm_version.hpp"
+# include "services/allocationSite.hpp"
# include "services/lowMemoryDetector.hpp"
+# include "services/mallocTracker.hpp"
+# include "services/memBaseline.hpp"
# include "services/memoryPool.hpp"
# include "services/memoryService.hpp"
# include "services/memoryUsage.hpp"
+# include "services/memReporter.hpp"
+# include "services/memTracker.hpp"
+# include "services/nmtCommon.hpp"
+# include "services/virtualMemoryTracker.hpp"
# include "utilities/accessFlags.hpp"
# include "utilities/array.hpp"
# include "utilities/bitMap.hpp"
@@ -240,6 +247,7 @@
# include "utilities/hashtable.hpp"
# include "utilities/histogram.hpp"
# include "utilities/macros.hpp"
+# include "utilities/nativeCallStack.hpp"
# include "utilities/numberSeq.hpp"
# include "utilities/ostream.hpp"
# include "utilities/preserveException.hpp"
--- a/hotspot/src/share/vm/prims/jni.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/prims/jni.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -74,6 +74,7 @@
#include "runtime/signature.hpp"
#include "runtime/thread.inline.hpp"
#include "runtime/vm_operations.hpp"
+#include "services/memTracker.hpp"
#include "services/runtimeService.hpp"
#include "trace/tracing.hpp"
#include "utilities/defaultStream.hpp"
@@ -2697,6 +2698,7 @@
if (bad_address != NULL) {
os::protect_memory(bad_address, size, os::MEM_PROT_READ,
/*is_committed*/false);
+ MemTracker::record_virtual_memory_type((void*)bad_address, mtInternal);
}
}
return bad_address;
@@ -3857,6 +3859,7 @@
void TestKlass_test();
void TestBitMap_test();
void TestAsUtf8();
+void Test_linked_list();
#if INCLUDE_ALL_GCS
void TestOldFreeSpaceCalculation_test();
void TestG1BiasedArray_test();
@@ -3887,6 +3890,7 @@
run_unit_test(TestBitMap_test());
run_unit_test(TestAsUtf8());
run_unit_test(ObjectMonitor::sanity_checks());
+ run_unit_test(Test_linked_list());
#if INCLUDE_VM_STRUCTS
run_unit_test(VMStructs::test());
#endif
--- a/hotspot/src/share/vm/prims/whitebox.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/prims/whitebox.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -52,8 +52,10 @@
#include "gc_implementation/g1/heapRegionRemSet.hpp"
#endif // INCLUDE_ALL_GCS
-#ifdef INCLUDE_NMT
+#if INCLUDE_NMT
+#include "services/mallocSiteTable.hpp"
#include "services/memTracker.hpp"
+#include "utilities/nativeCallStack.hpp"
#endif // INCLUDE_NMT
#include "compiler/compileBroker.hpp"
@@ -255,12 +257,16 @@
// NMT picks it up correctly
WB_ENTRY(jlong, WB_NMTMalloc(JNIEnv* env, jobject o, jlong size))
jlong addr = 0;
+ addr = (jlong)(uintptr_t)os::malloc(size, mtTest);
+ return addr;
+WB_END
- if (MemTracker::is_on() && !MemTracker::shutdown_in_progress()) {
- addr = (jlong)(uintptr_t)os::malloc(size, mtTest);
- }
-
- return addr;
+// Alloc memory with pseudo call stack. The test can create psudo malloc
+// allocation site to stress the malloc tracking.
+WB_ENTRY(jlong, WB_NMTMallocWithPseudoStack(JNIEnv* env, jobject o, jlong size, jint pseudo_stack))
+ address pc = (address)(size_t)pseudo_stack;
+ NativeCallStack stack(&pc, 1);
+ return (jlong)os::malloc(size, mtTest, stack);
WB_END
// Free the memory allocated by NMTAllocTest
@@ -271,10 +277,8 @@
WB_ENTRY(jlong, WB_NMTReserveMemory(JNIEnv* env, jobject o, jlong size))
jlong addr = 0;
- if (MemTracker::is_on() && !MemTracker::shutdown_in_progress()) {
addr = (jlong)(uintptr_t)os::reserve_memory(size);
MemTracker::record_virtual_memory_type((address)addr, mtTest);
- }
return addr;
WB_END
@@ -293,20 +297,20 @@
os::release_memory((char *)(uintptr_t)addr, size);
WB_END
-// Block until the current generation of NMT data to be merged, used to reliably test the NMT feature
-WB_ENTRY(jboolean, WB_NMTWaitForDataMerge(JNIEnv* env))
-
- if (!MemTracker::is_on() || MemTracker::shutdown_in_progress()) {
- return false;
- }
-
- return MemTracker::wbtest_wait_for_data_merge();
+WB_ENTRY(jboolean, WB_NMTIsDetailSupported(JNIEnv* env))
+ return MemTracker::tracking_level() == NMT_detail;
WB_END
-WB_ENTRY(jboolean, WB_NMTIsDetailSupported(JNIEnv* env))
- return MemTracker::tracking_level() == MemTracker::NMT_detail;
+WB_ENTRY(void, WB_NMTOverflowHashBucket(JNIEnv* env, jobject o, jlong num))
+ address pc = (address)1;
+ for (jlong index = 0; index < num; index ++) {
+ NativeCallStack stack(&pc, 1);
+ os::malloc(0, mtTest, stack);
+ pc += MallocSiteTable::hash_buckets();
+ }
WB_END
+
#endif // INCLUDE_NMT
static jmethodID reflected_method_to_jmid(JavaThread* thread, JNIEnv* env, jobject method) {
@@ -843,12 +847,13 @@
#endif // INCLUDE_ALL_GCS
#if INCLUDE_NMT
{CC"NMTMalloc", CC"(J)J", (void*)&WB_NMTMalloc },
+ {CC"NMTMallocWithPseudoStack", CC"(JI)J", (void*)&WB_NMTMallocWithPseudoStack},
{CC"NMTFree", CC"(J)V", (void*)&WB_NMTFree },
{CC"NMTReserveMemory", CC"(J)J", (void*)&WB_NMTReserveMemory },
{CC"NMTCommitMemory", CC"(JJ)V", (void*)&WB_NMTCommitMemory },
{CC"NMTUncommitMemory", CC"(JJ)V", (void*)&WB_NMTUncommitMemory },
{CC"NMTReleaseMemory", CC"(JJ)V", (void*)&WB_NMTReleaseMemory },
- {CC"NMTWaitForDataMerge", CC"()Z", (void*)&WB_NMTWaitForDataMerge},
+ {CC"NMTOverflowHashBucket", CC"(J)V", (void*)&WB_NMTOverflowHashBucket},
{CC"NMTIsDetailSupported",CC"()Z", (void*)&WB_NMTIsDetailSupported},
#endif // INCLUDE_NMT
{CC"deoptimizeAll", CC"()V", (void*)&WB_DeoptimizeAll },
--- a/hotspot/src/share/vm/runtime/arguments.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/runtime/arguments.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -300,6 +300,7 @@
{ "UseNewReflection", JDK_Version::jdk(9), JDK_Version::jdk(10) },
{ "ReflectionWrapResolutionErrors",JDK_Version::jdk(9), JDK_Version::jdk(10) },
{ "VerifyReflectionBytecodes", JDK_Version::jdk(9), JDK_Version::jdk(10) },
+ { "AutoShutdownNMT", JDK_Version::jdk(9), JDK_Version::jdk(10) },
{ NULL, JDK_Version(0), JDK_Version(0) }
};
@@ -2372,7 +2373,7 @@
if (PrintNMTStatistics) {
#if INCLUDE_NMT
- if (MemTracker::tracking_level() == MemTracker::NMT_off) {
+ if (MemTracker::tracking_level() == NMT_off) {
#endif // INCLUDE_NMT
warning("PrintNMTStatistics is disabled, because native memory tracking is not enabled");
PrintNMTStatistics = false;
@@ -3582,15 +3583,24 @@
CommandLineFlags::printFlags(tty, false);
vm_exit(0);
}
+#if INCLUDE_NMT
if (match_option(option, "-XX:NativeMemoryTracking", &tail)) {
-#if INCLUDE_NMT
- MemTracker::init_tracking_options(tail);
-#else
- jio_fprintf(defaultStream::error_stream(),
- "Native Memory Tracking is not supported in this VM\n");
- return JNI_ERR;
+ // The launcher did not setup nmt environment variable properly.
+// if (!MemTracker::check_launcher_nmt_support(tail)) {
+// warning("Native Memory Tracking did not setup properly, using wrong launcher?");
+// }
+
+ // Verify if nmt option is valid.
+ if (MemTracker::verify_nmt_option()) {
+ // Late initialization, still in single-threaded mode.
+ if (MemTracker::tracking_level() >= NMT_summary) {
+ MemTracker::init();
+ }
+ } else {
+ vm_exit_during_initialization("Syntax error, expecting -XX:NativeMemoryTracking=[off|summary|detail]", NULL);
+ }
+ }
#endif
- }
#ifndef PRODUCT
--- a/hotspot/src/share/vm/runtime/globals.hpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/runtime/globals.hpp Thu Aug 07 12:18:58 2014 -0700
@@ -945,11 +945,6 @@
diagnostic(bool, PrintNMTStatistics, false, \
"Print native memory tracking summary data if it is on") \
\
- diagnostic(bool, AutoShutdownNMT, true, \
- "Automatically shutdown native memory tracking under stress " \
- "situations. When set to false, native memory tracking tries to " \
- "stay alive at the expense of JVM performance") \
- \
diagnostic(bool, LogCompilation, false, \
"Log compilation activity in detail to LogFile") \
\
--- a/hotspot/src/share/vm/runtime/handles.hpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/runtime/handles.hpp Thu Aug 07 12:18:58 2014 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -227,7 +227,7 @@
HandleArea* _prev; // link to outer (older) area
public:
// Constructor
- HandleArea(HandleArea* prev) : Arena(Chunk::tiny_size) {
+ HandleArea(HandleArea* prev) : Arena(mtThread, Chunk::tiny_size) {
debug_only(_handle_mark_nesting = 0);
debug_only(_no_handle_mark_nesting = 0);
_prev = prev;
--- a/hotspot/src/share/vm/runtime/init.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/runtime/init.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -34,8 +34,10 @@
#include "runtime/init.hpp"
#include "runtime/safepoint.hpp"
#include "runtime/sharedRuntime.hpp"
+#include "services/memTracker.hpp"
#include "utilities/macros.hpp"
+
// Initialization done by VM thread in vm_init_globals()
void check_ThreadShadow();
void eventlog_init();
@@ -131,6 +133,12 @@
javaClasses_init(); // must happen after vtable initialization
stubRoutines_init2(); // note: StubRoutines need 2-phase init
+#if INCLUDE_NMT
+ // Solaris stack is walkable only after stubRoutines are set up.
+ // On Other platforms, the stack is always walkable.
+ NMT_stack_walkable = true;
+#endif // INCLUDE_NMT
+
// All the flags that get adjusted by VM_Version_init and os::init_2
// have been set so dump the flags now.
if (PrintFlagsFinal) {
--- a/hotspot/src/share/vm/runtime/java.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/runtime/java.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -57,7 +57,6 @@
#include "runtime/thread.inline.hpp"
#include "runtime/timer.hpp"
#include "runtime/vm_operations.hpp"
-#include "services/memReporter.hpp"
#include "services/memTracker.hpp"
#include "trace/tracing.hpp"
#include "utilities/dtrace.hpp"
@@ -349,12 +348,7 @@
#endif // ENABLE_ZAP_DEAD_LOCALS
// Native memory tracking data
if (PrintNMTStatistics) {
- if (MemTracker::is_on()) {
- BaselineTTYOutputer outputer(tty);
- MemTracker::print_memory_usage(outputer, K, false);
- } else {
- tty->print_cr("%s", MemTracker::reason());
- }
+ MemTracker::final_report(tty);
}
}
@@ -390,12 +384,7 @@
// Native memory tracking data
if (PrintNMTStatistics) {
- if (MemTracker::is_on()) {
- BaselineTTYOutputer outputer(tty);
- MemTracker::print_memory_usage(outputer, K, false);
- } else {
- tty->print_cr("%s", MemTracker::reason());
- }
+ MemTracker::final_report(tty);
}
}
@@ -544,10 +533,6 @@
BeforeExit_lock->notify_all();
}
- // Shutdown NMT before exit. Otherwise,
- // it will run into trouble when system destroys static variables.
- MemTracker::shutdown(MemTracker::NMT_normal);
-
if (VerifyStringTableAtExit) {
int fail_cnt = 0;
{
--- a/hotspot/src/share/vm/runtime/os.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/runtime/os.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -52,6 +52,7 @@
#include "runtime/thread.inline.hpp"
#include "runtime/vm_version.hpp"
#include "services/attachListener.hpp"
+#include "services/nmtCommon.hpp"
#include "services/memTracker.hpp"
#include "services/threadService.hpp"
#include "utilities/defaultStream.hpp"
@@ -553,7 +554,11 @@
return ptr;
}
-void* os::malloc(size_t size, MEMFLAGS memflags, address caller) {
+void* os::malloc(size_t size, MEMFLAGS flags) {
+ return os::malloc(size, flags, CALLER_PC);
+}
+
+void* os::malloc(size_t size, MEMFLAGS memflags, const NativeCallStack& stack) {
NOT_PRODUCT(inc_stat_counter(&num_mallocs, 1));
NOT_PRODUCT(inc_stat_counter(&alloc_bytes, size));
@@ -579,11 +584,15 @@
size = 1;
}
+ // NMT support
+ NMT_TrackingLevel level = MemTracker::tracking_level();
+ size_t nmt_header_size = MemTracker::malloc_header_size(level);
+
#ifndef ASSERT
- const size_t alloc_size = size;
+ const size_t alloc_size = size + nmt_header_size;
#else
- const size_t alloc_size = GuardedMemory::get_total_size(size);
- if (size > alloc_size) { // Check for rollover.
+ const size_t alloc_size = GuardedMemory::get_total_size(size + nmt_header_size);
+ if (size + nmt_header_size > alloc_size) { // Check for rollover.
return NULL;
}
#endif
@@ -602,7 +611,7 @@
return NULL;
}
// Wrap memory with guard
- GuardedMemory guarded(ptr, size);
+ GuardedMemory guarded(ptr, size + nmt_header_size);
ptr = guarded.get_user_ptr();
#endif
if ((intptr_t)ptr == (intptr_t)MallocCatchPtr) {
@@ -615,48 +624,50 @@
}
// we do not track guard memory
- MemTracker::record_malloc((address)ptr, size, memflags, caller == 0 ? CALLER_PC : caller);
-
- return ptr;
+ return MemTracker::record_malloc((address)ptr, size, memflags, stack, level);
}
+void* os::realloc(void *memblock, size_t size, MEMFLAGS flags) {
+ return os::realloc(memblock, size, flags, CALLER_PC);
+}
-void* os::realloc(void *memblock, size_t size, MEMFLAGS memflags, address caller) {
+void* os::realloc(void *memblock, size_t size, MEMFLAGS memflags, const NativeCallStack& stack) {
#ifndef ASSERT
NOT_PRODUCT(inc_stat_counter(&num_mallocs, 1));
NOT_PRODUCT(inc_stat_counter(&alloc_bytes, size));
- MemTracker::Tracker tkr = MemTracker::get_realloc_tracker();
- void* ptr = ::realloc(memblock, size);
- if (ptr != NULL) {
- tkr.record((address)memblock, (address)ptr, size, memflags,
- caller == 0 ? CALLER_PC : caller);
- } else {
- tkr.discard();
- }
- return ptr;
+ // NMT support
+ void* membase = MemTracker::record_free(memblock);
+ NMT_TrackingLevel level = MemTracker::tracking_level();
+ size_t nmt_header_size = MemTracker::malloc_header_size(level);
+ void* ptr = ::realloc(membase, size + nmt_header_size);
+ return MemTracker::record_malloc(ptr, size, memflags, stack, level);
#else
if (memblock == NULL) {
- return os::malloc(size, memflags, (caller == 0 ? CALLER_PC : caller));
+ return os::malloc(size, memflags, stack);
}
if ((intptr_t)memblock == (intptr_t)MallocCatchPtr) {
tty->print_cr("os::realloc caught " PTR_FORMAT, memblock);
breakpoint();
}
- verify_memory(memblock);
+ // NMT support
+ void* membase = MemTracker::malloc_base(memblock);
+ verify_memory(membase);
NOT_PRODUCT(if (MallocVerifyInterval > 0) check_heap());
if (size == 0) {
return NULL;
}
// always move the block
- void* ptr = os::malloc(size, memflags, caller == 0 ? CALLER_PC : caller);
+ void* ptr = os::malloc(size, memflags, stack);
if (PrintMalloc) {
tty->print_cr("os::remalloc " SIZE_FORMAT " bytes, " PTR_FORMAT " --> " PTR_FORMAT, size, memblock, ptr);
}
// Copy to new memory if malloc didn't fail
if ( ptr != NULL ) {
- GuardedMemory guarded(memblock);
- memcpy(ptr, memblock, MIN2(size, guarded.get_user_size()));
- if (paranoid) verify_memory(ptr);
+ GuardedMemory guarded(MemTracker::malloc_base(memblock));
+ // Guard's user data contains NMT header
+ size_t memblock_size = guarded.get_user_size() - MemTracker::malloc_header_size(memblock);
+ memcpy(ptr, memblock, MIN2(size, memblock_size));
+ if (paranoid) verify_memory(MemTracker::malloc_base(ptr));
if ((intptr_t)ptr == (intptr_t)MallocCatchPtr) {
tty->print_cr("os::realloc caught, " SIZE_FORMAT " bytes --> " PTR_FORMAT, size, ptr);
breakpoint();
@@ -669,7 +680,6 @@
void os::free(void *memblock, MEMFLAGS memflags) {
- address trackp = (address) memblock;
NOT_PRODUCT(inc_stat_counter(&num_frees, 1));
#ifdef ASSERT
if (memblock == NULL) return;
@@ -677,20 +687,22 @@
if (tty != NULL) tty->print_cr("os::free caught " PTR_FORMAT, memblock);
breakpoint();
}
- verify_memory(memblock);
+ void* membase = MemTracker::record_free(memblock);
+ verify_memory(membase);
NOT_PRODUCT(if (MallocVerifyInterval > 0) check_heap());
- GuardedMemory guarded(memblock);
+ GuardedMemory guarded(membase);
size_t size = guarded.get_user_size();
inc_stat_counter(&free_bytes, size);
- memblock = guarded.release_for_freeing();
+ membase = guarded.release_for_freeing();
if (PrintMalloc && tty != NULL) {
- fprintf(stderr, "os::free " SIZE_FORMAT " bytes --> " PTR_FORMAT "\n", size, (uintptr_t)memblock);
+ fprintf(stderr, "os::free " SIZE_FORMAT " bytes --> " PTR_FORMAT "\n", size, (uintptr_t)membase);
}
+ ::free(membase);
+#else
+ void* membase = MemTracker::record_free(memblock);
+ ::free(membase);
#endif
- MemTracker::record_free(trackp, memflags);
-
- ::free(memblock);
}
void os::init_random(long initval) {
@@ -1404,7 +1416,7 @@
char* os::reserve_memory(size_t bytes, char* addr, size_t alignment_hint) {
char* result = pd_reserve_memory(bytes, addr, alignment_hint);
if (result != NULL) {
- MemTracker::record_virtual_memory_reserve((address)result, bytes, mtNone, CALLER_PC);
+ MemTracker::record_virtual_memory_reserve((address)result, bytes, CALLER_PC);
}
return result;
@@ -1414,7 +1426,7 @@
MEMFLAGS flags) {
char* result = pd_reserve_memory(bytes, addr, alignment_hint);
if (result != NULL) {
- MemTracker::record_virtual_memory_reserve((address)result, bytes, mtNone, CALLER_PC);
+ MemTracker::record_virtual_memory_reserve((address)result, bytes, CALLER_PC);
MemTracker::record_virtual_memory_type((address)result, flags);
}
@@ -1424,7 +1436,7 @@
char* os::attempt_reserve_memory_at(size_t bytes, char* addr) {
char* result = pd_attempt_reserve_memory_at(bytes, addr);
if (result != NULL) {
- MemTracker::record_virtual_memory_reserve((address)result, bytes, mtNone, CALLER_PC);
+ MemTracker::record_virtual_memory_reserve((address)result, bytes, CALLER_PC);
}
return result;
}
@@ -1464,23 +1476,29 @@
}
bool os::uncommit_memory(char* addr, size_t bytes) {
- MemTracker::Tracker tkr = MemTracker::get_virtual_memory_uncommit_tracker();
- bool res = pd_uncommit_memory(addr, bytes);
- if (res) {
- tkr.record((address)addr, bytes);
+ bool res;
+ if (MemTracker::tracking_level() > NMT_minimal) {
+ Tracker tkr = MemTracker::get_virtual_memory_uncommit_tracker();
+ res = pd_uncommit_memory(addr, bytes);
+ if (res) {
+ tkr.record((address)addr, bytes);
+ }
} else {
- tkr.discard();
+ res = pd_uncommit_memory(addr, bytes);
}
return res;
}
bool os::release_memory(char* addr, size_t bytes) {
- MemTracker::Tracker tkr = MemTracker::get_virtual_memory_release_tracker();
- bool res = pd_release_memory(addr, bytes);
- if (res) {
- tkr.record((address)addr, bytes);
+ bool res;
+ if (MemTracker::tracking_level() > NMT_minimal) {
+ Tracker tkr = MemTracker::get_virtual_memory_release_tracker();
+ res = pd_release_memory(addr, bytes);
+ if (res) {
+ tkr.record((address)addr, bytes);
+ }
} else {
- tkr.discard();
+ res = pd_release_memory(addr, bytes);
}
return res;
}
@@ -1491,7 +1509,7 @@
bool allow_exec) {
char* result = pd_map_memory(fd, file_name, file_offset, addr, bytes, read_only, allow_exec);
if (result != NULL) {
- MemTracker::record_virtual_memory_reserve_and_commit((address)result, bytes, mtNone, CALLER_PC);
+ MemTracker::record_virtual_memory_reserve_and_commit((address)result, bytes, CALLER_PC);
}
return result;
}
@@ -1504,12 +1522,15 @@
}
bool os::unmap_memory(char *addr, size_t bytes) {
- MemTracker::Tracker tkr = MemTracker::get_virtual_memory_release_tracker();
- bool result = pd_unmap_memory(addr, bytes);
- if (result) {
- tkr.record((address)addr, bytes);
+ bool result;
+ if (MemTracker::tracking_level() > NMT_minimal) {
+ Tracker tkr = MemTracker::get_virtual_memory_release_tracker();
+ result = pd_unmap_memory(addr, bytes);
+ if (result) {
+ tkr.record((address)addr, bytes);
+ }
} else {
- tkr.discard();
+ result = pd_unmap_memory(addr, bytes);
}
return result;
}
--- a/hotspot/src/share/vm/runtime/os.hpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/runtime/os.hpp Thu Aug 07 12:18:58 2014 -0700
@@ -65,6 +65,8 @@
class Event;
class DLL;
class FileHandle;
+class NativeCallStack;
+
template<class E> class GrowableArray;
// %%%%% Moved ThreadState, START_FN, OSThread to new osThread.hpp. -- Rose
@@ -96,9 +98,11 @@
// Typedef for structured exception handling support
typedef void (*java_call_t)(JavaValue* value, methodHandle* method, JavaCallArguments* args, Thread* thread);
+class MallocTracker;
+
class os: AllStatic {
friend class VMStructs;
-
+ friend class MallocTracker;
public:
enum { page_sizes_max = 9 }; // Size of _page_sizes array (8 plus a sentinel)
@@ -160,7 +164,10 @@
// Override me as needed
static int file_name_strcmp(const char* s1, const char* s2);
+ // get/unset environment variable
static bool getenv(const char* name, char* buffer, int len);
+ static bool unsetenv(const char* name);
+
static bool have_special_privileges();
static jlong javaTimeMillis();
@@ -207,8 +214,13 @@
// Interface for detecting multiprocessor system
static inline bool is_MP() {
+#if !INCLUDE_NMT
assert(_processor_count > 0, "invalid processor count");
return _processor_count > 1 || AssumeMP;
+#else
+ // NMT needs atomic operations before this initialization.
+ return true;
+#endif
}
static julong available_memory();
static julong physical_memory();
@@ -635,12 +647,20 @@
static void* thread_local_storage_at(int index);
static void free_thread_local_storage(int index);
- // Stack walk
- static address get_caller_pc(int n = 0);
+ // Retrieve native stack frames.
+ // Parameter:
+ // stack: an array to storage stack pointers.
+ // frames: size of above array.
+ // toSkip: number of stack frames to skip at the beginning.
+ // Return: number of stack frames captured.
+ static int get_native_stack(address* stack, int size, int toSkip = 0);
// General allocation (must be MT-safe)
- static void* malloc (size_t size, MEMFLAGS flags, address caller_pc = 0);
- static void* realloc (void *memblock, size_t size, MEMFLAGS flags, address caller_pc = 0);
+ static void* malloc (size_t size, MEMFLAGS flags, const NativeCallStack& stack);
+ static void* malloc (size_t size, MEMFLAGS flags);
+ static void* realloc (void *memblock, size_t size, MEMFLAGS flag, const NativeCallStack& stack);
+ static void* realloc (void *memblock, size_t size, MEMFLAGS flag);
+
static void free (void *memblock, MEMFLAGS flags = mtNone);
static bool check_heap(bool force = false); // verify C heap integrity
static char* strdup(const char *, MEMFLAGS flags = mtInternal); // Like strdup
--- a/hotspot/src/share/vm/runtime/safepoint.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/runtime/safepoint.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -52,7 +52,6 @@
#include "runtime/sweeper.hpp"
#include "runtime/synchronizer.hpp"
#include "runtime/thread.inline.hpp"
-#include "services/memTracker.hpp"
#include "services/runtimeService.hpp"
#include "utilities/events.hpp"
#include "utilities/macros.hpp"
@@ -527,10 +526,6 @@
TraceTime t7("purging class loader data graph", TraceSafepointCleanupTime);
ClassLoaderDataGraph::purge_if_needed();
}
-
- if (MemTracker::is_on()) {
- MemTracker::sync();
- }
}
--- a/hotspot/src/share/vm/runtime/thread.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/runtime/thread.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -297,8 +297,7 @@
#if INCLUDE_NMT
// record thread's native stack, stack grows downward
address stack_low_addr = stack_base() - stack_size();
- MemTracker::record_thread_stack(stack_low_addr, stack_size(), this,
- CURRENT_PC);
+ MemTracker::record_thread_stack(stack_low_addr, stack_size());
#endif // INCLUDE_NMT
}
@@ -316,7 +315,7 @@
#if INCLUDE_NMT
if (_stack_base != NULL) {
address low_stack_addr = stack_base() - stack_size();
- MemTracker::release_thread_stack(low_stack_addr, stack_size(), this);
+ MemTracker::release_thread_stack(low_stack_addr, stack_size());
#ifdef ASSERT
set_stack_base(NULL);
#endif
@@ -1425,9 +1424,6 @@
set_monitor_chunks(NULL);
set_next(NULL);
set_thread_state(_thread_new);
-#if INCLUDE_NMT
- set_recorder(NULL);
-#endif
_terminated = _not_terminated;
_privileged_stack_top = NULL;
_array_for_gc = NULL;
@@ -1503,7 +1499,6 @@
_jni_attach_state = _not_attaching_via_jni;
}
assert(deferred_card_mark().is_empty(), "Default MemRegion ctor");
- _safepoint_visible = false;
}
bool JavaThread::reguard_stack(address cur_sp) {
@@ -1566,7 +1561,6 @@
thr_type = entry_point == &compiler_thread_entry ? os::compiler_thread :
os::java_thread;
os::create_thread(this, thr_type, stack_sz);
- _safepoint_visible = false;
// The _osthread may be NULL here because we ran out of memory (too many threads active).
// We need to throw and OutOfMemoryError - however we cannot do this here because the caller
// may hold a lock and all locks must be unlocked before throwing the exception (throwing
@@ -1584,13 +1578,6 @@
tty->print_cr("terminate thread %p", this);
}
- // By now, this thread should already be invisible to safepoint,
- // and its per-thread recorder also collected.
- assert(!is_safepoint_visible(), "wrong state");
-#if INCLUDE_NMT
- assert(get_recorder() == NULL, "Already collected");
-#endif // INCLUDE_NMT
-
// JSR166 -- return the parker to the free list
Parker::Release(_parker);
_parker = NULL;
@@ -3359,11 +3346,6 @@
// initialize TLS
ThreadLocalStorage::init();
- // Bootstrap native memory tracking, so it can start recording memory
- // activities before worker thread is started. This is the first phase
- // of bootstrapping, VM is currently running in single-thread mode.
- MemTracker::bootstrap_single_thread();
-
// Initialize output stream logging
ostream_init_log();
@@ -3414,9 +3396,6 @@
// Initialize Java-Level synchronization subsystem
ObjectMonitor::Initialize();
- // Second phase of bootstrapping, VM is about entering multi-thread mode
- MemTracker::bootstrap_multi_thread();
-
// Initialize global modules
jint status = init_globals();
if (status != JNI_OK) {
@@ -3438,9 +3417,6 @@
// real raw monitor. VM is setup enough here for raw monitor enter.
JvmtiExport::transition_pending_onload_raw_monitors();
- // Fully start NMT
- MemTracker::start();
-
// Create the VMThread
{ TraceTime timer("Start VMThread", TraceStartupTime);
VMThread::create();
@@ -3995,8 +3971,6 @@
daemon = false;
}
- p->set_safepoint_visible(true);
-
ThreadService::add_thread(p, daemon);
// Possible GC point.
@@ -4042,13 +4016,6 @@
// to do callbacks into the safepoint code. However, the safepoint code is not aware
// of this thread since it is removed from the queue.
p->set_terminated_value();
-
- // Now, this thread is not visible to safepoint
- p->set_safepoint_visible(false);
- // once the thread becomes safepoint invisible, we can not use its per-thread
- // recorder. And Threads::do_threads() no longer walks this thread, so we have
- // to release its per-thread recorder here.
- MemTracker::thread_exiting(p);
} // unlock Threads_lock
// Since Events::log uses a lock, we grab it outside the Threads_lock
--- a/hotspot/src/share/vm/runtime/thread.hpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/runtime/thread.hpp Thu Aug 07 12:18:58 2014 -0700
@@ -43,10 +43,6 @@
#include "runtime/unhandledOops.hpp"
#include "utilities/macros.hpp"
-#if INCLUDE_NMT
-#include "services/memRecorder.hpp"
-#endif // INCLUDE_NMT
-
#include "trace/traceBackend.hpp"
#include "trace/traceMacros.hpp"
#include "utilities/exceptions.hpp"
@@ -1036,16 +1032,6 @@
bool do_not_unlock_if_synchronized() { return _do_not_unlock_if_synchronized; }
void set_do_not_unlock_if_synchronized(bool val) { _do_not_unlock_if_synchronized = val; }
-#if INCLUDE_NMT
- // native memory tracking
- inline MemRecorder* get_recorder() const { return (MemRecorder*)_recorder; }
- inline void set_recorder(MemRecorder* rc) { _recorder = rc; }
-
- private:
- // per-thread memory recorder
- MemRecorder* volatile _recorder;
-#endif // INCLUDE_NMT
-
// Suspend/resume support for JavaThread
private:
inline void set_ext_suspended();
@@ -1485,19 +1471,6 @@
return result;
}
- // NMT (Native memory tracking) support.
- // This flag helps NMT to determine if this JavaThread will be blocked
- // at safepoint. If not, ThreadCritical is needed for writing memory records.
- // JavaThread is only safepoint visible when it is in Threads' thread list,
- // it is not visible until it is added to the list and becomes invisible
- // once it is removed from the list.
- public:
- bool is_safepoint_visible() const { return _safepoint_visible; }
- void set_safepoint_visible(bool visible) { _safepoint_visible = visible; }
- private:
- bool _safepoint_visible;
-
- // Static operations
public:
// Returns the running thread as a JavaThread
static inline JavaThread* current();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/services/allocationSite.hpp Thu Aug 07 12:18:58 2014 -0700
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#ifndef SHARE_VM_SERVICES_ALLOCATION_SITE_HPP
+#define SHARE_VM_SERVICES_ALLOCATION_SITE_HPP
+
+#include "memory/allocation.hpp"
+#include "utilities/nativeCallStack.hpp"
+
+// Allocation site represents a code path that makes a memory
+// allocation
+template <class E> class AllocationSite VALUE_OBJ_CLASS_SPEC {
+ private:
+ NativeCallStack _call_stack;
+ E e;
+ public:
+ AllocationSite(const NativeCallStack& stack) : _call_stack(stack) { }
+ int hash() const { return _call_stack.hash(); }
+ bool equals(const NativeCallStack& stack) const {
+ return _call_stack.equals(stack);
+ }
+
+ bool equals(const AllocationSite<E>& other) const {
+ return other.equals(_call_stack);
+ }
+
+ const NativeCallStack* call_stack() const {
+ return &_call_stack;
+ }
+
+ // Information regarding this allocation
+ E* data() { return &e; }
+ const E* peek() const { return &e; }
+};
+
+#endif // SHARE_VM_SERVICES_ALLOCATION_SITE_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/services/mallocSiteTable.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+#include "precompiled.hpp"
+
+
+#include "memory/allocation.inline.hpp"
+#include "runtime/atomic.hpp"
+#include "services/mallocSiteTable.hpp"
+
+/*
+ * Early os::malloc() calls come from initializations of static variables, long before entering any
+ * VM code. Upon the arrival of the first os::malloc() call, malloc site hashtable has to be
+ * initialized, along with the allocation site for the hashtable entries.
+ * To ensure that malloc site hashtable can be initialized without triggering any additional os::malloc()
+ * call, the hashtable bucket array and hashtable entry allocation site have to be static.
+ * It is not a problem for hashtable bucket, since it is an array of pointer type, C runtime just
+ * allocates a block memory and zero the memory for it.
+ * But for hashtable entry allocation site object, things get tricky. C runtime not only allocates
+ * memory for it, but also calls its constructor at some later time. If we initialize the allocation site
+ * at the first os::malloc() call, the object will be reinitialized when its constructor is called
+ * by C runtime.
+ * To workaround above issue, we declare a static size_t array with the size of the CallsiteHashtableEntry,
+ * the memory is used to instantiate CallsiteHashtableEntry for the hashtable entry allocation site.
+ * Given it is a primitive type array, C runtime will do nothing other than assign the memory block for the variable,
+ * which is exactly what we want.
+ * The same trick is also applied to create NativeCallStack object for CallsiteHashtableEntry memory allocation.
+ *
+ * Note: C++ object usually aligns to particular alignment, depends on compiler implementation, we declare
+ * the memory as size_t arrays, to ensure the memory is aligned to native machine word alignment.
+ */
+
+// Reserve enough memory for NativeCallStack and MallocSiteHashtableEntry objects
+size_t MallocSiteTable::_hash_entry_allocation_stack[CALC_OBJ_SIZE_IN_TYPE(NativeCallStack, size_t)];
+size_t MallocSiteTable::_hash_entry_allocation_site[CALC_OBJ_SIZE_IN_TYPE(MallocSiteHashtableEntry, size_t)];
+
+// Malloc site hashtable buckets
+MallocSiteHashtableEntry* MallocSiteTable::_table[MallocSiteTable::table_size];
+
+// concurrent access counter
+volatile int MallocSiteTable::_access_count = 0;
+
+// Tracking hashtable contention
+NOT_PRODUCT(int MallocSiteTable::_peak_count = 0;)
+
+
+/*
+ * Initialize malloc site table.
+ * Hashtable entry is malloc'd, so it can cause infinite recursion.
+ * To avoid above problem, we pre-initialize a hash entry for
+ * this allocation site.
+ * The method is called during C runtime static variable initialization
+ * time, it is in single-threaded mode from JVM perspective.
+ */
+bool MallocSiteTable::initialize() {
+ assert(sizeof(_hash_entry_allocation_stack) >= sizeof(NativeCallStack), "Sanity Check");
+ assert(sizeof(_hash_entry_allocation_site) >= sizeof(MallocSiteHashtableEntry),
+ "Sanity Check");
+ assert((size_t)table_size <= MAX_MALLOCSITE_TABLE_SIZE, "Hashtable overflow");
+
+ // Fake the call stack for hashtable entry allocation
+ assert(NMT_TrackingStackDepth > 1, "At least one tracking stack");
+
+ // Create pseudo call stack for hashtable entry allocation
+ address pc[3];
+ if (NMT_TrackingStackDepth >= 3) {
+ pc[2] = (address)MallocSiteTable::allocation_at;
+ }
+ if (NMT_TrackingStackDepth >= 2) {
+ pc[1] = (address)MallocSiteTable::lookup_or_add;
+ }
+ pc[0] = (address)MallocSiteTable::new_entry;
+
+ // Instantiate NativeCallStack object, have to use placement new operator. (see comments above)
+ NativeCallStack* stack = ::new ((void*)_hash_entry_allocation_stack)
+ NativeCallStack(pc, MIN2(((int)(sizeof(pc) / sizeof(address))), ((int)NMT_TrackingStackDepth)));
+
+ // Instantiate hash entry for hashtable entry allocation callsite
+ MallocSiteHashtableEntry* entry = ::new ((void*)_hash_entry_allocation_site)
+ MallocSiteHashtableEntry(*stack);
+
+ // Add the allocation site to hashtable.
+ int index = hash_to_index(stack->hash());
+ _table[index] = entry;
+
+ return true;
+}
+
+// Walks entries in the hashtable.
+// It stops walk if the walker returns false.
+bool MallocSiteTable::walk(MallocSiteWalker* walker) {
+ MallocSiteHashtableEntry* head;
+ for (int index = 0; index < table_size; index ++) {
+ head = _table[index];
+ while (head != NULL) {
+ if (!walker->do_malloc_site(head->peek())) {
+ return false;
+ }
+ head = (MallocSiteHashtableEntry*)head->next();
+ }
+ }
+ return true;
+}
+
+/*
+ * The hashtable does not have deletion policy on individual entry,
+ * and each linked list node is inserted via compare-and-swap,
+ * so each linked list is stable, the contention only happens
+ * at the end of linked list.
+ * This method should not return NULL under normal circumstance.
+ * If NULL is returned, it indicates:
+ * 1. Out of memory, it cannot allocate new hash entry.
+ * 2. Overflow hash bucket.
+ * Under any of above circumstances, caller should handle the situation.
+ */
+MallocSite* MallocSiteTable::lookup_or_add(const NativeCallStack& key, size_t* bucket_idx,
+ size_t* pos_idx) {
+ int index = hash_to_index(key.hash());
+ assert(index >= 0, "Negative index");
+ *bucket_idx = (size_t)index;
+ *pos_idx = 0;
+
+ // First entry for this hash bucket
+ if (_table[index] == NULL) {
+ MallocSiteHashtableEntry* entry = new_entry(key);
+ // OOM check
+ if (entry == NULL) return NULL;
+
+ // swap in the head
+ if (Atomic::cmpxchg_ptr((void*)entry, (volatile void *)&_table[index], NULL) == NULL) {
+ return entry->data();
+ }
+
+ delete entry;
+ }
+
+ MallocSiteHashtableEntry* head = _table[index];
+ while (head != NULL && (*pos_idx) <= MAX_BUCKET_LENGTH) {
+ MallocSite* site = head->data();
+ if (site->equals(key)) {
+ // found matched entry
+ return head->data();
+ }
+
+ if (head->next() == NULL && (*pos_idx) < MAX_BUCKET_LENGTH) {
+ MallocSiteHashtableEntry* entry = new_entry(key);
+ // OOM check
+ if (entry == NULL) return NULL;
+ if (head->atomic_insert(entry)) {
+ (*pos_idx) ++;
+ return entry->data();
+ }
+ // contended, other thread won
+ delete entry;
+ }
+ head = (MallocSiteHashtableEntry*)head->next();
+ (*pos_idx) ++;
+ }
+ return NULL;
+}
+
+// Access malloc site
+MallocSite* MallocSiteTable::malloc_site(size_t bucket_idx, size_t pos_idx) {
+ assert(bucket_idx < table_size, "Invalid bucket index");
+ MallocSiteHashtableEntry* head = _table[bucket_idx];
+ for (size_t index = 0; index < pos_idx && head != NULL;
+ index ++, head = (MallocSiteHashtableEntry*)head->next());
+ assert(head != NULL, "Invalid position index");
+ return head->data();
+}
+
+// Allocates MallocSiteHashtableEntry object. Special call stack
+// (pre-installed allocation site) has to be used to avoid infinite
+// recursion.
+MallocSiteHashtableEntry* MallocSiteTable::new_entry(const NativeCallStack& key) {
+ void* p = AllocateHeap(sizeof(MallocSiteHashtableEntry), mtNMT,
+ *hash_entry_allocation_stack(), AllocFailStrategy::RETURN_NULL);
+ return ::new (p) MallocSiteHashtableEntry(key);
+}
+
+void MallocSiteTable::reset() {
+ for (int index = 0; index < table_size; index ++) {
+ MallocSiteHashtableEntry* head = _table[index];
+ _table[index] = NULL;
+ delete_linked_list(head);
+ }
+}
+
+void MallocSiteTable::delete_linked_list(MallocSiteHashtableEntry* head) {
+ MallocSiteHashtableEntry* p;
+ while (head != NULL) {
+ p = head;
+ head = (MallocSiteHashtableEntry*)head->next();
+ if (p != (MallocSiteHashtableEntry*)_hash_entry_allocation_site) {
+ delete p;
+ }
+ }
+}
+
+void MallocSiteTable::shutdown() {
+ AccessLock locker(&_access_count);
+ locker.exclusiveLock();
+ reset();
+}
+
+bool MallocSiteTable::walk_malloc_site(MallocSiteWalker* walker) {
+ assert(walker != NULL, "NuLL walker");
+ AccessLock locker(&_access_count);
+ if (locker.sharedLock()) {
+ NOT_PRODUCT(_peak_count = MAX2(_peak_count, _access_count);)
+ return walk(walker);
+ }
+ return false;
+}
+
+
+void MallocSiteTable::AccessLock::exclusiveLock() {
+ jint target;
+ jint val;
+
+ assert(_lock_state != ExclusiveLock, "Can only call once");
+ assert(*_lock >= 0, "Can not content exclusive lock");
+
+ // make counter negative to block out shared locks
+ do {
+ val = *_lock;
+ target = _MAGIC_ + *_lock;
+ } while (Atomic::cmpxchg(target, _lock, val) != val);
+
+ // wait for all readers to exit
+ while (*_lock != _MAGIC_) {
+#ifdef _WINDOWS
+ os::naked_short_sleep(1);
+#else
+ os::naked_yield();
+#endif
+ }
+ _lock_state = ExclusiveLock;
+}
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/services/mallocSiteTable.hpp Thu Aug 07 12:18:58 2014 -0700
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#ifndef SHARE_VM_SERVICES_MALLOC_SITE_TABLE_HPP
+#define SHARE_VM_SERVICES_MALLOC_SITE_TABLE_HPP
+
+#if INCLUDE_NMT
+
+#include "memory/allocation.hpp"
+#include "runtime/atomic.hpp"
+#include "services/allocationSite.hpp"
+#include "services/mallocTracker.hpp"
+#include "services/nmtCommon.hpp"
+
+// MallocSite represents a code path that eventually calls
+// os::malloc() to allocate memory
+class MallocSite : public AllocationSite<MemoryCounter> {
+ public:
+ MallocSite() :
+ AllocationSite<MemoryCounter>(emptyStack) { }
+
+ MallocSite(const NativeCallStack& stack) :
+ AllocationSite<MemoryCounter>(stack) { }
+
+ void allocate(size_t size) { data()->allocate(size); }
+ void deallocate(size_t size) { data()->deallocate(size); }
+
+ // Memory allocated from this code path
+ size_t size() const { return peek()->size(); }
+ // The number of calls were made
+ size_t count() const { return peek()->count(); }
+};
+
+// Malloc site hashtable entry
+class MallocSiteHashtableEntry : public CHeapObj<mtNMT> {
+ private:
+ MallocSite _malloc_site;
+ MallocSiteHashtableEntry* _next;
+
+ public:
+ MallocSiteHashtableEntry() : _next(NULL) { }
+
+ MallocSiteHashtableEntry(NativeCallStack stack):
+ _malloc_site(stack), _next(NULL) { }
+
+ inline const MallocSiteHashtableEntry* next() const {
+ return _next;
+ }
+
+ // Insert an entry atomically.
+ // Return true if the entry is inserted successfully.
+ // The operation can be failed due to contention from other thread.
+ bool atomic_insert(const MallocSiteHashtableEntry* entry) {
+ return (Atomic::cmpxchg_ptr((void*)entry, (volatile void*)&_next,
+ NULL) == NULL);
+ }
+
+ void set_callsite(const MallocSite& site) {
+ _malloc_site = site;
+ }
+
+ inline const MallocSite* peek() const { return &_malloc_site; }
+ inline MallocSite* data() { return &_malloc_site; }
+
+ inline long hash() const { return _malloc_site.hash(); }
+ inline bool equals(const NativeCallStack& stack) const {
+ return _malloc_site.equals(stack);
+ }
+ // Allocation/deallocation on this allocation site
+ inline void allocate(size_t size) { _malloc_site.allocate(size); }
+ inline void deallocate(size_t size) { _malloc_site.deallocate(size); }
+ // Memory counters
+ inline size_t size() const { return _malloc_site.size(); }
+ inline size_t count() const { return _malloc_site.count(); }
+};
+
+// The walker walks every entry on MallocSiteTable
+class MallocSiteWalker : public StackObj {
+ public:
+ virtual bool do_malloc_site(const MallocSite* e) { return false; }
+};
+
+/*
+ * Native memory tracking call site table.
+ * The table is only needed when detail tracking is enabled.
+ */
+class MallocSiteTable : AllStatic {
+ private:
+ // The number of hash bucket in this hashtable. The number should
+ // be tuned if malloc activities changed significantly.
+ // The statistics data can be obtained via Jcmd
+ // jcmd <pid> VM.native_memory statistics.
+
+ // Currently, (number of buckets / number of entires) ratio is
+ // about 1 / 6
+ enum {
+ table_base_size = 128, // The base size is calculated from statistics to give
+ // table ratio around 1:6
+ table_size = (table_base_size * NMT_TrackingStackDepth - 1)
+ };
+
+
+ // This is a very special lock, that allows multiple shared accesses (sharedLock), but
+ // once exclusive access (exclusiveLock) is requested, all shared accesses are
+ // rejected forever.
+ class AccessLock : public StackObj {
+ enum LockState {
+ NoLock,
+ SharedLock,
+ ExclusiveLock
+ };
+
+ private:
+ // A very large negative number. The only possibility to "overflow"
+ // this number is when there are more than -min_jint threads in
+ // this process, which is not going to happen in foreseeable future.
+ const static int _MAGIC_ = min_jint;
+
+ LockState _lock_state;
+ volatile int* _lock;
+ public:
+ AccessLock(volatile int* lock) :
+ _lock(lock), _lock_state(NoLock) {
+ }
+
+ ~AccessLock() {
+ if (_lock_state == SharedLock) {
+ Atomic::dec((volatile jint*)_lock);
+ }
+ }
+ // Acquire shared lock.
+ // Return true if shared access is granted.
+ inline bool sharedLock() {
+ jint res = Atomic::add(1, _lock);
+ if (res < 0) {
+ Atomic::add(-1, _lock);
+ return false;
+ }
+ _lock_state = SharedLock;
+ return true;
+ }
+ // Acquire exclusive lock
+ void exclusiveLock();
+ };
+
+ public:
+ static bool initialize();
+ static void shutdown();
+
+ NOT_PRODUCT(static int access_peak_count() { return _peak_count; })
+
+ // Number of hash buckets
+ static inline int hash_buckets() { return (int)table_size; }
+
+ // Access and copy a call stack from this table. Shared lock should be
+ // acquired before access the entry.
+ static inline bool access_stack(NativeCallStack& stack, size_t bucket_idx,
+ size_t pos_idx) {
+ AccessLock locker(&_access_count);
+ if (locker.sharedLock()) {
+ NOT_PRODUCT(_peak_count = MAX2(_peak_count, _access_count);)
+ MallocSite* site = malloc_site(bucket_idx, pos_idx);
+ if (site != NULL) {
+ stack = *site->call_stack();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Record a new allocation from specified call path.
+ // Return true if the allocation is recorded successfully, bucket_idx
+ // and pos_idx are also updated to indicate the entry where the allocation
+ // information was recorded.
+ // Return false only occurs under rare scenarios:
+ // 1. out of memory
+ // 2. overflow hash bucket
+ static inline bool allocation_at(const NativeCallStack& stack, size_t size,
+ size_t* bucket_idx, size_t* pos_idx) {
+ AccessLock locker(&_access_count);
+ if (locker.sharedLock()) {
+ NOT_PRODUCT(_peak_count = MAX2(_peak_count, _access_count);)
+ MallocSite* site = lookup_or_add(stack, bucket_idx, pos_idx);
+ if (site != NULL) site->allocate(size);
+ return site != NULL;
+ }
+ return false;
+ }
+
+ // Record memory deallocation. bucket_idx and pos_idx indicate where the allocation
+ // information was recorded.
+ static inline bool deallocation_at(size_t size, size_t bucket_idx, size_t pos_idx) {
+ AccessLock locker(&_access_count);
+ if (locker.sharedLock()) {
+ NOT_PRODUCT(_peak_count = MAX2(_peak_count, _access_count);)
+ MallocSite* site = malloc_site(bucket_idx, pos_idx);
+ if (site != NULL) {
+ site->deallocate(size);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Walk this table.
+ static bool walk_malloc_site(MallocSiteWalker* walker);
+
+ private:
+ static MallocSiteHashtableEntry* new_entry(const NativeCallStack& key);
+ static void reset();
+
+ // Delete a bucket linked list
+ static void delete_linked_list(MallocSiteHashtableEntry* head);
+
+ static MallocSite* lookup_or_add(const NativeCallStack& key, size_t* bucket_idx, size_t* pos_idx);
+ static MallocSite* malloc_site(size_t bucket_idx, size_t pos_idx);
+ static bool walk(MallocSiteWalker* walker);
+
+ static inline int hash_to_index(int hash) {
+ hash = (hash > 0) ? hash : (-hash);
+ return (hash % table_size);
+ }
+
+ static inline const NativeCallStack* hash_entry_allocation_stack() {
+ return (NativeCallStack*)_hash_entry_allocation_stack;
+ }
+
+ private:
+ // Counter for counting concurrent access
+ static volatile int _access_count;
+
+ // The callsite hashtable. It has to be a static table,
+ // since malloc call can come from C runtime linker.
+ static MallocSiteHashtableEntry* _table[table_size];
+
+
+ // Reserve enough memory for placing the objects
+
+ // The memory for hashtable entry allocation stack object
+ static size_t _hash_entry_allocation_stack[CALC_OBJ_SIZE_IN_TYPE(NativeCallStack, size_t)];
+ // The memory for hashtable entry allocation callsite object
+ static size_t _hash_entry_allocation_site[CALC_OBJ_SIZE_IN_TYPE(MallocSiteHashtableEntry, size_t)];
+ NOT_PRODUCT(static int _peak_count;)
+};
+
+#endif // INCLUDE_NMT
+#endif // SHARE_VM_SERVICES_MALLOC_SITE_TABLE_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/services/mallocTracker.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+#include "precompiled.hpp"
+
+#include "runtime/atomic.hpp"
+#include "runtime/atomic.inline.hpp"
+#include "services/mallocSiteTable.hpp"
+#include "services/mallocTracker.hpp"
+#include "services/mallocTracker.inline.hpp"
+#include "services/memTracker.hpp"
+
+size_t MallocMemorySummary::_snapshot[CALC_OBJ_SIZE_IN_TYPE(MallocMemorySnapshot, size_t)];
+
+// Total malloc'd memory amount
+size_t MallocMemorySnapshot::total() const {
+ size_t amount = 0;
+ for (int index = 0; index < mt_number_of_types; index ++) {
+ amount += _malloc[index].malloc_size();
+ }
+ amount += _tracking_header.size() + total_arena();
+ return amount;
+}
+
+// Total malloc'd memory used by arenas
+size_t MallocMemorySnapshot::total_arena() const {
+ size_t amount = 0;
+ for (int index = 0; index < mt_number_of_types; index ++) {
+ amount += _malloc[index].arena_size();
+ }
+ return amount;
+}
+
+
+void MallocMemorySnapshot::reset() {
+ _tracking_header.reset();
+ for (int index = 0; index < mt_number_of_types; index ++) {
+ _malloc[index].reset();
+ }
+}
+
+// Make adjustment by subtracting chunks used by arenas
+// from total chunks to get total free chunck size
+void MallocMemorySnapshot::make_adjustment() {
+ size_t arena_size = total_arena();
+ int chunk_idx = NMTUtil::flag_to_index(mtChunk);
+ _malloc[chunk_idx].record_free(arena_size);
+}
+
+
+void MallocMemorySummary::initialize() {
+ assert(sizeof(_snapshot) >= sizeof(MallocMemorySnapshot), "Sanity Check");
+ // Uses placement new operator to initialize static area.
+ ::new ((void*)_snapshot)MallocMemorySnapshot();
+}
+
+void MallocHeader::release() const {
+ // Tracking already shutdown, no housekeeping is needed anymore
+ if (MemTracker::tracking_level() <= NMT_minimal) return;
+
+ MallocMemorySummary::record_free(size(), flags());
+ MallocMemorySummary::record_free_malloc_header(sizeof(MallocHeader));
+ if (tracking_level() == NMT_detail) {
+ MallocSiteTable::deallocation_at(size(), _bucket_idx, _pos_idx);
+ }
+}
+
+bool MallocHeader::record_malloc_site(const NativeCallStack& stack, size_t size,
+ size_t* bucket_idx, size_t* pos_idx) const {
+ bool ret = MallocSiteTable::allocation_at(stack, size, bucket_idx, pos_idx);
+
+ // Something went wrong, could be OOM or overflow malloc site table.
+ // We want to keep tracking data under OOM circumstance, so transition to
+ // summary tracking.
+ if (!ret) {
+ MemTracker::transition_to(NMT_summary);
+ }
+ return ret;
+}
+
+bool MallocHeader::get_stack(NativeCallStack& stack) const {
+ return MallocSiteTable::access_stack(stack, _bucket_idx, _pos_idx);
+}
+
+bool MallocTracker::initialize(NMT_TrackingLevel level) {
+ if (level >= NMT_summary) {
+ MallocMemorySummary::initialize();
+ }
+
+ if (level == NMT_detail) {
+ return MallocSiteTable::initialize();
+ }
+ return true;
+}
+
+bool MallocTracker::transition(NMT_TrackingLevel from, NMT_TrackingLevel to) {
+ assert(from != NMT_off, "Can not transition from off state");
+ assert(to != NMT_off, "Can not transition to off state");
+ if (from == NMT_minimal) {
+ MallocMemorySummary::reset();
+ }
+
+ if (to == NMT_detail) {
+ assert(from == NMT_minimal || from == NMT_summary, "Just check");
+ return MallocSiteTable::initialize();
+ } else if (from == NMT_detail) {
+ assert(to == NMT_minimal || to == NMT_summary, "Just check");
+ MallocSiteTable::shutdown();
+ }
+ return true;
+}
+
+// Record a malloc memory allocation
+void* MallocTracker::record_malloc(void* malloc_base, size_t size, MEMFLAGS flags,
+ const NativeCallStack& stack, NMT_TrackingLevel level) {
+ void* memblock; // the address for user data
+ MallocHeader* header = NULL;
+
+ if (malloc_base == NULL) {
+ return NULL;
+ }
+
+ // Check malloc size, size has to <= MAX_MALLOC_SIZE. This is only possible on 32-bit
+ // systems, when malloc size >= 1GB, but is is safe to assume it won't happen.
+ if (size > MAX_MALLOC_SIZE) {
+ fatal("Should not use malloc for big memory block, use virtual memory instead");
+ }
+ // Uses placement global new operator to initialize malloc header
+ switch(level) {
+ case NMT_off:
+ return malloc_base;
+ case NMT_minimal: {
+ MallocHeader* hdr = ::new (malloc_base) MallocHeader();
+ break;
+ }
+ case NMT_summary: {
+ header = ::new (malloc_base) MallocHeader(size, flags);
+ break;
+ }
+ case NMT_detail: {
+ header = ::new (malloc_base) MallocHeader(size, flags, stack);
+ break;
+ }
+ default:
+ ShouldNotReachHere();
+ }
+ memblock = (void*)((char*)malloc_base + sizeof(MallocHeader));
+
+ // The alignment check: 8 bytes alignment for 32 bit systems.
+ // 16 bytes alignment for 64-bit systems.
+ assert(((size_t)memblock & (sizeof(size_t) * 2 - 1)) == 0, "Alignment check");
+
+ // Sanity check
+ assert(get_memory_tracking_level(memblock) == level,
+ "Wrong tracking level");
+
+#ifdef ASSERT
+ if (level > NMT_minimal) {
+ // Read back
+ assert(get_size(memblock) == size, "Wrong size");
+ assert(get_flags(memblock) == flags, "Wrong flags");
+ }
+#endif
+
+ return memblock;
+}
+
+void* MallocTracker::record_free(void* memblock) {
+ // Never turned on
+ if (MemTracker::tracking_level() == NMT_off ||
+ memblock == NULL) {
+ return memblock;
+ }
+ MallocHeader* header = malloc_header(memblock);
+ header->release();
+
+ return (void*)header;
+}
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/services/mallocTracker.hpp Thu Aug 07 12:18:58 2014 -0700
@@ -0,0 +1,424 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#ifndef SHARE_VM_SERVICES_MALLOC_TRACKER_HPP
+#define SHARE_VM_SERVICES_MALLOC_TRACKER_HPP
+
+#if INCLUDE_NMT
+
+#include "memory/allocation.hpp"
+#include "runtime/atomic.hpp"
+#include "services/nmtCommon.hpp"
+#include "utilities/nativeCallStack.hpp"
+
+/*
+ * This counter class counts memory allocation and deallocation,
+ * records total memory allocation size and number of allocations.
+ * The counters are updated atomically.
+ */
+class MemoryCounter VALUE_OBJ_CLASS_SPEC {
+ private:
+ size_t _count;
+ size_t _size;
+
+ DEBUG_ONLY(size_t _peak_count;)
+ DEBUG_ONLY(size_t _peak_size; )
+
+ public:
+ MemoryCounter() : _count(0), _size(0) {
+ DEBUG_ONLY(_peak_count = 0;)
+ DEBUG_ONLY(_peak_size = 0;)
+ }
+
+ // Reset counters
+ void reset() {
+ _size = 0;
+ _count = 0;
+ DEBUG_ONLY(_peak_size = 0;)
+ DEBUG_ONLY(_peak_count = 0;)
+ }
+
+ inline void allocate(size_t sz) {
+ Atomic::add(1, (volatile MemoryCounterType*)&_count);
+ if (sz > 0) {
+ Atomic::add((MemoryCounterType)sz, (volatile MemoryCounterType*)&_size);
+ DEBUG_ONLY(_peak_size = MAX2(_peak_size, _size));
+ }
+ DEBUG_ONLY(_peak_count = MAX2(_peak_count, _count);)
+ }
+
+ inline void deallocate(size_t sz) {
+ assert(_count > 0, "Negative counter");
+ assert(_size >= sz, "Negative size");
+ Atomic::add(-1, (volatile MemoryCounterType*)&_count);
+ if (sz > 0) {
+ Atomic::add(-(MemoryCounterType)sz, (volatile MemoryCounterType*)&_size);
+ }
+ }
+
+ inline void resize(long sz) {
+ if (sz != 0) {
+ Atomic::add((MemoryCounterType)sz, (volatile MemoryCounterType*)&_size);
+ DEBUG_ONLY(_peak_size = MAX2(_size, _peak_size);)
+ }
+ }
+
+ inline size_t count() const { return _count; }
+ inline size_t size() const { return _size; }
+ DEBUG_ONLY(inline size_t peak_count() const { return _peak_count; })
+ DEBUG_ONLY(inline size_t peak_size() const { return _peak_size; })
+
+};
+
+/*
+ * Malloc memory used by a particular subsystem.
+ * It includes the memory acquired through os::malloc()
+ * call and arena's backing memory.
+ */
+class MallocMemory VALUE_OBJ_CLASS_SPEC {
+ private:
+ MemoryCounter _malloc;
+ MemoryCounter _arena;
+
+ public:
+ MallocMemory() { }
+
+ inline void record_malloc(size_t sz) {
+ _malloc.allocate(sz);
+ }
+
+ inline void record_free(size_t sz) {
+ _malloc.deallocate(sz);
+ }
+
+ inline void record_new_arena() {
+ _arena.allocate(0);
+ }
+
+ inline void record_arena_free() {
+ _arena.deallocate(0);
+ }
+
+ inline void record_arena_size_change(long sz) {
+ _arena.resize(sz);
+ }
+
+ void reset() {
+ _malloc.reset();
+ _arena.reset();
+ }
+
+ inline size_t malloc_size() const { return _malloc.size(); }
+ inline size_t malloc_count() const { return _malloc.count();}
+ inline size_t arena_size() const { return _arena.size(); }
+ inline size_t arena_count() const { return _arena.count(); }
+
+ DEBUG_ONLY(inline const MemoryCounter& malloc_counter() const { return _malloc; })
+ DEBUG_ONLY(inline const MemoryCounter& arena_counter() const { return _arena; })
+};
+
+class MallocMemorySummary;
+
+// A snapshot of malloc'd memory, includes malloc memory
+// usage by types and memory used by tracking itself.
+class MallocMemorySnapshot : public ResourceObj {
+ friend class MallocMemorySummary;
+
+ private:
+ MallocMemory _malloc[mt_number_of_types];
+ MemoryCounter _tracking_header;
+
+
+ public:
+ inline MallocMemory* by_type(MEMFLAGS flags) {
+ int index = NMTUtil::flag_to_index(flags);
+ return &_malloc[index];
+ }
+
+ inline MallocMemory* by_index(int index) {
+ assert(index >= 0, "Index out of bound");
+ assert(index < mt_number_of_types, "Index out of bound");
+ return &_malloc[index];
+ }
+
+ inline MemoryCounter* malloc_overhead() {
+ return &_tracking_header;
+ }
+
+ // Total malloc'd memory amount
+ size_t total() const;
+ // Total malloc'd memory used by arenas
+ size_t total_arena() const;
+
+ inline size_t thread_count() {
+ return by_type(mtThreadStack)->malloc_count();
+ }
+
+ void reset();
+
+ void copy_to(MallocMemorySnapshot* s) {
+ s->_tracking_header = _tracking_header;
+ for (int index = 0; index < mt_number_of_types; index ++) {
+ s->_malloc[index] = _malloc[index];
+ }
+ }
+
+ // Make adjustment by subtracting chunks used by arenas
+ // from total chunks to get total free chunk size
+ void make_adjustment();
+};
+
+/*
+ * This class is for collecting malloc statistics at summary level
+ */
+class MallocMemorySummary : AllStatic {
+ private:
+ // Reserve memory for placement of MallocMemorySnapshot object
+ static size_t _snapshot[CALC_OBJ_SIZE_IN_TYPE(MallocMemorySnapshot, size_t)];
+
+ public:
+ static void initialize();
+
+ static inline void record_malloc(size_t size, MEMFLAGS flag) {
+ as_snapshot()->by_type(flag)->record_malloc(size);
+ }
+
+ static inline void record_free(size_t size, MEMFLAGS flag) {
+ as_snapshot()->by_type(flag)->record_free(size);
+ }
+
+ static inline void record_new_arena(MEMFLAGS flag) {
+ as_snapshot()->by_type(flag)->record_new_arena();
+ }
+
+ static inline void record_arena_free(MEMFLAGS flag) {
+ as_snapshot()->by_type(flag)->record_arena_free();
+ }
+
+ static inline void record_arena_size_change(long size, MEMFLAGS flag) {
+ as_snapshot()->by_type(flag)->record_arena_size_change(size);
+ }
+
+ static void snapshot(MallocMemorySnapshot* s) {
+ as_snapshot()->copy_to(s);
+ s->make_adjustment();
+ }
+
+ // Record memory used by malloc tracking header
+ static inline void record_new_malloc_header(size_t sz) {
+ as_snapshot()->malloc_overhead()->allocate(sz);
+ }
+
+ static inline void record_free_malloc_header(size_t sz) {
+ as_snapshot()->malloc_overhead()->deallocate(sz);
+ }
+
+ // The memory used by malloc tracking headers
+ static inline size_t tracking_overhead() {
+ return as_snapshot()->malloc_overhead()->size();
+ }
+
+ // Reset all counters to zero
+ static void reset() {
+ as_snapshot()->reset();
+ }
+
+ static MallocMemorySnapshot* as_snapshot() {
+ return (MallocMemorySnapshot*)_snapshot;
+ }
+};
+
+
+/*
+ * Malloc tracking header.
+ * To satisfy malloc alignment requirement, NMT uses 2 machine words for tracking purpose,
+ * which ensures 8-bytes alignment on 32-bit systems and 16-bytes on 64-bit systems (Product build).
+ */
+
+class MallocHeader VALUE_OBJ_CLASS_SPEC {
+#ifdef _LP64
+ size_t _size : 62;
+ size_t _level : 2;
+ size_t _flags : 8;
+ size_t _pos_idx : 16;
+ size_t _bucket_idx: 40;
+#define MAX_MALLOCSITE_TABLE_SIZE ((size_t)1 << 40)
+#define MAX_BUCKET_LENGTH ((size_t)(1 << 16))
+#define MAX_MALLOC_SIZE (((size_t)1 << 62) - 1)
+#else
+ size_t _size : 30;
+ size_t _level : 2;
+ size_t _flags : 8;
+ size_t _pos_idx : 8;
+ size_t _bucket_idx: 16;
+#define MAX_MALLOCSITE_TABLE_SIZE ((size_t)(1 << 16))
+#define MAX_BUCKET_LENGTH ((size_t)(1 << 8))
+// Max malloc size = 1GB - 1 on 32 bit system, such has total 4GB memory
+#define MAX_MALLOC_SIZE ((size_t)(1 << 30) - 1)
+#endif // _LP64
+
+ public:
+ // Summary tracking header
+ MallocHeader(size_t size, MEMFLAGS flags) {
+ assert(sizeof(MallocHeader) == sizeof(void*) * 2,
+ "Wrong header size");
+
+ _level = NMT_summary;
+ _flags = flags;
+ set_size(size);
+ MallocMemorySummary::record_malloc(size, flags);
+ MallocMemorySummary::record_new_malloc_header(sizeof(MallocHeader));
+ }
+ // Detail tracking header
+ MallocHeader(size_t size, MEMFLAGS flags, const NativeCallStack& stack) {
+ assert(sizeof(MallocHeader) == sizeof(void*) * 2,
+ "Wrong header size");
+
+ _level = NMT_detail;
+ _flags = flags;
+ set_size(size);
+ size_t bucket_idx;
+ size_t pos_idx;
+ if (record_malloc_site(stack, size, &bucket_idx, &pos_idx)) {
+ assert(bucket_idx <= MAX_MALLOCSITE_TABLE_SIZE, "Overflow bucket index");
+ assert(pos_idx <= MAX_BUCKET_LENGTH, "Overflow bucket position index");
+ _bucket_idx = bucket_idx;
+ _pos_idx = pos_idx;
+ }
+ MallocMemorySummary::record_malloc(size, flags);
+ MallocMemorySummary::record_new_malloc_header(sizeof(MallocHeader));
+ }
+ // Minimal tracking header
+ MallocHeader() {
+ assert(sizeof(MallocHeader) == sizeof(void*) * 2,
+ "Wrong header size");
+
+ _level = (unsigned short)NMT_minimal;
+ }
+
+ inline NMT_TrackingLevel tracking_level() const {
+ return (NMT_TrackingLevel)_level;
+ }
+
+ inline size_t size() const { return _size; }
+ inline MEMFLAGS flags() const { return (MEMFLAGS)_flags; }
+ bool get_stack(NativeCallStack& stack) const;
+
+ // Cleanup tracking information before the memory is released.
+ void release() const;
+
+ private:
+ inline void set_size(size_t size) {
+ assert(size <= MAX_MALLOC_SIZE, "Malloc size too large, should use virtual memory?");
+ _size = size;
+ }
+ bool record_malloc_site(const NativeCallStack& stack, size_t size,
+ size_t* bucket_idx, size_t* pos_idx) const;
+};
+
+
+// Main class called from MemTracker to track malloc activities
+class MallocTracker : AllStatic {
+ public:
+ // Initialize malloc tracker for specific tracking level
+ static bool initialize(NMT_TrackingLevel level);
+
+ static bool transition(NMT_TrackingLevel from, NMT_TrackingLevel to);
+
+ // malloc tracking header size for specific tracking level
+ static inline size_t malloc_header_size(NMT_TrackingLevel level) {
+ return (level == NMT_off) ? 0 : sizeof(MallocHeader);
+ }
+
+ // Parameter name convention:
+ // memblock : the beginning address for user data
+ // malloc_base: the beginning address that includes malloc tracking header
+ //
+ // The relationship:
+ // memblock = (char*)malloc_base + sizeof(nmt header)
+ //
+
+ // Record malloc on specified memory block
+ static void* record_malloc(void* malloc_base, size_t size, MEMFLAGS flags,
+ const NativeCallStack& stack, NMT_TrackingLevel level);
+
+ // Record free on specified memory block
+ static void* record_free(void* memblock);
+
+ // Get tracking level of specified memory block
+ static inline NMT_TrackingLevel get_memory_tracking_level(void* memblock);
+
+
+ // Offset memory address to header address
+ static inline void* get_base(void* memblock);
+ static inline void* get_base(void* memblock, NMT_TrackingLevel level) {
+ if (memblock == NULL || level == NMT_off) return memblock;
+ return (char*)memblock - malloc_header_size(level);
+ }
+
+ // Get memory size
+ static inline size_t get_size(void* memblock) {
+ MallocHeader* header = malloc_header(memblock);
+ assert(header->tracking_level() >= NMT_summary,
+ "Wrong tracking level");
+ return header->size();
+ }
+
+ // Get memory type
+ static inline MEMFLAGS get_flags(void* memblock) {
+ MallocHeader* header = malloc_header(memblock);
+ assert(header->tracking_level() >= NMT_summary,
+ "Wrong tracking level");
+ return header->flags();
+ }
+
+ // Get header size
+ static inline size_t get_header_size(void* memblock) {
+ return (memblock == NULL) ? 0 : sizeof(MallocHeader);
+ }
+
+ static inline void record_new_arena(MEMFLAGS flags) {
+ MallocMemorySummary::record_new_arena(flags);
+ }
+
+ static inline void record_arena_free(MEMFLAGS flags) {
+ MallocMemorySummary::record_arena_free(flags);
+ }
+
+ static inline void record_arena_size_change(int size, MEMFLAGS flags) {
+ MallocMemorySummary::record_arena_size_change(size, flags);
+ }
+ private:
+ static inline MallocHeader* malloc_header(void *memblock) {
+ assert(memblock != NULL, "NULL pointer");
+ MallocHeader* header = (MallocHeader*)((char*)memblock - sizeof(MallocHeader));
+ assert(header->tracking_level() >= NMT_minimal, "Bad header");
+ return header;
+ }
+};
+
+#endif // INCLUDE_NMT
+
+
+#endif //SHARE_VM_SERVICES_MALLOC_TRACKER_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/services/mallocTracker.inline.hpp Thu Aug 07 12:18:58 2014 -0700
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#ifndef SHARE_VM_SERVICES_MALLOC_TRACKER_INLINE_HPP
+#define SHARE_VM_SERVICES_MALLOC_TRACKER_INLINE_HPP
+
+#include "services/mallocTracker.hpp"
+#include "services/memTracker.hpp"
+
+inline NMT_TrackingLevel MallocTracker::get_memory_tracking_level(void* memblock) {
+ assert(memblock != NULL, "Sanity check");
+ if (MemTracker::tracking_level() == NMT_off) return NMT_off;
+ MallocHeader* header = malloc_header(memblock);
+ return header->tracking_level();
+}
+
+inline void* MallocTracker::get_base(void* memblock){
+ return get_base(memblock, MemTracker::tracking_level());
+}
+
+#endif // SHARE_VM_SERVICES_MALLOC_TRACKER_INLINE_HPP
+
--- a/hotspot/src/share/vm/services/memBaseline.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/services/memBaseline.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -22,471 +22,301 @@
*
*/
#include "precompiled.hpp"
+
#include "memory/allocation.hpp"
#include "runtime/safepoint.hpp"
#include "runtime/thread.inline.hpp"
#include "services/memBaseline.hpp"
#include "services/memTracker.hpp"
-
-MemType2Name MemBaseline::MemType2NameMap[NUMBER_OF_MEMORY_TYPE] = {
- {mtJavaHeap, "Java Heap"},
- {mtClass, "Class"},
- {mtThreadStack,"Thread Stack"},
- {mtThread, "Thread"},
- {mtCode, "Code"},
- {mtGC, "GC"},
- {mtCompiler, "Compiler"},
- {mtInternal, "Internal"},
- {mtOther, "Other"},
- {mtSymbol, "Symbol"},
- {mtNMT, "Memory Tracking"},
- {mtTracing, "Tracing"},
- {mtChunk, "Pooled Free Chunks"},
- {mtClassShared,"Shared spaces for classes"},
- {mtTest, "Test"},
- {mtNone, "Unknown"} // It can happen when type tagging records are lagging
- // behind
-};
-
-MemBaseline::MemBaseline() {
- _baselined = false;
-
- for (int index = 0; index < NUMBER_OF_MEMORY_TYPE; index ++) {
- _malloc_data[index].set_type(MemType2NameMap[index]._flag);
- _vm_data[index].set_type(MemType2NameMap[index]._flag);
- _arena_data[index].set_type(MemType2NameMap[index]._flag);
+/*
+ * Sizes are sorted in descenting order for reporting
+ */
+int compare_malloc_size(const MallocSite& s1, const MallocSite& s2) {
+ if (s1.size() == s2.size()) {
+ return 0;
+ } else if (s1.size() > s2.size()) {
+ return -1;
+ } else {
+ return 1;
}
-
- _malloc_cs = NULL;
- _vm_cs = NULL;
- _vm_map = NULL;
-
- _number_of_classes = 0;
- _number_of_threads = 0;
}
-void MemBaseline::clear() {
- if (_malloc_cs != NULL) {
- delete _malloc_cs;
- _malloc_cs = NULL;
+int compare_virtual_memory_size(const VirtualMemoryAllocationSite& s1,
+ const VirtualMemoryAllocationSite& s2) {
+ if (s1.reserved() == s2.reserved()) {
+ return 0;
+ } else if (s1.reserved() > s2.reserved()) {
+ return -1;
+ } else {
+ return 1;
}
+}
- if (_vm_cs != NULL) {
- delete _vm_cs;
- _vm_cs = NULL;
- }
-
- if (_vm_map != NULL) {
- delete _vm_map;
- _vm_map = NULL;
- }
-
- reset();
+// Sort into allocation site addresses order for baseline comparison
+int compare_malloc_site(const MallocSite& s1, const MallocSite& s2) {
+ return s1.call_stack()->compare(*s2.call_stack());
}
-void MemBaseline::reset() {
- _baselined = false;
- _total_vm_reserved = 0;
- _total_vm_committed = 0;
- _total_malloced = 0;
- _number_of_classes = 0;
-
- if (_malloc_cs != NULL) _malloc_cs->clear();
- if (_vm_cs != NULL) _vm_cs->clear();
- if (_vm_map != NULL) _vm_map->clear();
-
- for (int index = 0; index < NUMBER_OF_MEMORY_TYPE; index ++) {
- _malloc_data[index].clear();
- _vm_data[index].clear();
- _arena_data[index].clear();
- }
+int compare_virtual_memory_site(const VirtualMemoryAllocationSite& s1,
+ const VirtualMemoryAllocationSite& s2) {
+ return s1.call_stack()->compare(*s2.call_stack());
}
-MemBaseline::~MemBaseline() {
- clear();
+/*
+ * Walker to walk malloc allocation site table
+ */
+class MallocAllocationSiteWalker : public MallocSiteWalker {
+ private:
+ SortedLinkedList<MallocSite, compare_malloc_size, ResourceObj::ARENA>
+ _malloc_sites;
+ size_t _count;
+
+ // Entries in MallocSiteTable with size = 0 and count = 0,
+ // when the malloc site is not longer there.
+ public:
+ MallocAllocationSiteWalker(Arena* arena) : _count(0), _malloc_sites(arena) {
+ }
+
+ inline size_t count() const { return _count; }
+
+ LinkedList<MallocSite>* malloc_sites() {
+ return &_malloc_sites;
+ }
+
+ bool do_malloc_site(const MallocSite* site) {
+ if (site->size() >= MemBaseline::SIZE_THRESHOLD) {
+ if (_malloc_sites.add(*site) != NULL) {
+ _count++;
+ return true;
+ } else {
+ return false; // OOM
+ }
+ } else {
+ // malloc site does not meet threshold, ignore and continue
+ return true;
+ }
+ }
+};
+
+// Compare virtual memory region's base address
+int compare_virtual_memory_base(const ReservedMemoryRegion& r1, const ReservedMemoryRegion& r2) {
+ return r1.compare(r2);
}
-// baseline malloc'd memory records, generate overall summary and summaries by
-// memory types
-bool MemBaseline::baseline_malloc_summary(const MemPointerArray* malloc_records) {
- MemPointerArrayIteratorImpl malloc_itr((MemPointerArray*)malloc_records);
- MemPointerRecord* malloc_ptr = (MemPointerRecord*)malloc_itr.current();
- size_t used_arena_size = 0;
- int index;
- while (malloc_ptr != NULL) {
- index = flag2index(FLAGS_TO_MEMORY_TYPE(malloc_ptr->flags()));
- size_t size = malloc_ptr->size();
- if (malloc_ptr->is_arena_memory_record()) {
- // We do have anonymous arenas, they are either used as value objects,
- // which are embedded inside other objects, or used as stack objects.
- _arena_data[index].inc(size);
- used_arena_size += size;
- } else {
- _total_malloced += size;
- _malloc_data[index].inc(size);
- if (malloc_ptr->is_arena_record()) {
- // see if arena memory record present
- MemPointerRecord* next_malloc_ptr = (MemPointerRecordEx*)malloc_itr.peek_next();
- if (next_malloc_ptr != NULL && next_malloc_ptr->is_arena_memory_record()) {
- assert(next_malloc_ptr->is_memory_record_of_arena(malloc_ptr),
- "Arena records do not match");
- size = next_malloc_ptr->size();
- _arena_data[index].inc(size);
- used_arena_size += size;
- malloc_itr.next();
- }
+// Walk all virtual memory regions for baselining
+class VirtualMemoryAllocationWalker : public VirtualMemoryWalker {
+ private:
+ SortedLinkedList<ReservedMemoryRegion, compare_virtual_memory_base, ResourceObj::ARENA>
+ _virtual_memory_regions;
+ size_t _count;
+
+ public:
+ VirtualMemoryAllocationWalker(Arena* a) : _count(0), _virtual_memory_regions(a) {
+ }
+
+ bool do_allocation_site(const ReservedMemoryRegion* rgn) {
+ if (rgn->size() >= MemBaseline::SIZE_THRESHOLD) {
+ if (_virtual_memory_regions.add(*rgn) != NULL) {
+ _count ++;
+ return true;
+ } else {
+ return false;
}
}
- malloc_ptr = (MemPointerRecordEx*)malloc_itr.next();
+ return true;
}
- // substract used arena size to get size of arena chunk in free list
- index = flag2index(mtChunk);
- _malloc_data[index].reduce(used_arena_size);
- // we really don't know how many chunks in free list, so just set to
- // 0
- _malloc_data[index].overwrite_counter(0);
+ LinkedList<ReservedMemoryRegion>* virtual_memory_allocations() {
+ return &_virtual_memory_regions;
+ }
+};
+
+
+bool MemBaseline::baseline_summary() {
+ assert(_malloc_memory_snapshot == NULL, "Malloc baseline not yet reset");
+ assert(_virtual_memory_snapshot == NULL, "Virtual baseline not yet reset");
+
+ _malloc_memory_snapshot = new (arena()) MallocMemorySnapshot();
+ _virtual_memory_snapshot = new (arena()) VirtualMemorySnapshot();
+ if (_malloc_memory_snapshot == NULL || _virtual_memory_snapshot == NULL) {
+ return false;
+ }
+ MallocMemorySummary::snapshot(_malloc_memory_snapshot);
+ VirtualMemorySummary::snapshot(_virtual_memory_snapshot);
+ return true;
+}
+
+bool MemBaseline::baseline_allocation_sites() {
+ assert(arena() != NULL, "Just check");
+ // Malloc allocation sites
+ MallocAllocationSiteWalker malloc_walker(arena());
+ if (!MallocSiteTable::walk_malloc_site(&malloc_walker)) {
+ return false;
+ }
+
+ _malloc_sites.set_head(malloc_walker.malloc_sites()->head());
+ // The malloc sites are collected in size order
+ _malloc_sites_order = by_size;
+
+ // Virtual memory allocation sites
+ VirtualMemoryAllocationWalker virtual_memory_walker(arena());
+ if (!VirtualMemoryTracker::walk_virtual_memory(&virtual_memory_walker)) {
+ return false;
+ }
+
+ // Virtual memory allocations are collected in call stack order
+ _virtual_memory_allocations.set_head(virtual_memory_walker.virtual_memory_allocations()->head());
+
+ if (!aggregate_virtual_memory_allocation_sites()) {
+ return false;
+ }
+ // Virtual memory allocation sites are aggregrated in call stack order
+ _virtual_memory_sites_order = by_address;
return true;
}
-// check if there is a safepoint in progress, if so, block the thread
-// for the safepoint
-void MemBaseline::check_safepoint(JavaThread* thr) {
- if (SafepointSynchronize::is_synchronizing()) {
- // grab and drop the SR_lock to honor the safepoint protocol
- MutexLocker ml(thr->SR_lock());
- }
-}
-
-// baseline mmap'd memory records, generate overall summary and summaries by
-// memory types
-bool MemBaseline::baseline_vm_summary(const MemPointerArray* vm_records) {
- MemPointerArrayIteratorImpl vm_itr((MemPointerArray*)vm_records);
- VMMemRegion* vm_ptr = (VMMemRegion*)vm_itr.current();
- int index;
- while (vm_ptr != NULL) {
- if (vm_ptr->is_reserved_region()) {
- index = flag2index(FLAGS_TO_MEMORY_TYPE(vm_ptr->flags()));
- // we use the number of thread stack to count threads
- if (IS_MEMORY_TYPE(vm_ptr->flags(), mtThreadStack)) {
- _number_of_threads ++;
- }
- _total_vm_reserved += vm_ptr->size();
- _vm_data[index].inc(vm_ptr->size(), 0);
- } else {
- _total_vm_committed += vm_ptr->size();
- _vm_data[index].inc(0, vm_ptr->size());
- }
- vm_ptr = (VMMemRegion*)vm_itr.next();
- }
- return true;
-}
-
-// baseline malloc'd memory by callsites, but only the callsites with memory allocation
-// over 1KB are stored.
-bool MemBaseline::baseline_malloc_details(const MemPointerArray* malloc_records) {
- assert(MemTracker::track_callsite(), "detail tracking is off");
-
- MemPointerArrayIteratorImpl malloc_itr(const_cast<MemPointerArray*>(malloc_records));
- MemPointerRecordEx* malloc_ptr = (MemPointerRecordEx*)malloc_itr.current();
- MallocCallsitePointer malloc_callsite;
-
- // initailize malloc callsite array
- if (_malloc_cs == NULL) {
- _malloc_cs = new (std::nothrow) MemPointerArrayImpl<MallocCallsitePointer>(64);
- // out of native memory
- if (_malloc_cs == NULL || _malloc_cs->out_of_memory()) {
- return false;
- }
- } else {
- _malloc_cs->clear();
- }
-
- MemPointerArray* malloc_data = const_cast<MemPointerArray*>(malloc_records);
-
- // sort into callsite pc order. Details are aggregated by callsites
- malloc_data->sort((FN_SORT)malloc_sort_by_pc);
- bool ret = true;
-
- // baseline memory that is totaled over 1 KB
- while (malloc_ptr != NULL) {
- if (!MemPointerRecord::is_arena_memory_record(malloc_ptr->flags())) {
- // skip thread stacks
- if (!IS_MEMORY_TYPE(malloc_ptr->flags(), mtThreadStack)) {
- if (malloc_callsite.addr() != malloc_ptr->pc()) {
- if ((malloc_callsite.amount()/K) > 0) {
- if (!_malloc_cs->append(&malloc_callsite)) {
- ret = false;
- break;
- }
- }
- malloc_callsite = MallocCallsitePointer(malloc_ptr->pc());
- }
- malloc_callsite.inc(malloc_ptr->size());
- }
- }
- malloc_ptr = (MemPointerRecordEx*)malloc_itr.next();
+bool MemBaseline::baseline(bool summaryOnly) {
+ if (arena() == NULL) {
+ _arena = new (std::nothrow, mtNMT) Arena(mtNMT);
+ if (arena() == NULL) return false;
}
- // restore to address order. Snapshot malloc data is maintained in memory
- // address order.
- malloc_data->sort((FN_SORT)malloc_sort_by_addr);
-
- if (!ret) {
- return false;
- }
- // deal with last record
- if (malloc_callsite.addr() != 0 && (malloc_callsite.amount()/K) > 0) {
- if (!_malloc_cs->append(&malloc_callsite)) {
- return false;
- }
- }
- return true;
-}
-
-// baseline mmap'd memory by callsites
-bool MemBaseline::baseline_vm_details(const MemPointerArray* vm_records) {
- assert(MemTracker::track_callsite(), "detail tracking is off");
-
- VMCallsitePointer vm_callsite;
- VMCallsitePointer* cur_callsite = NULL;
- MemPointerArrayIteratorImpl vm_itr((MemPointerArray*)vm_records);
- VMMemRegionEx* vm_ptr = (VMMemRegionEx*)vm_itr.current();
-
- // initialize virtual memory map array
- if (_vm_map == NULL) {
- _vm_map = new (std::nothrow) MemPointerArrayImpl<VMMemRegionEx>(vm_records->length());
- if (_vm_map == NULL || _vm_map->out_of_memory()) {
- return false;
- }
- } else {
- _vm_map->clear();
- }
-
- // initialize virtual memory callsite array
- if (_vm_cs == NULL) {
- _vm_cs = new (std::nothrow) MemPointerArrayImpl<VMCallsitePointer>(64);
- if (_vm_cs == NULL || _vm_cs->out_of_memory()) {
- return false;
- }
- } else {
- _vm_cs->clear();
- }
-
- // consolidate virtual memory data
- VMMemRegionEx* reserved_rec = NULL;
- VMMemRegionEx* committed_rec = NULL;
+ reset();
- // vm_ptr is coming in increasing base address order
- while (vm_ptr != NULL) {
- if (vm_ptr->is_reserved_region()) {
- // consolidate reserved memory regions for virtual memory map.
- // The criteria for consolidation is:
- // 1. two adjacent reserved memory regions
- // 2. belong to the same memory type
- // 3. reserved from the same callsite
- if (reserved_rec == NULL ||
- reserved_rec->base() + reserved_rec->size() != vm_ptr->addr() ||
- FLAGS_TO_MEMORY_TYPE(reserved_rec->flags()) != FLAGS_TO_MEMORY_TYPE(vm_ptr->flags()) ||
- reserved_rec->pc() != vm_ptr->pc()) {
- if (!_vm_map->append(vm_ptr)) {
- return false;
- }
- // inserted reserved region, we need the pointer to the element in virtual
- // memory map array.
- reserved_rec = (VMMemRegionEx*)_vm_map->at(_vm_map->length() - 1);
- } else {
- reserved_rec->expand_region(vm_ptr->addr(), vm_ptr->size());
- }
+ _class_count = InstanceKlass::number_of_instance_classes();
- if (cur_callsite != NULL && !_vm_cs->append(cur_callsite)) {
- return false;
- }
- vm_callsite = VMCallsitePointer(vm_ptr->pc());
- cur_callsite = &vm_callsite;
- vm_callsite.inc(vm_ptr->size(), 0);
- } else {
- // consolidate committed memory regions for virtual memory map
- // The criterial is:
- // 1. two adjacent committed memory regions
- // 2. committed from the same callsite
- if (committed_rec == NULL ||
- committed_rec->base() + committed_rec->size() != vm_ptr->addr() ||
- committed_rec->pc() != vm_ptr->pc()) {
- if (!_vm_map->append(vm_ptr)) {
- return false;
- }
- committed_rec = (VMMemRegionEx*)_vm_map->at(_vm_map->length() - 1);
- } else {
- committed_rec->expand_region(vm_ptr->addr(), vm_ptr->size());
- }
- vm_callsite.inc(0, vm_ptr->size());
- }
- vm_ptr = (VMMemRegionEx*)vm_itr.next();
- }
- // deal with last record
- if (cur_callsite != NULL && !_vm_cs->append(cur_callsite)) {
+ if (!baseline_summary()) {
return false;
}
- // sort it into callsite pc order. Details are aggregated by callsites
- _vm_cs->sort((FN_SORT)bl_vm_sort_by_pc);
+ _baseline_type = Summary_baselined;
- // walk the array to consolidate record by pc
- MemPointerArrayIteratorImpl itr(_vm_cs);
- VMCallsitePointer* callsite_rec = (VMCallsitePointer*)itr.current();
- VMCallsitePointer* next_rec = (VMCallsitePointer*)itr.next();
- while (next_rec != NULL) {
- assert(callsite_rec != NULL, "Sanity check");
- if (next_rec->addr() == callsite_rec->addr()) {
- callsite_rec->inc(next_rec->reserved_amount(), next_rec->committed_amount());
- itr.remove();
- next_rec = (VMCallsitePointer*)itr.current();
- } else {
- callsite_rec = next_rec;
- next_rec = (VMCallsitePointer*)itr.next();
- }
+ // baseline details
+ if (!summaryOnly &&
+ MemTracker::tracking_level() == NMT_detail) {
+ baseline_allocation_sites();
+ _baseline_type = Detail_baselined;
}
return true;
}
-// baseline a snapshot. If summary_only = false, memory usages aggregated by
-// callsites are also baselined.
-// The method call can be lengthy, especially when detail tracking info is
-// requested. So the method checks for safepoint explicitly.
-bool MemBaseline::baseline(MemSnapshot& snapshot, bool summary_only) {
- Thread* THREAD = Thread::current();
- assert(THREAD->is_Java_thread(), "must be a JavaThread");
- MutexLocker snapshot_locker(snapshot._lock);
- reset();
- _baselined = baseline_malloc_summary(snapshot._alloc_ptrs);
- if (_baselined) {
- check_safepoint((JavaThread*)THREAD);
- _baselined = baseline_vm_summary(snapshot._vm_ptrs);
+int compare_allocation_site(const VirtualMemoryAllocationSite& s1,
+ const VirtualMemoryAllocationSite& s2) {
+ return s1.call_stack()->compare(*s2.call_stack());
+}
+
+bool MemBaseline::aggregate_virtual_memory_allocation_sites() {
+ SortedLinkedList<VirtualMemoryAllocationSite, compare_allocation_site, ResourceObj::ARENA>
+ allocation_sites(arena());
+
+ VirtualMemoryAllocationIterator itr = virtual_memory_allocations();
+ const ReservedMemoryRegion* rgn;
+ VirtualMemoryAllocationSite* site;
+ while ((rgn = itr.next()) != NULL) {
+ VirtualMemoryAllocationSite tmp(*rgn->call_stack());
+ site = allocation_sites.find(tmp);
+ if (site == NULL) {
+ LinkedListNode<VirtualMemoryAllocationSite>* node =
+ allocation_sites.add(tmp);
+ if (node == NULL) return false;
+ site = node->data();
+ }
+ site->reserve_memory(rgn->size());
+ site->commit_memory(rgn->committed_size());
}
- _number_of_classes = snapshot.number_of_classes();
- if (!summary_only && MemTracker::track_callsite() && _baselined) {
- check_safepoint((JavaThread*)THREAD);
- _baselined = baseline_malloc_details(snapshot._alloc_ptrs);
- if (_baselined) {
- check_safepoint((JavaThread*)THREAD);
- _baselined = baseline_vm_details(snapshot._vm_ptrs);
- }
- }
- return _baselined;
+ _virtual_memory_sites.set_head(allocation_sites.head());
+ return true;
}
-
-int MemBaseline::flag2index(MEMFLAGS flag) const {
- for (int index = 0; index < NUMBER_OF_MEMORY_TYPE; index ++) {
- if (MemType2NameMap[index]._flag == flag) {
- return index;
- }
+MallocSiteIterator MemBaseline::malloc_sites(SortingOrder order) {
+ assert(!_malloc_sites.is_empty(), "Detail baseline?");
+ switch(order) {
+ case by_size:
+ malloc_sites_to_size_order();
+ break;
+ case by_site:
+ malloc_sites_to_allocation_site_order();
+ break;
+ case by_address:
+ default:
+ ShouldNotReachHere();
}
- assert(false, "no type");
- return -1;
+ return MallocSiteIterator(_malloc_sites.head());
}
-const char* MemBaseline::type2name(MEMFLAGS type) {
- for (int index = 0; index < NUMBER_OF_MEMORY_TYPE; index ++) {
- if (MemType2NameMap[index]._flag == type) {
- return MemType2NameMap[index]._name;
- }
+VirtualMemorySiteIterator MemBaseline::virtual_memory_sites(SortingOrder order) {
+ assert(!_virtual_memory_sites.is_empty(), "Detail baseline?");
+ switch(order) {
+ case by_size:
+ virtual_memory_sites_to_size_order();
+ break;
+ case by_site:
+ virtual_memory_sites_to_reservation_site_order();
+ break;
+ case by_address:
+ default:
+ ShouldNotReachHere();
}
- assert(false, err_msg("bad type %x", type));
- return NULL;
+ return VirtualMemorySiteIterator(_virtual_memory_sites.head());
}
-MemBaseline& MemBaseline::operator=(const MemBaseline& other) {
- _total_malloced = other._total_malloced;
- _total_vm_reserved = other._total_vm_reserved;
- _total_vm_committed = other._total_vm_committed;
-
- _baselined = other._baselined;
- _number_of_classes = other._number_of_classes;
-
- for (int index = 0; index < NUMBER_OF_MEMORY_TYPE; index ++) {
- _malloc_data[index] = other._malloc_data[index];
- _vm_data[index] = other._vm_data[index];
- _arena_data[index] = other._arena_data[index];
- }
+// Sorting allocations sites in different orders
+void MemBaseline::malloc_sites_to_size_order() {
+ if (_malloc_sites_order != by_size) {
+ SortedLinkedList<MallocSite, compare_malloc_size, ResourceObj::ARENA>
+ tmp(arena());
- if (MemTracker::track_callsite()) {
- assert(_malloc_cs != NULL && _vm_cs != NULL, "out of memory");
- assert(other._malloc_cs != NULL && other._vm_cs != NULL,
- "not properly baselined");
- _malloc_cs->clear();
- _vm_cs->clear();
- int index;
- for (index = 0; index < other._malloc_cs->length(); index ++) {
- _malloc_cs->append(other._malloc_cs->at(index));
- }
-
- for (index = 0; index < other._vm_cs->length(); index ++) {
- _vm_cs->append(other._vm_cs->at(index));
- }
+ // Add malloc sites to sorted linked list to sort into size order
+ tmp.move(&_malloc_sites);
+ _malloc_sites.set_head(tmp.head());
+ tmp.set_head(NULL);
+ _malloc_sites_order = by_size;
}
- return *this;
}
-/* compare functions for sorting */
-
-// sort snapshot malloc'd records in callsite pc order
-int MemBaseline::malloc_sort_by_pc(const void* p1, const void* p2) {
- assert(MemTracker::track_callsite(),"Just check");
- const MemPointerRecordEx* mp1 = (const MemPointerRecordEx*)p1;
- const MemPointerRecordEx* mp2 = (const MemPointerRecordEx*)p2;
- return UNSIGNED_COMPARE(mp1->pc(), mp2->pc());
+void MemBaseline::malloc_sites_to_allocation_site_order() {
+ if (_malloc_sites_order != by_site) {
+ SortedLinkedList<MallocSite, compare_malloc_site, ResourceObj::ARENA>
+ tmp(arena());
+ // Add malloc sites to sorted linked list to sort into site (address) order
+ tmp.move(&_malloc_sites);
+ _malloc_sites.set_head(tmp.head());
+ tmp.set_head(NULL);
+ _malloc_sites_order = by_site;
+ }
}
-// sort baselined malloc'd records in size order
-int MemBaseline::bl_malloc_sort_by_size(const void* p1, const void* p2) {
- assert(MemTracker::is_on(), "Just check");
- const MallocCallsitePointer* mp1 = (const MallocCallsitePointer*)p1;
- const MallocCallsitePointer* mp2 = (const MallocCallsitePointer*)p2;
- return UNSIGNED_COMPARE(mp2->amount(), mp1->amount());
-}
+void MemBaseline::virtual_memory_sites_to_size_order() {
+ if (_virtual_memory_sites_order != by_size) {
+ SortedLinkedList<VirtualMemoryAllocationSite, compare_virtual_memory_size, ResourceObj::ARENA>
+ tmp(arena());
-// sort baselined malloc'd records in callsite pc order
-int MemBaseline::bl_malloc_sort_by_pc(const void* p1, const void* p2) {
- assert(MemTracker::is_on(), "Just check");
- const MallocCallsitePointer* mp1 = (const MallocCallsitePointer*)p1;
- const MallocCallsitePointer* mp2 = (const MallocCallsitePointer*)p2;
- return UNSIGNED_COMPARE(mp1->addr(), mp2->addr());
+ tmp.move(&_virtual_memory_sites);
+
+ _virtual_memory_sites.set_head(tmp.head());
+ tmp.set_head(NULL);
+ _virtual_memory_sites_order = by_size;
+ }
}
+void MemBaseline::virtual_memory_sites_to_reservation_site_order() {
+ if (_virtual_memory_sites_order != by_size) {
+ SortedLinkedList<VirtualMemoryAllocationSite, compare_virtual_memory_site, ResourceObj::ARENA>
+ tmp(arena());
-// sort baselined mmap'd records in size (reserved size) order
-int MemBaseline::bl_vm_sort_by_size(const void* p1, const void* p2) {
- assert(MemTracker::is_on(), "Just check");
- const VMCallsitePointer* mp1 = (const VMCallsitePointer*)p1;
- const VMCallsitePointer* mp2 = (const VMCallsitePointer*)p2;
- return UNSIGNED_COMPARE(mp2->reserved_amount(), mp1->reserved_amount());
+ tmp.add(&_virtual_memory_sites);
+
+ _virtual_memory_sites.set_head(tmp.head());
+ tmp.set_head(NULL);
+
+ _virtual_memory_sites_order = by_size;
+ }
}
-// sort baselined mmap'd records in callsite pc order
-int MemBaseline::bl_vm_sort_by_pc(const void* p1, const void* p2) {
- assert(MemTracker::is_on(), "Just check");
- const VMCallsitePointer* mp1 = (const VMCallsitePointer*)p1;
- const VMCallsitePointer* mp2 = (const VMCallsitePointer*)p2;
- return UNSIGNED_COMPARE(mp1->addr(), mp2->addr());
-}
-
-
-// sort snapshot malloc'd records in memory block address order
-int MemBaseline::malloc_sort_by_addr(const void* p1, const void* p2) {
- assert(MemTracker::is_on(), "Just check");
- const MemPointerRecord* mp1 = (const MemPointerRecord*)p1;
- const MemPointerRecord* mp2 = (const MemPointerRecord*)p2;
- int delta = UNSIGNED_COMPARE(mp1->addr(), mp2->addr());
- assert(p1 == p2 || delta != 0, "dup pointer");
- return delta;
-}
-
--- a/hotspot/src/share/vm/services/memBaseline.hpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/services/memBaseline.hpp Thu Aug 07 12:18:58 2014 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,425 +25,205 @@
#ifndef SHARE_VM_SERVICES_MEM_BASELINE_HPP
#define SHARE_VM_SERVICES_MEM_BASELINE_HPP
+#if INCLUDE_NMT
+
#include "memory/allocation.hpp"
#include "runtime/mutex.hpp"
-#include "services/memPtr.hpp"
-#include "services/memSnapshot.hpp"
+#include "services/mallocSiteTable.hpp"
+#include "services/mallocTracker.hpp"
+#include "services/nmtCommon.hpp"
+#include "services/virtualMemoryTracker.hpp"
+#include "utilities/linkedlist.hpp"
-// compare unsigned number
-#define UNSIGNED_COMPARE(a, b) ((a > b) ? 1 : ((a == b) ? 0 : -1))
+typedef LinkedListIterator<MallocSite> MallocSiteIterator;
+typedef LinkedListIterator<VirtualMemoryAllocationSite> VirtualMemorySiteIterator;
+typedef LinkedListIterator<ReservedMemoryRegion> VirtualMemoryAllocationIterator;
/*
- * MallocCallsitePointer and VMCallsitePointer are used
- * to baseline memory blocks with their callsite information.
- * They are only available when detail tracking is turned
- * on.
+ * Baseline a memory snapshot
*/
-
-/* baselined malloc record aggregated by callsite */
-class MallocCallsitePointer : public MemPointer {
- private:
- size_t _count; // number of malloc invocation from this callsite
- size_t _amount; // total amount of memory malloc-ed from this callsite
-
+class MemBaseline VALUE_OBJ_CLASS_SPEC {
public:
- MallocCallsitePointer() {
- _count = 0;
- _amount = 0;
- }
-
- MallocCallsitePointer(address pc) : MemPointer(pc) {
- _count = 0;
- _amount = 0;
- }
+ enum BaselineThreshold {
+ SIZE_THRESHOLD = K // Only allocation size over this threshold will be baselined.
+ };
- MallocCallsitePointer& operator=(const MallocCallsitePointer& p) {
- MemPointer::operator=(p);
- _count = p.count();
- _amount = p.amount();
- return *this;
- }
+ enum BaselineType {
+ Not_baselined,
+ Summary_baselined,
+ Detail_baselined
+ };
- inline void inc(size_t size) {
- _count ++;
- _amount += size;
+ enum SortingOrder {
+ by_address, // by memory address
+ by_size, // by memory size
+ by_site // by call site where the memory is allocated from
};
- inline size_t count() const {
- return _count;
- }
+ private:
+ // All baseline data is stored in this arena
+ Arena* _arena;
+
+ // Summary information
+ MallocMemorySnapshot* _malloc_memory_snapshot;
+ VirtualMemorySnapshot* _virtual_memory_snapshot;
+
+ size_t _class_count;
- inline size_t amount() const {
- return _amount;
- }
-};
+ // Allocation sites information
+ // Malloc allocation sites
+ LinkedListImpl<MallocSite, ResourceObj::ARENA>
+ _malloc_sites;
+
+ // All virtual memory allocations
+ LinkedListImpl<ReservedMemoryRegion, ResourceObj::ARENA>
+ _virtual_memory_allocations;
-// baselined virtual memory record aggregated by callsite
-class VMCallsitePointer : public MemPointer {
- private:
- size_t _count; // number of invocation from this callsite
- size_t _reserved_amount; // total reserved amount
- size_t _committed_amount; // total committed amount
+ // Virtual memory allocations by allocation sites, always in by_address
+ // order
+ LinkedListImpl<VirtualMemoryAllocationSite, ResourceObj::ARENA>
+ _virtual_memory_sites;
+
+ SortingOrder _malloc_sites_order;
+ SortingOrder _virtual_memory_sites_order;
+
+ BaselineType _baseline_type;
public:
- VMCallsitePointer() {
- _count = 0;
- _reserved_amount = 0;
- _committed_amount = 0;
- }
-
- VMCallsitePointer(address pc) : MemPointer(pc) {
- _count = 0;
- _reserved_amount = 0;
- _committed_amount = 0;
- }
-
- VMCallsitePointer& operator=(const VMCallsitePointer& p) {
- MemPointer::operator=(p);
- _count = p.count();
- _reserved_amount = p.reserved_amount();
- _committed_amount = p.committed_amount();
- return *this;
- }
-
- inline void inc(size_t reserved, size_t committed) {
- _count ++;
- _reserved_amount += reserved;
- _committed_amount += committed;
- }
-
- inline size_t count() const {
- return _count;
- }
-
- inline size_t reserved_amount() const {
- return _reserved_amount;
+ // create a memory baseline
+ MemBaseline():
+ _baseline_type(Not_baselined),
+ _class_count(0),
+ _arena(NULL),
+ _malloc_memory_snapshot(NULL),
+ _virtual_memory_snapshot(NULL),
+ _malloc_sites(NULL) {
}
- inline size_t committed_amount() const {
- return _committed_amount;
- }
-};
-
-// maps a memory type flag to readable name
-typedef struct _memType2Name {
- MEMFLAGS _flag;
- const char* _name;
-} MemType2Name;
-
-
-// This class aggregates malloc'd records by memory type
-class MallocMem VALUE_OBJ_CLASS_SPEC {
- private:
- MEMFLAGS _type;
-
- size_t _count;
- size_t _amount;
-
- public:
- MallocMem() {
- _type = mtNone;
- _count = 0;
- _amount = 0;
+ ~MemBaseline() {
+ reset();
+ if (_arena != NULL) {
+ delete _arena;
+ }
}
- MallocMem(MEMFLAGS flags) {
- assert(HAS_VALID_MEMORY_TYPE(flags), "no type");
- _type = FLAGS_TO_MEMORY_TYPE(flags);
- _count = 0;
- _amount = 0;
- }
+ bool baseline(bool summaryOnly = true);
- inline void set_type(MEMFLAGS flag) {
- _type = flag;
- }
+ BaselineType baseline_type() const { return _baseline_type; }
- inline void clear() {
- _count = 0;
- _amount = 0;
- _type = mtNone;
+ MallocMemorySnapshot* malloc_memory_snapshot() const {
+ return _malloc_memory_snapshot;
}
- MallocMem& operator=(const MallocMem& m) {
- assert(_type == m.type(), "different type");
- _count = m.count();
- _amount = m.amount();
- return *this;
+ VirtualMemorySnapshot* virtual_memory_snapshot() const {
+ return _virtual_memory_snapshot;
}
- inline void inc(size_t amt) {
- _amount += amt;
- _count ++;
- }
+ MallocSiteIterator malloc_sites(SortingOrder order);
+ VirtualMemorySiteIterator virtual_memory_sites(SortingOrder order);
- inline void reduce(size_t amt) {
- assert(_amount >= amt, "Just check");
- _amount -= amt;
- }
-
- inline void overwrite_counter(size_t count) {
- _count = count;
+ // Virtual memory allocation iterator always returns in virtual memory
+ // base address order.
+ VirtualMemoryAllocationIterator virtual_memory_allocations() {
+ assert(!_virtual_memory_allocations.is_empty(), "Not detail baseline");
+ return VirtualMemoryAllocationIterator(_virtual_memory_allocations.head());
}
- inline MEMFLAGS type() const {
- return _type;
- }
-
- inline bool is_type(MEMFLAGS flags) const {
- return FLAGS_TO_MEMORY_TYPE(flags) == _type;
- }
-
- inline size_t count() const {
- return _count;
+ // Total reserved memory = total malloc'd memory + total reserved virtual
+ // memory
+ size_t total_reserved_memory() const {
+ assert(baseline_type() != Not_baselined, "Not yet baselined");
+ assert(_virtual_memory_snapshot != NULL, "No virtual memory snapshot");
+ assert(_malloc_memory_snapshot != NULL, "No malloc memory snapshot");
+ size_t amount = _malloc_memory_snapshot->total() +
+ _virtual_memory_snapshot->total_reserved();
+ return amount;
}
- inline size_t amount() const {
- return _amount;
- }
-};
-
-// This class records live arena's memory usage
-class ArenaMem : public MallocMem {
- public:
- ArenaMem(MEMFLAGS typeflag): MallocMem(typeflag) {
- }
- ArenaMem() { }
-};
-
-// This class aggregates virtual memory by its memory type
-class VMMem VALUE_OBJ_CLASS_SPEC {
- private:
- MEMFLAGS _type;
-
- size_t _count;
- size_t _reserved_amount;
- size_t _committed_amount;
-
- public:
- VMMem() {
- _type = mtNone;
- _count = 0;
- _reserved_amount = 0;
- _committed_amount = 0;
+ // Total committed memory = total malloc'd memory + total committed
+ // virtual memory
+ size_t total_committed_memory() const {
+ assert(baseline_type() != Not_baselined, "Not yet baselined");
+ assert(_virtual_memory_snapshot != NULL,
+ "Not a snapshot");
+ size_t amount = _malloc_memory_snapshot->total() +
+ _virtual_memory_snapshot->total_committed();
+ return amount;
}
- VMMem(MEMFLAGS flags) {
- assert(HAS_VALID_MEMORY_TYPE(flags), "no type");
- _type = FLAGS_TO_MEMORY_TYPE(flags);
- _count = 0;
- _reserved_amount = 0;
- _committed_amount = 0;
+ size_t total_arena_memory() const {
+ assert(baseline_type() != Not_baselined, "Not yet baselined");
+ assert(_malloc_memory_snapshot != NULL, "Not yet baselined");
+ return _malloc_memory_snapshot->total_arena();
}
- inline void clear() {
- _type = mtNone;
- _count = 0;
- _reserved_amount = 0;
- _committed_amount = 0;
+ size_t malloc_tracking_overhead() const {
+ assert(baseline_type() != Not_baselined, "Not yet baselined");
+ return _malloc_memory_snapshot->malloc_overhead()->size();
}
- inline void set_type(MEMFLAGS flags) {
- _type = FLAGS_TO_MEMORY_TYPE(flags);
+ const MallocMemory* malloc_memory(MEMFLAGS flag) const {
+ assert(_malloc_memory_snapshot != NULL, "Not a snapshot");
+ return _malloc_memory_snapshot->by_type(flag);
}
- VMMem& operator=(const VMMem& m) {
- assert(_type == m.type(), "different type");
-
- _count = m.count();
- _reserved_amount = m.reserved_amount();
- _committed_amount = m.committed_amount();
- return *this;
+ const VirtualMemory* virtual_memory(MEMFLAGS flag) const {
+ assert(_virtual_memory_snapshot != NULL, "Not a snapshot");
+ return _virtual_memory_snapshot->by_type(flag);
}
- inline MEMFLAGS type() const {
- return _type;
- }
-
- inline bool is_type(MEMFLAGS flags) const {
- return FLAGS_TO_MEMORY_TYPE(flags) == _type;
- }
-
- inline void inc(size_t reserved_amt, size_t committed_amt) {
- _reserved_amount += reserved_amt;
- _committed_amount += committed_amt;
- _count ++;
- }
-
- inline size_t count() const {
- return _count;
- }
-
- inline size_t reserved_amount() const {
- return _reserved_amount;
+ size_t class_count() const {
+ assert(baseline_type() != Not_baselined, "Not yet baselined");
+ return _class_count;
}
- inline size_t committed_amount() const {
- return _committed_amount;
+ size_t thread_count() const {
+ assert(baseline_type() != Not_baselined, "Not yet baselined");
+ assert(_malloc_memory_snapshot != NULL, "Baselined?");
+ return _malloc_memory_snapshot->thread_count();
}
-};
-
-
-
-#define NUMBER_OF_MEMORY_TYPE (mt_number_of_types + 1)
-
-class BaselineReporter;
-class BaselineComparisonReporter;
-
-/*
- * This class baselines current memory snapshot.
- * A memory baseline summarizes memory usage by memory type,
- * aggregates memory usage by callsites when detail tracking
- * is on.
- */
-class MemBaseline VALUE_OBJ_CLASS_SPEC {
- friend class BaselineReporter;
- friend class BaselineComparisonReporter;
-
- private:
- // overall summaries
- size_t _total_malloced;
- size_t _total_vm_reserved;
- size_t _total_vm_committed;
- size_t _number_of_classes;
- size_t _number_of_threads;
-
- // if it has properly baselined
- bool _baselined;
-
- // we categorize memory into three categories within the memory type
- MallocMem _malloc_data[NUMBER_OF_MEMORY_TYPE];
- VMMem _vm_data[NUMBER_OF_MEMORY_TYPE];
- ArenaMem _arena_data[NUMBER_OF_MEMORY_TYPE];
-
- // memory records that aggregate memory usage by callsites.
- // only available when detail tracking is on.
- MemPointerArray* _malloc_cs;
- MemPointerArray* _vm_cs;
- // virtual memory map
- MemPointerArray* _vm_map;
-
- private:
- static MemType2Name MemType2NameMap[NUMBER_OF_MEMORY_TYPE];
-
- private:
- // should not use copy constructor
- MemBaseline(MemBaseline& copy) { ShouldNotReachHere(); }
-
- // check and block at a safepoint
- static inline void check_safepoint(JavaThread* thr);
-
- public:
- // create a memory baseline
- MemBaseline();
-
- ~MemBaseline();
-
- inline bool baselined() const {
- return _baselined;
- }
-
- MemBaseline& operator=(const MemBaseline& other);
// reset the baseline for reuse
- void clear();
-
- // baseline the snapshot
- bool baseline(MemSnapshot& snapshot, bool summary_only = true);
-
- bool baseline(const MemPointerArray* malloc_records,
- const MemPointerArray* vm_records,
- bool summary_only = true);
+ void reset() {
+ _baseline_type = Not_baselined;
+ _malloc_memory_snapshot = NULL;
+ _virtual_memory_snapshot = NULL;
+ _class_count = 0;
- // total malloc'd memory of specified memory type
- inline size_t malloc_amount(MEMFLAGS flag) const {
- return _malloc_data[flag2index(flag)].amount();
- }
- // number of malloc'd memory blocks of specified memory type
- inline size_t malloc_count(MEMFLAGS flag) const {
- return _malloc_data[flag2index(flag)].count();
- }
- // total memory used by arenas of specified memory type
- inline size_t arena_amount(MEMFLAGS flag) const {
- return _arena_data[flag2index(flag)].amount();
- }
- // number of arenas of specified memory type
- inline size_t arena_count(MEMFLAGS flag) const {
- return _arena_data[flag2index(flag)].count();
- }
- // total reserved memory of specified memory type
- inline size_t reserved_amount(MEMFLAGS flag) const {
- return _vm_data[flag2index(flag)].reserved_amount();
- }
- // total committed memory of specified memory type
- inline size_t committed_amount(MEMFLAGS flag) const {
- return _vm_data[flag2index(flag)].committed_amount();
- }
- // total memory (malloc'd + mmap'd + arena) of specified
- // memory type
- inline size_t total_amount(MEMFLAGS flag) const {
- int index = flag2index(flag);
- return _malloc_data[index].amount() +
- _vm_data[index].reserved_amount() +
- _arena_data[index].amount();
+ _malloc_sites = NULL;
+ _virtual_memory_sites = NULL;
+ _virtual_memory_allocations = NULL;
+
+ if (_arena != NULL) {
+ _arena->destruct_contents();
+ }
}
- /* overall summaries */
+ private:
+ // Baseline summary information
+ bool baseline_summary();
- // total malloc'd memory in snapshot
- inline size_t total_malloc_amount() const {
- return _total_malloced;
- }
- // total mmap'd memory in snapshot
- inline size_t total_reserved_amount() const {
- return _total_vm_reserved;
- }
- // total committed memory in snapshot
- inline size_t total_committed_amount() const {
- return _total_vm_committed;
- }
- // number of loaded classes
- inline size_t number_of_classes() const {
- return _number_of_classes;
- }
- // number of running threads
- inline size_t number_of_threads() const {
- return _number_of_threads;
- }
- // lookup human readable name of a memory type
- static const char* type2name(MEMFLAGS type);
+ // Baseline allocation sites (detail tracking only)
+ bool baseline_allocation_sites();
+
+ // Aggregate virtual memory allocation by allocation sites
+ bool aggregate_virtual_memory_allocation_sites();
- private:
- // convert memory flag to the index to mapping table
- int flag2index(MEMFLAGS flag) const;
-
- // reset baseline values
- void reset();
-
- // summarize the records in global snapshot
- bool baseline_malloc_summary(const MemPointerArray* malloc_records);
- bool baseline_vm_summary(const MemPointerArray* vm_records);
- bool baseline_malloc_details(const MemPointerArray* malloc_records);
- bool baseline_vm_details(const MemPointerArray* vm_records);
+ Arena* arena() { return _arena; }
- // print a line of malloc'd memory aggregated by callsite
- void print_malloc_callsite(outputStream* st, address pc, size_t size,
- size_t count, int diff_amt, int diff_count) const;
- // print a line of mmap'd memory aggregated by callsite
- void print_vm_callsite(outputStream* st, address pc, size_t rsz,
- size_t csz, int diff_rsz, int diff_csz) const;
+ // Sorting allocation sites in different orders
+ // Sort allocation sites in size order
+ void malloc_sites_to_size_order();
+ // Sort allocation sites in call site address order
+ void malloc_sites_to_allocation_site_order();
- // sorting functions for raw records
- static int malloc_sort_by_pc(const void* p1, const void* p2);
- static int malloc_sort_by_addr(const void* p1, const void* p2);
-
- private:
- // sorting functions for baselined records
- static int bl_malloc_sort_by_size(const void* p1, const void* p2);
- static int bl_vm_sort_by_size(const void* p1, const void* p2);
- static int bl_malloc_sort_by_pc(const void* p1, const void* p2);
- static int bl_vm_sort_by_pc(const void* p1, const void* p2);
+ // Sort allocation sites in reserved size order
+ void virtual_memory_sites_to_size_order();
+ // Sort allocation sites in call site address order
+ void virtual_memory_sites_to_reservation_site_order();
};
+#endif // INCLUDE_NMT
#endif // SHARE_VM_SERVICES_MEM_BASELINE_HPP
--- a/hotspot/src/share/vm/services/memPtr.cpp Tue Aug 05 14:18:44 2014 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +0,0 @@
-/*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- *
- */
-
-#include "precompiled.hpp"
-#include "runtime/atomic.inline.hpp"
-#include "services/memPtr.hpp"
-#include "services/memTracker.hpp"
-
-volatile jint SequenceGenerator::_seq_number = 1;
-volatile unsigned long SequenceGenerator::_generation = 1;
-NOT_PRODUCT(jint SequenceGenerator::_max_seq_number = 1;)
-
-jint SequenceGenerator::next() {
- jint seq = Atomic::add(1, &_seq_number);
- if (seq < 0) {
- MemTracker::shutdown(MemTracker::NMT_sequence_overflow);
- } else {
- NOT_PRODUCT(_max_seq_number = (seq > _max_seq_number) ? seq : _max_seq_number;)
- }
- return seq;
-}
-
--- a/hotspot/src/share/vm/services/memPtr.hpp Tue Aug 05 14:18:44 2014 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,509 +0,0 @@
-/*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- *
- */
-
-#ifndef SHARE_VM_SERVICES_MEM_PTR_HPP
-#define SHARE_VM_SERVICES_MEM_PTR_HPP
-
-#include "memory/allocation.hpp"
-#include "runtime/os.hpp"
-#include "runtime/safepoint.hpp"
-
-/*
- * global sequence generator that generates sequence numbers to serialize
- * memory records.
- */
-class SequenceGenerator : AllStatic {
- public:
- static jint next();
-
- // peek last sequence number
- static jint peek() {
- return _seq_number;
- }
-
- // reset sequence number
- static void reset() {
- assert(SafepointSynchronize::is_at_safepoint(), "Safepoint required");
- _seq_number = 1;
- _generation ++;
- };
-
- static unsigned long current_generation() { return _generation; }
- NOT_PRODUCT(static jint max_seq_num() { return _max_seq_number; })
-
- private:
- static volatile jint _seq_number;
- static volatile unsigned long _generation;
- NOT_PRODUCT(static jint _max_seq_number; )
-};
-
-/*
- * followings are the classes that are used to hold memory activity records in different stages.
- * MemPointer
- * |--------MemPointerRecord
- * |
- * |----MemPointerRecordEx
- * | |
- * | |-------SeqMemPointerRecordEx
- * |
- * |----SeqMemPointerRecord
- * |
- * |----VMMemRegion
- * |
- * |-----VMMemRegionEx
- *
- *
- * prefix 'Seq' - sequenced, the record contains a sequence number
- * surfix 'Ex' - extension, the record contains a caller's pc
- *
- * per-thread recorder : SeqMemPointerRecord(Ex)
- * snapshot staging : SeqMemPointerRecord(Ex)
- * snapshot : MemPointerRecord(Ex) and VMMemRegion(Ex)
- *
- */
-
-/*
- * class that wraps an address to a memory block,
- * the memory pointer either points to a malloc'd
- * memory block, or a mmap'd memory block
- */
-class MemPointer VALUE_OBJ_CLASS_SPEC {
- public:
- MemPointer(): _addr(0) { }
- MemPointer(address addr): _addr(addr) { }
-
- MemPointer(const MemPointer& copy_from) {
- _addr = copy_from.addr();
- }
-
- inline address addr() const {
- return _addr;
- }
-
- inline operator address() const {
- return addr();
- }
-
- inline bool operator == (const MemPointer& other) const {
- return addr() == other.addr();
- }
-
- inline MemPointer& operator = (const MemPointer& other) {
- _addr = other.addr();
- return *this;
- }
-
- protected:
- inline void set_addr(address addr) { _addr = addr; }
-
- protected:
- // memory address
- address _addr;
-};
-
-/* MemPointerRecord records an activityand associated
- * attributes on a memory block.
- */
-class MemPointerRecord : public MemPointer {
- private:
- MEMFLAGS _flags;
- size_t _size;
-
-public:
- /* extension of MemoryType enum
- * see share/vm/memory/allocation.hpp for details.
- *
- * The tag values are associated to sorting orders, so be
- * careful if changes are needed.
- * The allocation records should be sorted ahead of tagging
- * records, which in turn ahead of deallocation records
- */
- enum MemPointerTags {
- tag_alloc = 0x0001, // malloc or reserve record
- tag_commit = 0x0002, // commit record
- tag_type = 0x0003, // tag virtual memory to a memory type
- tag_uncommit = 0x0004, // uncommit record
- tag_release = 0x0005, // free or release record
- tag_size = 0x0006, // arena size
- tag_masks = 0x0007, // all tag bits
- vmBit = 0x0008
- };
-
- /* helper functions to interpret the tagging flags */
-
- inline static bool is_allocation_record(MEMFLAGS flags) {
- return (flags & tag_masks) == tag_alloc;
- }
-
- inline static bool is_deallocation_record(MEMFLAGS flags) {
- return (flags & tag_masks) == tag_release;
- }
-
- inline static bool is_arena_record(MEMFLAGS flags) {
- return (flags & (otArena | tag_size)) == otArena;
- }
-
- inline static bool is_arena_memory_record(MEMFLAGS flags) {
- return (flags & (otArena | tag_size)) == (otArena | tag_size);
- }
-
- inline static bool is_virtual_memory_record(MEMFLAGS flags) {
- return (flags & vmBit) != 0;
- }
-
- inline static bool is_virtual_memory_reserve_record(MEMFLAGS flags) {
- return (flags & 0x0F) == (tag_alloc | vmBit);
- }
-
- inline static bool is_virtual_memory_commit_record(MEMFLAGS flags) {
- return (flags & 0x0F) == (tag_commit | vmBit);
- }
-
- inline static bool is_virtual_memory_uncommit_record(MEMFLAGS flags) {
- return (flags & 0x0F) == (tag_uncommit | vmBit);
- }
-
- inline static bool is_virtual_memory_release_record(MEMFLAGS flags) {
- return (flags & 0x0F) == (tag_release | vmBit);
- }
-
- inline static bool is_virtual_memory_type_record(MEMFLAGS flags) {
- return (flags & 0x0F) == (tag_type | vmBit);
- }
-
- /* tagging flags */
- inline static MEMFLAGS malloc_tag() { return tag_alloc; }
- inline static MEMFLAGS free_tag() { return tag_release; }
- inline static MEMFLAGS arena_size_tag() { return tag_size | otArena; }
- inline static MEMFLAGS virtual_memory_tag() { return vmBit; }
- inline static MEMFLAGS virtual_memory_reserve_tag() { return (tag_alloc | vmBit); }
- inline static MEMFLAGS virtual_memory_commit_tag() { return (tag_commit | vmBit); }
- inline static MEMFLAGS virtual_memory_uncommit_tag(){ return (tag_uncommit | vmBit); }
- inline static MEMFLAGS virtual_memory_release_tag() { return (tag_release | vmBit); }
- inline static MEMFLAGS virtual_memory_type_tag() { return (tag_type | vmBit); }
-
- public:
- MemPointerRecord(): _size(0), _flags(mtNone) { }
-
- MemPointerRecord(address addr, MEMFLAGS memflags, size_t size = 0):
- MemPointer(addr), _flags(memflags), _size(size) { }
-
- MemPointerRecord(const MemPointerRecord& copy_from):
- MemPointer(copy_from), _flags(copy_from.flags()),
- _size(copy_from.size()) {
- }
-
- /* MemPointerRecord is not sequenced, it always return
- * 0 to indicate non-sequenced
- */
- virtual jint seq() const { return 0; }
-
- inline size_t size() const { return _size; }
- inline void set_size(size_t size) { _size = size; }
-
- inline MEMFLAGS flags() const { return _flags; }
- inline void set_flags(MEMFLAGS flags) { _flags = flags; }
-
- MemPointerRecord& operator= (const MemPointerRecord& ptr) {
- MemPointer::operator=(ptr);
- _flags = ptr.flags();
-#ifdef ASSERT
- if (IS_ARENA_OBJ(_flags)) {
- assert(!is_vm_pointer(), "wrong flags");
- assert((_flags & ot_masks) == otArena, "wrong flags");
- }
-#endif
- _size = ptr.size();
- return *this;
- }
-
- // if the pointer represents a malloc-ed memory address
- inline bool is_malloced_pointer() const {
- return !is_vm_pointer();
- }
-
- // if the pointer represents a virtual memory address
- inline bool is_vm_pointer() const {
- return is_virtual_memory_record(_flags);
- }
-
- // if this record records a 'malloc' or virtual memory
- // 'reserve' call
- inline bool is_allocation_record() const {
- return is_allocation_record(_flags);
- }
-
- // if this record records a size information of an arena
- inline bool is_arena_memory_record() const {
- return is_arena_memory_record(_flags);
- }
-
- // if this pointer represents an address to an arena object
- inline bool is_arena_record() const {
- return is_arena_record(_flags);
- }
-
- // if this record represents a size information of specific arena
- inline bool is_memory_record_of_arena(const MemPointerRecord* arena_rc) {
- assert(is_arena_memory_record(), "not size record");
- assert(arena_rc->is_arena_record(), "not arena record");
- return (arena_rc->addr() + sizeof(void*)) == addr();
- }
-
- // if this record records a 'free' or virtual memory 'free' call
- inline bool is_deallocation_record() const {
- return is_deallocation_record(_flags);
- }
-
- // if this record records a virtual memory 'commit' call
- inline bool is_commit_record() const {
- return is_virtual_memory_commit_record(_flags);
- }
-
- // if this record records a virtual memory 'uncommit' call
- inline bool is_uncommit_record() const {
- return is_virtual_memory_uncommit_record(_flags);
- }
-
- // if this record is a tagging record of a virtual memory block
- inline bool is_type_tagging_record() const {
- return is_virtual_memory_type_record(_flags);
- }
-
- // if the two memory pointer records actually represent the same
- // memory block
- inline bool is_same_region(const MemPointerRecord* other) const {
- return (addr() == other->addr() && size() == other->size());
- }
-
- // if this memory region fully contains another one
- inline bool contains_region(const MemPointerRecord* other) const {
- return contains_region(other->addr(), other->size());
- }
-
- // if this memory region fully contains specified memory range
- inline bool contains_region(address add, size_t sz) const {
- return (addr() <= add && addr() + size() >= add + sz);
- }
-
- inline bool contains_address(address add) const {
- return (addr() <= add && addr() + size() > add);
- }
-
- // if this memory region overlaps another region
- inline bool overlaps_region(const MemPointerRecord* other) const {
- assert(other != NULL, "Just check");
- assert(size() > 0 && other->size() > 0, "empty range");
- return contains_address(other->addr()) ||
- contains_address(other->addr() + other->size() - 1) || // exclude end address
- other->contains_address(addr()) ||
- other->contains_address(addr() + size() - 1); // exclude end address
- }
-
-};
-
-// MemPointerRecordEx also records callsite pc, from where
-// the memory block is allocated
-class MemPointerRecordEx : public MemPointerRecord {
- private:
- address _pc; // callsite pc
-
- public:
- MemPointerRecordEx(): _pc(0) { }
-
- MemPointerRecordEx(address addr, MEMFLAGS memflags, size_t size = 0, address pc = 0):
- MemPointerRecord(addr, memflags, size), _pc(pc) {}
-
- MemPointerRecordEx(const MemPointerRecordEx& copy_from):
- MemPointerRecord(copy_from), _pc(copy_from.pc()) {}
-
- inline address pc() const { return _pc; }
-
- void init(const MemPointerRecordEx* mpe) {
- MemPointerRecord::operator=(*mpe);
- _pc = mpe->pc();
- }
-
- void init(const MemPointerRecord* mp) {
- MemPointerRecord::operator=(*mp);
- _pc = 0;
- }
-};
-
-// a virtual memory region. The region can represent a reserved
-// virtual memory region or a committed memory region
-class VMMemRegion : public MemPointerRecord {
-public:
- VMMemRegion() { }
-
- void init(const MemPointerRecord* mp) {
- assert(mp->is_vm_pointer(), "Sanity check");
- _addr = mp->addr();
- set_size(mp->size());
- set_flags(mp->flags());
- }
-
- VMMemRegion& operator=(const VMMemRegion& other) {
- MemPointerRecord::operator=(other);
- return *this;
- }
-
- inline bool is_reserved_region() const {
- return is_allocation_record();
- }
-
- inline bool is_committed_region() const {
- return is_commit_record();
- }
-
- /* base address of this virtual memory range */
- inline address base() const {
- return addr();
- }
-
- /* tag this virtual memory range to the specified memory type */
- inline void tag(MEMFLAGS f) {
- set_flags(flags() | (f & mt_masks));
- }
-
- // expand this region to also cover specified range.
- // The range has to be on either end of the memory region.
- void expand_region(address addr, size_t sz) {
- if (addr < base()) {
- assert(addr + sz == base(), "Sanity check");
- _addr = addr;
- set_size(size() + sz);
- } else {
- assert(base() + size() == addr, "Sanity check");
- set_size(size() + sz);
- }
- }
-
- // exclude the specified address range from this region.
- // The excluded memory range has to be on either end of this memory
- // region.
- inline void exclude_region(address add, size_t sz) {
- assert(is_reserved_region() || is_committed_region(), "Sanity check");
- assert(addr() != NULL && size() != 0, "Sanity check");
- assert(add >= addr() && add < addr() + size(), "Sanity check");
- assert(add == addr() || (add + sz) == (addr() + size()),
- "exclude in the middle");
- if (add == addr()) {
- set_addr(add + sz);
- set_size(size() - sz);
- } else {
- set_size(size() - sz);
- }
- }
-};
-
-class VMMemRegionEx : public VMMemRegion {
- private:
- jint _seq; // sequence number
-
- public:
- VMMemRegionEx(): _pc(0) { }
-
- void init(const MemPointerRecordEx* mpe) {
- VMMemRegion::init(mpe);
- _pc = mpe->pc();
- }
-
- void init(const MemPointerRecord* mpe) {
- VMMemRegion::init(mpe);
- _pc = 0;
- }
-
- VMMemRegionEx& operator=(const VMMemRegionEx& other) {
- VMMemRegion::operator=(other);
- _pc = other.pc();
- return *this;
- }
-
- inline address pc() const { return _pc; }
- private:
- address _pc;
-};
-
-/*
- * Sequenced memory record
- */
-class SeqMemPointerRecord : public MemPointerRecord {
- private:
- jint _seq; // sequence number
-
- public:
- SeqMemPointerRecord(): _seq(0){ }
-
- SeqMemPointerRecord(address addr, MEMFLAGS flags, size_t size, jint seq)
- : MemPointerRecord(addr, flags, size), _seq(seq) {
- }
-
- SeqMemPointerRecord(const SeqMemPointerRecord& copy_from)
- : MemPointerRecord(copy_from) {
- _seq = copy_from.seq();
- }
-
- SeqMemPointerRecord& operator= (const SeqMemPointerRecord& ptr) {
- MemPointerRecord::operator=(ptr);
- _seq = ptr.seq();
- return *this;
- }
-
- inline jint seq() const {
- return _seq;
- }
-};
-
-
-
-class SeqMemPointerRecordEx : public MemPointerRecordEx {
- private:
- jint _seq; // sequence number
-
- public:
- SeqMemPointerRecordEx(): _seq(0) { }
-
- SeqMemPointerRecordEx(address addr, MEMFLAGS flags, size_t size,
- jint seq, address pc):
- MemPointerRecordEx(addr, flags, size, pc), _seq(seq) {
- }
-
- SeqMemPointerRecordEx(const SeqMemPointerRecordEx& copy_from)
- : MemPointerRecordEx(copy_from) {
- _seq = copy_from.seq();
- }
-
- SeqMemPointerRecordEx& operator= (const SeqMemPointerRecordEx& ptr) {
- MemPointerRecordEx::operator=(ptr);
- _seq = ptr.seq();
- return *this;
- }
-
- inline jint seq() const {
- return _seq;
- }
-};
-
-#endif // SHARE_VM_SERVICES_MEM_PTR_HPP
--- a/hotspot/src/share/vm/services/memPtrArray.hpp Tue Aug 05 14:18:44 2014 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,306 +0,0 @@
-/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- *
- */
-#ifndef SHARE_VM_UTILITIES_MEM_PTR_ARRAY_HPP
-#define SHARE_VM_UTILITIES_MEM_PTR_ARRAY_HPP
-
-#include "memory/allocation.hpp"
-#include "services/memPtr.hpp"
-
-class MemPtr;
-class MemRecorder;
-class ArenaInfo;
-class MemSnapshot;
-
-extern "C" {
- typedef int (*FN_SORT)(const void *, const void *);
-}
-
-
-// Memory pointer array interface. This array is used by NMT to hold
-// various memory block information.
-// The memory pointer arrays are usually walked with their iterators.
-
-class MemPointerArray : public CHeapObj<mtNMT> {
- public:
- virtual ~MemPointerArray() { }
-
- // return true if it can not allocate storage for the data
- virtual bool out_of_memory() const = 0;
- virtual bool is_empty() const = 0;
- virtual bool is_full() = 0;
- virtual int length() const = 0;
- virtual void clear() = 0;
- virtual bool append(MemPointer* ptr) = 0;
- virtual bool insert_at(MemPointer* ptr, int pos) = 0;
- virtual bool remove_at(int pos) = 0;
- virtual MemPointer* at(int index) const = 0;
- virtual void sort(FN_SORT fn) = 0;
- virtual size_t instance_size() const = 0;
- virtual bool shrink() = 0;
-
- NOT_PRODUCT(virtual int capacity() const = 0;)
-};
-
-// Iterator interface
-class MemPointerArrayIterator VALUE_OBJ_CLASS_SPEC {
- public:
- // return the pointer at current position
- virtual MemPointer* current() const = 0;
- // return the next pointer and advance current position
- virtual MemPointer* next() = 0;
- // return next pointer without advancing current position
- virtual MemPointer* peek_next() const = 0;
- // return previous pointer without changing current position
- virtual MemPointer* peek_prev() const = 0;
- // remove the pointer at current position
- virtual void remove() = 0;
- // insert the pointer at current position
- virtual bool insert(MemPointer* ptr) = 0;
- // insert specified element after current position and
- // move current position to newly inserted position
- virtual bool insert_after(MemPointer* ptr) = 0;
-};
-
-// implementation class
-class MemPointerArrayIteratorImpl : public MemPointerArrayIterator {
- protected:
- MemPointerArray* _array;
- int _pos;
-
- public:
- MemPointerArrayIteratorImpl(MemPointerArray* arr) {
- assert(arr != NULL, "Parameter check");
- _array = arr;
- _pos = 0;
- }
-
- virtual MemPointer* current() const {
- if (_pos < _array->length()) {
- return _array->at(_pos);
- }
- return NULL;
- }
-
- virtual MemPointer* next() {
- if (_pos + 1 < _array->length()) {
- return _array->at(++_pos);
- }
- _pos = _array->length();
- return NULL;
- }
-
- virtual MemPointer* peek_next() const {
- if (_pos + 1 < _array->length()) {
- return _array->at(_pos + 1);
- }
- return NULL;
- }
-
- virtual MemPointer* peek_prev() const {
- if (_pos > 0) {
- return _array->at(_pos - 1);
- }
- return NULL;
- }
-
- virtual void remove() {
- if (_pos < _array->length()) {
- _array->remove_at(_pos);
- }
- }
-
- virtual bool insert(MemPointer* ptr) {
- return _array->insert_at(ptr, _pos);
- }
-
- virtual bool insert_after(MemPointer* ptr) {
- if (_array->insert_at(ptr, _pos + 1)) {
- _pos ++;
- return true;
- }
- return false;
- }
-};
-
-
-
-// Memory pointer array implementation.
-// This implementation implements expandable array
-#define DEFAULT_PTR_ARRAY_SIZE 1024
-
-template <class E> class MemPointerArrayImpl : public MemPointerArray {
- private:
- int _max_size;
- int _size;
- bool _init_elements;
- E* _data;
-
- public:
- MemPointerArrayImpl(int initial_size = DEFAULT_PTR_ARRAY_SIZE, bool init_elements = true):
- _max_size(initial_size), _size(0), _init_elements(init_elements) {
- _data = (E*)raw_allocate(sizeof(E), initial_size);
- if (_init_elements) {
- for (int index = 0; index < _max_size; index ++) {
- ::new ((void*)&_data[index]) E();
- }
- }
- }
-
- virtual ~MemPointerArrayImpl() {
- if (_data != NULL) {
- raw_free(_data);
- }
- }
-
- public:
- bool out_of_memory() const {
- return (_data == NULL);
- }
-
- size_t instance_size() const {
- return sizeof(MemPointerArrayImpl<E>) + _max_size * sizeof(E);
- }
-
- bool is_empty() const {
- assert(_data != NULL, "Just check");
- return _size == 0;
- }
-
- bool is_full() {
- assert(_data != NULL, "Just check");
- if (_size < _max_size) {
- return false;
- } else {
- return !expand_array();
- }
- }
-
- int length() const {
- assert(_data != NULL, "Just check");
- return _size;
- }
-
- NOT_PRODUCT(int capacity() const { return _max_size; })
-
- void clear() {
- assert(_data != NULL, "Just check");
- _size = 0;
- }
-
- bool append(MemPointer* ptr) {
- assert(_data != NULL, "Just check");
- if (is_full()) {
- return false;
- }
- _data[_size ++] = *(E*)ptr;
- return true;
- }
-
- bool insert_at(MemPointer* ptr, int pos) {
- assert(_data != NULL, "Just check");
- if (is_full()) {
- return false;
- }
- for (int index = _size; index > pos; index --) {
- _data[index] = _data[index - 1];
- }
- _data[pos] = *(E*)ptr;
- _size ++;
- return true;
- }
-
- bool remove_at(int pos) {
- assert(_data != NULL, "Just check");
- if (_size <= pos && pos >= 0) {
- return false;
- }
- -- _size;
-
- for (int index = pos; index < _size; index ++) {
- _data[index] = _data[index + 1];
- }
- return true;
- }
-
- MemPointer* at(int index) const {
- assert(_data != NULL, "Just check");
- assert(index >= 0 && index < _size, "illegal index");
- return &_data[index];
- }
-
- bool shrink() {
- float used = ((float)_size) / ((float)_max_size);
- if (used < 0.40) {
- E* old_ptr = _data;
- int new_size = ((_max_size) / (2 * DEFAULT_PTR_ARRAY_SIZE) + 1) * DEFAULT_PTR_ARRAY_SIZE;
- _data = (E*)raw_reallocate(_data, sizeof(E), new_size);
- if (_data == NULL) {
- _data = old_ptr;
- return false;
- } else {
- _max_size = new_size;
- return true;
- }
- }
- return false;
- }
-
- void sort(FN_SORT fn) {
- assert(_data != NULL, "Just check");
- qsort((void*)_data, _size, sizeof(E), fn);
- }
-
- private:
- bool expand_array() {
- assert(_data != NULL, "Not yet allocated");
- E* old_ptr = _data;
- if ((_data = (E*)raw_reallocate((void*)_data, sizeof(E),
- _max_size + DEFAULT_PTR_ARRAY_SIZE)) == NULL) {
- _data = old_ptr;
- return false;
- } else {
- _max_size += DEFAULT_PTR_ARRAY_SIZE;
- if (_init_elements) {
- for (int index = _size; index < _max_size; index ++) {
- ::new ((void*)&_data[index]) E();
- }
- }
- return true;
- }
- }
-
- void* raw_allocate(size_t elementSize, int items) {
- return os::malloc(elementSize * items, mtNMT);
- }
-
- void* raw_reallocate(void* ptr, size_t elementSize, int items) {
- return os::realloc(ptr, elementSize * items, mtNMT);
- }
-
- void raw_free(void* ptr) {
- os::free(ptr, mtNMT);
- }
-};
-
-#endif // SHARE_VM_UTILITIES_MEM_PTR_ARRAY_HPP
--- a/hotspot/src/share/vm/services/memRecorder.cpp Tue Aug 05 14:18:44 2014 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,171 +0,0 @@
-/*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- *
- */
-
-#include "precompiled.hpp"
-
-#include "runtime/atomic.inline.hpp"
-#include "services/memBaseline.hpp"
-#include "services/memRecorder.hpp"
-#include "services/memPtr.hpp"
-#include "services/memTracker.hpp"
-
-MemPointer* SequencedRecordIterator::next_record() {
- MemPointerRecord* itr_cur = (MemPointerRecord*)_itr.current();
- if (itr_cur == NULL) {
- return itr_cur;
- }
-
- MemPointerRecord* itr_next = (MemPointerRecord*)_itr.next();
-
- // don't collapse virtual memory records
- while (itr_next != NULL && !itr_cur->is_vm_pointer() &&
- !itr_next->is_vm_pointer() &&
- same_kind(itr_cur, itr_next)) {
- itr_cur = itr_next;
- itr_next = (MemPointerRecord*)_itr.next();
- }
-
- return itr_cur;
-}
-
-
-volatile jint MemRecorder::_instance_count = 0;
-
-MemRecorder::MemRecorder() {
- assert(MemTracker::is_on(), "Native memory tracking is off");
- Atomic::inc(&_instance_count);
- set_generation();
-
- if (MemTracker::track_callsite()) {
- _pointer_records = new (std::nothrow)FixedSizeMemPointerArray<SeqMemPointerRecordEx,
- DEFAULT_RECORDER_PTR_ARRAY_SIZE>();
- } else {
- _pointer_records = new (std::nothrow)FixedSizeMemPointerArray<SeqMemPointerRecord,
- DEFAULT_RECORDER_PTR_ARRAY_SIZE>();
- }
- _next = NULL;
-
-
- if (_pointer_records != NULL) {
- // recode itself
- address pc = CURRENT_PC;
- record((address)this, (MemPointerRecord::malloc_tag()|mtNMT|otNMTRecorder),
- sizeof(MemRecorder), SequenceGenerator::next(), pc);
- record((address)_pointer_records, (MemPointerRecord::malloc_tag()|mtNMT|otNMTRecorder),
- _pointer_records->instance_size(), SequenceGenerator::next(), pc);
- }
-}
-
-MemRecorder::~MemRecorder() {
- if (_pointer_records != NULL) {
- if (MemTracker::is_on()) {
- MemTracker::record_free((address)_pointer_records, mtNMT);
- MemTracker::record_free((address)this, mtNMT);
- }
- delete _pointer_records;
- }
- // delete all linked recorders
- while (_next != NULL) {
- MemRecorder* tmp = _next;
- _next = _next->next();
- tmp->set_next(NULL);
- delete tmp;
- }
- Atomic::dec(&_instance_count);
-}
-
-// Sorting order:
-// 1. memory block address
-// 2. mem pointer record tags
-// 3. sequence number
-int MemRecorder::sort_record_fn(const void* e1, const void* e2) {
- const MemPointerRecord* p1 = (const MemPointerRecord*)e1;
- const MemPointerRecord* p2 = (const MemPointerRecord*)e2;
- int delta = UNSIGNED_COMPARE(p1->addr(), p2->addr());
- if (delta == 0) {
- int df = UNSIGNED_COMPARE((p1->flags() & MemPointerRecord::tag_masks),
- (p2->flags() & MemPointerRecord::tag_masks));
- if (df == 0) {
- assert(p1->seq() != p2->seq(), "dup seq");
- return p1->seq() - p2->seq();
- } else {
- return df;
- }
- } else {
- return delta;
- }
-}
-
-bool MemRecorder::record(address p, MEMFLAGS flags, size_t size, jint seq, address pc) {
- assert(seq > 0, "No sequence number");
-#ifdef ASSERT
- if (MemPointerRecord::is_virtual_memory_record(flags)) {
- assert((flags & MemPointerRecord::tag_masks) != 0, "bad virtual memory record");
- } else {
- assert((flags & MemPointerRecord::tag_masks) == MemPointerRecord::malloc_tag() ||
- (flags & MemPointerRecord::tag_masks) == MemPointerRecord::free_tag() ||
- IS_ARENA_OBJ(flags),
- "bad malloc record");
- }
- // a recorder should only hold records within the same generation
- unsigned long cur_generation = SequenceGenerator::current_generation();
- assert(cur_generation == _generation,
- "this thread did not enter sync point");
-#endif
-
- if (MemTracker::track_callsite()) {
- SeqMemPointerRecordEx ap(p, flags, size, seq, pc);
- debug_only(check_dup_seq(ap.seq());)
- return _pointer_records->append(&ap);
- } else {
- SeqMemPointerRecord ap(p, flags, size, seq);
- debug_only(check_dup_seq(ap.seq());)
- return _pointer_records->append(&ap);
- }
-}
-
- // iterator for alloc pointers
-SequencedRecordIterator MemRecorder::pointer_itr() {
- assert(_pointer_records != NULL, "just check");
- _pointer_records->sort((FN_SORT)sort_record_fn);
- return SequencedRecordIterator(_pointer_records);
-}
-
-
-void MemRecorder::set_generation() {
- _generation = SequenceGenerator::current_generation();
-}
-
-#ifdef ASSERT
-
-void MemRecorder::check_dup_seq(jint seq) const {
- MemPointerArrayIteratorImpl itr(_pointer_records);
- MemPointerRecord* rc = (MemPointerRecord*)itr.current();
- while (rc != NULL) {
- assert(rc->seq() != seq, "dup seq");
- rc = (MemPointerRecord*)itr.next();
- }
-}
-
-#endif
--- a/hotspot/src/share/vm/services/memRecorder.hpp Tue Aug 05 14:18:44 2014 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,271 +0,0 @@
-/*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- *
- */
-
-#ifndef SHARE_VM_SERVICES_MEM_RECORDER_HPP
-#define SHARE_VM_SERVICES_MEM_RECORDER_HPP
-
-#include "memory/allocation.hpp"
-#include "runtime/os.hpp"
-#include "services/memPtrArray.hpp"
-
-class MemSnapshot;
-class MemTracker;
-class MemTrackWorker;
-
-// Fixed size memory pointer array implementation
-template <class E, int SIZE> class FixedSizeMemPointerArray :
- public MemPointerArray {
- // This implementation is for memory recorder only
- friend class MemRecorder;
-
- private:
- E _data[SIZE];
- int _size;
-
- protected:
- FixedSizeMemPointerArray(bool init_elements = false):
- _size(0){
- if (init_elements) {
- for (int index = 0; index < SIZE; index ++) {
- ::new ((void*)&_data[index]) E();
- }
- }
- }
-
- void* operator new(size_t size, const std::nothrow_t& nothrow_constant) throw() {
- // the instance is part of memRecorder, needs to be tagged with 'otNMTRecorder'
- // to avoid recursion
- return os::malloc(size, (mtNMT | otNMTRecorder));
- }
-
- void* operator new(size_t size) throw() {
- assert(false, "use nothrow version");
- return NULL;
- }
-
- void operator delete(void* p) {
- os::free(p, (mtNMT | otNMTRecorder));
- }
-
- // instance size
- inline size_t instance_size() const {
- return sizeof(FixedSizeMemPointerArray<E, SIZE>);
- }
-
- NOT_PRODUCT(int capacity() const { return SIZE; })
-
- public:
- // implementation of public interface
- bool out_of_memory() const { return false; }
- bool is_empty() const { return _size == 0; }
- bool is_full() { return length() >= SIZE; }
- int length() const { return _size; }
-
- void clear() {
- _size = 0;
- }
-
- bool append(MemPointer* ptr) {
- if (is_full()) return false;
- _data[_size ++] = *(E*)ptr;
- return true;
- }
-
- virtual bool insert_at(MemPointer* p, int pos) {
- assert(false, "append only");
- return false;
- }
-
- virtual bool remove_at(int pos) {
- assert(false, "not supported");
- return false;
- }
-
- MemPointer* at(int index) const {
- assert(index >= 0 && index < length(),
- "parameter check");
- return ((E*)&_data[index]);
- }
-
- void sort(FN_SORT fn) {
- qsort((void*)_data, _size, sizeof(E), fn);
- }
-
- bool shrink() {
- return false;
- }
-};
-
-
-// This iterator requires pre-sorted MemPointerArray, which is sorted by:
-// 1. address
-// 2. allocation type
-// 3. sequence number
-// During the array walking, iterator collapses pointers with the same
-// address and allocation type, and only returns the one with highest
-// sequence number.
-//
-// This is read-only iterator, update methods are asserted.
-class SequencedRecordIterator : public MemPointerArrayIterator {
- private:
- MemPointerArrayIteratorImpl _itr;
- MemPointer* _cur;
-
- public:
- SequencedRecordIterator(const MemPointerArray* arr):
- _itr(const_cast<MemPointerArray*>(arr)) {
- _cur = next_record();
- }
-
- SequencedRecordIterator(const SequencedRecordIterator& itr):
- _itr(itr._itr) {
- _cur = next_record();
- }
-
- // return the pointer at current position
- virtual MemPointer* current() const {
- return _cur;
- };
-
- // return the next pointer and advance current position
- virtual MemPointer* next() {
- _cur = next_record();
- return _cur;
- }
-
- // return the next pointer without advancing current position
- virtual MemPointer* peek_next() const {
- assert(false, "not implemented");
- return NULL;
-
- }
- // return the previous pointer without changing current position
- virtual MemPointer* peek_prev() const {
- assert(false, "not implemented");
- return NULL;
- }
-
- // remove the pointer at current position
- virtual void remove() {
- assert(false, "read-only iterator");
- };
- // insert the pointer at current position
- virtual bool insert(MemPointer* ptr) {
- assert(false, "read-only iterator");
- return false;
- }
-
- virtual bool insert_after(MemPointer* ptr) {
- assert(false, "read-only iterator");
- return false;
- }
- private:
- // collapse the 'same kind' of records, and return this 'kind' of
- // record with highest sequence number
- MemPointer* next_record();
-
- // Test if the two records are the same kind: the same memory block and allocation
- // type.
- inline bool same_kind(const MemPointerRecord* p1, const MemPointerRecord* p2) const {
- assert(!p1->is_vm_pointer() && !p2->is_vm_pointer(), "malloc pointer only");
- return (p1->addr() == p2->addr() &&
- (p1->flags() &MemPointerRecord::tag_masks) ==
- (p2->flags() & MemPointerRecord::tag_masks));
- }
-};
-
-
-
-#define DEFAULT_RECORDER_PTR_ARRAY_SIZE 512
-
-class MemRecorder : public CHeapObj<mtNMT|otNMTRecorder> {
- friend class MemSnapshot;
- friend class MemTracker;
- friend class MemTrackWorker;
- friend class GenerationData;
-
- protected:
- // the array that holds memory records
- MemPointerArray* _pointer_records;
-
- private:
- // used for linked list
- MemRecorder* _next;
- // active recorder can only record a certain generation data
- unsigned long _generation;
-
- protected:
- _NOINLINE_ MemRecorder();
- ~MemRecorder();
-
- // record a memory operation
- bool record(address addr, MEMFLAGS flags, size_t size, jint seq, address caller_pc = 0);
-
- // linked list support
- inline void set_next(MemRecorder* rec) {
- _next = rec;
- }
-
- inline MemRecorder* next() const {
- return _next;
- }
-
- // if the recorder is full
- inline bool is_full() const {
- assert(_pointer_records != NULL, "just check");
- return _pointer_records->is_full();
- }
-
- // if running out of memory when initializing recorder's internal
- // data
- inline bool out_of_memory() const {
- return (_pointer_records == NULL ||
- _pointer_records->out_of_memory());
- }
-
- inline void clear() {
- assert(_pointer_records != NULL, "Just check");
- _pointer_records->clear();
- }
-
- SequencedRecordIterator pointer_itr();
-
- // return the generation of this recorder which it belongs to
- unsigned long get_generation() const { return _generation; }
- protected:
- // number of MemRecorder instance
- static volatile jint _instance_count;
-
- private:
- // sorting function, sort records into following order
- // 1. memory address
- // 2. allocation type
- // 3. sequence number
- static int sort_record_fn(const void* e1, const void* e2);
-
- debug_only(void check_dup_seq(jint seq) const;)
- void set_generation();
-};
-
-#endif // SHARE_VM_SERVICES_MEM_RECORDER_HPP
--- a/hotspot/src/share/vm/services/memReporter.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/services/memReporter.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -22,618 +22,595 @@
*
*/
#include "precompiled.hpp"
-#include "classfile/systemDictionary.hpp"
-#include "runtime/os.hpp"
+
+#include "memory/allocation.hpp"
+#include "services/mallocTracker.hpp"
#include "services/memReporter.hpp"
-#include "services/memPtrArray.hpp"
-#include "services/memTracker.hpp"
+#include "services/virtualMemoryTracker.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+size_t MemReporterBase::reserved_total(const MallocMemory* malloc, const VirtualMemory* vm) const {
+ return malloc->malloc_size() + malloc->arena_size() + vm->reserved();
+}
+
+size_t MemReporterBase::committed_total(const MallocMemory* malloc, const VirtualMemory* vm) const {
+ return malloc->malloc_size() + malloc->arena_size() + vm->committed();
+}
-PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC
+void MemReporterBase::print_total(size_t reserved, size_t committed) const {
+ const char* scale = current_scale();
+ output()->print("reserved=" SIZE_FORMAT "%s, committed=" SIZE_FORMAT "%s",
+ amount_in_current_scale(reserved), scale, amount_in_current_scale(committed), scale);
+}
+
+void MemReporterBase::print_malloc(size_t amount, size_t count) const {
+ const char* scale = current_scale();
+ outputStream* out = output();
+ out->print("(malloc=" SIZE_FORMAT "%s",
+ amount_in_current_scale(amount), scale);
+
+ if (count > 0) {
+ out->print(" #" SIZE_FORMAT "", count);
+ }
-const char* BaselineOutputer::memory_unit(size_t scale) {
- switch(scale) {
- case K: return "KB";
- case M: return "MB";
- case G: return "GB";
- }
- ShouldNotReachHere();
- return NULL;
+ out->print(")");
+}
+
+void MemReporterBase::print_virtual_memory(size_t reserved, size_t committed) const {
+ const char* scale = current_scale();
+ output()->print("(mmap: reserved=" SIZE_FORMAT "%s, committed=" SIZE_FORMAT "%s)",
+ amount_in_current_scale(reserved), scale, amount_in_current_scale(committed), scale);
+}
+
+void MemReporterBase::print_malloc_line(size_t amount, size_t count) const {
+ output()->print("%28s", " ");
+ print_malloc(amount, count);
+ output()->print_cr(" ");
+}
+
+void MemReporterBase::print_virtual_memory_line(size_t reserved, size_t committed) const {
+ output()->print("%28s", " ");
+ print_virtual_memory(reserved, committed);
+ output()->print_cr(" ");
+}
+
+void MemReporterBase::print_arena_line(size_t amount, size_t count) const {
+ const char* scale = current_scale();
+ output()->print_cr("%27s (arena=" SIZE_FORMAT "%s #" SIZE_FORMAT ")", " ",
+ amount_in_current_scale(amount), scale, count);
+}
+
+void MemReporterBase::print_virtual_memory_region(const char* type, address base, size_t size) const {
+ const char* scale = current_scale();
+ output()->print("[" PTR_FORMAT " - " PTR_FORMAT "] %s " SIZE_FORMAT "%s",
+ p2i(base), p2i(base + size), type, amount_in_current_scale(size), scale);
}
-void BaselineReporter::report_baseline(const MemBaseline& baseline, bool summary_only) {
- assert(MemTracker::is_on(), "Native memory tracking is off");
- _outputer.start(scale());
- _outputer.total_usage(
- amount_in_current_scale(baseline.total_malloc_amount() + baseline.total_reserved_amount()),
- amount_in_current_scale(baseline.total_malloc_amount() + baseline.total_committed_amount()));
-
- _outputer.num_of_classes(baseline.number_of_classes());
- _outputer.num_of_threads(baseline.number_of_threads());
-
- report_summaries(baseline);
- if (!summary_only && MemTracker::track_callsite()) {
- report_virtual_memory_map(baseline);
- report_callsites(baseline);
- }
- _outputer.done();
-}
-
-void BaselineReporter::report_summaries(const MemBaseline& baseline) {
- _outputer.start_category_summary();
- MEMFLAGS type;
+void MemSummaryReporter::report() {
+ const char* scale = current_scale();
+ outputStream* out = output();
+ size_t total_reserved_amount = _malloc_snapshot->total() +
+ _vm_snapshot->total_reserved();
+ size_t total_committed_amount = _malloc_snapshot->total() +
+ _vm_snapshot->total_committed();
- for (int index = 0; index < NUMBER_OF_MEMORY_TYPE; index ++) {
- type = MemBaseline::MemType2NameMap[index]._flag;
- _outputer.category_summary(type,
- amount_in_current_scale(baseline.reserved_amount(type)),
- amount_in_current_scale(baseline.committed_amount(type)),
- amount_in_current_scale(baseline.malloc_amount(type)),
- baseline.malloc_count(type),
- amount_in_current_scale(baseline.arena_amount(type)),
- baseline.arena_count(type));
- }
-
- _outputer.done_category_summary();
-}
+ // Overall total
+ out->print_cr("\nNative Memory Tracking:\n");
+ out->print("Total: ");
+ print_total(total_reserved_amount, total_committed_amount);
+ out->print("\n");
-void BaselineReporter::report_virtual_memory_map(const MemBaseline& baseline) {
- _outputer.start_virtual_memory_map();
- MemBaseline* pBL = const_cast<MemBaseline*>(&baseline);
- MemPointerArrayIteratorImpl itr = MemPointerArrayIteratorImpl(pBL->_vm_map);
- VMMemRegionEx* rgn = (VMMemRegionEx*)itr.current();
- while (rgn != NULL) {
- if (rgn->is_reserved_region()) {
- _outputer.reserved_memory_region(FLAGS_TO_MEMORY_TYPE(rgn->flags()),
- rgn->base(), rgn->base() + rgn->size(), amount_in_current_scale(rgn->size()), rgn->pc());
- } else {
- _outputer.committed_memory_region(rgn->base(), rgn->base() + rgn->size(),
- amount_in_current_scale(rgn->size()), rgn->pc());
- }
- rgn = (VMMemRegionEx*)itr.next();
+ // Summary by memory type
+ for (int index = 0; index < mt_number_of_types; index ++) {
+ MEMFLAGS flag = NMTUtil::index_to_flag(index);
+ // thread stack is reported as part of thread category
+ if (flag == mtThreadStack) continue;
+ MallocMemory* malloc_memory = _malloc_snapshot->by_type(flag);
+ VirtualMemory* virtual_memory = _vm_snapshot->by_type(flag);
+
+ report_summary_of_type(flag, malloc_memory, virtual_memory);
}
-
- _outputer.done_virtual_memory_map();
}
-void BaselineReporter::report_callsites(const MemBaseline& baseline) {
- _outputer.start_callsite();
- MemBaseline* pBL = const_cast<MemBaseline*>(&baseline);
+void MemSummaryReporter::report_summary_of_type(MEMFLAGS flag,
+ MallocMemory* malloc_memory, VirtualMemory* virtual_memory) {
- pBL->_malloc_cs->sort((FN_SORT)MemBaseline::bl_malloc_sort_by_size);
- pBL->_vm_cs->sort((FN_SORT)MemBaseline::bl_vm_sort_by_size);
+ size_t reserved_amount = reserved_total (malloc_memory, virtual_memory);
+ size_t committed_amount = committed_total(malloc_memory, virtual_memory);
- // walk malloc callsites
- MemPointerArrayIteratorImpl malloc_itr(pBL->_malloc_cs);
- MallocCallsitePointer* malloc_callsite =
- (MallocCallsitePointer*)malloc_itr.current();
- while (malloc_callsite != NULL) {
- _outputer.malloc_callsite(malloc_callsite->addr(),
- amount_in_current_scale(malloc_callsite->amount()), malloc_callsite->count());
- malloc_callsite = (MallocCallsitePointer*)malloc_itr.next();
+ // Count thread's native stack in "Thread" category
+ if (flag == mtThread) {
+ const VirtualMemory* thread_stack_usage =
+ (const VirtualMemory*)_vm_snapshot->by_type(mtThreadStack);
+ reserved_amount += thread_stack_usage->reserved();
+ committed_amount += thread_stack_usage->committed();
+ } else if (flag == mtNMT) {
+ // Count malloc headers in "NMT" category
+ reserved_amount += _malloc_snapshot->malloc_overhead()->size();
+ committed_amount += _malloc_snapshot->malloc_overhead()->size();
}
- // walk virtual memory callsite
- MemPointerArrayIteratorImpl vm_itr(pBL->_vm_cs);
- VMCallsitePointer* vm_callsite = (VMCallsitePointer*)vm_itr.current();
- while (vm_callsite != NULL) {
- _outputer.virtual_memory_callsite(vm_callsite->addr(),
- amount_in_current_scale(vm_callsite->reserved_amount()),
- amount_in_current_scale(vm_callsite->committed_amount()));
- vm_callsite = (VMCallsitePointer*)vm_itr.next();
- }
- pBL->_malloc_cs->sort((FN_SORT)MemBaseline::bl_malloc_sort_by_pc);
- pBL->_vm_cs->sort((FN_SORT)MemBaseline::bl_vm_sort_by_pc);
- _outputer.done_callsite();
-}
+ if (amount_in_current_scale(reserved_amount) > 0) {
+ outputStream* out = output();
+ const char* scale = current_scale();
+ out->print("-%26s (", NMTUtil::flag_to_name(flag));
+ print_total(reserved_amount, committed_amount);
+ out->print_cr(")");
-void BaselineReporter::diff_baselines(const MemBaseline& cur, const MemBaseline& prev,
- bool summary_only) {
- assert(MemTracker::is_on(), "Native memory tracking is off");
- _outputer.start(scale());
- size_t total_reserved = cur.total_malloc_amount() + cur.total_reserved_amount();
- size_t total_committed = cur.total_malloc_amount() + cur.total_committed_amount();
-
- _outputer.diff_total_usage(
- amount_in_current_scale(total_reserved), amount_in_current_scale(total_committed),
- diff_in_current_scale(total_reserved, (prev.total_malloc_amount() + prev.total_reserved_amount())),
- diff_in_current_scale(total_committed, (prev.total_committed_amount() + prev.total_malloc_amount())));
+ if (flag == mtClass) {
+ // report class count
+ out->print_cr("%27s (classes #" SIZE_FORMAT ")", " ", _class_count);
+ } else if (flag == mtThread) {
+ // report thread count
+ out->print_cr("%27s (thread #" SIZE_FORMAT ")", " ", _malloc_snapshot->thread_count());
+ const VirtualMemory* thread_stack_usage =
+ _vm_snapshot->by_type(mtThreadStack);
+ out->print("%27s (stack: ", " ");
+ print_total(thread_stack_usage->reserved(), thread_stack_usage->committed());
+ out->print_cr(")");
+ }
- _outputer.diff_num_of_classes(cur.number_of_classes(),
- diff(cur.number_of_classes(), prev.number_of_classes()));
- _outputer.diff_num_of_threads(cur.number_of_threads(),
- diff(cur.number_of_threads(), prev.number_of_threads()));
+ // report malloc'd memory
+ if (amount_in_current_scale(malloc_memory->malloc_size()) > 0) {
+ // We don't know how many arena chunks are in used, so don't report the count
+ size_t count = (flag == mtChunk) ? 0 : malloc_memory->malloc_count();
+ print_malloc_line(malloc_memory->malloc_size(), count);
+ }
- diff_summaries(cur, prev);
- if (!summary_only && MemTracker::track_callsite()) {
- diff_callsites(cur, prev);
- }
- _outputer.done();
-}
-
-void BaselineReporter::diff_summaries(const MemBaseline& cur, const MemBaseline& prev) {
- _outputer.start_category_summary();
- MEMFLAGS type;
+ if (amount_in_current_scale(virtual_memory->reserved()) > 0) {
+ print_virtual_memory_line(virtual_memory->reserved(), virtual_memory->committed());
+ }
- for (int index = 0; index < NUMBER_OF_MEMORY_TYPE; index ++) {
- type = MemBaseline::MemType2NameMap[index]._flag;
- _outputer.diff_category_summary(type,
- amount_in_current_scale(cur.reserved_amount(type)),
- amount_in_current_scale(cur.committed_amount(type)),
- amount_in_current_scale(cur.malloc_amount(type)),
- cur.malloc_count(type),
- amount_in_current_scale(cur.arena_amount(type)),
- cur.arena_count(type),
- diff_in_current_scale(cur.reserved_amount(type), prev.reserved_amount(type)),
- diff_in_current_scale(cur.committed_amount(type), prev.committed_amount(type)),
- diff_in_current_scale(cur.malloc_amount(type), prev.malloc_amount(type)),
- diff(cur.malloc_count(type), prev.malloc_count(type)),
- diff_in_current_scale(cur.arena_amount(type), prev.arena_amount(type)),
- diff(cur.arena_count(type), prev.arena_count(type)));
+ if (amount_in_current_scale(malloc_memory->arena_size()) > 0) {
+ print_arena_line(malloc_memory->arena_size(), malloc_memory->arena_count());
+ }
+
+ if (flag == mtNMT &&
+ amount_in_current_scale(_malloc_snapshot->malloc_overhead()->size()) > 0) {
+ out->print_cr("%27s (tracking overhead=" SIZE_FORMAT "%s)", " ",
+ amount_in_current_scale(_malloc_snapshot->malloc_overhead()->size()), scale);
+ }
+
+ out->print_cr(" ");
}
-
- _outputer.done_category_summary();
}
-void BaselineReporter::diff_callsites(const MemBaseline& cur, const MemBaseline& prev) {
- _outputer.start_callsite();
- MemBaseline* pBL_cur = const_cast<MemBaseline*>(&cur);
- MemBaseline* pBL_prev = const_cast<MemBaseline*>(&prev);
+void MemDetailReporter::report_detail() {
+ // Start detail report
+ outputStream* out = output();
+ out->print_cr("Details:\n");
+
+ report_malloc_sites();
+ report_virtual_memory_allocation_sites();
+}
+
+void MemDetailReporter::report_malloc_sites() {
+ MallocSiteIterator malloc_itr = _baseline.malloc_sites(MemBaseline::by_size);
+ if (malloc_itr.is_empty()) return;
+
+ outputStream* out = output();
- // walk malloc callsites
- MemPointerArrayIteratorImpl cur_malloc_itr(pBL_cur->_malloc_cs);
- MemPointerArrayIteratorImpl prev_malloc_itr(pBL_prev->_malloc_cs);
+ const MallocSite* malloc_site;
+ while ((malloc_site = malloc_itr.next()) != NULL) {
+ // Don't report if size is too small
+ if (amount_in_current_scale(malloc_site->size()) == 0)
+ continue;
- MallocCallsitePointer* cur_malloc_callsite =
- (MallocCallsitePointer*)cur_malloc_itr.current();
- MallocCallsitePointer* prev_malloc_callsite =
- (MallocCallsitePointer*)prev_malloc_itr.current();
+ const NativeCallStack* stack = malloc_site->call_stack();
+ stack->print_on(out);
+ out->print("%29s", " ");
+ print_malloc(malloc_site->size(), malloc_site->count());
+ out->print_cr("\n");
+ }
+}
+
+void MemDetailReporter::report_virtual_memory_allocation_sites() {
+ VirtualMemorySiteIterator virtual_memory_itr =
+ _baseline.virtual_memory_sites(MemBaseline::by_size);
+
+ if (virtual_memory_itr.is_empty()) return;
+
+ outputStream* out = output();
+ const VirtualMemoryAllocationSite* virtual_memory_site;
- while (cur_malloc_callsite != NULL || prev_malloc_callsite != NULL) {
- if (prev_malloc_callsite == NULL) {
- assert(cur_malloc_callsite != NULL, "sanity check");
- // this is a new callsite
- _outputer.diff_malloc_callsite(cur_malloc_callsite->addr(),
- amount_in_current_scale(cur_malloc_callsite->amount()),
- cur_malloc_callsite->count(),
- diff_in_current_scale(cur_malloc_callsite->amount(), 0),
- diff(cur_malloc_callsite->count(), 0));
- cur_malloc_callsite = (MallocCallsitePointer*)cur_malloc_itr.next();
- } else if (cur_malloc_callsite == NULL) {
- assert(prev_malloc_callsite != NULL, "Sanity check");
- // this callsite is already gone
- _outputer.diff_malloc_callsite(prev_malloc_callsite->addr(),
- 0, 0,
- diff_in_current_scale(0, prev_malloc_callsite->amount()),
- diff(0, prev_malloc_callsite->count()));
- prev_malloc_callsite = (MallocCallsitePointer*)prev_malloc_itr.next();
- } else {
- assert(cur_malloc_callsite != NULL, "Sanity check");
- assert(prev_malloc_callsite != NULL, "Sanity check");
- if (cur_malloc_callsite->addr() < prev_malloc_callsite->addr()) {
- // this is a new callsite
- _outputer.diff_malloc_callsite(cur_malloc_callsite->addr(),
- amount_in_current_scale(cur_malloc_callsite->amount()),
- cur_malloc_callsite->count(),
- diff_in_current_scale(cur_malloc_callsite->amount(), 0),
- diff(cur_malloc_callsite->count(), 0));
- cur_malloc_callsite = (MallocCallsitePointer*)cur_malloc_itr.next();
- } else if (cur_malloc_callsite->addr() > prev_malloc_callsite->addr()) {
- // this callsite is already gone
- _outputer.diff_malloc_callsite(prev_malloc_callsite->addr(),
- 0, 0,
- diff_in_current_scale(0, prev_malloc_callsite->amount()),
- diff(0, prev_malloc_callsite->count()));
- prev_malloc_callsite = (MallocCallsitePointer*)prev_malloc_itr.next();
- } else {
- // the same callsite
- _outputer.diff_malloc_callsite(cur_malloc_callsite->addr(),
- amount_in_current_scale(cur_malloc_callsite->amount()),
- cur_malloc_callsite->count(),
- diff_in_current_scale(cur_malloc_callsite->amount(), prev_malloc_callsite->amount()),
- diff(cur_malloc_callsite->count(), prev_malloc_callsite->count()));
- cur_malloc_callsite = (MallocCallsitePointer*)cur_malloc_itr.next();
- prev_malloc_callsite = (MallocCallsitePointer*)prev_malloc_itr.next();
- }
- }
+ while ((virtual_memory_site = virtual_memory_itr.next()) != NULL) {
+ // Don't report if size is too small
+ if (amount_in_current_scale(virtual_memory_site->reserved()) == 0)
+ continue;
+
+ const NativeCallStack* stack = virtual_memory_site->call_stack();
+ stack->print_on(out);
+ out->print("%28s (", " ");
+ print_total(virtual_memory_site->reserved(), virtual_memory_site->committed());
+ out->print_cr(")\n");
+ }
+}
+
+
+void MemDetailReporter::report_virtual_memory_map() {
+ // Virtual memory map always in base address order
+ VirtualMemoryAllocationIterator itr = _baseline.virtual_memory_allocations();
+ const ReservedMemoryRegion* rgn;
+
+ output()->print_cr("Virtual memory map:");
+ while ((rgn = itr.next()) != NULL) {
+ report_virtual_memory_region(rgn);
+ }
+}
+
+void MemDetailReporter::report_virtual_memory_region(const ReservedMemoryRegion* reserved_rgn) {
+ assert(reserved_rgn != NULL, "NULL pointer");
+
+ // Don't report if size is too small
+ if (amount_in_current_scale(reserved_rgn->size()) == 0) return;
+
+ outputStream* out = output();
+ const char* scale = current_scale();
+ const NativeCallStack* stack = reserved_rgn->call_stack();
+ bool all_committed = reserved_rgn->all_committed();
+ const char* region_type = (all_committed ? "reserved and committed" : "reserved");
+ out->print_cr(" ");
+ print_virtual_memory_region(region_type, reserved_rgn->base(), reserved_rgn->size());
+ out->print(" for %s", NMTUtil::flag_to_name(reserved_rgn->flag()));
+ if (stack->is_empty()) {
+ out->print_cr(" ");
+ } else {
+ out->print_cr(" from");
+ stack->print_on(out, 4);
}
- // walk virtual memory callsite
- MemPointerArrayIteratorImpl cur_vm_itr(pBL_cur->_vm_cs);
- MemPointerArrayIteratorImpl prev_vm_itr(pBL_prev->_vm_cs);
- VMCallsitePointer* cur_vm_callsite = (VMCallsitePointer*)cur_vm_itr.current();
- VMCallsitePointer* prev_vm_callsite = (VMCallsitePointer*)prev_vm_itr.current();
- while (cur_vm_callsite != NULL || prev_vm_callsite != NULL) {
- if (prev_vm_callsite == NULL || cur_vm_callsite->addr() < prev_vm_callsite->addr()) {
- // this is a new callsite
- _outputer.diff_virtual_memory_callsite(cur_vm_callsite->addr(),
- amount_in_current_scale(cur_vm_callsite->reserved_amount()),
- amount_in_current_scale(cur_vm_callsite->committed_amount()),
- diff_in_current_scale(cur_vm_callsite->reserved_amount(), 0),
- diff_in_current_scale(cur_vm_callsite->committed_amount(), 0));
- cur_vm_callsite = (VMCallsitePointer*)cur_vm_itr.next();
- } else if (cur_vm_callsite == NULL || cur_vm_callsite->addr() > prev_vm_callsite->addr()) {
- // this callsite is already gone
- _outputer.diff_virtual_memory_callsite(prev_vm_callsite->addr(),
- amount_in_current_scale(0),
- amount_in_current_scale(0),
- diff_in_current_scale(0, prev_vm_callsite->reserved_amount()),
- diff_in_current_scale(0, prev_vm_callsite->committed_amount()));
- prev_vm_callsite = (VMCallsitePointer*)prev_vm_itr.next();
- } else { // the same callsite
- _outputer.diff_virtual_memory_callsite(cur_vm_callsite->addr(),
- amount_in_current_scale(cur_vm_callsite->reserved_amount()),
- amount_in_current_scale(cur_vm_callsite->committed_amount()),
- diff_in_current_scale(cur_vm_callsite->reserved_amount(), prev_vm_callsite->reserved_amount()),
- diff_in_current_scale(cur_vm_callsite->committed_amount(), prev_vm_callsite->committed_amount()));
- cur_vm_callsite = (VMCallsitePointer*)cur_vm_itr.next();
- prev_vm_callsite = (VMCallsitePointer*)prev_vm_itr.next();
- }
- }
-
- _outputer.done_callsite();
-}
-
-size_t BaselineReporter::amount_in_current_scale(size_t amt) const {
- return (size_t)(((float)amt/(float)_scale) + 0.5);
-}
-
-int BaselineReporter::diff_in_current_scale(size_t value1, size_t value2) const {
- return (int)(((float)value1 - (float)value2)/((float)_scale) + 0.5);
-}
-
-int BaselineReporter::diff(size_t value1, size_t value2) const {
- return ((int)value1 - (int)value2);
-}
-
-void BaselineTTYOutputer::start(size_t scale, bool report_diff) {
- _scale = scale;
- _output->print_cr(" ");
- _output->print_cr("Native Memory Tracking:");
- _output->print_cr(" ");
-}
-
-void BaselineTTYOutputer::done() {
-
-}
+ if (all_committed) return;
-void BaselineTTYOutputer::total_usage(size_t total_reserved, size_t total_committed) {
- const char* unit = memory_unit(_scale);
- _output->print_cr("Total: reserved=%d%s, committed=%d%s",
- total_reserved, unit, total_committed, unit);
-}
-
-void BaselineTTYOutputer::start_category_summary() {
- _output->print_cr(" ");
-}
-
-/**
- * report a summary of memory type
- */
-void BaselineTTYOutputer::category_summary(MEMFLAGS type,
- size_t reserved_amt, size_t committed_amt, size_t malloc_amt,
- size_t malloc_count, size_t arena_amt, size_t arena_count) {
-
- // we report mtThreadStack under mtThread category
- if (type == mtThreadStack) {
- assert(malloc_amt == 0 && malloc_count == 0 && arena_amt == 0,
- "Just check");
- _thread_stack_reserved = reserved_amt;
- _thread_stack_committed = committed_amt;
- } else {
- const char* unit = memory_unit(_scale);
- size_t total_reserved = (reserved_amt + malloc_amt + arena_amt);
- size_t total_committed = (committed_amt + malloc_amt + arena_amt);
- if (type == mtThread) {
- total_reserved += _thread_stack_reserved;
- total_committed += _thread_stack_committed;
- }
-
- if (total_reserved > 0) {
- _output->print_cr("-%26s (reserved=%d%s, committed=%d%s)",
- MemBaseline::type2name(type), total_reserved, unit,
- total_committed, unit);
-
- if (type == mtClass) {
- _output->print_cr("%27s (classes #%d)", " ", _num_of_classes);
- } else if (type == mtThread) {
- _output->print_cr("%27s (thread #%d)", " ", _num_of_threads);
- _output->print_cr("%27s (stack: reserved=%d%s, committed=%d%s)", " ",
- _thread_stack_reserved, unit, _thread_stack_committed, unit);
- }
-
- if (malloc_amt > 0) {
- if (type != mtChunk) {
- _output->print_cr("%27s (malloc=%d%s, #%d)", " ", malloc_amt, unit,
- malloc_count);
- } else {
- _output->print_cr("%27s (malloc=%d%s)", " ", malloc_amt, unit);
- }
- }
-
- if (reserved_amt > 0) {
- _output->print_cr("%27s (mmap: reserved=%d%s, committed=%d%s)",
- " ", reserved_amt, unit, committed_amt, unit);
- }
-
- if (arena_amt > 0) {
- _output->print_cr("%27s (arena=%d%s, #%d)", " ", arena_amt, unit, arena_count);
- }
-
- _output->print_cr(" ");
+ CommittedRegionIterator itr = reserved_rgn->iterate_committed_regions();
+ const CommittedMemoryRegion* committed_rgn;
+ while ((committed_rgn = itr.next()) != NULL) {
+ // Don't report if size is too small
+ if (amount_in_current_scale(committed_rgn->size()) == 0) continue;
+ stack = committed_rgn->call_stack();
+ out->print("\n\t");
+ print_virtual_memory_region("committed", committed_rgn->base(), committed_rgn->size());
+ if (stack->is_empty()) {
+ out->print_cr(" ");
+ } else {
+ out->print_cr(" from");
+ stack->print_on(out, 12);
}
}
}
-void BaselineTTYOutputer::done_category_summary() {
- _output->print_cr(" ");
-}
+void MemSummaryDiffReporter::report_diff() {
+ const char* scale = current_scale();
+ outputStream* out = output();
+ out->print_cr("\nNative Memory Tracking:\n");
-
-void BaselineTTYOutputer::start_virtual_memory_map() {
- _output->print_cr("Virtual memory map:");
-}
+ // Overall diff
+ out->print("Total: ");
+ print_virtual_memory_diff(_current_baseline.total_reserved_memory(),
+ _current_baseline.total_committed_memory(), _early_baseline.total_reserved_memory(),
+ _early_baseline.total_committed_memory());
-void BaselineTTYOutputer::reserved_memory_region(MEMFLAGS type, address base, address end,
- size_t size, address pc) {
- const char* unit = memory_unit(_scale);
- char buf[128];
- int offset;
- _output->print_cr(" ");
- _output->print_cr("[" PTR_FORMAT " - " PTR_FORMAT "] reserved %d%s for %s", base, end, size, unit,
- MemBaseline::type2name(type));
- if (os::dll_address_to_function_name(pc, buf, sizeof(buf), &offset)) {
- _output->print_cr("\t\tfrom [%s+0x%x]", buf, offset);
- }
-}
+ out->print_cr("\n");
-void BaselineTTYOutputer::committed_memory_region(address base, address end, size_t size, address pc) {
- const char* unit = memory_unit(_scale);
- char buf[128];
- int offset;
- _output->print("\t[" PTR_FORMAT " - " PTR_FORMAT "] committed %d%s", base, end, size, unit);
- if (os::dll_address_to_function_name(pc, buf, sizeof(buf), &offset)) {
- _output->print_cr(" from [%s+0x%x]", buf, offset);
+ // Summary diff by memory type
+ for (int index = 0; index < mt_number_of_types; index ++) {
+ MEMFLAGS flag = NMTUtil::index_to_flag(index);
+ // thread stack is reported as part of thread category
+ if (flag == mtThreadStack) continue;
+ diff_summary_of_type(flag, _early_baseline.malloc_memory(flag),
+ _early_baseline.virtual_memory(flag), _current_baseline.malloc_memory(flag),
+ _current_baseline.virtual_memory(flag));
}
}
-void BaselineTTYOutputer::done_virtual_memory_map() {
- _output->print_cr(" ");
-}
-
-
-
-void BaselineTTYOutputer::start_callsite() {
- _output->print_cr("Details:");
- _output->print_cr(" ");
-}
-
-void BaselineTTYOutputer::done_callsite() {
- _output->print_cr(" ");
-}
+void MemSummaryDiffReporter::print_malloc_diff(size_t current_amount, size_t current_count,
+ size_t early_amount, size_t early_count) const {
+ const char* scale = current_scale();
+ outputStream* out = output();
-void BaselineTTYOutputer::malloc_callsite(address pc, size_t malloc_amt,
- size_t malloc_count) {
- if (malloc_amt > 0) {
- const char* unit = memory_unit(_scale);
- char buf[128];
- int offset;
- if (pc == 0) {
- _output->print("[BOOTSTRAP]%18s", " ");
- } else if (os::dll_address_to_function_name(pc, buf, sizeof(buf), &offset)) {
- _output->print_cr("[" PTR_FORMAT "] %s+0x%x", pc, buf, offset);
- _output->print("%28s", " ");
- } else {
- _output->print("[" PTR_FORMAT "]%18s", pc, " ");
+ out->print("malloc=" SIZE_FORMAT "%s", amount_in_current_scale(current_amount), scale);
+ long amount_diff = diff_in_current_scale(current_amount, early_amount);
+ if (amount_diff != 0) {
+ out->print(" %+ld%s", amount_diff, scale);
+ }
+ if (current_count > 0) {
+ out->print(" #" SIZE_FORMAT "", current_count);
+ if (current_count != early_count) {
+ out->print(" %+d", (int)(current_count - early_count));
}
-
- _output->print_cr("(malloc=%d%s #%d)", malloc_amt, unit, malloc_count);
- _output->print_cr(" ");
}
}
-void BaselineTTYOutputer::virtual_memory_callsite(address pc, size_t reserved_amt,
- size_t committed_amt) {
- if (reserved_amt > 0) {
- const char* unit = memory_unit(_scale);
- char buf[128];
- int offset;
- if (pc == 0) {
- _output->print("[BOOTSTRAP]%18s", " ");
- } else if (os::dll_address_to_function_name(pc, buf, sizeof(buf), &offset)) {
- _output->print_cr("[" PTR_FORMAT "] %s+0x%x", pc, buf, offset);
- _output->print("%28s", " ");
- } else {
- _output->print("[" PTR_FORMAT "]%18s", pc, " ");
- }
+void MemSummaryDiffReporter::print_arena_diff(size_t current_amount, size_t current_count,
+ size_t early_amount, size_t early_count) const {
+ const char* scale = current_scale();
+ outputStream* out = output();
+ out->print("arena=" SIZE_FORMAT "%s", amount_in_current_scale(current_amount), scale);
+ if (diff_in_current_scale(current_amount, early_amount) != 0) {
+ out->print(" %+ld", diff_in_current_scale(current_amount, early_amount));
+ }
+
+ out->print(" #" SIZE_FORMAT "", current_count);
+ if (current_count != early_count) {
+ out->print(" %+d", (int)(current_count - early_count));
+ }
+}
- _output->print_cr("(mmap: reserved=%d%s, committed=%d%s)",
- reserved_amt, unit, committed_amt, unit);
- _output->print_cr(" ");
+void MemSummaryDiffReporter::print_virtual_memory_diff(size_t current_reserved, size_t current_committed,
+ size_t early_reserved, size_t early_committed) const {
+ const char* scale = current_scale();
+ outputStream* out = output();
+ out->print("reserved=" SIZE_FORMAT "%s", amount_in_current_scale(current_reserved), scale);
+ long reserved_diff = diff_in_current_scale(current_reserved, early_reserved);
+ if (reserved_diff != 0) {
+ out->print(" %+ld%s", reserved_diff, scale);
+ }
+
+ out->print(", committed=" SIZE_FORMAT "%s", amount_in_current_scale(current_committed), scale);
+ long committed_diff = diff_in_current_scale(current_committed, early_committed);
+ if (committed_diff != 0) {
+ out->print(" %+ld%s", committed_diff, scale);
}
}
-void BaselineTTYOutputer::diff_total_usage(size_t total_reserved,
- size_t total_committed, int reserved_diff, int committed_diff) {
- const char* unit = memory_unit(_scale);
- _output->print_cr("Total: reserved=%d%s %+d%s, committed=%d%s %+d%s",
- total_reserved, unit, reserved_diff, unit, total_committed, unit,
- committed_diff, unit);
-}
+
+void MemSummaryDiffReporter::diff_summary_of_type(MEMFLAGS flag, const MallocMemory* early_malloc,
+ const VirtualMemory* early_vm, const MallocMemory* current_malloc,
+ const VirtualMemory* current_vm) const {
+
+ outputStream* out = output();
+ const char* scale = current_scale();
+
+ // Total reserved and committed memory in current baseline
+ size_t current_reserved_amount = reserved_total (current_malloc, current_vm);
+ size_t current_committed_amount = committed_total(current_malloc, current_vm);
+
+ // Total reserved and committed memory in early baseline
+ size_t early_reserved_amount = reserved_total(early_malloc, early_vm);
+ size_t early_committed_amount = committed_total(early_malloc, early_vm);
-void BaselineTTYOutputer::diff_category_summary(MEMFLAGS type,
- size_t cur_reserved_amt, size_t cur_committed_amt,
- size_t cur_malloc_amt, size_t cur_malloc_count,
- size_t cur_arena_amt, size_t cur_arena_count,
- int reserved_diff, int committed_diff, int malloc_diff,
- int malloc_count_diff, int arena_diff, int arena_count_diff) {
+ // Adjust virtual memory total
+ if (flag == mtThread) {
+ const VirtualMemory* early_thread_stack_usage =
+ _early_baseline.virtual_memory(mtThreadStack);
+ const VirtualMemory* current_thread_stack_usage =
+ _current_baseline.virtual_memory(mtThreadStack);
+
+ early_reserved_amount += early_thread_stack_usage->reserved();
+ early_committed_amount += early_thread_stack_usage->committed();
+
+ current_reserved_amount += current_thread_stack_usage->reserved();
+ current_committed_amount += current_thread_stack_usage->committed();
+ } else if (flag == mtNMT) {
+ early_reserved_amount += _early_baseline.malloc_tracking_overhead();
+ early_committed_amount += _early_baseline.malloc_tracking_overhead();
+
+ current_reserved_amount += _current_baseline.malloc_tracking_overhead();
+ current_committed_amount += _current_baseline.malloc_tracking_overhead();
+ }
- if (type == mtThreadStack) {
- assert(cur_malloc_amt == 0 && cur_malloc_count == 0 &&
- cur_arena_amt == 0, "Just check");
- _thread_stack_reserved = cur_reserved_amt;
- _thread_stack_committed = cur_committed_amt;
- _thread_stack_reserved_diff = reserved_diff;
- _thread_stack_committed_diff = committed_diff;
- } else {
- const char* unit = memory_unit(_scale);
- size_t total_reserved = (cur_reserved_amt + cur_malloc_amt + cur_arena_amt);
- // nothing to report in this category
- if (total_reserved == 0) {
- return;
- }
- int diff_reserved = (reserved_diff + malloc_diff + arena_diff);
+ if (amount_in_current_scale(current_reserved_amount) > 0 ||
+ diff_in_current_scale(current_reserved_amount, early_reserved_amount) != 0) {
+
+ // print summary line
+ out->print("-%26s (", NMTUtil::flag_to_name(flag));
+ print_virtual_memory_diff(current_reserved_amount, current_committed_amount,
+ early_reserved_amount, early_committed_amount);
+ out->print_cr(")");
- // category summary
- _output->print("-%26s (reserved=%d%s", MemBaseline::type2name(type),
- total_reserved, unit);
+ // detail lines
+ if (flag == mtClass) {
+ // report class count
+ out->print("%27s (classes #" SIZE_FORMAT "", " ", _current_baseline.class_count());
+ int class_count_diff = (int)(_current_baseline.class_count() -
+ _early_baseline.class_count());
+ if (_current_baseline.class_count() != _early_baseline.class_count()) {
+ out->print(" %+d", (int)(_current_baseline.class_count() - _early_baseline.class_count()));
+ }
+ out->print_cr(")");
+ } else if (flag == mtThread) {
+ // report thread count
+ out->print("%27s (thread #" SIZE_FORMAT "", " ", _current_baseline.thread_count());
+ int thread_count_diff = (int)(_current_baseline.thread_count() -
+ _early_baseline.thread_count());
+ if (thread_count_diff != 0) {
+ out->print(" %+d", thread_count_diff);
+ }
+ out->print_cr(")");
- if (diff_reserved != 0) {
- _output->print(" %+d%s", diff_reserved, unit);
- }
+ // report thread stack
+ const VirtualMemory* current_thread_stack =
+ _current_baseline.virtual_memory(mtThreadStack);
+ const VirtualMemory* early_thread_stack =
+ _early_baseline.virtual_memory(mtThreadStack);
- size_t total_committed = cur_committed_amt + cur_malloc_amt + cur_arena_amt;
- _output->print(", committed=%d%s", total_committed, unit);
-
- int total_committed_diff = committed_diff + malloc_diff + arena_diff;
- if (total_committed_diff != 0) {
- _output->print(" %+d%s", total_committed_diff, unit);
+ out->print("%27s (stack: ", " ");
+ print_virtual_memory_diff(current_thread_stack->reserved(), current_thread_stack->committed(),
+ early_thread_stack->reserved(), early_thread_stack->committed());
+ out->print_cr(")");
}
- _output->print_cr(")");
+ // Report malloc'd memory
+ size_t current_malloc_amount = current_malloc->malloc_size();
+ size_t early_malloc_amount = early_malloc->malloc_size();
+ if (amount_in_current_scale(current_malloc_amount) > 0 ||
+ diff_in_current_scale(current_malloc_amount, early_malloc_amount) != 0) {
+ out->print("%28s(", " ");
+ print_malloc_diff(current_malloc_amount, (flag == mtChunk) ? 0 : current_malloc->malloc_count(),
+ early_malloc_amount, early_malloc->malloc_count());
+ out->print_cr(")");
+ }
- // special cases
- if (type == mtClass) {
- _output->print("%27s (classes #%d", " ", _num_of_classes);
- if (_num_of_classes_diff != 0) {
- _output->print(" %+d", _num_of_classes_diff);
- }
- _output->print_cr(")");
- } else if (type == mtThread) {
- // thread count
- _output->print("%27s (thread #%d", " ", _num_of_threads);
- if (_num_of_threads_diff != 0) {
- _output->print_cr(" %+d)", _num_of_threads_diff);
- } else {
- _output->print_cr(")");
- }
- _output->print("%27s (stack: reserved=%d%s", " ", _thread_stack_reserved, unit);
- if (_thread_stack_reserved_diff != 0) {
- _output->print(" %+d%s", _thread_stack_reserved_diff, unit);
- }
-
- _output->print(", committed=%d%s", _thread_stack_committed, unit);
- if (_thread_stack_committed_diff != 0) {
- _output->print(" %+d%s",_thread_stack_committed_diff, unit);
- }
-
- _output->print_cr(")");
+ // Report virtual memory
+ if (amount_in_current_scale(current_vm->reserved()) > 0 ||
+ diff_in_current_scale(current_vm->reserved(), early_vm->reserved()) != 0) {
+ out->print("%27s (mmap: ", " ");
+ print_virtual_memory_diff(current_vm->reserved(), current_vm->committed(),
+ early_vm->reserved(), early_vm->committed());
+ out->print_cr(")");
}
- // malloc'd memory
- if (cur_malloc_amt > 0) {
- _output->print("%27s (malloc=%d%s", " ", cur_malloc_amt, unit);
- if (malloc_diff != 0) {
- _output->print(" %+d%s", malloc_diff, unit);
- }
- if (type != mtChunk) {
- _output->print(", #%d", cur_malloc_count);
- if (malloc_count_diff) {
- _output->print(" %+d", malloc_count_diff);
- }
- }
- _output->print_cr(")");
+ // Report arena memory
+ if (amount_in_current_scale(current_malloc->arena_size()) > 0 ||
+ diff_in_current_scale(current_malloc->arena_size(), early_malloc->arena_size()) != 0) {
+ out->print("%28s(", " ");
+ print_arena_diff(current_malloc->arena_size(), current_malloc->arena_count(),
+ early_malloc->arena_size(), early_malloc->arena_count());
+ out->print_cr(")");
}
- // mmap'd memory
- if (cur_reserved_amt > 0) {
- _output->print("%27s (mmap: reserved=%d%s", " ", cur_reserved_amt, unit);
- if (reserved_diff != 0) {
- _output->print(" %+d%s", reserved_diff, unit);
- }
+ // Report native memory tracking overhead
+ if (flag == mtNMT) {
+ size_t current_tracking_overhead = amount_in_current_scale(_current_baseline.malloc_tracking_overhead());
+ size_t early_tracking_overhead = amount_in_current_scale(_early_baseline.malloc_tracking_overhead());
- _output->print(", committed=%d%s", cur_committed_amt, unit);
- if (committed_diff != 0) {
- _output->print(" %+d%s", committed_diff, unit);
- }
- _output->print_cr(")");
- }
+ out->print("%27s (tracking overhead=" SIZE_FORMAT "%s", " ",
+ amount_in_current_scale(_current_baseline.malloc_tracking_overhead()), scale);
- // arena memory
- if (cur_arena_amt > 0) {
- _output->print("%27s (arena=%d%s", " ", cur_arena_amt, unit);
- if (arena_diff != 0) {
- _output->print(" %+d%s", arena_diff, unit);
+ long overhead_diff = diff_in_current_scale(_current_baseline.malloc_tracking_overhead(),
+ _early_baseline.malloc_tracking_overhead());
+ if (overhead_diff != 0) {
+ out->print(" %+ld%s", overhead_diff, scale);
}
- _output->print(", #%d", cur_arena_count);
- if (arena_count_diff != 0) {
- _output->print(" %+d", arena_count_diff);
- }
- _output->print_cr(")");
+ out->print_cr(")");
}
-
- _output->print_cr(" ");
+ out->print_cr(" ");
}
}
-void BaselineTTYOutputer::diff_malloc_callsite(address pc,
- size_t cur_malloc_amt, size_t cur_malloc_count,
- int malloc_diff, int malloc_count_diff) {
- if (malloc_diff != 0) {
- const char* unit = memory_unit(_scale);
- char buf[128];
- int offset;
- if (pc == 0) {
- _output->print_cr("[BOOTSTRAP]%18s", " ");
+void MemDetailDiffReporter::report_diff() {
+ MemSummaryDiffReporter::report_diff();
+ diff_malloc_sites();
+ diff_virtual_memory_sites();
+}
+
+void MemDetailDiffReporter::diff_malloc_sites() const {
+ MallocSiteIterator early_itr = _early_baseline.malloc_sites(MemBaseline::by_site);
+ MallocSiteIterator current_itr = _current_baseline.malloc_sites(MemBaseline::by_site);
+
+ const MallocSite* early_site = early_itr.next();
+ const MallocSite* current_site = current_itr.next();
+
+ while (early_site != NULL || current_site != NULL) {
+ if (early_site == NULL) {
+ new_malloc_site(current_site);
+ current_site = current_itr.next();
+ } else if (current_site == NULL) {
+ old_malloc_site(early_site);
+ early_site = early_itr.next();
} else {
- if (os::dll_address_to_function_name(pc, buf, sizeof(buf), &offset)) {
- _output->print_cr("[" PTR_FORMAT "] %s+0x%x", pc, buf, offset);
- _output->print("%28s", " ");
+ int compVal = current_site->call_stack()->compare(*early_site->call_stack());
+ if (compVal < 0) {
+ new_malloc_site(current_site);
+ current_site = current_itr.next();
+ } else if (compVal > 0) {
+ old_malloc_site(early_site);
+ early_site = early_itr.next();
} else {
- _output->print("[" PTR_FORMAT "]%18s", pc, " ");
+ diff_malloc_site(early_site, current_site);
+ early_site = early_itr.next();
+ current_site = current_itr.next();
}
}
+ }
+}
- _output->print("(malloc=%d%s", cur_malloc_amt, unit);
- if (malloc_diff != 0) {
- _output->print(" %+d%s", malloc_diff, unit);
+void MemDetailDiffReporter::diff_virtual_memory_sites() const {
+ VirtualMemorySiteIterator early_itr = _early_baseline.virtual_memory_sites(MemBaseline::by_site);
+ VirtualMemorySiteIterator current_itr = _current_baseline.virtual_memory_sites(MemBaseline::by_site);
+
+ const VirtualMemoryAllocationSite* early_site = early_itr.next();
+ const VirtualMemoryAllocationSite* current_site = current_itr.next();
+
+ while (early_site != NULL || current_site != NULL) {
+ if (early_site == NULL) {
+ new_virtual_memory_site(current_site);
+ current_site = current_itr.next();
+ } else if (current_site == NULL) {
+ old_virtual_memory_site(early_site);
+ early_site = early_itr.next();
+ } else {
+ int compVal = current_site->call_stack()->compare(*early_site->call_stack());
+ if (compVal < 0) {
+ new_virtual_memory_site(current_site);
+ current_site = current_itr.next();
+ } else if (compVal > 0) {
+ old_virtual_memory_site(early_site);
+ early_site = early_itr.next();
+ } else {
+ diff_virtual_memory_site(early_site, current_site);
+ early_site = early_itr.next();
+ current_site = current_itr.next();
+ }
}
- _output->print(", #%d", cur_malloc_count);
- if (malloc_count_diff != 0) {
- _output->print(" %+d", malloc_count_diff);
- }
- _output->print_cr(")");
- _output->print_cr(" ");
}
}
-void BaselineTTYOutputer::diff_virtual_memory_callsite(address pc,
- size_t cur_reserved_amt, size_t cur_committed_amt,
- int reserved_diff, int committed_diff) {
- if (reserved_diff != 0 || committed_diff != 0) {
- const char* unit = memory_unit(_scale);
- char buf[64];
- int offset;
- if (pc == 0) {
- _output->print_cr("[BOOSTRAP]%18s", " ");
- } else {
- if (os::dll_address_to_function_name(pc, buf, sizeof(buf), &offset)) {
- _output->print_cr("[" PTR_FORMAT "] %s+0x%x", pc, buf, offset);
- _output->print("%28s", " ");
- } else {
- _output->print("[" PTR_FORMAT "]%18s", pc, " ");
- }
- }
+
+void MemDetailDiffReporter::new_malloc_site(const MallocSite* malloc_site) const {
+ diff_malloc_site(malloc_site->call_stack(), malloc_site->size(), malloc_site->count(),
+ 0, 0);
+}
+
+void MemDetailDiffReporter::old_malloc_site(const MallocSite* malloc_site) const {
+ diff_malloc_site(malloc_site->call_stack(), 0, 0, malloc_site->size(),
+ malloc_site->count());
+}
+
+void MemDetailDiffReporter::diff_malloc_site(const MallocSite* early,
+ const MallocSite* current) const {
+ diff_malloc_site(current->call_stack(), current->size(), current->count(),
+ early->size(), early->count());
+}
+
+void MemDetailDiffReporter::diff_malloc_site(const NativeCallStack* stack, size_t current_size,
+ size_t current_count, size_t early_size, size_t early_count) const {
+ outputStream* out = output();
+
+ assert(stack != NULL, "NULL stack");
+
+ if (diff_in_current_scale(current_size, early_size) == 0) {
+ return;
+ }
+
+ stack->print_on(out);
+ out->print("%28s (", " ");
+ print_malloc_diff(current_size, current_count,
+ early_size, early_count);
- _output->print("(mmap: reserved=%d%s", cur_reserved_amt, unit);
- if (reserved_diff != 0) {
- _output->print(" %+d%s", reserved_diff, unit);
- }
- _output->print(", committed=%d%s", cur_committed_amt, unit);
- if (committed_diff != 0) {
- _output->print(" %+d%s", committed_diff, unit);
- }
- _output->print_cr(")");
- _output->print_cr(" ");
+ out->print_cr(")\n");
+}
+
+
+void MemDetailDiffReporter::new_virtual_memory_site(const VirtualMemoryAllocationSite* site) const {
+ diff_virtual_memory_site(site->call_stack(), site->reserved(), site->committed(), 0, 0);
+}
+
+void MemDetailDiffReporter::old_virtual_memory_site(const VirtualMemoryAllocationSite* site) const {
+ diff_virtual_memory_site(site->call_stack(), 0, 0, site->reserved(), site->committed());
+}
+
+void MemDetailDiffReporter::diff_virtual_memory_site(const VirtualMemoryAllocationSite* early,
+ const VirtualMemoryAllocationSite* current) const {
+ diff_virtual_memory_site(current->call_stack(), current->reserved(), current->committed(),
+ early->reserved(), early->committed());
+}
+
+void MemDetailDiffReporter::diff_virtual_memory_site(const NativeCallStack* stack, size_t current_reserved,
+ size_t current_committed, size_t early_reserved, size_t early_committed) const {
+ outputStream* out = output();
+
+ // no change
+ if (diff_in_current_scale(current_reserved, early_reserved) == 0 &&
+ diff_in_current_scale(current_committed, early_committed) == 0) {
+ return;
}
-}
+
+ stack->print_on(out);
+ out->print("%28s (mmap: ", " ");
+ print_virtual_memory_diff(current_reserved, current_committed,
+ early_reserved, early_committed);
+
+ out->print_cr(")\n");
+ }
+
--- a/hotspot/src/share/vm/services/memReporter.hpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/services/memReporter.hpp Thu Aug 07 12:18:58 2014 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,262 +25,217 @@
#ifndef SHARE_VM_SERVICES_MEM_REPORTER_HPP
#define SHARE_VM_SERVICES_MEM_REPORTER_HPP
-#include "runtime/mutexLocker.hpp"
+#if INCLUDE_NMT
+
+#include "oops/instanceKlass.hpp"
#include "services/memBaseline.hpp"
-#include "services/memTracker.hpp"
-#include "utilities/ostream.hpp"
-#include "utilities/macros.hpp"
-
-#if INCLUDE_NMT
+#include "services/nmtCommon.hpp"
+#include "services/mallocTracker.hpp"
+#include "services/virtualMemoryTracker.hpp"
/*
- * MemBaselineReporter reports data to this outputer class,
- * ReportOutputer is responsible for format, store and redirect
- * the data to the final destination.
- */
-class BaselineOutputer : public StackObj {
+ * Base class that provides helpers
+*/
+class MemReporterBase : public StackObj {
+ private:
+ size_t _scale; // report in this scale
+ outputStream* _output; // destination
+
public:
- // start to report memory usage in specified scale.
- // if report_diff = true, the reporter reports baseline comparison
- // information.
-
- virtual void start(size_t scale, bool report_diff = false) = 0;
- // Done reporting
- virtual void done() = 0;
+ MemReporterBase(outputStream* out = NULL, size_t scale = K)
+ : _scale(scale) {
+ _output = (out == NULL) ? tty : out;
+ }
- /* report baseline summary information */
- virtual void total_usage(size_t total_reserved,
- size_t total_committed) = 0;
- virtual void num_of_classes(size_t classes) = 0;
- virtual void num_of_threads(size_t threads) = 0;
-
- virtual void thread_info(size_t stack_reserved_amt, size_t stack_committed_amt) = 0;
+ protected:
+ inline outputStream* output() const {
+ return _output;
+ }
+ // Current reporting scale
+ inline const char* current_scale() const {
+ return NMTUtil::scale_name(_scale);
+ }
+ // Convert memory amount in bytes to current reporting scale
+ inline size_t amount_in_current_scale(size_t amount) const {
+ return NMTUtil::amount_in_scale(amount, _scale);
+ }
- /* report baseline summary comparison */
- virtual void diff_total_usage(size_t total_reserved,
- size_t total_committed,
- int reserved_diff,
- int committed_diff) = 0;
- virtual void diff_num_of_classes(size_t classes, int diff) = 0;
- virtual void diff_num_of_threads(size_t threads, int diff) = 0;
+ // Convert diff amount in bytes to current reporting scale
+ inline long diff_in_current_scale(size_t s1, size_t s2) const {
+ long amount = (long)(s1 - s2);
+ long scale = (long)_scale;
+ amount = (amount > 0) ? (amount + scale / 2) : (amount - scale / 2);
+ return amount / scale;
+ }
- virtual void diff_thread_info(size_t stack_reserved, size_t stack_committed,
- int stack_reserved_diff, int stack_committed_diff) = 0;
+ // Helper functions
+ // Calculate total reserved and committed amount
+ size_t reserved_total(const MallocMemory* malloc, const VirtualMemory* vm) const;
+ size_t committed_total(const MallocMemory* malloc, const VirtualMemory* vm) const;
- /*
- * memory summary by memory types.
- * for each memory type, following summaries are reported:
- * - reserved amount, committed amount
- * - malloc'd amount, malloc count
- * - arena amount, arena count
- */
+ // Print summary total, malloc and virtual memory
+ void print_total(size_t reserved, size_t committed) const;
+ void print_malloc(size_t amount, size_t count) const;
+ void print_virtual_memory(size_t reserved, size_t committed) const;
- // start reporting memory summary by memory type
- virtual void start_category_summary() = 0;
+ void print_malloc_line(size_t amount, size_t count) const;
+ void print_virtual_memory_line(size_t reserved, size_t committed) const;
+ void print_arena_line(size_t amount, size_t count) const;
- virtual void category_summary(MEMFLAGS type, size_t reserved_amt,
- size_t committed_amt,
- size_t malloc_amt, size_t malloc_count,
- size_t arena_amt, size_t arena_count) = 0;
+ void print_virtual_memory_region(const char* type, address base, size_t size) const;
+};
- virtual void diff_category_summary(MEMFLAGS type, size_t cur_reserved_amt,
- size_t cur_committed_amt,
- size_t cur_malloc_amt, size_t cur_malloc_count,
- size_t cur_arena_amt, size_t cur_arena_count,
- int reserved_diff, int committed_diff, int malloc_diff,
- int malloc_count_diff, int arena_diff,
- int arena_count_diff) = 0;
+/*
+ * The class is for generating summary tracking report.
+ */
+class MemSummaryReporter : public MemReporterBase {
+ private:
+ MallocMemorySnapshot* _malloc_snapshot;
+ VirtualMemorySnapshot* _vm_snapshot;
+ size_t _class_count;
- virtual void done_category_summary() = 0;
-
- virtual void start_virtual_memory_map() = 0;
- virtual void reserved_memory_region(MEMFLAGS type, address base, address end, size_t size, address pc) = 0;
- virtual void committed_memory_region(address base, address end, size_t size, address pc) = 0;
- virtual void done_virtual_memory_map() = 0;
+ public:
+ // Report summary tracking data from global snapshots directly.
+ // This constructor is used for final reporting and hs_err reporting.
+ MemSummaryReporter(MallocMemorySnapshot* malloc_snapshot,
+ VirtualMemorySnapshot* vm_snapshot, outputStream* output,
+ size_t class_count = 0, size_t scale = K) :
+ MemReporterBase(output, scale),
+ _malloc_snapshot(malloc_snapshot),
+ _vm_snapshot(vm_snapshot) {
+ if (class_count == 0) {
+ _class_count = InstanceKlass::number_of_instance_classes();
+ } else {
+ _class_count = class_count;
+ }
+ }
+ // This constructor is for normal reporting from a recent baseline.
+ MemSummaryReporter(MemBaseline& baseline, outputStream* output,
+ size_t scale = K) : MemReporterBase(output, scale),
+ _malloc_snapshot(baseline.malloc_memory_snapshot()),
+ _vm_snapshot(baseline.virtual_memory_snapshot()),
+ _class_count(baseline.class_count()) { }
- /*
- * Report callsite information
- */
- virtual void start_callsite() = 0;
- virtual void malloc_callsite(address pc, size_t malloc_amt, size_t malloc_count) = 0;
- virtual void virtual_memory_callsite(address pc, size_t reserved_amt, size_t committed_amt) = 0;
- virtual void diff_malloc_callsite(address pc, size_t cur_malloc_amt, size_t cur_malloc_count,
- int malloc_diff, int malloc_count_diff) = 0;
- virtual void diff_virtual_memory_callsite(address pc, size_t cur_reserved_amt, size_t cur_committed_amt,
- int reserved_diff, int committed_diff) = 0;
-
- virtual void done_callsite() = 0;
-
- // return current scale in "KB", "MB" or "GB"
- static const char* memory_unit(size_t scale);
+ // Generate summary report
+ virtual void report();
+ private:
+ // Report summary for each memory type
+ void report_summary_of_type(MEMFLAGS type, MallocMemory* malloc_memory,
+ VirtualMemory* virtual_memory);
};
/*
- * This class reports processed data from a baseline or
- * the changes between the two baseline.
+ * The class is for generating detail tracking report.
*/
-class BaselineReporter : public StackObj {
+class MemDetailReporter : public MemSummaryReporter {
private:
- BaselineOutputer& _outputer;
- size_t _scale;
+ MemBaseline& _baseline;
public:
- // construct a reporter that reports memory usage
- // in specified scale
- BaselineReporter(BaselineOutputer& outputer, size_t scale = K):
- _outputer(outputer) {
- _scale = scale;
+ MemDetailReporter(MemBaseline& baseline, outputStream* output, size_t scale = K) :
+ MemSummaryReporter(baseline, output, scale),
+ _baseline(baseline) { }
+
+ // Generate detail report.
+ // The report contains summary and detail sections.
+ virtual void report() {
+ MemSummaryReporter::report();
+ report_virtual_memory_map();
+ report_detail();
}
- virtual void report_baseline(const MemBaseline& baseline, bool summary_only = false);
- virtual void diff_baselines(const MemBaseline& cur, const MemBaseline& prev,
- bool summary_only = false);
-
- void set_scale(size_t scale);
- size_t scale() const { return _scale; }
private:
- void report_summaries(const MemBaseline& baseline);
- void report_virtual_memory_map(const MemBaseline& baseline);
- void report_callsites(const MemBaseline& baseline);
-
- void diff_summaries(const MemBaseline& cur, const MemBaseline& prev);
- void diff_callsites(const MemBaseline& cur, const MemBaseline& prev);
+ // Report detail tracking data.
+ void report_detail();
+ // Report virtual memory map
+ void report_virtual_memory_map();
+ // Report malloc allocation sites
+ void report_malloc_sites();
+ // Report virtual memory reservation sites
+ void report_virtual_memory_allocation_sites();
- // calculate memory size in current memory scale
- size_t amount_in_current_scale(size_t amt) const;
- // diff two unsigned values in current memory scale
- int diff_in_current_scale(size_t value1, size_t value2) const;
- // diff two unsigned value
- int diff(size_t value1, size_t value2) const;
+ // Report a virtual memory region
+ void report_virtual_memory_region(const ReservedMemoryRegion* rgn);
};
/*
- * tty output implementation. Native memory tracking
- * DCmd uses this outputer.
+ * The class is for generating summary comparison report.
+ * It compares current memory baseline against an early baseline.
*/
-class BaselineTTYOutputer : public BaselineOutputer {
- private:
- size_t _scale;
-
- size_t _num_of_classes;
- size_t _num_of_threads;
- size_t _thread_stack_reserved;
- size_t _thread_stack_committed;
-
- int _num_of_classes_diff;
- int _num_of_threads_diff;
- int _thread_stack_reserved_diff;
- int _thread_stack_committed_diff;
-
- outputStream* _output;
+class MemSummaryDiffReporter : public MemReporterBase {
+ protected:
+ MemBaseline& _early_baseline;
+ MemBaseline& _current_baseline;
public:
- BaselineTTYOutputer(outputStream* st) {
- _scale = K;
- _num_of_classes = 0;
- _num_of_threads = 0;
- _thread_stack_reserved = 0;
- _thread_stack_committed = 0;
- _num_of_classes_diff = 0;
- _num_of_threads_diff = 0;
- _thread_stack_reserved_diff = 0;
- _thread_stack_committed_diff = 0;
- _output = st;
- }
-
- // begin reporting memory usage in specified scale
- void start(size_t scale, bool report_diff = false);
- // done reporting
- void done();
-
- // total memory usage
- void total_usage(size_t total_reserved,
- size_t total_committed);
- // report total loaded classes
- void num_of_classes(size_t classes) {
- _num_of_classes = classes;
- }
-
- void num_of_threads(size_t threads) {
- _num_of_threads = threads;
- }
-
- void thread_info(size_t stack_reserved_amt, size_t stack_committed_amt) {
- _thread_stack_reserved = stack_reserved_amt;
- _thread_stack_committed = stack_committed_amt;
+ MemSummaryDiffReporter(MemBaseline& early_baseline, MemBaseline& current_baseline,
+ outputStream* output, size_t scale = K) : MemReporterBase(output, scale),
+ _early_baseline(early_baseline), _current_baseline(current_baseline) {
+ assert(early_baseline.baseline_type() != MemBaseline::Not_baselined, "Not baselined");
+ assert(current_baseline.baseline_type() != MemBaseline::Not_baselined, "Not baselined");
}
- void diff_total_usage(size_t total_reserved,
- size_t total_committed,
- int reserved_diff,
- int committed_diff);
-
- void diff_num_of_classes(size_t classes, int diff) {
- _num_of_classes = classes;
- _num_of_classes_diff = diff;
- }
-
- void diff_num_of_threads(size_t threads, int diff) {
- _num_of_threads = threads;
- _num_of_threads_diff = diff;
- }
-
- void diff_thread_info(size_t stack_reserved_amt, size_t stack_committed_amt,
- int stack_reserved_diff, int stack_committed_diff) {
- _thread_stack_reserved = stack_reserved_amt;
- _thread_stack_committed = stack_committed_amt;
- _thread_stack_reserved_diff = stack_reserved_diff;
- _thread_stack_committed_diff = stack_committed_diff;
- }
+ // Generate summary comparison report
+ virtual void report_diff();
- /*
- * Report memory summary categoriuzed by memory types.
- * For each memory type, following summaries are reported:
- * - reserved amount, committed amount
- * - malloc-ed amount, malloc count
- * - arena amount, arena count
- */
- // start reporting memory summary by memory type
- void start_category_summary();
- void category_summary(MEMFLAGS type, size_t reserved_amt, size_t committed_amt,
- size_t malloc_amt, size_t malloc_count,
- size_t arena_amt, size_t arena_count);
-
- void diff_category_summary(MEMFLAGS type, size_t cur_reserved_amt,
- size_t cur_committed_amt,
- size_t cur_malloc_amt, size_t cur_malloc_count,
- size_t cur_arena_amt, size_t cur_arena_count,
- int reserved_diff, int committed_diff, int malloc_diff,
- int malloc_count_diff, int arena_diff,
- int arena_count_diff);
+ private:
+ // report the comparison of each memory type
+ void diff_summary_of_type(MEMFLAGS type,
+ const MallocMemory* early_malloc, const VirtualMemory* early_vm,
+ const MallocMemory* current_malloc, const VirtualMemory* current_vm) const;
- void done_category_summary();
-
- // virtual memory map
- void start_virtual_memory_map();
- void reserved_memory_region(MEMFLAGS type, address base, address end, size_t size, address pc);
- void committed_memory_region(address base, address end, size_t size, address pc);
- void done_virtual_memory_map();
-
-
- /*
- * Report callsite information
- */
- void start_callsite();
- void malloc_callsite(address pc, size_t malloc_amt, size_t malloc_count);
- void virtual_memory_callsite(address pc, size_t reserved_amt, size_t committed_amt);
-
- void diff_malloc_callsite(address pc, size_t cur_malloc_amt, size_t cur_malloc_count,
- int malloc_diff, int malloc_count_diff);
- void diff_virtual_memory_callsite(address pc, size_t cur_reserved_amt, size_t cur_committed_amt,
- int reserved_diff, int committed_diff);
-
- void done_callsite();
+ protected:
+ void print_malloc_diff(size_t current_amount, size_t current_count,
+ size_t early_amount, size_t early_count) const;
+ void print_virtual_memory_diff(size_t current_reserved, size_t current_committed,
+ size_t early_reserved, size_t early_committed) const;
+ void print_arena_diff(size_t current_amount, size_t current_count,
+ size_t early_amount, size_t early_count) const;
};
+/*
+ * The class is for generating detail comparison report.
+ * It compares current memory baseline against an early baseline,
+ * both baselines have to be detail baseline.
+ */
+class MemDetailDiffReporter : public MemSummaryDiffReporter {
+ public:
+ MemDetailDiffReporter(MemBaseline& early_baseline, MemBaseline& current_baseline,
+ outputStream* output, size_t scale = K) :
+ MemSummaryDiffReporter(early_baseline, current_baseline, output, scale) { }
+
+ // Generate detail comparison report
+ virtual void report_diff();
+
+ // Malloc allocation site comparison
+ void diff_malloc_sites() const;
+ // Virutal memory reservation site comparison
+ void diff_virtual_memory_sites() const;
+
+ // New malloc allocation site in recent baseline
+ void new_malloc_site (const MallocSite* site) const;
+ // The malloc allocation site is not in recent baseline
+ void old_malloc_site (const MallocSite* site) const;
+ // Compare malloc allocation site, it is in both baselines
+ void diff_malloc_site(const MallocSite* early, const MallocSite* current) const;
+
+ // New virtual memory allocation site in recent baseline
+ void new_virtual_memory_site (const VirtualMemoryAllocationSite* callsite) const;
+ // The virtual memory allocation site is not in recent baseline
+ void old_virtual_memory_site (const VirtualMemoryAllocationSite* callsite) const;
+ // Compare virtual memory allocation site, it is in both baseline
+ void diff_virtual_memory_site(const VirtualMemoryAllocationSite* early,
+ const VirtualMemoryAllocationSite* current) const;
+
+ void diff_malloc_site(const NativeCallStack* stack, size_t current_size,
+ size_t currrent_count, size_t early_size, size_t early_count) const;
+ void diff_virtual_memory_site(const NativeCallStack* stack, size_t current_reserved,
+ size_t current_committed, size_t early_reserved, size_t early_committed) const;
+};
#endif // INCLUDE_NMT
-#endif // SHARE_VM_SERVICES_MEM_REPORTER_HPP
+#endif
+
--- a/hotspot/src/share/vm/services/memSnapshot.cpp Tue Aug 05 14:18:44 2014 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,748 +0,0 @@
-/*
- * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- *
- */
-
-#include "precompiled.hpp"
-#include "runtime/mutexLocker.hpp"
-#include "utilities/decoder.hpp"
-#include "services/memBaseline.hpp"
-#include "services/memPtr.hpp"
-#include "services/memPtrArray.hpp"
-#include "services/memSnapshot.hpp"
-#include "services/memTracker.hpp"
-
-PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC
-
-#ifdef ASSERT
-
-void decode_pointer_record(MemPointerRecord* rec) {
- tty->print("Pointer: [" PTR_FORMAT " - " PTR_FORMAT "] size = %d bytes", rec->addr(),
- rec->addr() + rec->size(), (int)rec->size());
- tty->print(" type = %s", MemBaseline::type2name(FLAGS_TO_MEMORY_TYPE(rec->flags())));
- if (rec->is_vm_pointer()) {
- if (rec->is_allocation_record()) {
- tty->print_cr(" (reserve)");
- } else if (rec->is_commit_record()) {
- tty->print_cr(" (commit)");
- } else if (rec->is_uncommit_record()) {
- tty->print_cr(" (uncommit)");
- } else if (rec->is_deallocation_record()) {
- tty->print_cr(" (release)");
- } else {
- tty->print_cr(" (tag)");
- }
- } else {
- if (rec->is_arena_memory_record()) {
- tty->print_cr(" (arena size)");
- } else if (rec->is_allocation_record()) {
- tty->print_cr(" (malloc)");
- } else {
- tty->print_cr(" (free)");
- }
- }
- if (MemTracker::track_callsite()) {
- char buf[1024];
- address pc = ((MemPointerRecordEx*)rec)->pc();
- if (pc != NULL && os::dll_address_to_function_name(pc, buf, sizeof(buf), NULL)) {
- tty->print_cr("\tfrom %s", buf);
- } else {
- tty->print_cr("\tcould not decode pc = " PTR_FORMAT "", pc);
- }
- }
-}
-
-void decode_vm_region_record(VMMemRegion* rec) {
- tty->print("VM Region [" PTR_FORMAT " - " PTR_FORMAT "]", rec->addr(),
- rec->addr() + rec->size());
- tty->print(" type = %s", MemBaseline::type2name(FLAGS_TO_MEMORY_TYPE(rec->flags())));
- if (rec->is_allocation_record()) {
- tty->print_cr(" (reserved)");
- } else if (rec->is_commit_record()) {
- tty->print_cr(" (committed)");
- } else {
- ShouldNotReachHere();
- }
- if (MemTracker::track_callsite()) {
- char buf[1024];
- address pc = ((VMMemRegionEx*)rec)->pc();
- if (pc != NULL && os::dll_address_to_function_name(pc, buf, sizeof(buf), NULL)) {
- tty->print_cr("\tfrom %s", buf);
- } else {
- tty->print_cr("\tcould not decode pc = " PTR_FORMAT "", pc);
- }
-
- }
-}
-
-#endif
-
-
-bool VMMemPointerIterator::insert_record(MemPointerRecord* rec) {
- VMMemRegionEx new_rec;
- assert(rec->is_allocation_record() || rec->is_commit_record(),
- "Sanity check");
- if (MemTracker::track_callsite()) {
- new_rec.init((MemPointerRecordEx*)rec);
- } else {
- new_rec.init(rec);
- }
- return insert(&new_rec);
-}
-
-bool VMMemPointerIterator::insert_record_after(MemPointerRecord* rec) {
- VMMemRegionEx new_rec;
- assert(rec->is_allocation_record() || rec->is_commit_record(),
- "Sanity check");
- if (MemTracker::track_callsite()) {
- new_rec.init((MemPointerRecordEx*)rec);
- } else {
- new_rec.init(rec);
- }
- return insert_after(&new_rec);
-}
-
-// we don't consolidate reserved regions, since they may be categorized
-// in different types.
-bool VMMemPointerIterator::add_reserved_region(MemPointerRecord* rec) {
- assert(rec->is_allocation_record(), "Sanity check");
- VMMemRegion* reserved_region = (VMMemRegion*)current();
-
- // we don't have anything yet
- if (reserved_region == NULL) {
- return insert_record(rec);
- }
-
- assert(reserved_region->is_reserved_region(), "Sanity check");
- // duplicated records
- if (reserved_region->is_same_region(rec)) {
- return true;
- }
- // Overlapping stack regions indicate that a JNI thread failed to
- // detach from the VM before exiting. This leaks the JavaThread object.
- if (CheckJNICalls) {
- guarantee(FLAGS_TO_MEMORY_TYPE(reserved_region->flags()) != mtThreadStack ||
- !reserved_region->overlaps_region(rec),
- "Attached JNI thread exited without being detached");
- }
- // otherwise, we should not have overlapping reserved regions
- assert(FLAGS_TO_MEMORY_TYPE(reserved_region->flags()) == mtThreadStack ||
- reserved_region->base() > rec->addr(), "Just check: locate()");
- assert(FLAGS_TO_MEMORY_TYPE(reserved_region->flags()) == mtThreadStack ||
- !reserved_region->overlaps_region(rec), "overlapping reserved regions");
-
- return insert_record(rec);
-}
-
-// we do consolidate committed regions
-bool VMMemPointerIterator::add_committed_region(MemPointerRecord* rec) {
- assert(rec->is_commit_record(), "Sanity check");
- VMMemRegion* reserved_rgn = (VMMemRegion*)current();
- assert(reserved_rgn->is_reserved_region() && reserved_rgn->contains_region(rec),
- "Sanity check");
-
- // thread's native stack is always marked as "committed", ignore
- // the "commit" operation for creating stack guard pages
- if (FLAGS_TO_MEMORY_TYPE(reserved_rgn->flags()) == mtThreadStack &&
- FLAGS_TO_MEMORY_TYPE(rec->flags()) != mtThreadStack) {
- return true;
- }
-
- // if the reserved region has any committed regions
- VMMemRegion* committed_rgn = (VMMemRegion*)next();
- while (committed_rgn != NULL && committed_rgn->is_committed_region()) {
- // duplicated commit records
- if(committed_rgn->contains_region(rec)) {
- return true;
- } else if (committed_rgn->overlaps_region(rec)) {
- // overlaps front part
- if (rec->addr() < committed_rgn->addr()) {
- committed_rgn->expand_region(rec->addr(),
- committed_rgn->addr() - rec->addr());
- } else {
- // overlaps tail part
- address committed_rgn_end = committed_rgn->addr() +
- committed_rgn->size();
- assert(committed_rgn_end < rec->addr() + rec->size(),
- "overlap tail part");
- committed_rgn->expand_region(committed_rgn_end,
- (rec->addr() + rec->size()) - committed_rgn_end);
- }
- } else if (committed_rgn->base() + committed_rgn->size() == rec->addr()) {
- // adjunct each other
- committed_rgn->expand_region(rec->addr(), rec->size());
- VMMemRegion* next_reg = (VMMemRegion*)next();
- // see if we can consolidate next committed region
- if (next_reg != NULL && next_reg->is_committed_region() &&
- next_reg->base() == committed_rgn->base() + committed_rgn->size()) {
- committed_rgn->expand_region(next_reg->base(), next_reg->size());
- // delete merged region
- remove();
- }
- return true;
- } else if (committed_rgn->base() > rec->addr()) {
- // found the location, insert this committed region
- return insert_record(rec);
- }
- committed_rgn = (VMMemRegion*)next();
- }
- return insert_record(rec);
-}
-
-bool VMMemPointerIterator::remove_uncommitted_region(MemPointerRecord* rec) {
- assert(rec->is_uncommit_record(), "sanity check");
- VMMemRegion* cur;
- cur = (VMMemRegion*)current();
- assert(cur->is_reserved_region() && cur->contains_region(rec),
- "Sanity check");
- // thread's native stack is always marked as "committed", ignore
- // the "commit" operation for creating stack guard pages
- if (FLAGS_TO_MEMORY_TYPE(cur->flags()) == mtThreadStack &&
- FLAGS_TO_MEMORY_TYPE(rec->flags()) != mtThreadStack) {
- return true;
- }
-
- cur = (VMMemRegion*)next();
- while (cur != NULL && cur->is_committed_region()) {
- // region already uncommitted, must be due to duplicated record
- if (cur->addr() >= rec->addr() + rec->size()) {
- break;
- } else if (cur->contains_region(rec)) {
- // uncommit whole region
- if (cur->is_same_region(rec)) {
- remove();
- break;
- } else if (rec->addr() == cur->addr() ||
- rec->addr() + rec->size() == cur->addr() + cur->size()) {
- // uncommitted from either end of current memory region.
- cur->exclude_region(rec->addr(), rec->size());
- break;
- } else { // split the committed region and release the middle
- address high_addr = cur->addr() + cur->size();
- size_t sz = high_addr - rec->addr();
- cur->exclude_region(rec->addr(), sz);
- sz = high_addr - (rec->addr() + rec->size());
- if (MemTracker::track_callsite()) {
- MemPointerRecordEx tmp(rec->addr() + rec->size(), cur->flags(), sz,
- ((VMMemRegionEx*)cur)->pc());
- return insert_record_after(&tmp);
- } else {
- MemPointerRecord tmp(rec->addr() + rec->size(), cur->flags(), sz);
- return insert_record_after(&tmp);
- }
- }
- }
- cur = (VMMemRegion*)next();
- }
-
- // we may not find committed record due to duplicated records
- return true;
-}
-
-bool VMMemPointerIterator::remove_released_region(MemPointerRecord* rec) {
- assert(rec->is_deallocation_record(), "Sanity check");
- VMMemRegion* cur = (VMMemRegion*)current();
- assert(cur->is_reserved_region() && cur->contains_region(rec),
- "Sanity check");
- if (rec->is_same_region(cur)) {
-
- // In snapshot, the virtual memory records are sorted in following orders:
- // 1. virtual memory's base address
- // 2. virtual memory reservation record, followed by commit records within this reservation.
- // The commit records are also in base address order.
- // When a reserved region is released, we want to remove the reservation record and all
- // commit records following it.
-#ifdef ASSERT
- address low_addr = cur->addr();
- address high_addr = low_addr + cur->size();
-#endif
- // remove virtual memory reservation record
- remove();
- // remove committed regions within above reservation
- VMMemRegion* next_region = (VMMemRegion*)current();
- while (next_region != NULL && next_region->is_committed_region()) {
- assert(next_region->addr() >= low_addr &&
- next_region->addr() + next_region->size() <= high_addr,
- "Range check");
- remove();
- next_region = (VMMemRegion*)current();
- }
- } else if (rec->addr() == cur->addr() ||
- rec->addr() + rec->size() == cur->addr() + cur->size()) {
- // released region is at either end of this region
- cur->exclude_region(rec->addr(), rec->size());
- assert(check_reserved_region(), "Integrity check");
- } else { // split the reserved region and release the middle
- address high_addr = cur->addr() + cur->size();
- size_t sz = high_addr - rec->addr();
- cur->exclude_region(rec->addr(), sz);
- sz = high_addr - rec->addr() - rec->size();
- if (MemTracker::track_callsite()) {
- MemPointerRecordEx tmp(rec->addr() + rec->size(), cur->flags(), sz,
- ((VMMemRegionEx*)cur)->pc());
- bool ret = insert_reserved_region(&tmp);
- assert(!ret || check_reserved_region(), "Integrity check");
- return ret;
- } else {
- MemPointerRecord tmp(rec->addr() + rec->size(), cur->flags(), sz);
- bool ret = insert_reserved_region(&tmp);
- assert(!ret || check_reserved_region(), "Integrity check");
- return ret;
- }
- }
- return true;
-}
-
-bool VMMemPointerIterator::insert_reserved_region(MemPointerRecord* rec) {
- // skip all 'commit' records associated with previous reserved region
- VMMemRegion* p = (VMMemRegion*)next();
- while (p != NULL && p->is_committed_region() &&
- p->base() + p->size() < rec->addr()) {
- p = (VMMemRegion*)next();
- }
- return insert_record(rec);
-}
-
-bool VMMemPointerIterator::split_reserved_region(VMMemRegion* rgn, address new_rgn_addr, size_t new_rgn_size) {
- assert(rgn->contains_region(new_rgn_addr, new_rgn_size), "Not fully contained");
- address pc = (MemTracker::track_callsite() ? ((VMMemRegionEx*)rgn)->pc() : NULL);
- if (rgn->base() == new_rgn_addr) { // new region is at the beginning of the region
- size_t sz = rgn->size() - new_rgn_size;
- // the original region becomes 'new' region
- rgn->exclude_region(new_rgn_addr + new_rgn_size, sz);
- // remaining becomes next region
- MemPointerRecordEx next_rgn(new_rgn_addr + new_rgn_size, rgn->flags(), sz, pc);
- return insert_reserved_region(&next_rgn);
- } else if (rgn->base() + rgn->size() == new_rgn_addr + new_rgn_size) {
- rgn->exclude_region(new_rgn_addr, new_rgn_size);
- MemPointerRecordEx next_rgn(new_rgn_addr, rgn->flags(), new_rgn_size, pc);
- return insert_reserved_region(&next_rgn);
- } else {
- // the orginal region will be split into three
- address rgn_high_addr = rgn->base() + rgn->size();
- // first region
- rgn->exclude_region(new_rgn_addr, (rgn_high_addr - new_rgn_addr));
- // the second region is the new region
- MemPointerRecordEx new_rgn(new_rgn_addr, rgn->flags(), new_rgn_size, pc);
- if (!insert_reserved_region(&new_rgn)) return false;
- // the remaining region
- MemPointerRecordEx rem_rgn(new_rgn_addr + new_rgn_size, rgn->flags(),
- rgn_high_addr - (new_rgn_addr + new_rgn_size), pc);
- return insert_reserved_region(&rem_rgn);
- }
-}
-
-static int sort_in_seq_order(const void* p1, const void* p2) {
- assert(p1 != NULL && p2 != NULL, "Sanity check");
- const MemPointerRecord* mp1 = (MemPointerRecord*)p1;
- const MemPointerRecord* mp2 = (MemPointerRecord*)p2;
- return (mp1->seq() - mp2->seq());
-}
-
-bool StagingArea::init() {
- if (MemTracker::track_callsite()) {
- _malloc_data = new (std::nothrow)MemPointerArrayImpl<SeqMemPointerRecordEx>();
- _vm_data = new (std::nothrow)MemPointerArrayImpl<SeqMemPointerRecordEx>();
- } else {
- _malloc_data = new (std::nothrow)MemPointerArrayImpl<SeqMemPointerRecord>();
- _vm_data = new (std::nothrow)MemPointerArrayImpl<SeqMemPointerRecord>();
- }
-
- if (_malloc_data != NULL && _vm_data != NULL &&
- !_malloc_data->out_of_memory() &&
- !_vm_data->out_of_memory()) {
- return true;
- } else {
- if (_malloc_data != NULL) delete _malloc_data;
- if (_vm_data != NULL) delete _vm_data;
- _malloc_data = NULL;
- _vm_data = NULL;
- return false;
- }
-}
-
-
-VMRecordIterator StagingArea::virtual_memory_record_walker() {
- MemPointerArray* arr = vm_data();
- // sort into seq number order
- arr->sort((FN_SORT)sort_in_seq_order);
- return VMRecordIterator(arr);
-}
-
-
-MemSnapshot::MemSnapshot() {
- if (MemTracker::track_callsite()) {
- _alloc_ptrs = new (std::nothrow) MemPointerArrayImpl<MemPointerRecordEx>();
- _vm_ptrs = new (std::nothrow)MemPointerArrayImpl<VMMemRegionEx>(64, true);
- } else {
- _alloc_ptrs = new (std::nothrow) MemPointerArrayImpl<MemPointerRecord>();
- _vm_ptrs = new (std::nothrow)MemPointerArrayImpl<VMMemRegion>(64, true);
- }
-
- _staging_area.init();
- _lock = new (std::nothrow) Mutex(Monitor::max_nonleaf - 1, "memSnapshotLock");
- NOT_PRODUCT(_untracked_count = 0;)
- _number_of_classes = 0;
-}
-
-MemSnapshot::~MemSnapshot() {
- assert(MemTracker::shutdown_in_progress(), "native memory tracking still on");
- {
- MutexLockerEx locker(_lock);
- if (_alloc_ptrs != NULL) {
- delete _alloc_ptrs;
- _alloc_ptrs = NULL;
- }
-
- if (_vm_ptrs != NULL) {
- delete _vm_ptrs;
- _vm_ptrs = NULL;
- }
- }
-
- if (_lock != NULL) {
- delete _lock;
- _lock = NULL;
- }
-}
-
-
-void MemSnapshot::copy_seq_pointer(MemPointerRecord* dest, const MemPointerRecord* src) {
- assert(dest != NULL && src != NULL, "Just check");
- assert(dest->addr() == src->addr(), "Just check");
- assert(dest->seq() > 0 && src->seq() > 0, "not sequenced");
-
- if (MemTracker::track_callsite()) {
- *(SeqMemPointerRecordEx*)dest = *(SeqMemPointerRecordEx*)src;
- } else {
- *(SeqMemPointerRecord*)dest = *(SeqMemPointerRecord*)src;
- }
-}
-
-void MemSnapshot::assign_pointer(MemPointerRecord*dest, const MemPointerRecord* src) {
- assert(src != NULL && dest != NULL, "Just check");
- assert(dest->seq() == 0 && src->seq() >0, "cast away sequence");
-
- if (MemTracker::track_callsite()) {
- *(MemPointerRecordEx*)dest = *(MemPointerRecordEx*)src;
- } else {
- *(MemPointerRecord*)dest = *(MemPointerRecord*)src;
- }
-}
-
-// merge a recorder to the staging area
-bool MemSnapshot::merge(MemRecorder* rec) {
- assert(rec != NULL && !rec->out_of_memory(), "Just check");
-
- SequencedRecordIterator itr(rec->pointer_itr());
-
- MutexLockerEx lock(_lock, true);
- MemPointerIterator malloc_staging_itr(_staging_area.malloc_data());
- MemPointerRecord* incoming_rec = (MemPointerRecord*) itr.current();
- MemPointerRecord* matched_rec;
-
- while (incoming_rec != NULL) {
- if (incoming_rec->is_vm_pointer()) {
- // we don't do anything with virtual memory records during merge
- if (!_staging_area.vm_data()->append(incoming_rec)) {
- return false;
- }
- } else {
- // locate matched record and/or also position the iterator to proper
- // location for this incoming record.
- matched_rec = (MemPointerRecord*)malloc_staging_itr.locate(incoming_rec->addr());
- // we have not seen this memory block in this generation,
- // so just add to staging area
- if (matched_rec == NULL) {
- if (!malloc_staging_itr.insert(incoming_rec)) {
- return false;
- }
- } else if (incoming_rec->addr() == matched_rec->addr()) {
- // whoever has higher sequence number wins
- if (incoming_rec->seq() > matched_rec->seq()) {
- copy_seq_pointer(matched_rec, incoming_rec);
- }
- } else if (incoming_rec->addr() < matched_rec->addr()) {
- if (!malloc_staging_itr.insert(incoming_rec)) {
- return false;
- }
- } else {
- ShouldNotReachHere();
- }
- }
- incoming_rec = (MemPointerRecord*)itr.next();
- }
- NOT_PRODUCT(void check_staging_data();)
- return true;
-}
-
-
-// promote data to next generation
-bool MemSnapshot::promote(int number_of_classes) {
- assert(_alloc_ptrs != NULL && _vm_ptrs != NULL, "Just check");
- assert(_staging_area.malloc_data() != NULL && _staging_area.vm_data() != NULL,
- "Just check");
- MutexLockerEx lock(_lock, true);
-
- MallocRecordIterator malloc_itr = _staging_area.malloc_record_walker();
- bool promoted = false;
- if (promote_malloc_records(&malloc_itr)) {
- VMRecordIterator vm_itr = _staging_area.virtual_memory_record_walker();
- if (promote_virtual_memory_records(&vm_itr)) {
- promoted = true;
- }
- }
-
- NOT_PRODUCT(check_malloc_pointers();)
- _staging_area.clear();
- _number_of_classes = number_of_classes;
- return promoted;
-}
-
-bool MemSnapshot::promote_malloc_records(MemPointerArrayIterator* itr) {
- MemPointerIterator malloc_snapshot_itr(_alloc_ptrs);
- MemPointerRecord* new_rec = (MemPointerRecord*)itr->current();
- MemPointerRecord* matched_rec;
- while (new_rec != NULL) {
- matched_rec = (MemPointerRecord*)malloc_snapshot_itr.locate(new_rec->addr());
- // found matched memory block
- if (matched_rec != NULL && new_rec->addr() == matched_rec->addr()) {
- // snapshot already contains 'live' records
- assert(matched_rec->is_allocation_record() || matched_rec->is_arena_memory_record(),
- "Sanity check");
- // update block states
- if (new_rec->is_allocation_record()) {
- assign_pointer(matched_rec, new_rec);
- } else if (new_rec->is_arena_memory_record()) {
- if (new_rec->size() == 0) {
- // remove size record once size drops to 0
- malloc_snapshot_itr.remove();
- } else {
- assign_pointer(matched_rec, new_rec);
- }
- } else {
- // a deallocation record
- assert(new_rec->is_deallocation_record(), "Sanity check");
- // an arena record can be followed by a size record, we need to remove both
- if (matched_rec->is_arena_record()) {
- MemPointerRecord* next = (MemPointerRecord*)malloc_snapshot_itr.peek_next();
- if (next != NULL && next->is_arena_memory_record() &&
- next->is_memory_record_of_arena(matched_rec)) {
- malloc_snapshot_itr.remove();
- }
- }
- // the memory is deallocated, remove related record(s)
- malloc_snapshot_itr.remove();
- }
- } else {
- // don't insert size 0 record
- if (new_rec->is_arena_memory_record() && new_rec->size() == 0) {
- new_rec = NULL;
- }
-
- if (new_rec != NULL) {
- if (new_rec->is_allocation_record() || new_rec->is_arena_memory_record()) {
- if (matched_rec != NULL && new_rec->addr() > matched_rec->addr()) {
- if (!malloc_snapshot_itr.insert_after(new_rec)) {
- return false;
- }
- } else {
- if (!malloc_snapshot_itr.insert(new_rec)) {
- return false;
- }
- }
- }
-#ifndef PRODUCT
- else if (!has_allocation_record(new_rec->addr())) {
- // NMT can not track some startup memory, which is allocated before NMT is on
- _untracked_count ++;
- }
-#endif
- }
- }
- new_rec = (MemPointerRecord*)itr->next();
- }
- return true;
-}
-
-bool MemSnapshot::promote_virtual_memory_records(MemPointerArrayIterator* itr) {
- VMMemPointerIterator vm_snapshot_itr(_vm_ptrs);
- MemPointerRecord* new_rec = (MemPointerRecord*)itr->current();
- VMMemRegion* reserved_rec;
- while (new_rec != NULL) {
- assert(new_rec->is_vm_pointer(), "Sanity check");
-
- // locate a reserved region that contains the specified address, or
- // the nearest reserved region has base address just above the specified
- // address
- reserved_rec = (VMMemRegion*)vm_snapshot_itr.locate(new_rec->addr());
- if (reserved_rec != NULL && reserved_rec->contains_region(new_rec)) {
- // snapshot can only have 'live' records
- assert(reserved_rec->is_reserved_region(), "Sanity check");
- if (new_rec->is_allocation_record()) {
- if (!reserved_rec->is_same_region(new_rec)) {
- // only deal with split a bigger reserved region into smaller regions.
- // So far, CDS is the only use case.
- if (!vm_snapshot_itr.split_reserved_region(reserved_rec, new_rec->addr(), new_rec->size())) {
- return false;
- }
- }
- } else if (new_rec->is_uncommit_record()) {
- if (!vm_snapshot_itr.remove_uncommitted_region(new_rec)) {
- return false;
- }
- } else if (new_rec->is_commit_record()) {
- // insert or expand existing committed region to cover this
- // newly committed region
- if (!vm_snapshot_itr.add_committed_region(new_rec)) {
- return false;
- }
- } else if (new_rec->is_deallocation_record()) {
- // release part or all memory region
- if (!vm_snapshot_itr.remove_released_region(new_rec)) {
- return false;
- }
- } else if (new_rec->is_type_tagging_record()) {
- // tag this reserved virtual memory range to a memory type. Can not re-tag a memory range
- // to different type.
- assert(FLAGS_TO_MEMORY_TYPE(reserved_rec->flags()) == mtNone ||
- FLAGS_TO_MEMORY_TYPE(reserved_rec->flags()) == FLAGS_TO_MEMORY_TYPE(new_rec->flags()),
- "Sanity check");
- reserved_rec->tag(new_rec->flags());
- } else {
- ShouldNotReachHere();
- }
- } else {
- /*
- * The assertion failure indicates mis-matched virtual memory records. The likely
- * scenario is, that some virtual memory operations are not going through os::xxxx_memory()
- * api, which have to be tracked manually. (perfMemory is an example).
- */
- assert(new_rec->is_allocation_record(), "Sanity check");
- if (!vm_snapshot_itr.add_reserved_region(new_rec)) {
- return false;
- }
- }
- new_rec = (MemPointerRecord*)itr->next();
- }
- return true;
-}
-
-#ifndef PRODUCT
-void MemSnapshot::print_snapshot_stats(outputStream* st) {
- st->print_cr("Snapshot:");
- st->print_cr("\tMalloced: %d/%d [%5.2f%%] %dKB", _alloc_ptrs->length(), _alloc_ptrs->capacity(),
- (100.0 * (float)_alloc_ptrs->length()) / (float)_alloc_ptrs->capacity(), _alloc_ptrs->instance_size()/K);
-
- st->print_cr("\tVM: %d/%d [%5.2f%%] %dKB", _vm_ptrs->length(), _vm_ptrs->capacity(),
- (100.0 * (float)_vm_ptrs->length()) / (float)_vm_ptrs->capacity(), _vm_ptrs->instance_size()/K);
-
- st->print_cr("\tMalloc staging Area: %d/%d [%5.2f%%] %dKB", _staging_area.malloc_data()->length(),
- _staging_area.malloc_data()->capacity(),
- (100.0 * (float)_staging_area.malloc_data()->length()) / (float)_staging_area.malloc_data()->capacity(),
- _staging_area.malloc_data()->instance_size()/K);
-
- st->print_cr("\tVirtual memory staging Area: %d/%d [%5.2f%%] %dKB", _staging_area.vm_data()->length(),
- _staging_area.vm_data()->capacity(),
- (100.0 * (float)_staging_area.vm_data()->length()) / (float)_staging_area.vm_data()->capacity(),
- _staging_area.vm_data()->instance_size()/K);
-
- st->print_cr("\tUntracked allocation: %d", _untracked_count);
-}
-
-void MemSnapshot::check_malloc_pointers() {
- MemPointerArrayIteratorImpl mItr(_alloc_ptrs);
- MemPointerRecord* p = (MemPointerRecord*)mItr.current();
- MemPointerRecord* prev = NULL;
- while (p != NULL) {
- if (prev != NULL) {
- assert(p->addr() >= prev->addr(), "sorting order");
- }
- prev = p;
- p = (MemPointerRecord*)mItr.next();
- }
-}
-
-bool MemSnapshot::has_allocation_record(address addr) {
- MemPointerArrayIteratorImpl itr(_staging_area.malloc_data());
- MemPointerRecord* cur = (MemPointerRecord*)itr.current();
- while (cur != NULL) {
- if (cur->addr() == addr && cur->is_allocation_record()) {
- return true;
- }
- cur = (MemPointerRecord*)itr.next();
- }
- return false;
-}
-#endif // PRODUCT
-
-#ifdef ASSERT
-void MemSnapshot::check_staging_data() {
- MemPointerArrayIteratorImpl itr(_staging_area.malloc_data());
- MemPointerRecord* cur = (MemPointerRecord*)itr.current();
- MemPointerRecord* next = (MemPointerRecord*)itr.next();
- while (next != NULL) {
- assert((next->addr() > cur->addr()) ||
- ((next->flags() & MemPointerRecord::tag_masks) >
- (cur->flags() & MemPointerRecord::tag_masks)),
- "sorting order");
- cur = next;
- next = (MemPointerRecord*)itr.next();
- }
-
- MemPointerArrayIteratorImpl vm_itr(_staging_area.vm_data());
- cur = (MemPointerRecord*)vm_itr.current();
- while (cur != NULL) {
- assert(cur->is_vm_pointer(), "virtual memory pointer only");
- cur = (MemPointerRecord*)vm_itr.next();
- }
-}
-
-void MemSnapshot::dump_all_vm_pointers() {
- MemPointerArrayIteratorImpl itr(_vm_ptrs);
- VMMemRegion* ptr = (VMMemRegion*)itr.current();
- tty->print_cr("dump virtual memory pointers:");
- while (ptr != NULL) {
- if (ptr->is_committed_region()) {
- tty->print("\t");
- }
- tty->print("[" PTR_FORMAT " - " PTR_FORMAT "] [%x]", ptr->addr(),
- (ptr->addr() + ptr->size()), ptr->flags());
-
- if (MemTracker::track_callsite()) {
- VMMemRegionEx* ex = (VMMemRegionEx*)ptr;
- if (ex->pc() != NULL) {
- char buf[1024];
- if (os::dll_address_to_function_name(ex->pc(), buf, sizeof(buf), NULL)) {
- tty->print_cr("\t%s", buf);
- } else {
- tty->cr();
- }
- }
- }
-
- ptr = (VMMemRegion*)itr.next();
- }
- tty->flush();
-}
-#endif // ASSERT
-
--- a/hotspot/src/share/vm/services/memSnapshot.hpp Tue Aug 05 14:18:44 2014 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,408 +0,0 @@
-/*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- *
- */
-
-#ifndef SHARE_VM_SERVICES_MEM_SNAPSHOT_HPP
-#define SHARE_VM_SERVICES_MEM_SNAPSHOT_HPP
-
-#include "memory/allocation.hpp"
-#include "runtime/mutex.hpp"
-#include "runtime/mutexLocker.hpp"
-#include "services/memBaseline.hpp"
-#include "services/memPtrArray.hpp"
-
-// Snapshot pointer array iterator
-
-// The pointer array contains malloc-ed pointers
-class MemPointerIterator : public MemPointerArrayIteratorImpl {
- public:
- MemPointerIterator(MemPointerArray* arr):
- MemPointerArrayIteratorImpl(arr) {
- assert(arr != NULL, "null array");
- }
-
-#ifdef ASSERT
- virtual bool is_dup_pointer(const MemPointer* ptr1,
- const MemPointer* ptr2) const {
- MemPointerRecord* p1 = (MemPointerRecord*)ptr1;
- MemPointerRecord* p2 = (MemPointerRecord*)ptr2;
-
- if (p1->addr() != p2->addr()) return false;
- if ((p1->flags() & MemPointerRecord::tag_masks) !=
- (p2->flags() & MemPointerRecord::tag_masks)) {
- return false;
- }
- // we do see multiple commit/uncommit on the same memory, it is ok
- return (p1->flags() & MemPointerRecord::tag_masks) == MemPointerRecord::tag_alloc ||
- (p1->flags() & MemPointerRecord::tag_masks) == MemPointerRecord::tag_release;
- }
-
- virtual bool insert(MemPointer* ptr) {
- if (_pos > 0) {
- MemPointer* p1 = (MemPointer*)ptr;
- MemPointer* p2 = (MemPointer*)_array->at(_pos - 1);
- assert(!is_dup_pointer(p1, p2),
- err_msg("duplicated pointer, flag = [%x]", (unsigned int)((MemPointerRecord*)p1)->flags()));
- }
- if (_pos < _array->length() -1) {
- MemPointer* p1 = (MemPointer*)ptr;
- MemPointer* p2 = (MemPointer*)_array->at(_pos + 1);
- assert(!is_dup_pointer(p1, p2),
- err_msg("duplicated pointer, flag = [%x]", (unsigned int)((MemPointerRecord*)p1)->flags()));
- }
- return _array->insert_at(ptr, _pos);
- }
-
- virtual bool insert_after(MemPointer* ptr) {
- if (_pos > 0) {
- MemPointer* p1 = (MemPointer*)ptr;
- MemPointer* p2 = (MemPointer*)_array->at(_pos - 1);
- assert(!is_dup_pointer(p1, p2),
- err_msg("duplicated pointer, flag = [%x]", (unsigned int)((MemPointerRecord*)p1)->flags()));
- }
- if (_pos < _array->length() - 1) {
- MemPointer* p1 = (MemPointer*)ptr;
- MemPointer* p2 = (MemPointer*)_array->at(_pos + 1);
-
- assert(!is_dup_pointer(p1, p2),
- err_msg("duplicated pointer, flag = [%x]", (unsigned int)((MemPointerRecord*)p1)->flags()));
- }
- if (_array->insert_at(ptr, _pos + 1)) {
- _pos ++;
- return true;
- }
- return false;
- }
-#endif
-
- virtual MemPointer* locate(address addr) {
- MemPointer* cur = current();
- while (cur != NULL && cur->addr() < addr) {
- cur = next();
- }
- return cur;
- }
-};
-
-class VMMemPointerIterator : public MemPointerIterator {
- public:
- VMMemPointerIterator(MemPointerArray* arr):
- MemPointerIterator(arr) {
- }
-
- // locate an existing reserved memory region that contains specified address,
- // or the reserved region just above this address, where the incoming
- // reserved region should be inserted.
- virtual MemPointer* locate(address addr) {
- reset();
- VMMemRegion* reg = (VMMemRegion*)current();
- while (reg != NULL) {
- if (reg->is_reserved_region()) {
- if (reg->contains_address(addr) || addr < reg->base()) {
- return reg;
- }
- }
- reg = (VMMemRegion*)next();
- }
- return NULL;
- }
-
- // following methods update virtual memory in the context
- // of 'current' position, which is properly positioned by
- // callers via locate method.
- bool add_reserved_region(MemPointerRecord* rec);
- bool add_committed_region(MemPointerRecord* rec);
- bool remove_uncommitted_region(MemPointerRecord* rec);
- bool remove_released_region(MemPointerRecord* rec);
-
- // split a reserved region to create a new memory region with specified base and size
- bool split_reserved_region(VMMemRegion* rgn, address new_rgn_addr, size_t new_rgn_size);
- private:
- bool insert_record(MemPointerRecord* rec);
- bool insert_record_after(MemPointerRecord* rec);
-
- bool insert_reserved_region(MemPointerRecord* rec);
-
- // reset current position
- inline void reset() { _pos = 0; }
-#ifdef ASSERT
- // check integrity of records on current reserved memory region.
- bool check_reserved_region() {
- VMMemRegion* reserved_region = (VMMemRegion*)current();
- assert(reserved_region != NULL && reserved_region->is_reserved_region(),
- "Sanity check");
- // all committed regions that follow current reserved region, should all
- // belong to the reserved region.
- VMMemRegion* next_region = (VMMemRegion*)next();
- for (; next_region != NULL && next_region->is_committed_region();
- next_region = (VMMemRegion*)next() ) {
- if(!reserved_region->contains_region(next_region)) {
- return false;
- }
- }
- return true;
- }
-
- virtual bool is_dup_pointer(const MemPointer* ptr1,
- const MemPointer* ptr2) const {
- VMMemRegion* p1 = (VMMemRegion*)ptr1;
- VMMemRegion* p2 = (VMMemRegion*)ptr2;
-
- if (p1->addr() != p2->addr()) return false;
- if ((p1->flags() & MemPointerRecord::tag_masks) !=
- (p2->flags() & MemPointerRecord::tag_masks)) {
- return false;
- }
- // we do see multiple commit/uncommit on the same memory, it is ok
- return (p1->flags() & MemPointerRecord::tag_masks) == MemPointerRecord::tag_alloc ||
- (p1->flags() & MemPointerRecord::tag_masks) == MemPointerRecord::tag_release;
- }
-#endif
-};
-
-class MallocRecordIterator : public MemPointerArrayIterator {
- private:
- MemPointerArrayIteratorImpl _itr;
-
-
-
- public:
- MallocRecordIterator(MemPointerArray* arr) : _itr(arr) {
- }
-
- virtual MemPointer* current() const {
-#ifdef ASSERT
- MemPointer* cur_rec = _itr.current();
- if (cur_rec != NULL) {
- MemPointer* prev_rec = _itr.peek_prev();
- MemPointer* next_rec = _itr.peek_next();
- assert(prev_rec == NULL || prev_rec->addr() < cur_rec->addr(), "Sorting order");
- assert(next_rec == NULL || next_rec->addr() > cur_rec->addr(), "Sorting order");
- }
-#endif
- return _itr.current();
- }
- virtual MemPointer* next() {
- MemPointerRecord* next_rec = (MemPointerRecord*)_itr.next();
- // arena memory record is a special case, which we have to compare
- // sequence number against its associated arena record.
- if (next_rec != NULL && next_rec->is_arena_memory_record()) {
- MemPointerRecord* prev_rec = (MemPointerRecord*)_itr.peek_prev();
- // if there is an associated arena record, it has to be previous
- // record because of sorting order (by address) - NMT generates a pseudo address
- // for arena's size record by offsetting arena's address, that guarantees
- // the order of arena record and it's size record.
- if (prev_rec != NULL && prev_rec->is_arena_record() &&
- next_rec->is_memory_record_of_arena(prev_rec)) {
- if (prev_rec->seq() > next_rec->seq()) {
- // Skip this arena memory record
- // Two scenarios:
- // - if the arena record is an allocation record, this early
- // size record must be leftover by previous arena,
- // and the last size record should have size = 0.
- // - if the arena record is a deallocation record, this
- // size record should be its cleanup record, which should
- // also have size = 0. In other world, arena alway reset
- // its size before gone (see Arena's destructor)
- assert(next_rec->size() == 0, "size not reset");
- return _itr.next();
- } else {
- assert(prev_rec->is_allocation_record(),
- "Arena size record ahead of allocation record");
- }
- }
- }
- return next_rec;
- }
-
- MemPointer* peek_next() const { ShouldNotReachHere(); return NULL; }
- MemPointer* peek_prev() const { ShouldNotReachHere(); return NULL; }
- void remove() { ShouldNotReachHere(); }
- bool insert(MemPointer* ptr) { ShouldNotReachHere(); return false; }
- bool insert_after(MemPointer* ptr) { ShouldNotReachHere(); return false; }
-};
-
-// collapse duplicated records. Eliminating duplicated records here, is much
-// cheaper than during promotion phase. However, it does have limitation - it
-// can only eliminate duplicated records within the generation, there are
-// still chances seeing duplicated records during promotion.
-// We want to use the record with higher sequence number, because it has
-// more accurate callsite pc.
-class VMRecordIterator : public MemPointerArrayIterator {
- private:
- MemPointerArrayIteratorImpl _itr;
-
- public:
- VMRecordIterator(MemPointerArray* arr) : _itr(arr) {
- MemPointerRecord* cur = (MemPointerRecord*)_itr.current();
- MemPointerRecord* next = (MemPointerRecord*)_itr.peek_next();
- while (next != NULL) {
- assert(cur != NULL, "Sanity check");
- assert(((SeqMemPointerRecord*)next)->seq() > ((SeqMemPointerRecord*)cur)->seq(),
- "pre-sort order");
-
- if (is_duplicated_record(cur, next)) {
- _itr.next();
- next = (MemPointerRecord*)_itr.peek_next();
- } else {
- break;
- }
- }
- }
-
- virtual MemPointer* current() const {
- return _itr.current();
- }
-
- // get next record, but skip the duplicated records
- virtual MemPointer* next() {
- MemPointerRecord* cur = (MemPointerRecord*)_itr.next();
- MemPointerRecord* next = (MemPointerRecord*)_itr.peek_next();
- while (next != NULL) {
- assert(cur != NULL, "Sanity check");
- assert(((SeqMemPointerRecord*)next)->seq() > ((SeqMemPointerRecord*)cur)->seq(),
- "pre-sort order");
-
- if (is_duplicated_record(cur, next)) {
- _itr.next();
- cur = next;
- next = (MemPointerRecord*)_itr.peek_next();
- } else {
- break;
- }
- }
- return cur;
- }
-
- MemPointer* peek_next() const { ShouldNotReachHere(); return NULL; }
- MemPointer* peek_prev() const { ShouldNotReachHere(); return NULL; }
- void remove() { ShouldNotReachHere(); }
- bool insert(MemPointer* ptr) { ShouldNotReachHere(); return false; }
- bool insert_after(MemPointer* ptr) { ShouldNotReachHere(); return false; }
-
- private:
- bool is_duplicated_record(MemPointerRecord* p1, MemPointerRecord* p2) const {
- bool ret = (p1->addr() == p2->addr() && p1->size() == p2->size() && p1->flags() == p2->flags());
- assert(!(ret && FLAGS_TO_MEMORY_TYPE(p1->flags()) == mtThreadStack), "dup on stack record");
- return ret;
- }
-};
-
-class StagingArea VALUE_OBJ_CLASS_SPEC {
- private:
- MemPointerArray* _malloc_data;
- MemPointerArray* _vm_data;
-
- public:
- StagingArea() : _malloc_data(NULL), _vm_data(NULL) {
- init();
- }
-
- ~StagingArea() {
- if (_malloc_data != NULL) delete _malloc_data;
- if (_vm_data != NULL) delete _vm_data;
- }
-
- MallocRecordIterator malloc_record_walker() {
- return MallocRecordIterator(malloc_data());
- }
-
- VMRecordIterator virtual_memory_record_walker();
-
- bool init();
- void clear() {
- assert(_malloc_data != NULL && _vm_data != NULL, "Just check");
- _malloc_data->shrink();
- _malloc_data->clear();
- _vm_data->clear();
- }
-
- inline MemPointerArray* malloc_data() { return _malloc_data; }
- inline MemPointerArray* vm_data() { return _vm_data; }
-};
-
-class MemBaseline;
-class MemSnapshot : public CHeapObj<mtNMT> {
- private:
- // the following two arrays contain records of all known lived memory blocks
- // live malloc-ed memory pointers
- MemPointerArray* _alloc_ptrs;
- // live virtual memory pointers
- MemPointerArray* _vm_ptrs;
-
- StagingArea _staging_area;
-
- // the lock to protect this snapshot
- Monitor* _lock;
-
- // the number of instance classes
- int _number_of_classes;
-
- NOT_PRODUCT(size_t _untracked_count;)
- friend class MemBaseline;
-
- public:
- MemSnapshot();
- virtual ~MemSnapshot();
-
- // if we are running out of native memory
- bool out_of_memory() {
- return (_alloc_ptrs == NULL ||
- _staging_area.malloc_data() == NULL ||
- _staging_area.vm_data() == NULL ||
- _vm_ptrs == NULL || _lock == NULL ||
- _alloc_ptrs->out_of_memory() ||
- _vm_ptrs->out_of_memory());
- }
-
- // merge a per-thread memory recorder into staging area
- bool merge(MemRecorder* rec);
- // promote staged data to snapshot
- bool promote(int number_of_classes);
-
- int number_of_classes() const { return _number_of_classes; }
-
- void wait(long timeout) {
- assert(_lock != NULL, "Just check");
- MonitorLockerEx locker(_lock);
- locker.wait(true, timeout);
- }
-
- NOT_PRODUCT(void print_snapshot_stats(outputStream* st);)
- NOT_PRODUCT(void check_staging_data();)
- NOT_PRODUCT(void check_malloc_pointers();)
- NOT_PRODUCT(bool has_allocation_record(address addr);)
- // dump all virtual memory pointers in snapshot
- DEBUG_ONLY( void dump_all_vm_pointers();)
-
- private:
- // copy sequenced pointer from src to dest
- void copy_seq_pointer(MemPointerRecord* dest, const MemPointerRecord* src);
- // assign a sequenced pointer to non-sequenced pointer
- void assign_pointer(MemPointerRecord*dest, const MemPointerRecord* src);
-
- bool promote_malloc_records(MemPointerArrayIterator* itr);
- bool promote_virtual_memory_records(MemPointerArrayIterator* itr);
-};
-
-#endif // SHARE_VM_SERVICES_MEM_SNAPSHOT_HPP
--- a/hotspot/src/share/vm/services/memTrackWorker.cpp Tue Aug 05 14:18:44 2014 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,212 +0,0 @@
-/*
- * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- *
- */
-
-#include "precompiled.hpp"
-#include "runtime/threadCritical.hpp"
-#include "services/memTracker.hpp"
-#include "services/memTrackWorker.hpp"
-#include "utilities/decoder.hpp"
-#include "utilities/vmError.hpp"
-
-
-void GenerationData::reset() {
- _number_of_classes = 0;
- while (_recorder_list != NULL) {
- MemRecorder* tmp = _recorder_list;
- _recorder_list = _recorder_list->next();
- MemTracker::release_thread_recorder(tmp);
- }
-}
-
-MemTrackWorker::MemTrackWorker(MemSnapshot* snapshot): _snapshot(snapshot) {
- // create thread uses cgc thread type for now. We should revisit
- // the option, or create new thread type.
- _has_error = !os::create_thread(this, os::cgc_thread);
- set_name("MemTrackWorker");
-
- // initial generation circuit buffer
- if (!has_error()) {
- _head = _tail = 0;
- for(int index = 0; index < MAX_GENERATIONS; index ++) {
- ::new ((void*)&_gen[index]) GenerationData();
- }
- }
- NOT_PRODUCT(_sync_point_count = 0;)
- NOT_PRODUCT(_merge_count = 0;)
- NOT_PRODUCT(_last_gen_in_use = 0;)
-}
-
-MemTrackWorker::~MemTrackWorker() {
- for (int index = 0; index < MAX_GENERATIONS; index ++) {
- _gen[index].reset();
- }
-}
-
-void* MemTrackWorker::operator new(size_t size) throw() {
- assert(false, "use nothrow version");
- return NULL;
-}
-
-void* MemTrackWorker::operator new(size_t size, const std::nothrow_t& nothrow_constant) throw() {
- return allocate(size, false, mtNMT);
-}
-
-void MemTrackWorker::start() {
- os::start_thread(this);
-}
-
-/*
- * Native memory tracking worker thread loop:
- * 1. merge one generation of memory recorders to staging area
- * 2. promote staging data to memory snapshot
- *
- * This thread can run through safepoint.
- */
-
-void MemTrackWorker::run() {
- assert(MemTracker::is_on(), "native memory tracking is off");
- this->initialize_thread_local_storage();
- this->record_stack_base_and_size();
- assert(_snapshot != NULL, "Worker should not be started");
- MemRecorder* rec;
- unsigned long processing_generation = 0;
- bool worker_idle = false;
-
- while (!MemTracker::shutdown_in_progress()) {
- NOT_PRODUCT(_last_gen_in_use = generations_in_use();)
- {
- // take a recorder from earliest generation in buffer
- ThreadCritical tc;
- rec = _gen[_head].next_recorder();
- }
- if (rec != NULL) {
- if (rec->get_generation() != processing_generation || worker_idle) {
- processing_generation = rec->get_generation();
- worker_idle = false;
- MemTracker::set_current_processing_generation(processing_generation);
- }
-
- // merge the recorder into staging area
- if (!_snapshot->merge(rec)) {
- MemTracker::shutdown(MemTracker::NMT_out_of_memory);
- } else {
- NOT_PRODUCT(_merge_count ++;)
- }
- MemTracker::release_thread_recorder(rec);
- } else {
- // no more recorder to merge, promote staging area
- // to snapshot
- if (_head != _tail) {
- long number_of_classes;
- {
- ThreadCritical tc;
- if (_gen[_head].has_more_recorder() || _head == _tail) {
- continue;
- }
- number_of_classes = _gen[_head].number_of_classes();
- _gen[_head].reset();
-
- // done with this generation, increment _head pointer
- _head = (_head + 1) % MAX_GENERATIONS;
- }
- // promote this generation data to snapshot
- if (!_snapshot->promote(number_of_classes)) {
- // failed to promote, means out of memory
- MemTracker::shutdown(MemTracker::NMT_out_of_memory);
- }
- } else {
- // worker thread is idle
- worker_idle = true;
- MemTracker::report_worker_idle();
- _snapshot->wait(1000);
- ThreadCritical tc;
- // check if more data arrived
- if (!_gen[_head].has_more_recorder()) {
- _gen[_head].add_recorders(MemTracker::get_pending_recorders());
- }
- }
- }
- }
- assert(MemTracker::shutdown_in_progress(), "just check");
-
- // transits to final shutdown
- MemTracker::final_shutdown();
-}
-
-// at synchronization point, where 'safepoint visible' Java threads are blocked
-// at a safepoint, and the rest of threads are blocked on ThreadCritical lock.
-// The caller MemTracker::sync() already takes ThreadCritical before calling this
-// method.
-//
-// Following tasks are performed:
-// 1. add all recorders in pending queue to current generation
-// 2. increase generation
-
-void MemTrackWorker::at_sync_point(MemRecorder* rec, int number_of_classes) {
- NOT_PRODUCT(_sync_point_count ++;)
- assert(count_recorder(rec) <= MemRecorder::_instance_count,
- "pending queue has infinite loop");
-
- bool out_of_generation_buffer = false;
- // check shutdown state inside ThreadCritical
- if (MemTracker::shutdown_in_progress()) return;
-
- _gen[_tail].set_number_of_classes(number_of_classes);
- // append the recorders to the end of the generation
- _gen[_tail].add_recorders(rec);
- assert(count_recorder(_gen[_tail].peek()) <= MemRecorder::_instance_count,
- "after add to current generation has infinite loop");
- // we have collected all recorders for this generation. If there is data,
- // we need to increment _tail to start a new generation.
- if (_gen[_tail].has_more_recorder() || _head == _tail) {
- _tail = (_tail + 1) % MAX_GENERATIONS;
- out_of_generation_buffer = (_tail == _head);
- }
-
- if (out_of_generation_buffer) {
- MemTracker::shutdown(MemTracker::NMT_out_of_generation);
- }
-}
-
-#ifndef PRODUCT
-int MemTrackWorker::count_recorder(const MemRecorder* head) {
- int count = 0;
- while(head != NULL) {
- count ++;
- head = head->next();
- }
- return count;
-}
-
-int MemTrackWorker::count_pending_recorders() const {
- int count = 0;
- for (int index = 0; index < MAX_GENERATIONS; index ++) {
- MemRecorder* head = _gen[index].peek();
- if (head != NULL) {
- count += count_recorder(head);
- }
- }
- return count;
-}
-#endif
--- a/hotspot/src/share/vm/services/memTrackWorker.hpp Tue Aug 05 14:18:44 2014 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,118 +0,0 @@
-/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- *
- */
-
-#ifndef SHARE_VM_SERVICES_MEM_TRACK_WORKER_HPP
-#define SHARE_VM_SERVICES_MEM_TRACK_WORKER_HPP
-
-#include "memory/allocation.hpp"
-#include "runtime/thread.hpp"
-#include "services/memRecorder.hpp"
-
-// Maximum MAX_GENERATIONS generation data can be tracked.
-#define MAX_GENERATIONS 512
-
-class GenerationData VALUE_OBJ_CLASS_SPEC {
- private:
- int _number_of_classes;
- MemRecorder* _recorder_list;
-
- public:
- GenerationData(): _number_of_classes(0), _recorder_list(NULL) { }
-
- inline int number_of_classes() const { return _number_of_classes; }
- inline void set_number_of_classes(long num) { _number_of_classes = num; }
-
- inline MemRecorder* next_recorder() {
- if (_recorder_list == NULL) {
- return NULL;
- } else {
- MemRecorder* tmp = _recorder_list;
- _recorder_list = _recorder_list->next();
- return tmp;
- }
- }
-
- inline bool has_more_recorder() const {
- return (_recorder_list != NULL);
- }
-
- // add recorders to this generation
- void add_recorders(MemRecorder* head) {
- if (head != NULL) {
- if (_recorder_list == NULL) {
- _recorder_list = head;
- } else {
- MemRecorder* tmp = _recorder_list;
- for (; tmp->next() != NULL; tmp = tmp->next());
- tmp->set_next(head);
- }
- }
- }
-
- void reset();
-
- NOT_PRODUCT(MemRecorder* peek() const { return _recorder_list; })
-};
-
-class MemTrackWorker : public NamedThread {
- private:
- // circular buffer. This buffer contains generation data to be merged into global
- // snaphsot.
- // Each slot holds a generation
- GenerationData _gen[MAX_GENERATIONS];
- int _head, _tail; // head and tail pointers to above circular buffer
-
- bool _has_error;
-
- MemSnapshot* _snapshot;
-
- public:
- MemTrackWorker(MemSnapshot* snapshot);
- ~MemTrackWorker();
- _NOINLINE_ void* operator new(size_t size) throw();
- _NOINLINE_ void* operator new(size_t size, const std::nothrow_t& nothrow_constant) throw();
-
- void start();
- void run();
-
- inline bool has_error() const { return _has_error; }
-
- // task at synchronization point
- void at_sync_point(MemRecorder* pending_recorders, int number_of_classes);
-
- // for debugging purpose, they are not thread safe.
- NOT_PRODUCT(static int count_recorder(const MemRecorder* head);)
- NOT_PRODUCT(int count_pending_recorders() const;)
-
- NOT_PRODUCT(int _sync_point_count;)
- NOT_PRODUCT(int _merge_count;)
- NOT_PRODUCT(int _last_gen_in_use;)
-
- // how many generations are queued
- inline int generations_in_use() const {
- return (_tail >= _head ? (_tail - _head + 1) : (MAX_GENERATIONS - (_head - _tail) + 1));
- }
-};
-
-#endif // SHARE_VM_SERVICES_MEM_TRACK_WORKER_HPP
--- a/hotspot/src/share/vm/services/memTracker.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/services/memTracker.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -23,862 +23,308 @@
*/
#include "precompiled.hpp"
-#include "oops/instanceKlass.hpp"
-#include "runtime/atomic.inline.hpp"
-#include "runtime/interfaceSupport.hpp"
-#include "runtime/mutexLocker.hpp"
-#include "runtime/safepoint.hpp"
-#include "runtime/threadCritical.hpp"
-#include "runtime/thread.inline.hpp"
-#include "runtime/vm_operations.hpp"
-#include "services/memPtr.hpp"
+#include "runtime/mutex.hpp"
+#include "services/memBaseline.hpp"
#include "services/memReporter.hpp"
+#include "services/mallocTracker.inline.hpp"
#include "services/memTracker.hpp"
-#include "utilities/decoder.hpp"
#include "utilities/defaultStream.hpp"
-#include "utilities/globalDefinitions.hpp"
-bool NMT_track_callsite = false;
+#ifdef SOLARIS
+ volatile bool NMT_stack_walkable = false;
+#else
+ volatile bool NMT_stack_walkable = true;
+#endif
-// walk all 'known' threads at NMT sync point, and collect their recorders
-void SyncThreadRecorderClosure::do_thread(Thread* thread) {
- assert(SafepointSynchronize::is_at_safepoint(), "Safepoint required");
- if (thread->is_Java_thread()) {
- JavaThread* javaThread = (JavaThread*)thread;
- MemRecorder* recorder = javaThread->get_recorder();
- if (recorder != NULL) {
- MemTracker::enqueue_pending_recorder(recorder);
- javaThread->set_recorder(NULL);
- }
- }
- _thread_count ++;
-}
+volatile NMT_TrackingLevel MemTracker::_tracking_level = NMT_unknown;
+NMT_TrackingLevel MemTracker::_cmdline_tracking_level = NMT_unknown;
+
+NativeCallStack emptyStack(0, false);
+
+MemBaseline MemTracker::_baseline;
+Mutex* MemTracker::_query_lock = NULL;
+bool MemTracker::_is_nmt_env_valid = true;
-MemRecorder* volatile MemTracker::_global_recorder = NULL;
-MemSnapshot* MemTracker::_snapshot = NULL;
-MemBaseline MemTracker::_baseline;
-Mutex* MemTracker::_query_lock = NULL;
-MemRecorder* volatile MemTracker::_merge_pending_queue = NULL;
-MemRecorder* volatile MemTracker::_pooled_recorders = NULL;
-MemTrackWorker* MemTracker::_worker_thread = NULL;
-int MemTracker::_sync_point_skip_count = 0;
-MemTracker::NMTLevel MemTracker::_tracking_level = MemTracker::NMT_off;
-volatile MemTracker::NMTStates MemTracker::_state = NMT_uninited;
-MemTracker::ShutdownReason MemTracker::_reason = NMT_shutdown_none;
-int MemTracker::_thread_count = 255;
-volatile jint MemTracker::_pooled_recorder_count = 0;
-volatile unsigned long MemTracker::_processing_generation = 0;
-volatile bool MemTracker::_worker_thread_idle = false;
-volatile jint MemTracker::_pending_op_count = 0;
-volatile bool MemTracker::_slowdown_calling_thread = false;
-debug_only(intx MemTracker::_main_thread_tid = 0;)
-NOT_PRODUCT(volatile jint MemTracker::_pending_recorder_count = 0;)
-
-void MemTracker::init_tracking_options(const char* option_line) {
- _tracking_level = NMT_off;
- if (strcmp(option_line, "=summary") == 0) {
- _tracking_level = NMT_summary;
- } else if (strcmp(option_line, "=detail") == 0) {
- // detail relies on a stack-walking ability that may not
- // be available depending on platform and/or compiler flags
+NMT_TrackingLevel MemTracker::init_tracking_level() {
+ NMT_TrackingLevel level = NMT_off;
+ char buf[64];
+ char nmt_option[64];
+ jio_snprintf(buf, sizeof(buf), "NMT_LEVEL_%d", os::current_process_id());
+ if (os::getenv(buf, nmt_option, sizeof(nmt_option))) {
+ if (strcmp(nmt_option, "summary") == 0) {
+ level = NMT_summary;
+ } else if (strcmp(nmt_option, "detail") == 0) {
#if PLATFORM_NATIVE_STACK_WALKING_SUPPORTED
- _tracking_level = NMT_detail;
+ level = NMT_detail;
#else
- jio_fprintf(defaultStream::error_stream(),
- "NMT detail is not supported on this platform. Using NMT summary instead.\n");
- _tracking_level = NMT_summary;
-#endif
- } else if (strcmp(option_line, "=off") != 0) {
- vm_exit_during_initialization("Syntax error, expecting -XX:NativeMemoryTracking=[off|summary|detail]", NULL);
- }
-}
-
-// first phase of bootstrapping, when VM is still in single-threaded mode.
-void MemTracker::bootstrap_single_thread() {
- if (_tracking_level > NMT_off) {
- assert(_state == NMT_uninited, "wrong state");
-
- // NMT is not supported with UseMallocOnly is on. NMT can NOT
- // handle the amount of malloc data without significantly impacting
- // runtime performance when this flag is on.
- if (UseMallocOnly) {
- shutdown(NMT_use_malloc_only);
- return;
+ level = NMT_summary;
+#endif // PLATFORM_NATIVE_STACK_WALKING_SUPPORTED
+ } else if (strcmp(nmt_option, "off") != 0) {
+ // The option value is invalid
+ _is_nmt_env_valid = false;
}
- _query_lock = new (std::nothrow) Mutex(Monitor::max_nonleaf, "NMT_queryLock");
- if (_query_lock == NULL) {
- shutdown(NMT_out_of_memory);
- return;
- }
+ // Remove the environment variable to avoid leaking to child processes
+ os::unsetenv(buf);
+ }
- debug_only(_main_thread_tid = os::current_thread_id();)
- _state = NMT_bootstrapping_single_thread;
- NMT_track_callsite = (_tracking_level == NMT_detail && can_walk_stack());
+ if (!MallocTracker::initialize(level) ||
+ !VirtualMemoryTracker::initialize(level)) {
+ level = NMT_off;
}
+ return level;
}
-// second phase of bootstrapping, when VM is about to or already entered multi-theaded mode.
-void MemTracker::bootstrap_multi_thread() {
- if (_tracking_level > NMT_off && _state == NMT_bootstrapping_single_thread) {
- // create nmt lock for multi-thread execution
- assert(_main_thread_tid == os::current_thread_id(), "wrong thread");
- _state = NMT_bootstrapping_multi_thread;
- NMT_track_callsite = (_tracking_level == NMT_detail && can_walk_stack());
- }
-}
-
-// fully start nmt
-void MemTracker::start() {
- // Native memory tracking is off from command line option
- if (_tracking_level == NMT_off || shutdown_in_progress()) return;
-
- assert(_main_thread_tid == os::current_thread_id(), "wrong thread");
- assert(_state == NMT_bootstrapping_multi_thread, "wrong state");
-
- _snapshot = new (std::nothrow)MemSnapshot();
- if (_snapshot != NULL) {
- if (!_snapshot->out_of_memory() && start_worker(_snapshot)) {
- _state = NMT_started;
- NMT_track_callsite = (_tracking_level == NMT_detail && can_walk_stack());
- return;
- }
-
- delete _snapshot;
- _snapshot = NULL;
- }
-
- // fail to start native memory tracking, shut it down
- shutdown(NMT_initialization);
-}
-
-/**
- * Shutting down native memory tracking.
- * We can not shutdown native memory tracking immediately, so we just
- * setup shutdown pending flag, every native memory tracking component
- * should orderly shut itself down.
- *
- * The shutdown sequences:
- * 1. MemTracker::shutdown() sets MemTracker to shutdown pending state
- * 2. Worker thread calls MemTracker::final_shutdown(), which transites
- * MemTracker to final shutdown state.
- * 3. At sync point, MemTracker does final cleanup, before sets memory
- * tracking level to off to complete shutdown.
- */
-void MemTracker::shutdown(ShutdownReason reason) {
- if (_tracking_level == NMT_off) return;
-
- if (_state <= NMT_bootstrapping_single_thread) {
- // we still in single thread mode, there is not contention
- _state = NMT_shutdown_pending;
- _reason = reason;
- } else {
- // we want to know who initialized shutdown
- if ((jint)NMT_started == Atomic::cmpxchg((jint)NMT_shutdown_pending,
- (jint*)&_state, (jint)NMT_started)) {
- _reason = reason;
- }
- }
-}
-
-// final phase of shutdown
-void MemTracker::final_shutdown() {
- // delete all pending recorders and pooled recorders
- delete_all_pending_recorders();
- delete_all_pooled_recorders();
-
- {
- // shared baseline and snapshot are the only objects needed to
- // create query results
- MutexLockerEx locker(_query_lock, true);
- // cleanup baseline data and snapshot
- _baseline.clear();
- delete _snapshot;
- _snapshot = NULL;
- }
-
- // shutdown shared decoder instance, since it is only
- // used by native memory tracking so far.
- Decoder::shutdown();
-
- MemTrackWorker* worker = NULL;
- {
- ThreadCritical tc;
- // can not delete worker inside the thread critical
- if (_worker_thread != NULL && Thread::current() == _worker_thread) {
- worker = _worker_thread;
- _worker_thread = NULL;
- }
- }
- if (worker != NULL) {
- delete worker;
- }
- _state = NMT_final_shutdown;
-}
-
-// delete all pooled recorders
-void MemTracker::delete_all_pooled_recorders() {
- // free all pooled recorders
- MemRecorder* volatile cur_head = _pooled_recorders;
- if (cur_head != NULL) {
- MemRecorder* null_ptr = NULL;
- while (cur_head != NULL && (void*)cur_head != Atomic::cmpxchg_ptr((void*)null_ptr,
- (void*)&_pooled_recorders, (void*)cur_head)) {
- cur_head = _pooled_recorders;
- }
- if (cur_head != NULL) {
- delete cur_head;
- _pooled_recorder_count = 0;
+void MemTracker::init() {
+ if (tracking_level() >= NMT_summary) {
+ _query_lock = new (std::nothrow) Mutex(Monitor::max_nonleaf, "NMT_queryLock");
+ // Already OOM. It is unlikely, but still have to handle it.
+ if (_query_lock == NULL) {
+ shutdown();
}
}
}
-// delete all recorders in pending queue
-void MemTracker::delete_all_pending_recorders() {
- // free all pending recorders
- MemRecorder* pending_head = get_pending_recorders();
- if (pending_head != NULL) {
- delete pending_head;
+bool MemTracker::check_launcher_nmt_support(const char* value) {
+ if (strcmp(value, "=detail") == 0) {
+#if !PLATFORM_NATIVE_STACK_WALKING_SUPPORTED
+ jio_fprintf(defaultStream::error_stream(),
+ "NMT detail is not supported on this platform. Using NMT summary instead.\n");
+ if (MemTracker::tracking_level() != NMT_summary) {
+ return false;
+ }
+#else
+ if (MemTracker::tracking_level() != NMT_detail) {
+ return false;
+ }
+#endif
+ } else if (strcmp(value, "=summary") == 0) {
+ if (MemTracker::tracking_level() != NMT_summary) {
+ return false;
+ }
+ } else if (strcmp(value, "=off") == 0) {
+ if (MemTracker::tracking_level() != NMT_off) {
+ return false;
+ }
+ } else {
+ _is_nmt_env_valid = false;
+ }
+
+ return true;
+}
+
+bool MemTracker::verify_nmt_option() {
+ return _is_nmt_env_valid;
+}
+
+void* MemTracker::malloc_base(void* memblock) {
+ return MallocTracker::get_base(memblock);
+}
+
+void Tracker::record(address addr, size_t size) {
+ if (MemTracker::tracking_level() < NMT_summary) return;
+ switch(_type) {
+ case uncommit:
+ VirtualMemoryTracker::remove_uncommitted_region(addr, size);
+ break;
+ case release:
+ VirtualMemoryTracker::remove_released_region(addr, size);
+ break;
+ default:
+ ShouldNotReachHere();
}
}
-/*
- * retrieve per-thread recorder of specified thread.
- * if thread == NULL, it means global recorder
- */
-MemRecorder* MemTracker::get_thread_recorder(JavaThread* thread) {
- if (shutdown_in_progress()) return NULL;
+
+// Shutdown can only be issued via JCmd, and NMT JCmd is serialized
+// by lock
+void MemTracker::shutdown() {
+ // We can only shutdown NMT to minimal tracking level if it is
+ // ever on.
+ if (tracking_level () > NMT_minimal) {
+ transition_to(NMT_minimal);
+ }
+}
+
+bool MemTracker::transition_to(NMT_TrackingLevel level) {
+ NMT_TrackingLevel current_level = tracking_level();
- MemRecorder* rc;
- if (thread == NULL) {
- rc = _global_recorder;
+ if (current_level == level) {
+ return true;
+ } else if (current_level > level) {
+ // Downgrade tracking level, we want to lower the tracking
+ // level first
+ _tracking_level = level;
+ // Make _tracking_level visible immediately.
+ OrderAccess::fence();
+ VirtualMemoryTracker::transition(current_level, level);
+ MallocTracker::transition(current_level, level);
+
+ if (level == NMT_minimal) _baseline.reset();
} else {
- rc = thread->get_recorder();
- }
+ VirtualMemoryTracker::transition(current_level, level);
+ MallocTracker::transition(current_level, level);
- if (rc != NULL && rc->is_full()) {
- enqueue_pending_recorder(rc);
- rc = NULL;
+ _tracking_level = level;
+ // Make _tracking_level visible immediately.
+ OrderAccess::fence();
}
- if (rc == NULL) {
- rc = get_new_or_pooled_instance();
- if (thread == NULL) {
- _global_recorder = rc;
- } else {
- thread->set_recorder(rc);
- }
- }
- return rc;
+ return true;
}
-/*
- * get a per-thread recorder from pool, or create a new one if
- * there is not one available.
- */
-MemRecorder* MemTracker::get_new_or_pooled_instance() {
- MemRecorder* cur_head = const_cast<MemRecorder*> (_pooled_recorders);
- if (cur_head == NULL) {
- MemRecorder* rec = new (std::nothrow)MemRecorder();
- if (rec == NULL || rec->out_of_memory()) {
- shutdown(NMT_out_of_memory);
- if (rec != NULL) {
- delete rec;
- rec = NULL;
- }
- }
- return rec;
- } else {
- MemRecorder* next_head = cur_head->next();
- if ((void*)cur_head != Atomic::cmpxchg_ptr((void*)next_head, (void*)&_pooled_recorders,
- (void*)cur_head)) {
- return get_new_or_pooled_instance();
- }
- cur_head->set_next(NULL);
- Atomic::dec(&_pooled_recorder_count);
- cur_head->set_generation();
- return cur_head;
+void MemTracker::final_report(outputStream* output) {
+ assert(output != NULL, "No output stream");
+ if (tracking_level() >= NMT_summary) {
+ MallocMemorySnapshot* malloc_memory_snapshot =
+ MallocMemorySummary::as_snapshot();
+ malloc_memory_snapshot->make_adjustment();
+
+ VirtualMemorySnapshot* virtual_memory_snapshot =
+ VirtualMemorySummary::as_snapshot();
+
+ MemSummaryReporter rptr(malloc_memory_snapshot,
+ virtual_memory_snapshot, output);
+ rptr.report();
+ // shutdown NMT, the data no longer accurate
+ shutdown();
}
}
-/*
- * retrieve all recorders in pending queue, and empty the queue
- */
-MemRecorder* MemTracker::get_pending_recorders() {
- MemRecorder* cur_head = const_cast<MemRecorder*>(_merge_pending_queue);
- MemRecorder* null_ptr = NULL;
- while ((void*)cur_head != Atomic::cmpxchg_ptr((void*)null_ptr, (void*)&_merge_pending_queue,
- (void*)cur_head)) {
- cur_head = const_cast<MemRecorder*>(_merge_pending_queue);
- }
- NOT_PRODUCT(Atomic::store(0, &_pending_recorder_count));
- return cur_head;
-}
+// This is a walker to gather malloc site hashtable statistics,
+// the result is used for tuning.
+class StatisticsWalker : public MallocSiteWalker {
+ private:
+ enum Threshold {
+ // aggregates statistics over this threshold into one
+ // line item.
+ report_threshold = 20
+ };
-/*
- * release a recorder to recorder pool.
- */
-void MemTracker::release_thread_recorder(MemRecorder* rec) {
- assert(rec != NULL, "null recorder");
- // we don't want to pool too many recorders
- rec->set_next(NULL);
- if (shutdown_in_progress() || _pooled_recorder_count > _thread_count * 2) {
- delete rec;
- return;
- }
+ private:
+ // Number of allocation sites that have all memory freed
+ int _empty_entries;
+ // Total number of allocation sites, include empty sites
+ int _total_entries;
+ // Number of captured call stack distribution
+ int _stack_depth_distribution[NMT_TrackingStackDepth];
+ // Hash distribution
+ int _hash_distribution[report_threshold];
+ // Number of hash buckets that have entries over the threshold
+ int _bucket_over_threshold;
- rec->clear();
- MemRecorder* cur_head = const_cast<MemRecorder*>(_pooled_recorders);
- rec->set_next(cur_head);
- while ((void*)cur_head != Atomic::cmpxchg_ptr((void*)rec, (void*)&_pooled_recorders,
- (void*)cur_head)) {
- cur_head = const_cast<MemRecorder*>(_pooled_recorders);
- rec->set_next(cur_head);
- }
- Atomic::inc(&_pooled_recorder_count);
-}
-
-// write a record to proper recorder. No lock can be taken from this method
-// down.
-void MemTracker::write_tracking_record(address addr, MEMFLAGS flags,
- size_t size, jint seq, address pc, JavaThread* thread) {
+ // The hash bucket that walker is currently walking
+ int _current_hash_bucket;
+ // The length of current hash bucket
+ int _current_bucket_length;
+ // Number of hash buckets that are not empty
+ int _used_buckets;
+ // Longest hash bucket length
+ int _longest_bucket_length;
- MemRecorder* rc = get_thread_recorder(thread);
- if (rc != NULL) {
- rc->record(addr, flags, size, seq, pc);
+ public:
+ StatisticsWalker() : _empty_entries(0), _total_entries(0) {
+ int index = 0;
+ for (index = 0; index < NMT_TrackingStackDepth; index ++) {
+ _stack_depth_distribution[index] = 0;
}
-}
-
-/**
- * enqueue a recorder to pending queue
- */
-void MemTracker::enqueue_pending_recorder(MemRecorder* rec) {
- assert(rec != NULL, "null recorder");
-
- // we are shutting down, so just delete it
- if (shutdown_in_progress()) {
- rec->set_next(NULL);
- delete rec;
- return;
+ for (index = 0; index < report_threshold; index ++) {
+ _hash_distribution[index] = 0;
+ }
+ _bucket_over_threshold = 0;
+ _longest_bucket_length = 0;
+ _current_hash_bucket = -1;
+ _current_bucket_length = 0;
+ _used_buckets = 0;
}
- MemRecorder* cur_head = const_cast<MemRecorder*>(_merge_pending_queue);
- rec->set_next(cur_head);
- while ((void*)cur_head != Atomic::cmpxchg_ptr((void*)rec, (void*)&_merge_pending_queue,
- (void*)cur_head)) {
- cur_head = const_cast<MemRecorder*>(_merge_pending_queue);
- rec->set_next(cur_head);
+ virtual bool do_malloc_site(const MallocSite* e) {
+ if (e->size() == 0) _empty_entries ++;
+ _total_entries ++;
+
+ // stack depth distrubution
+ int frames = e->call_stack()->frames();
+ _stack_depth_distribution[frames - 1] ++;
+
+ // hash distribution
+ int hash_bucket = e->hash() % MallocSiteTable::hash_buckets();
+ if (_current_hash_bucket == -1) {
+ _current_hash_bucket = hash_bucket;
+ _current_bucket_length = 1;
+ } else if (_current_hash_bucket == hash_bucket) {
+ _current_bucket_length ++;
+ } else {
+ record_bucket_length(_current_bucket_length);
+ _current_hash_bucket = hash_bucket;
+ _current_bucket_length = 1;
+ }
+ return true;
}
- NOT_PRODUCT(Atomic::inc(&_pending_recorder_count);)
-}
-/*
- * The method is called at global safepoint
- * during it synchronization process.
- * 1. enqueue all JavaThreads' per-thread recorders
- * 2. enqueue global recorder
- * 3. retrieve all pending recorders
- * 4. reset global sequence number generator
- * 5. call worker's sync
- */
-#define MAX_SAFEPOINTS_TO_SKIP 128
-#define SAFE_SEQUENCE_THRESHOLD 30
-#define HIGH_GENERATION_THRESHOLD 60
-#define MAX_RECORDER_THREAD_RATIO 30
-#define MAX_RECORDER_PER_THREAD 100
-
-void MemTracker::sync() {
- assert(_tracking_level > NMT_off, "NMT is not enabled");
- assert(SafepointSynchronize::is_at_safepoint(), "Safepoint required");
+ // walk completed
+ void completed() {
+ record_bucket_length(_current_bucket_length);
+ }
- // Some GC tests hit large number of safepoints in short period of time
- // without meaningful activities. We should prevent going to
- // sync point in these cases, which can potentially exhaust generation buffer.
- // Here is the factots to determine if we should go into sync point:
- // 1. not to overflow sequence number
- // 2. if we are in danger to overflow generation buffer
- // 3. how many safepoints we already skipped sync point
- if (_state == NMT_started) {
- // worker thread is not ready, no one can manage generation
- // buffer, so skip this safepoint
- if (_worker_thread == NULL) return;
-
- if (_sync_point_skip_count < MAX_SAFEPOINTS_TO_SKIP) {
- int per_seq_in_use = SequenceGenerator::peek() * 100 / max_jint;
- int per_gen_in_use = _worker_thread->generations_in_use() * 100 / MAX_GENERATIONS;
- if (per_seq_in_use < SAFE_SEQUENCE_THRESHOLD && per_gen_in_use >= HIGH_GENERATION_THRESHOLD) {
- _sync_point_skip_count ++;
- return;
+ void report_statistics(outputStream* out) {
+ int index;
+ out->print_cr("Malloc allocation site table:");
+ out->print_cr("\tTotal entries: %d", _total_entries);
+ out->print_cr("\tEmpty entries: %d (%2.2f%%)", _empty_entries, ((float)_empty_entries * 100) / _total_entries);
+ out->print_cr(" ");
+ out->print_cr("Hash distribution:");
+ if (_used_buckets < MallocSiteTable::hash_buckets()) {
+ out->print_cr("empty bucket: %d", (MallocSiteTable::hash_buckets() - _used_buckets));
+ }
+ for (index = 0; index < report_threshold; index ++) {
+ if (_hash_distribution[index] != 0) {
+ if (index == 0) {
+ out->print_cr(" %d entry: %d", 1, _hash_distribution[0]);
+ } else if (index < 9) { // single digit
+ out->print_cr(" %d entries: %d", (index + 1), _hash_distribution[index]);
+ } else {
+ out->print_cr(" %d entries: %d", (index + 1), _hash_distribution[index]);
+ }
}
}
- {
- // This method is running at safepoint, with ThreadCritical lock,
- // it should guarantee that NMT is fully sync-ed.
- ThreadCritical tc;
-
- // We can NOT execute NMT sync-point if there are pending tracking ops.
- if (_pending_op_count == 0) {
- SequenceGenerator::reset();
- _sync_point_skip_count = 0;
-
- // walk all JavaThreads to collect recorders
- SyncThreadRecorderClosure stc;
- Threads::threads_do(&stc);
-
- _thread_count = stc.get_thread_count();
- MemRecorder* pending_recorders = get_pending_recorders();
-
- if (_global_recorder != NULL) {
- _global_recorder->set_next(pending_recorders);
- pending_recorders = _global_recorder;
- _global_recorder = NULL;
- }
-
- // see if NMT has too many outstanding recorder instances, it usually
- // means that worker thread is lagging behind in processing them.
- if (!AutoShutdownNMT) {
- _slowdown_calling_thread = (MemRecorder::_instance_count > MAX_RECORDER_THREAD_RATIO * _thread_count);
- } else {
- // If auto shutdown is on, enforce MAX_RECORDER_PER_THREAD threshold to prevent OOM
- if (MemRecorder::_instance_count >= _thread_count * MAX_RECORDER_PER_THREAD) {
- shutdown(NMT_out_of_memory);
- }
- }
-
- // check _worker_thread with lock to avoid racing condition
- if (_worker_thread != NULL) {
- _worker_thread->at_sync_point(pending_recorders, InstanceKlass::number_of_instance_classes());
- }
- assert(SequenceGenerator::peek() == 1, "Should not have memory activities during sync-point");
- } else {
- _sync_point_skip_count ++;
+ if (_bucket_over_threshold > 0) {
+ out->print_cr(" >%d entries: %d", report_threshold, _bucket_over_threshold);
+ }
+ out->print_cr("most entries: %d", _longest_bucket_length);
+ out->print_cr(" ");
+ out->print_cr("Call stack depth distribution:");
+ for (index = 0; index < NMT_TrackingStackDepth; index ++) {
+ if (_stack_depth_distribution[index] > 0) {
+ out->print_cr("\t%d: %d", index + 1, _stack_depth_distribution[index]);
}
}
}
- // now, it is the time to shut whole things off
- if (_state == NMT_final_shutdown) {
- // walk all JavaThreads to delete all recorders
- SyncThreadRecorderClosure stc;
- Threads::threads_do(&stc);
- // delete global recorder
- {
- ThreadCritical tc;
- if (_global_recorder != NULL) {
- delete _global_recorder;
- _global_recorder = NULL;
- }
- }
- MemRecorder* pending_recorders = get_pending_recorders();
- if (pending_recorders != NULL) {
- delete pending_recorders;
- }
- // try at a later sync point to ensure MemRecorder instance drops to zero to
- // completely shutdown NMT
- if (MemRecorder::_instance_count == 0) {
- _state = NMT_shutdown;
- _tracking_level = NMT_off;
+ private:
+ void record_bucket_length(int length) {
+ _used_buckets ++;
+ if (length <= report_threshold) {
+ _hash_distribution[length - 1] ++;
+ } else {
+ _bucket_over_threshold ++;
}
- }
-}
-
-/*
- * Start worker thread.
- */
-bool MemTracker::start_worker(MemSnapshot* snapshot) {
- assert(_worker_thread == NULL && _snapshot != NULL, "Just Check");
- _worker_thread = new (std::nothrow) MemTrackWorker(snapshot);
- if (_worker_thread == NULL) {
- return false;
- } else if (_worker_thread->has_error()) {
- delete _worker_thread;
- _worker_thread = NULL;
- return false;
- }
- _worker_thread->start();
- return true;
-}
-
-/*
- * We need to collect a JavaThread's per-thread recorder
- * before it exits.
- */
-void MemTracker::thread_exiting(JavaThread* thread) {
- if (is_on()) {
- MemRecorder* rec = thread->get_recorder();
- if (rec != NULL) {
- enqueue_pending_recorder(rec);
- thread->set_recorder(NULL);
- }
- }
-}
-
-// baseline current memory snapshot
-bool MemTracker::baseline() {
- MutexLocker lock(_query_lock);
- MemSnapshot* snapshot = get_snapshot();
- if (snapshot != NULL) {
- return _baseline.baseline(*snapshot, false);
- }
- return false;
-}
-
-// print memory usage from current snapshot
-bool MemTracker::print_memory_usage(BaselineOutputer& out, size_t unit, bool summary_only) {
- MemBaseline baseline;
- MutexLocker lock(_query_lock);
- MemSnapshot* snapshot = get_snapshot();
- if (snapshot != NULL && baseline.baseline(*snapshot, summary_only)) {
- BaselineReporter reporter(out, unit);
- reporter.report_baseline(baseline, summary_only);
- return true;
+ _longest_bucket_length = MAX2(_longest_bucket_length, length);
}
- return false;
-}
-
-// Whitebox API for blocking until the current generation of NMT data has been merged
-bool MemTracker::wbtest_wait_for_data_merge() {
- // NMT can't be shutdown while we're holding _query_lock
- MutexLocker lock(_query_lock);
- assert(_worker_thread != NULL, "Invalid query");
- // the generation at query time, so NMT will spin till this generation is processed
- unsigned long generation_at_query_time = SequenceGenerator::current_generation();
- unsigned long current_processing_generation = _processing_generation;
- // if generation counter overflown
- bool generation_overflown = (generation_at_query_time < current_processing_generation);
- long generations_to_wrap = MAX_UNSIGNED_LONG - current_processing_generation;
- // spin
- while (!shutdown_in_progress()) {
- if (!generation_overflown) {
- if (current_processing_generation > generation_at_query_time) {
- return true;
- }
- } else {
- assert(generations_to_wrap >= 0, "Sanity check");
- long current_generations_to_wrap = MAX_UNSIGNED_LONG - current_processing_generation;
- assert(current_generations_to_wrap >= 0, "Sanity check");
- // to overflow an unsigned long should take long time, so to_wrap check should be sufficient
- if (current_generations_to_wrap > generations_to_wrap &&
- current_processing_generation > generation_at_query_time) {
- return true;
- }
- }
-
- // if worker thread is idle, but generation is not advancing, that means
- // there is not safepoint to let NMT advance generation, force one.
- if (_worker_thread_idle) {
- VM_ForceSafepoint vfs;
- VMThread::execute(&vfs);
- }
- MemSnapshot* snapshot = get_snapshot();
- if (snapshot == NULL) {
- return false;
- }
- snapshot->wait(1000);
- current_processing_generation = _processing_generation;
- }
- // We end up here if NMT is shutting down before our data has been merged
- return false;
-}
-
-// compare memory usage between current snapshot and baseline
-bool MemTracker::compare_memory_usage(BaselineOutputer& out, size_t unit, bool summary_only) {
- MutexLocker lock(_query_lock);
- if (_baseline.baselined()) {
- MemBaseline baseline;
- MemSnapshot* snapshot = get_snapshot();
- if (snapshot != NULL && baseline.baseline(*snapshot, summary_only)) {
- BaselineReporter reporter(out, unit);
- reporter.diff_baselines(baseline, _baseline, summary_only);
- return true;
- }
- }
- return false;
-}
-
-#ifndef PRODUCT
-void MemTracker::walk_stack(int toSkip, char* buf, int len) {
- int cur_len = 0;
- char tmp[1024];
- address pc;
-
- while (cur_len < len) {
- pc = os::get_caller_pc(toSkip + 1);
- if (pc != NULL && os::dll_address_to_function_name(pc, tmp, sizeof(tmp), NULL)) {
- jio_snprintf(&buf[cur_len], (len - cur_len), "%s\n", tmp);
- cur_len = (int)strlen(buf);
- } else {
- buf[cur_len] = '\0';
- break;
- }
- toSkip ++;
- }
-}
-
-void MemTracker::print_tracker_stats(outputStream* st) {
- st->print_cr("\nMemory Tracker Stats:");
- st->print_cr("\tMax sequence number = %d", SequenceGenerator::max_seq_num());
- st->print_cr("\tthead count = %d", _thread_count);
- st->print_cr("\tArena instance = %d", Arena::_instance_count);
- st->print_cr("\tpooled recorder count = %d", _pooled_recorder_count);
- st->print_cr("\tqueued recorder count = %d", _pending_recorder_count);
- st->print_cr("\tmemory recorder instance count = %d", MemRecorder::_instance_count);
- if (_worker_thread != NULL) {
- st->print_cr("\tWorker thread:");
- st->print_cr("\t\tSync point count = %d", _worker_thread->_sync_point_count);
- st->print_cr("\t\tpending recorder count = %d", _worker_thread->count_pending_recorders());
- st->print_cr("\t\tmerge count = %d", _worker_thread->_merge_count);
- } else {
- st->print_cr("\tWorker thread is not started");
- }
- st->print_cr(" ");
-
- if (_snapshot != NULL) {
- _snapshot->print_snapshot_stats(st);
- } else {
- st->print_cr("No snapshot");
- }
-}
-#endif
+};
-// Tracker Implementation
+void MemTracker::tuning_statistics(outputStream* out) {
+ // NMT statistics
+ StatisticsWalker walker;
+ MallocSiteTable::walk_malloc_site(&walker);
+ walker.completed();
-/*
- * Create a tracker.
- * This is a fairly complicated constructor, as it has to make two important decisions:
- * 1) Does it need to take ThreadCritical lock to write tracking record
- * 2) Does it need to pre-reserve a sequence number for the tracking record
- *
- * The rules to determine if ThreadCritical is needed:
- * 1. When nmt is in single-threaded bootstrapping mode, no lock is needed as VM
- * still in single thread mode.
- * 2. For all threads other than JavaThread, ThreadCritical is needed
- * to write to recorders to global recorder.
- * 3. For JavaThreads that are no longer visible by safepoint, also
- * need to take ThreadCritical and records are written to global
- * recorders, since these threads are NOT walked by Threads.do_thread().
- * 4. JavaThreads that are running in safepoint-safe states do not stop
- * for safepoints, ThreadCritical lock should be taken to write
- * memory records.
- * 5. JavaThreads that are running in VM state do not need any lock and
- * records are written to per-thread recorders.
- * 6. For a thread has yet to attach VM 'Thread', they need to take
- * ThreadCritical to write to global recorder.
- *
- * The memory operations that need pre-reserve sequence numbers:
- * The memory operations that "release" memory blocks and the
- * operations can fail, need to pre-reserve sequence number. They
- * are realloc, uncommit and release.
- *
- * The reason for pre-reserve sequence number, is to prevent race condition:
- * Thread 1 Thread 2
- * <release>
- * <allocate>
- * <write allocate record>
- * <write release record>
- * if Thread 2 happens to obtain the memory address Thread 1 just released,
- * then NMT can mistakenly report the memory is free.
- *
- * Noticeably, free() does not need pre-reserve sequence number, because the call
- * does not fail, so we can alway write "release" record before the memory is actaully
- * freed.
- *
- * For realloc, uncommit and release, following coding pattern should be used:
- *
- * MemTracker::Tracker tkr = MemTracker::get_realloc_tracker();
- * ptr = ::realloc(...);
- * if (ptr == NULL) {
- * tkr.record(...)
- * } else {
- * tkr.discard();
- * }
- *
- * MemTracker::Tracker tkr = MemTracker::get_virtual_memory_uncommit_tracker();
- * if (uncommit(...)) {
- * tkr.record(...);
- * } else {
- * tkr.discard();
- * }
- *
- * MemTracker::Tracker tkr = MemTracker::get_virtual_memory_release_tracker();
- * if (release(...)) {
- * tkr.record(...);
- * } else {
- * tkr.discard();
- * }
- *
- * Since pre-reserved sequence number is only good for the generation that it is acquired,
- * when there is pending Tracker that reserved sequence number, NMT sync-point has
- * to be skipped to prevent from advancing generation. This is done by inc and dec
- * MemTracker::_pending_op_count, when MemTracker::_pending_op_count > 0, NMT sync-point is skipped.
- * Not all pre-reservation of sequence number will increment pending op count. For JavaThreads
- * that honor safepoints, safepoint can not occur during the memory operations, so the
- * pre-reserved sequence number won't cross the generation boundry.
- */
-MemTracker::Tracker::Tracker(MemoryOperation op, Thread* thr) {
- _op = NoOp;
- _seq = 0;
- if (MemTracker::is_on()) {
- _java_thread = NULL;
- _op = op;
-
- // figure out if ThreadCritical lock is needed to write this operation
- // to MemTracker
- if (MemTracker::is_single_threaded_bootstrap()) {
- thr = NULL;
- } else if (thr == NULL) {
- // don't use Thread::current(), since it is possible that
- // the calling thread has yet to attach to VM 'Thread',
- // which will result assertion failure
- thr = ThreadLocalStorage::thread();
- }
-
- if (thr != NULL) {
- // Check NMT load
- MemTracker::check_NMT_load(thr);
-
- if (thr->is_Java_thread() && ((JavaThread*)thr)->is_safepoint_visible()) {
- _java_thread = (JavaThread*)thr;
- JavaThreadState state = _java_thread->thread_state();
- // JavaThreads that are safepoint safe, can run through safepoint,
- // so ThreadCritical is needed to ensure no threads at safepoint create
- // new records while the records are being gathered and the sequence number is changing
- _need_thread_critical_lock =
- SafepointSynchronize::safepoint_safe(_java_thread, state);
- } else {
- _need_thread_critical_lock = true;
- }
- } else {
- _need_thread_critical_lock
- = !MemTracker::is_single_threaded_bootstrap();
- }
-
- // see if we need to pre-reserve sequence number for this operation
- if (_op == Realloc || _op == Uncommit || _op == Release) {
- if (_need_thread_critical_lock) {
- ThreadCritical tc;
- MemTracker::inc_pending_op_count();
- _seq = SequenceGenerator::next();
- } else {
- // for the threads that honor safepoints, no safepoint can occur
- // during the lifespan of tracker, so we don't need to increase
- // pending op count.
- _seq = SequenceGenerator::next();
- }
- }
- }
+ out->print_cr("Native Memory Tracking Statistics:");
+ out->print_cr("Malloc allocation site table size: %d", MallocSiteTable::hash_buckets());
+ out->print_cr(" Tracking stack depth: %d", NMT_TrackingStackDepth);
+ NOT_PRODUCT(out->print_cr("Peak concurrent access: %d", MallocSiteTable::access_peak_count());)
+ out->print_cr(" ");
+ walker.report_statistics(out);
}
-void MemTracker::Tracker::discard() {
- if (MemTracker::is_on() && _seq != 0) {
- if (_need_thread_critical_lock) {
- ThreadCritical tc;
- MemTracker::dec_pending_op_count();
- }
- _seq = 0;
- }
-}
-
-
-void MemTracker::Tracker::record(address old_addr, address new_addr, size_t size,
- MEMFLAGS flags, address pc) {
- assert(old_addr != NULL && new_addr != NULL, "Sanity check");
- assert(_op == Realloc || _op == NoOp, "Wrong call");
- if (MemTracker::is_on() && NMT_CAN_TRACK(flags) && _op != NoOp && !MemTracker::shutdown_in_progress()) {
- assert(_seq > 0, "Need pre-reserve sequence number");
- if (_need_thread_critical_lock) {
- ThreadCritical tc;
- // free old address, use pre-reserved sequence number
- MemTracker::write_tracking_record(old_addr, MemPointerRecord::free_tag(),
- 0, _seq, pc, _java_thread);
- MemTracker::write_tracking_record(new_addr, flags | MemPointerRecord::malloc_tag(),
- size, SequenceGenerator::next(), pc, _java_thread);
- // decrement MemTracker pending_op_count
- MemTracker::dec_pending_op_count();
- } else {
- // free old address, use pre-reserved sequence number
- MemTracker::write_tracking_record(old_addr, MemPointerRecord::free_tag(),
- 0, _seq, pc, _java_thread);
- MemTracker::write_tracking_record(new_addr, flags | MemPointerRecord::malloc_tag(),
- size, SequenceGenerator::next(), pc, _java_thread);
- }
- _seq = 0;
- }
-}
-
-void MemTracker::Tracker::record(address addr, size_t size, MEMFLAGS flags, address pc) {
- // OOM already?
- if (addr == NULL) return;
-
- if (MemTracker::is_on() && NMT_CAN_TRACK(flags) && _op != NoOp && !MemTracker::shutdown_in_progress()) {
- bool pre_reserved_seq = (_seq != 0);
- address pc = CALLER_CALLER_PC;
- MEMFLAGS orig_flags = flags;
-
- // or the tagging flags
- switch(_op) {
- case Malloc:
- flags |= MemPointerRecord::malloc_tag();
- break;
- case Free:
- flags = MemPointerRecord::free_tag();
- break;
- case Realloc:
- fatal("Use the other Tracker::record()");
- break;
- case Reserve:
- case ReserveAndCommit:
- flags |= MemPointerRecord::virtual_memory_reserve_tag();
- break;
- case Commit:
- flags = MemPointerRecord::virtual_memory_commit_tag();
- break;
- case Type:
- flags |= MemPointerRecord::virtual_memory_type_tag();
- break;
- case Uncommit:
- assert(pre_reserved_seq, "Need pre-reserve sequence number");
- flags = MemPointerRecord::virtual_memory_uncommit_tag();
- break;
- case Release:
- assert(pre_reserved_seq, "Need pre-reserve sequence number");
- flags = MemPointerRecord::virtual_memory_release_tag();
- break;
- case ArenaSize:
- // a bit of hack here, add a small postive offset to arena
- // address for its size record, so the size record is sorted
- // right after arena record.
- flags = MemPointerRecord::arena_size_tag();
- addr += sizeof(void*);
- break;
- case StackRelease:
- flags = MemPointerRecord::virtual_memory_release_tag();
- break;
- default:
- ShouldNotReachHere();
- }
-
- // write memory tracking record
- if (_need_thread_critical_lock) {
- ThreadCritical tc;
- if (_seq == 0) _seq = SequenceGenerator::next();
- MemTracker::write_tracking_record(addr, flags, size, _seq, pc, _java_thread);
- if (_op == ReserveAndCommit) {
- MemTracker::write_tracking_record(addr, orig_flags | MemPointerRecord::virtual_memory_commit_tag(),
- size, SequenceGenerator::next(), pc, _java_thread);
- }
- if (pre_reserved_seq) MemTracker::dec_pending_op_count();
- } else {
- if (_seq == 0) _seq = SequenceGenerator::next();
- MemTracker::write_tracking_record(addr, flags, size, _seq, pc, _java_thread);
- if (_op == ReserveAndCommit) {
- MemTracker::write_tracking_record(addr, orig_flags | MemPointerRecord::virtual_memory_commit_tag(),
- size, SequenceGenerator::next(), pc, _java_thread);
- }
- }
- _seq = 0;
- }
-}
-
--- a/hotspot/src/share/vm/services/memTracker.hpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/services/memTracker.hpp Thu Aug 07 12:18:58 2014 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,566 +25,289 @@
#ifndef SHARE_VM_SERVICES_MEM_TRACKER_HPP
#define SHARE_VM_SERVICES_MEM_TRACKER_HPP
-#include "utilities/macros.hpp"
+#include "services/nmtCommon.hpp"
+
+class NativeCallStack;
+extern NativeCallStack emptyStack;
#if !INCLUDE_NMT
-#include "utilities/ostream.hpp"
+#define CURRENT_PC emptyStack
+#define CALLER_PC emptyStack
-class BaselineOutputer : public StackObj {
-
+class Tracker : public StackObj {
+ public:
+ Tracker() { }
+ void record(address addr, size_t size) { }
};
-class BaselineTTYOutputer : public BaselineOutputer {
- public:
- BaselineTTYOutputer(outputStream* st) { }
+class MemTracker : AllStatic {
+ public:
+ static inline NMT_TrackingLevel tracking_level() { return NMT_off; }
+ static inline void shutdown() { }
+ static inline void init() { }
+ static bool check_launcher_nmt_support(const char* value) { return true; }
+ static bool verify_nmt_option() { return true; }
+
+ static inline void* record_malloc(void* mem_base, size_t size, MEMFLAGS flag,
+ const NativeCallStack& stack, NMT_TrackingLevel level) { return mem_base; }
+ static inline size_t malloc_header_size(NMT_TrackingLevel level) { return 0; }
+ static inline size_t malloc_header_size(void* memblock) { return 0; }
+ static inline void* malloc_base(void* memblock) { return memblock; }
+ static inline void* record_free(void* memblock) { return memblock; }
+
+ static inline void record_new_arena(MEMFLAGS flag) { }
+ static inline void record_arena_free(MEMFLAGS flag) { }
+ static inline void record_arena_size_change(int diff, MEMFLAGS flag) { }
+ static inline void record_virtual_memory_reserve(void* addr, size_t size, const NativeCallStack& stack,
+ MEMFLAGS flag = mtNone) { }
+ static inline void record_virtual_memory_reserve_and_commit(void* addr, size_t size,
+ const NativeCallStack& stack, MEMFLAGS flag = mtNone) { }
+ static inline void record_virtual_memory_commit(void* addr, size_t size, const NativeCallStack& stack) { }
+ static inline Tracker get_virtual_memory_uncommit_tracker() { return Tracker(); }
+ static inline Tracker get_virtual_memory_release_tracker() { }
+ static inline void record_virtual_memory_type(void* addr, MEMFLAGS flag) { }
+ static inline void record_thread_stack(void* addr, size_t size) { }
+ static inline void release_thread_stack(void* addr, size_t size) { }
+
+ static void final_report(outputStream*) { }
+};
+
+#else
+
+#include "runtime/atomic.hpp"
+#include "runtime/threadCritical.hpp"
+#include "services/mallocTracker.hpp"
+#include "services/virtualMemoryTracker.hpp"
+
+extern volatile bool NMT_stack_walkable;
+
+#define CURRENT_PC ((MemTracker::tracking_level() == NMT_detail && NMT_stack_walkable) ? \
+ NativeCallStack(0, true) : emptyStack)
+#define CALLER_PC ((MemTracker::tracking_level() == NMT_detail && NMT_stack_walkable) ? \
+ NativeCallStack(1, true) : emptyStack)
+
+class MemBaseline;
+class Mutex;
+
+// Tracker is used for guarding 'release' semantics of virtual memory operation, to avoid
+// the other thread obtains and records the same region that is just 'released' by current
+// thread but before it can record the operation.
+class Tracker : public StackObj {
+ public:
+ enum TrackerType {
+ uncommit,
+ release
+ };
+
+ public:
+ Tracker(enum TrackerType type) : _type(type) { }
+ void record(address addr, size_t size);
+ private:
+ enum TrackerType _type;
+ // Virtual memory tracking data structures are protected by ThreadCritical lock.
+ ThreadCritical _tc;
};
class MemTracker : AllStatic {
- public:
- enum ShutdownReason {
- NMT_shutdown_none, // no shutdown requested
- NMT_shutdown_user, // user requested shutdown
- NMT_normal, // normal shutdown, process exit
- NMT_out_of_memory, // shutdown due to out of memory
- NMT_initialization, // shutdown due to initialization failure
- NMT_use_malloc_only, // can not combine NMT with UseMallocOnly flag
- NMT_error_reporting, // shutdown by vmError::report_and_die()
- NMT_out_of_generation, // running out of generation queue
- NMT_sequence_overflow // overflow the sequence number
- };
-
- class Tracker {
- public:
- void discard() { }
-
- void record(address addr, size_t size = 0, MEMFLAGS flags = mtNone, address pc = NULL) { }
- void record(address old_addr, address new_addr, size_t size,
- MEMFLAGS flags, address pc = NULL) { }
- };
-
- private:
- static Tracker _tkr;
-
-
- public:
- static inline void init_tracking_options(const char* option_line) { }
- static inline bool is_on() { return false; }
- static const char* reason() { return "Native memory tracking is not implemented"; }
- static inline bool can_walk_stack() { return false; }
-
- static inline void bootstrap_single_thread() { }
- static inline void bootstrap_multi_thread() { }
- static inline void start() { }
-
- static inline void record_malloc(address addr, size_t size, MEMFLAGS flags,
- address pc = 0, Thread* thread = NULL) { }
- static inline void record_free(address addr, MEMFLAGS flags, Thread* thread = NULL) { }
- static inline void record_arena_size(address addr, size_t size) { }
- static inline void record_virtual_memory_reserve(address addr, size_t size,
- MEMFLAGS flags, address pc = 0, Thread* thread = NULL) { }
- static inline void record_virtual_memory_reserve_and_commit(address addr, size_t size,
- MEMFLAGS flags, address pc = 0, Thread* thread = NULL) { }
- static inline void record_virtual_memory_commit(address addr, size_t size,
- address pc = 0, Thread* thread = NULL) { }
- static inline void record_virtual_memory_release(address addr, size_t size,
- Thread* thread = NULL) { }
- static inline void record_virtual_memory_type(address base, MEMFLAGS flags,
- Thread* thread = NULL) { }
- static inline Tracker get_realloc_tracker() { return _tkr; }
- static inline Tracker get_virtual_memory_uncommit_tracker() { return _tkr; }
- static inline Tracker get_virtual_memory_release_tracker() { return _tkr; }
- static inline bool baseline() { return false; }
- static inline bool has_baseline() { return false; }
-
- static inline void set_autoShutdown(bool value) { }
- static void shutdown(ShutdownReason reason) { }
- static inline bool shutdown_in_progress() { return false; }
- static bool print_memory_usage(BaselineOutputer& out, size_t unit,
- bool summary_only = true) { return false; }
- static bool compare_memory_usage(BaselineOutputer& out, size_t unit,
- bool summary_only = true) { return false; }
-
- static bool wbtest_wait_for_data_merge() { return false; }
-
- static inline void sync() { }
- static inline void thread_exiting(JavaThread* thread) { }
-};
-
-
-#else // !INCLUDE_NMT
-
-#include "memory/allocation.hpp"
-#include "runtime/globals.hpp"
-#include "runtime/mutex.hpp"
-#include "runtime/os.hpp"
-#include "runtime/thread.hpp"
-#include "services/memPtr.hpp"
-#include "services/memRecorder.hpp"
-#include "services/memSnapshot.hpp"
-#include "services/memTrackWorker.hpp"
-
-extern bool NMT_track_callsite;
-
-#ifndef MAX_UNSIGNED_LONG
-#define MAX_UNSIGNED_LONG (unsigned long)(-1)
-#endif
-
-#ifdef ASSERT
- #define DEBUG_CALLER_PC (NMT_track_callsite ? os::get_caller_pc(2) : 0)
-#else
- #define DEBUG_CALLER_PC 0
-#endif
-
-// The thread closure walks threads to collect per-thread
-// memory recorders at NMT sync point
-class SyncThreadRecorderClosure : public ThreadClosure {
- private:
- int _thread_count;
-
public:
- SyncThreadRecorderClosure() {
- _thread_count =0;
- }
-
- void do_thread(Thread* thread);
- int get_thread_count() const {
- return _thread_count;
- }
-};
-
-class BaselineOutputer;
-class MemSnapshot;
-class MemTrackWorker;
-class Thread;
-/*
- * MemTracker is the 'gate' class to native memory tracking runtime.
- */
-class MemTracker : AllStatic {
- friend class GenerationData;
- friend class MemTrackWorker;
- friend class MemSnapshot;
- friend class SyncThreadRecorderClosure;
-
- // NMT state
- enum NMTStates {
- NMT_uninited, // not yet initialized
- NMT_bootstrapping_single_thread, // bootstrapping, VM is in single thread mode
- NMT_bootstrapping_multi_thread, // bootstrapping, VM is about to enter multi-thread mode
- NMT_started, // NMT fully started
- NMT_shutdown_pending, // shutdown pending
- NMT_final_shutdown, // in final phase of shutdown
- NMT_shutdown // shutdown
- };
-
- public:
- class Tracker : public StackObj {
- friend class MemTracker;
- public:
- enum MemoryOperation {
- NoOp, // no op
- Malloc, // malloc
- Realloc, // realloc
- Free, // free
- Reserve, // virtual memory reserve
- Commit, // virtual memory commit
- ReserveAndCommit, // virtual memory reserve and commit
- StackAlloc = ReserveAndCommit, // allocate thread stack
- Type, // assign virtual memory type
- Uncommit, // virtual memory uncommit
- Release, // virtual memory release
- ArenaSize, // set arena size
- StackRelease // release thread stack
- };
-
-
- protected:
- Tracker(MemoryOperation op, Thread* thr = NULL);
-
- public:
- void discard();
-
- void record(address addr, size_t size = 0, MEMFLAGS flags = mtNone, address pc = NULL);
- void record(address old_addr, address new_addr, size_t size,
- MEMFLAGS flags, address pc = NULL);
-
- private:
- bool _need_thread_critical_lock;
- JavaThread* _java_thread;
- MemoryOperation _op; // memory operation
- jint _seq; // reserved sequence number
- };
-
-
- public:
- // native memory tracking level
- enum NMTLevel {
- NMT_off, // native memory tracking is off
- NMT_summary, // don't track callsite
- NMT_detail // track callsite also
- };
-
- enum ShutdownReason {
- NMT_shutdown_none, // no shutdown requested
- NMT_shutdown_user, // user requested shutdown
- NMT_normal, // normal shutdown, process exit
- NMT_out_of_memory, // shutdown due to out of memory
- NMT_initialization, // shutdown due to initialization failure
- NMT_use_malloc_only, // can not combine NMT with UseMallocOnly flag
- NMT_error_reporting, // shutdown by vmError::report_and_die()
- NMT_out_of_generation, // running out of generation queue
- NMT_sequence_overflow // overflow the sequence number
- };
-
- public:
- // initialize NMT tracking level from command line options, called
- // from VM command line parsing code
- static void init_tracking_options(const char* option_line);
-
- // if NMT is enabled to record memory activities
- static inline bool is_on() {
- return (_tracking_level >= NMT_summary &&
- _state >= NMT_bootstrapping_single_thread);
- }
-
- static inline enum NMTLevel tracking_level() {
+ static inline NMT_TrackingLevel tracking_level() {
+ if (_tracking_level == NMT_unknown) {
+ // No fencing is needed here, since JVM is in single-threaded
+ // mode.
+ _tracking_level = init_tracking_level();
+ _cmdline_tracking_level = _tracking_level;
+ }
return _tracking_level;
}
- // user readable reason for shutting down NMT
- static const char* reason() {
- switch(_reason) {
- case NMT_shutdown_none:
- return "Native memory tracking is not enabled";
- case NMT_shutdown_user:
- return "Native memory tracking has been shutdown by user";
- case NMT_normal:
- return "Native memory tracking has been shutdown due to process exiting";
- case NMT_out_of_memory:
- return "Native memory tracking has been shutdown due to out of native memory";
- case NMT_initialization:
- return "Native memory tracking failed to initialize";
- case NMT_error_reporting:
- return "Native memory tracking has been shutdown due to error reporting";
- case NMT_out_of_generation:
- return "Native memory tracking has been shutdown due to running out of generation buffer";
- case NMT_sequence_overflow:
- return "Native memory tracking has been shutdown due to overflow the sequence number";
- case NMT_use_malloc_only:
- return "Native memory tracking is not supported when UseMallocOnly is on";
- default:
- ShouldNotReachHere();
- return NULL;
- }
+ // A late initialization, for the stuff(s) can not be
+ // done in init_tracking_level(), which can NOT malloc
+ // any memory.
+ static void init();
+
+ // Shutdown native memory tracking
+ static void shutdown();
+
+ // Verify native memory tracking command line option.
+ // This check allows JVM to detect if compatible launcher
+ // is used.
+ // If an incompatible launcher is used, NMT may not be
+ // able to start, even it is enabled by command line option.
+ // A warning message should be given if it is encountered.
+ static bool check_launcher_nmt_support(const char* value);
+
+ // This method checks native memory tracking environment
+ // variable value passed by launcher.
+ // Launcher only obligated to pass native memory tracking
+ // option value, but not obligated to validate the value,
+ // and launcher has option to discard native memory tracking
+ // option from the command line once it sets up the environment
+ // variable, so NMT has to catch the bad value here.
+ static bool verify_nmt_option();
+
+ // Transition the tracking level to specified level
+ static bool transition_to(NMT_TrackingLevel level);
+
+ static inline void* record_malloc(void* mem_base, size_t size, MEMFLAGS flag,
+ const NativeCallStack& stack, NMT_TrackingLevel level) {
+ return MallocTracker::record_malloc(mem_base, size, flag, stack, level);
+ }
+
+ static inline size_t malloc_header_size(NMT_TrackingLevel level) {
+ return MallocTracker::malloc_header_size(level);
}
- // test if we can walk native stack
- static bool can_walk_stack() {
- // native stack is not walkable during bootstrapping on sparc
-#if defined(SPARC)
- return (_state == NMT_started);
-#else
- return (_state >= NMT_bootstrapping_single_thread && _state <= NMT_started);
-#endif
+ static size_t malloc_header_size(void* memblock) {
+ if (tracking_level() != NMT_off) {
+ return MallocTracker::get_header_size(memblock);
+ }
+ return 0;
+ }
+
+ // To malloc base address, which is the starting address
+ // of malloc tracking header if tracking is enabled.
+ // Otherwise, it returns the same address.
+ static void* malloc_base(void* memblock);
+
+ // Record malloc free and return malloc base address
+ static inline void* record_free(void* memblock) {
+ return MallocTracker::record_free(memblock);
}
- // if native memory tracking tracks callsite
- static inline bool track_callsite() { return _tracking_level == NMT_detail; }
+
+ // Record creation of an arena
+ static inline void record_new_arena(MEMFLAGS flag) {
+ if (tracking_level() < NMT_summary) return;
+ MallocTracker::record_new_arena(flag);
+ }
+
+ // Record destruction of an arena
+ static inline void record_arena_free(MEMFLAGS flag) {
+ if (tracking_level() < NMT_summary) return;
+ MallocTracker::record_arena_free(flag);
+ }
- // NMT automatically shuts itself down under extreme situation by default.
- // When the value is set to false, NMT will try its best to stay alive,
- // even it has to slow down VM.
- static inline void set_autoShutdown(bool value) {
- AutoShutdownNMT = value;
- if (AutoShutdownNMT && _slowdown_calling_thread) {
- _slowdown_calling_thread = false;
+ // Record arena size change. Arena size is the size of all arena
+ // chuncks that backing up the arena.
+ static inline void record_arena_size_change(int diff, MEMFLAGS flag) {
+ if (tracking_level() < NMT_summary) return;
+ MallocTracker::record_arena_size_change(diff, flag);
+ }
+
+ static inline void record_virtual_memory_reserve(void* addr, size_t size, const NativeCallStack& stack,
+ MEMFLAGS flag = mtNone) {
+ if (tracking_level() < NMT_summary) return;
+ if (addr != NULL) {
+ ThreadCritical tc;
+ // Recheck to avoid potential racing during NMT shutdown
+ if (tracking_level() < NMT_summary) return;
+ VirtualMemoryTracker::add_reserved_region((address)addr, size, stack, flag);
}
}
- // shutdown native memory tracking capability. Native memory tracking
- // can be shutdown by VM when it encounters low memory scenarios.
- // Memory tracker should gracefully shutdown itself, and preserve the
- // latest memory statistics for post morten diagnosis.
- static void shutdown(ShutdownReason reason);
-
- // if there is shutdown requested
- static inline bool shutdown_in_progress() {
- return (_state >= NMT_shutdown_pending);
- }
-
- // bootstrap native memory tracking, so it can start to collect raw data
- // before worker thread can start
-
- // the first phase of bootstrapping, when VM still in single-threaded mode
- static void bootstrap_single_thread();
- // the second phase of bootstrapping, VM is about or already in multi-threaded mode
- static void bootstrap_multi_thread();
-
-
- // start() has to be called when VM still in single thread mode, but after
- // command line option parsing is done.
- static void start();
-
- // record a 'malloc' call
- static inline void record_malloc(address addr, size_t size, MEMFLAGS flags,
- address pc = 0, Thread* thread = NULL) {
- Tracker tkr(Tracker::Malloc, thread);
- tkr.record(addr, size, flags, pc);
- }
- // record a 'free' call
- static inline void record_free(address addr, MEMFLAGS flags, Thread* thread = NULL) {
- Tracker tkr(Tracker::Free, thread);
- tkr.record(addr, 0, flags, DEBUG_CALLER_PC);
- }
-
- static inline void record_arena_size(address addr, size_t size) {
- Tracker tkr(Tracker::ArenaSize);
- tkr.record(addr, size);
- }
-
- // record a virtual memory 'reserve' call
- static inline void record_virtual_memory_reserve(address addr, size_t size,
- MEMFLAGS flags, address pc = 0, Thread* thread = NULL) {
- assert(size > 0, "Sanity check");
- Tracker tkr(Tracker::Reserve, thread);
- tkr.record(addr, size, flags, pc);
- }
-
- static inline void record_thread_stack(address addr, size_t size, Thread* thr,
- address pc = 0) {
- Tracker tkr(Tracker::StackAlloc, thr);
- tkr.record(addr, size, mtThreadStack, pc);
- }
-
- static inline void release_thread_stack(address addr, size_t size, Thread* thr) {
- Tracker tkr(Tracker::StackRelease, thr);
- tkr.record(addr, size, mtThreadStack, DEBUG_CALLER_PC);
- }
-
- // record a virtual memory 'commit' call
- static inline void record_virtual_memory_commit(address addr, size_t size,
- address pc, Thread* thread = NULL) {
- Tracker tkr(Tracker::Commit, thread);
- tkr.record(addr, size, mtNone, pc);
- }
-
- static inline void record_virtual_memory_reserve_and_commit(address addr, size_t size,
- MEMFLAGS flags, address pc, Thread* thread = NULL) {
- Tracker tkr(Tracker::ReserveAndCommit, thread);
- tkr.record(addr, size, flags, pc);
- }
-
- static inline void record_virtual_memory_release(address addr, size_t size,
- Thread* thread = NULL) {
- if (is_on()) {
- Tracker tkr(Tracker::Release, thread);
- tkr.record(addr, size);
+ static inline void record_virtual_memory_reserve_and_commit(void* addr, size_t size,
+ const NativeCallStack& stack, MEMFLAGS flag = mtNone) {
+ if (tracking_level() < NMT_summary) return;
+ if (addr != NULL) {
+ ThreadCritical tc;
+ if (tracking_level() < NMT_summary) return;
+ VirtualMemoryTracker::add_reserved_region((address)addr, size,
+ stack, flag, true);
}
}
- // record memory type on virtual memory base address
- static inline void record_virtual_memory_type(address base, MEMFLAGS flags,
- Thread* thread = NULL) {
- Tracker tkr(Tracker::Type);
- tkr.record(base, 0, flags);
- }
-
- // Get memory trackers for memory operations that can result race conditions.
- // The memory tracker has to be obtained before realloc, virtual memory uncommit
- // and virtual memory release, and call tracker.record() method if operation
- // succeeded, or tracker.discard() to abort the tracking.
- static inline Tracker get_realloc_tracker() {
- return Tracker(Tracker::Realloc);
+ static inline void record_virtual_memory_commit(void* addr, size_t size,
+ const NativeCallStack& stack) {
+ if (tracking_level() < NMT_summary) return;
+ if (addr != NULL) {
+ ThreadCritical tc;
+ if (tracking_level() < NMT_summary) return;
+ VirtualMemoryTracker::add_committed_region((address)addr, size, stack);
+ }
}
static inline Tracker get_virtual_memory_uncommit_tracker() {
- return Tracker(Tracker::Uncommit);
+ assert(tracking_level() >= NMT_summary, "Check by caller");
+ return Tracker(Tracker::uncommit);
}
static inline Tracker get_virtual_memory_release_tracker() {
- return Tracker(Tracker::Release);
- }
-
-
- // create memory baseline of current memory snapshot
- static bool baseline();
- // is there a memory baseline
- static bool has_baseline() {
- return _baseline.baselined();
+ assert(tracking_level() >= NMT_summary, "Check by caller");
+ return Tracker(Tracker::release);
}
- // print memory usage from current snapshot
- static bool print_memory_usage(BaselineOutputer& out, size_t unit,
- bool summary_only = true);
- // compare memory usage between current snapshot and baseline
- static bool compare_memory_usage(BaselineOutputer& out, size_t unit,
- bool summary_only = true);
-
- // the version for whitebox testing support, it ensures that all memory
- // activities before this method call, are reflected in the snapshot
- // database.
- static bool wbtest_wait_for_data_merge();
-
- // sync is called within global safepoint to synchronize nmt data
- static void sync();
-
- // called when a thread is about to exit
- static void thread_exiting(JavaThread* thread);
-
- // retrieve global snapshot
- static MemSnapshot* get_snapshot() {
- if (shutdown_in_progress()) {
- return NULL;
+ static inline void record_virtual_memory_type(void* addr, MEMFLAGS flag) {
+ if (tracking_level() < NMT_summary) return;
+ if (addr != NULL) {
+ ThreadCritical tc;
+ if (tracking_level() < NMT_summary) return;
+ VirtualMemoryTracker::set_reserved_region_type((address)addr, flag);
}
- return _snapshot;
}
- // print tracker stats
- NOT_PRODUCT(static void print_tracker_stats(outputStream* st);)
- NOT_PRODUCT(static void walk_stack(int toSkip, char* buf, int len);)
-
- private:
- // start native memory tracking worker thread
- static bool start_worker(MemSnapshot* snapshot);
-
- // called by worker thread to complete shutdown process
- static void final_shutdown();
-
- protected:
- // retrieve per-thread recorder of the specified thread.
- // if the recorder is full, it will be enqueued to overflow
- // queue, a new recorder is acquired from recorder pool or a
- // new instance is created.
- // when thread == NULL, it means global recorder
- static MemRecorder* get_thread_recorder(JavaThread* thread);
-
- // per-thread recorder pool
- static void release_thread_recorder(MemRecorder* rec);
- static void delete_all_pooled_recorders();
-
- // pending recorder queue. Recorders are queued to pending queue
- // when they are overflowed or collected at nmt sync point.
- static void enqueue_pending_recorder(MemRecorder* rec);
- static MemRecorder* get_pending_recorders();
- static void delete_all_pending_recorders();
-
- // write a memory tracking record in recorder
- static void write_tracking_record(address addr, MEMFLAGS type,
- size_t size, jint seq, address pc, JavaThread* thread);
-
- static bool is_single_threaded_bootstrap() {
- return _state == NMT_bootstrapping_single_thread;
+ static inline void record_thread_stack(void* addr, size_t size) {
+ if (tracking_level() < NMT_summary) return;
+ if (addr != NULL) {
+ // uses thread stack malloc slot for book keeping number of threads
+ MallocMemorySummary::record_malloc(0, mtThreadStack);
+ record_virtual_memory_reserve_and_commit(addr, size, CALLER_PC, mtThreadStack);
+ }
}
- static void check_NMT_load(Thread* thr) {
- assert(thr != NULL, "Sanity check");
- if (_slowdown_calling_thread && thr != _worker_thread) {
-#ifdef _WINDOWS
- // On Windows, os::NakedYield() does not work as well
- // as short sleep.
- os::naked_short_sleep(1);
-#else
- os::naked_yield();
-#endif
+ static inline void release_thread_stack(void* addr, size_t size) {
+ if (tracking_level() < NMT_summary) return;
+ if (addr != NULL) {
+ // uses thread stack malloc slot for book keeping number of threads
+ MallocMemorySummary::record_free(0, mtThreadStack);
+ ThreadCritical tc;
+ if (tracking_level() < NMT_summary) return;
+ VirtualMemoryTracker::remove_released_region((address)addr, size);
}
}
- static void inc_pending_op_count() {
- Atomic::inc(&_pending_op_count);
- }
+ // Query lock is used to synchronize the access to tracking data.
+ // So far, it is only used by JCmd query, but it may be used by
+ // other tools.
+ static inline Mutex* query_lock() { return _query_lock; }
- static void dec_pending_op_count() {
- Atomic::dec(&_pending_op_count);
- assert(_pending_op_count >= 0, "Sanity check");
+ // Make a final report and shutdown.
+ // This function generates summary report without creating snapshots,
+ // to avoid additional memory allocation. It uses native memory summary
+ // counters, and makes adjustment to them, once the adjustment is made,
+ // the counters are no longer accurate. As the result, this function
+ // should only be used for final reporting before shutting down.
+ static void final_report(outputStream*);
+
+ // Stored baseline
+ static inline MemBaseline& get_baseline() {
+ return _baseline;
}
-
- private:
- // retrieve a pooled memory record or create new one if there is not
- // one available
- static MemRecorder* get_new_or_pooled_instance();
- static void create_memory_record(address addr, MEMFLAGS type,
- size_t size, address pc, Thread* thread);
- static void create_record_in_recorder(address addr, MEMFLAGS type,
- size_t size, address pc, JavaThread* thread);
-
- static void set_current_processing_generation(unsigned long generation) {
- _worker_thread_idle = false;
- _processing_generation = generation;
+ static NMT_TrackingLevel cmdline_tracking_level() {
+ return _cmdline_tracking_level;
}
- static void report_worker_idle() {
- _worker_thread_idle = true;
- }
+ static void tuning_statistics(outputStream* out);
private:
- // global memory snapshot
- static MemSnapshot* _snapshot;
-
- // a memory baseline of snapshot
- static MemBaseline _baseline;
-
- // query lock
- static Mutex* _query_lock;
-
- // a thread can start to allocate memory before it is attached
- // to VM 'Thread', those memory activities are recorded here.
- // ThreadCritical is required to guard this global recorder.
- static MemRecorder* volatile _global_recorder;
-
- // main thread id
- debug_only(static intx _main_thread_tid;)
-
- // pending recorders to be merged
- static MemRecorder* volatile _merge_pending_queue;
-
- NOT_PRODUCT(static volatile jint _pending_recorder_count;)
-
- // pooled memory recorders
- static MemRecorder* volatile _pooled_recorders;
-
- // memory recorder pool management, uses following
- // counter to determine if a released memory recorder
- // should be pooled
+ static NMT_TrackingLevel init_tracking_level();
- // latest thread count
- static int _thread_count;
- // pooled recorder count
- static volatile jint _pooled_recorder_count;
-
-
- // worker thread to merge pending recorders into snapshot
- static MemTrackWorker* _worker_thread;
-
- // how many safepoints we skipped without entering sync point
- static int _sync_point_skip_count;
-
- // if the tracker is properly intialized
- static bool _is_tracker_ready;
- // tracking level (off, summary and detail)
- static enum NMTLevel _tracking_level;
-
- // current nmt state
- static volatile enum NMTStates _state;
- // the reason for shutting down nmt
- static enum ShutdownReason _reason;
- // the generation that NMT is processing
- static volatile unsigned long _processing_generation;
- // although NMT is still procesing current generation, but
- // there is not more recorder to process, set idle state
- static volatile bool _worker_thread_idle;
-
- // if NMT should slow down calling thread to allow
- // worker thread to catch up
- static volatile bool _slowdown_calling_thread;
-
- // pending memory op count.
- // Certain memory ops need to pre-reserve sequence number
- // before memory operation can happen to avoid race condition.
- // See MemTracker::Tracker for detail
- static volatile jint _pending_op_count;
+ private:
+ // Tracking level
+ static volatile NMT_TrackingLevel _tracking_level;
+ // If NMT option value passed by launcher through environment
+ // variable is valid
+ static bool _is_nmt_env_valid;
+ // command line tracking level
+ static NMT_TrackingLevel _cmdline_tracking_level;
+ // Stored baseline
+ static MemBaseline _baseline;
+ // Query lock
+ static Mutex* _query_lock;
};
-#endif // !INCLUDE_NMT
+#endif // INCLUDE_NMT
#endif // SHARE_VM_SERVICES_MEM_TRACKER_HPP
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/services/nmtCommon.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+#include "precompiled.hpp"
+#include "services/nmtCommon.hpp"
+
+const char* NMTUtil::_memory_type_names[] = {
+ "Java Heap",
+ "Class",
+ "Thread",
+ "Thread Stack",
+ "Code",
+ "GC",
+ "Compiler",
+ "Internal",
+ "Other",
+ "Symbol",
+ "Native Memory Tracking",
+ "Shared class space",
+ "Arena Chunk",
+ "Test",
+ "Tracing",
+ "Unknown"
+};
+
+
+const char* NMTUtil::scale_name(size_t scale) {
+ switch(scale) {
+ case K: return "KB";
+ case M: return "MB";
+ case G: return "GB";
+ }
+ ShouldNotReachHere();
+ return NULL;
+}
+
+size_t NMTUtil::scale_from_name(const char* scale) {
+ assert(scale != NULL, "Null pointer check");
+ if (strncmp(scale, "KB", 2) == 0 ||
+ strncmp(scale, "kb", 2) == 0) {
+ return K;
+ } else if (strncmp(scale, "MB", 2) == 0 ||
+ strncmp(scale, "mb", 2) == 0) {
+ return M;
+ } else if (strncmp(scale, "GB", 2) == 0 ||
+ strncmp(scale, "gb", 2) == 0) {
+ return G;
+ } else {
+ return 0; // Invalid value
+ }
+ return K;
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/services/nmtCommon.hpp Thu Aug 07 12:18:58 2014 -0700
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#ifndef SHARE_VM_SERVICES_NMT_COMMON_HPP
+#define SHARE_VM_SERVICES_NMT_COMMON_HPP
+
+#include "memory/allocation.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+#define CALC_OBJ_SIZE_IN_TYPE(obj, type) (align_size_up_(sizeof(obj), sizeof(type))/sizeof(type))
+
+// Data type for memory counters
+#ifdef _LP64
+ typedef jlong MemoryCounterType;
+#else
+ typedef jint MemoryCounterType;
+#endif
+
+// Native memory tracking level
+enum NMT_TrackingLevel {
+ NMT_unknown = 0xFF,
+ NMT_off = 0x00,
+ NMT_minimal = 0x01,
+ NMT_summary = 0x02,
+ NMT_detail = 0x03
+};
+
+// Number of stack frames to capture. This is a
+// build time decision.
+const int NMT_TrackingStackDepth = 4;
+
+class NativeCallStack;
+extern NativeCallStack emptyStack;
+
+// A few common utilities for native memory tracking
+class NMTUtil : AllStatic {
+ public:
+ // Map memory type to index
+ static inline int flag_to_index(MEMFLAGS flag) {
+ return (flag & 0xff);
+ }
+
+ // Map memory type to human readable name
+ static const char* flag_to_name(MEMFLAGS flag) {
+ return _memory_type_names[flag_to_index(flag)];
+ }
+
+ // Map an index to memory type
+ static MEMFLAGS index_to_flag(int index) {
+ return (MEMFLAGS)index;
+ }
+
+ // Memory size scale
+ static const char* scale_name(size_t scale);
+ static size_t scale_from_name(const char* scale);
+
+ // Translate memory size in specified scale
+ static size_t amount_in_scale(size_t amount, size_t scale) {
+ return (amount + scale / 2) / scale;
+ }
+ private:
+ static const char* _memory_type_names[mt_number_of_types];
+};
+
+
+#endif
--- a/hotspot/src/share/vm/services/nmtDCmd.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/services/nmtDCmd.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -22,6 +22,8 @@
*
*/
#include "precompiled.hpp"
+
+#include "runtime/mutexLocker.hpp"
#include "services/nmtDCmd.hpp"
#include "services/memReporter.hpp"
#include "services/memTracker.hpp"
@@ -49,13 +51,8 @@
_shutdown("shutdown", "request runtime to shutdown itself and free the " \
"memory used by runtime.",
"BOOLEAN", false, "false"),
- _auto_shutdown("autoShutdown", "automatically shutdown itself under " \
- "stress situation",
- "BOOLEAN", true, "true"),
-#ifndef PRODUCT
- _debug("debug", "print tracker statistics. Debug only, not thread safe", \
+ _statistics("statistics", "print tracker statistics for tuning purpose.", \
"BOOLEAN", false, "false"),
-#endif
_scale("scale", "Memory usage in which scale, KB, MB or GB",
"STRING", false, "KB") {
_dcmdparser.add_dcmd_option(&_summary);
@@ -64,25 +61,30 @@
_dcmdparser.add_dcmd_option(&_summary_diff);
_dcmdparser.add_dcmd_option(&_detail_diff);
_dcmdparser.add_dcmd_option(&_shutdown);
- _dcmdparser.add_dcmd_option(&_auto_shutdown);
-#ifndef PRODUCT
- _dcmdparser.add_dcmd_option(&_debug);
-#endif
+ _dcmdparser.add_dcmd_option(&_statistics);
_dcmdparser.add_dcmd_option(&_scale);
}
+
+size_t NMTDCmd::get_scale(const char* scale) const {
+ if (scale == NULL) return 0;
+ return NMTUtil::scale_from_name(scale);
+}
+
void NMTDCmd::execute(DCmdSource source, TRAPS) {
+ // Check NMT state
+ // native memory tracking has to be on
+ if (MemTracker::tracking_level() == NMT_off) {
+ output()->print_cr("Native memory tracking is not enabled");
+ return;
+ } else if (MemTracker::tracking_level() == NMT_minimal) {
+ output()->print_cr("Native memory tracking has been shutdown");
+ return;
+ }
+
const char* scale_value = _scale.value();
- size_t scale_unit;
- if (strcmp(scale_value, "KB") == 0 || strcmp(scale_value, "kb") == 0) {
- scale_unit = K;
- } else if (strcmp(scale_value, "MB") == 0 ||
- strcmp(scale_value, "mb") == 0) {
- scale_unit = M;
- } else if (strcmp(scale_value, "GB") == 0 ||
- strcmp(scale_value, "gb") == 0) {
- scale_unit = G;
- } else {
+ size_t scale_unit = get_scale(scale_value);
+ if (scale_unit == 0) {
output()->print_cr("Incorrect scale value: %s", scale_value);
return;
}
@@ -94,19 +96,11 @@
if (_summary_diff.is_set() && _summary_diff.value()) { ++nopt; }
if (_detail_diff.is_set() && _detail_diff.value()) { ++nopt; }
if (_shutdown.is_set() && _shutdown.value()) { ++nopt; }
- if (_auto_shutdown.is_set()) { ++nopt; }
-
-#ifndef PRODUCT
- if (_debug.is_set() && _debug.value()) { ++nopt; }
-#endif
+ if (_statistics.is_set() && _statistics.value()) { ++nopt; }
if (nopt > 1) {
output()->print_cr("At most one of the following option can be specified: " \
- "summary, detail, baseline, summary.diff, detail.diff, shutdown"
-#ifndef PRODUCT
- ", debug"
-#endif
- );
+ "summary, detail, baseline, summary.diff, detail.diff, shutdown");
return;
} else if (nopt == 0) {
if (_summary.is_set()) {
@@ -117,53 +111,47 @@
}
}
-#ifndef PRODUCT
- if (_debug.value()) {
- output()->print_cr("debug command is NOT thread-safe, may cause crash");
- MemTracker::print_tracker_stats(output());
- return;
- }
-#endif
-
- // native memory tracking has to be on
- if (!MemTracker::is_on() || MemTracker::shutdown_in_progress()) {
- // if it is not on, what's the reason?
- output()->print_cr("%s", MemTracker::reason());
- return;
- }
+ // Serialize NMT query
+ MutexLocker locker(MemTracker::query_lock());
if (_summary.value()) {
- BaselineTTYOutputer outputer(output());
- MemTracker::print_memory_usage(outputer, scale_unit, true);
+ report(true, scale_unit);
} else if (_detail.value()) {
- BaselineTTYOutputer outputer(output());
- MemTracker::print_memory_usage(outputer, scale_unit, false);
+ if (!check_detail_tracking_level(output())) {
+ return;
+ }
+ report(false, scale_unit);
} else if (_baseline.value()) {
- if (MemTracker::baseline()) {
- output()->print_cr("Successfully baselined.");
+ MemBaseline& baseline = MemTracker::get_baseline();
+ if (!baseline.baseline(MemTracker::tracking_level() != NMT_detail)) {
+ output()->print_cr("Baseline failed");
} else {
- output()->print_cr("Baseline failed.");
+ output()->print_cr("Baseline succeeded");
}
} else if (_summary_diff.value()) {
- if (MemTracker::has_baseline()) {
- BaselineTTYOutputer outputer(output());
- MemTracker::compare_memory_usage(outputer, scale_unit, true);
+ MemBaseline& baseline = MemTracker::get_baseline();
+ if (baseline.baseline_type() >= MemBaseline::Summary_baselined) {
+ report_diff(true, scale_unit);
} else {
- output()->print_cr("No baseline to compare, run 'baseline' command first");
+ output()->print_cr("No baseline for comparison");
}
} else if (_detail_diff.value()) {
- if (MemTracker::has_baseline()) {
- BaselineTTYOutputer outputer(output());
- MemTracker::compare_memory_usage(outputer, scale_unit, false);
+ if (!check_detail_tracking_level(output())) {
+ return;
+ }
+ MemBaseline& baseline = MemTracker::get_baseline();
+ if (baseline.baseline_type() == MemBaseline::Detail_baselined) {
+ report_diff(false, scale_unit);
} else {
- output()->print_cr("No baseline to compare to, run 'baseline' command first");
+ output()->print_cr("No detail baseline for comparison");
}
} else if (_shutdown.value()) {
- MemTracker::shutdown(MemTracker::NMT_shutdown_user);
- output()->print_cr("Shutdown is in progress, it will take a few moments to " \
- "completely shutdown");
- } else if (_auto_shutdown.is_set()) {
- MemTracker::set_autoShutdown(_auto_shutdown.value());
+ MemTracker::shutdown();
+ output()->print_cr("Native memory tracking has been turned off");
+ } else if (_statistics.value()) {
+ if (check_detail_tracking_level(output())) {
+ MemTracker::tuning_statistics(output());
+ }
} else {
ShouldNotReachHere();
output()->print_cr("Unknown command");
@@ -181,3 +169,46 @@
}
}
+void NMTDCmd::report(bool summaryOnly, size_t scale_unit) {
+ MemBaseline baseline;
+ if (baseline.baseline(summaryOnly)) {
+ if (summaryOnly) {
+ MemSummaryReporter rpt(baseline, output(), scale_unit);
+ rpt.report();
+ } else {
+ MemDetailReporter rpt(baseline, output(), scale_unit);
+ rpt.report();
+ }
+ }
+}
+
+void NMTDCmd::report_diff(bool summaryOnly, size_t scale_unit) {
+ MemBaseline& early_baseline = MemTracker::get_baseline();
+ assert(early_baseline.baseline_type() != MemBaseline::Not_baselined,
+ "Not yet baselined");
+ assert(summaryOnly || early_baseline.baseline_type() == MemBaseline::Detail_baselined,
+ "Not a detail baseline");
+
+ MemBaseline baseline;
+ if (baseline.baseline(summaryOnly)) {
+ if (summaryOnly) {
+ MemSummaryDiffReporter rpt(early_baseline, baseline, output(), scale_unit);
+ rpt.report_diff();
+ } else {
+ MemDetailDiffReporter rpt(early_baseline, baseline, output(), scale_unit);
+ rpt.report_diff();
+ }
+ }
+}
+
+bool NMTDCmd::check_detail_tracking_level(outputStream* out) {
+ if (MemTracker::tracking_level() == NMT_detail) {
+ return true;
+ } else if (MemTracker::cmdline_tracking_level() == NMT_detail) {
+ out->print_cr("Tracking level has been downgraded due to lack of resources");
+ return false;
+ } else {
+ out->print_cr("Detail tracking is not enabled");
+ return false;
+ }
+}
--- a/hotspot/src/share/vm/services/nmtDCmd.hpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/services/nmtDCmd.hpp Thu Aug 07 12:18:58 2014 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,8 +25,12 @@
#ifndef SHARE_VM_SERVICES_NMT_DCMD_HPP
#define SHARE_VM_SERVICES_NMT_DCMD_HPP
+#if INCLUDE_NMT
+
#include "services/diagnosticArgument.hpp"
#include "services/diagnosticFramework.hpp"
+#include "services/memBaseline.hpp"
+#include "services/mallocTracker.hpp"
/**
* Native memory tracking DCmd implementation
@@ -39,10 +43,7 @@
DCmdArgument<bool> _summary_diff;
DCmdArgument<bool> _detail_diff;
DCmdArgument<bool> _shutdown;
- DCmdArgument<bool> _auto_shutdown;
-#ifndef PRODUCT
- DCmdArgument<bool> _debug;
-#endif
+ DCmdArgument<bool> _statistics;
DCmdArgument<char*> _scale;
public:
@@ -61,6 +62,17 @@
}
static int num_arguments();
virtual void execute(DCmdSource source, TRAPS);
+
+ private:
+ void report(bool summaryOnly, size_t scale);
+ void report_diff(bool summaryOnly, size_t scale);
+
+ size_t get_scale(const char* scale) const;
+
+ // check if NMT running at detail tracking level
+ bool check_detail_tracking_level(outputStream* out);
};
+#endif // INCLUDE_NMT
+
#endif // SHARE_VM_SERVICES_NMT_DCMD_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/services/virtualMemoryTracker.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -0,0 +1,448 @@
+/*
+ * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+#include "precompiled.hpp"
+
+#include "runtime/threadCritical.hpp"
+#include "services/virtualMemoryTracker.hpp"
+
+size_t VirtualMemorySummary::_snapshot[CALC_OBJ_SIZE_IN_TYPE(VirtualMemorySnapshot, size_t)];
+
+void VirtualMemorySummary::initialize() {
+ assert(sizeof(_snapshot) >= sizeof(VirtualMemorySnapshot), "Sanity Check");
+ // Use placement operator new to initialize static data area.
+ ::new ((void*)_snapshot) VirtualMemorySnapshot();
+}
+
+SortedLinkedList<ReservedMemoryRegion, compare_reserved_region_base> VirtualMemoryTracker::_reserved_regions;
+
+int compare_committed_region(const CommittedMemoryRegion& r1, const CommittedMemoryRegion& r2) {
+ return r1.compare(r2);
+}
+
+int compare_reserved_region_base(const ReservedMemoryRegion& r1, const ReservedMemoryRegion& r2) {
+ return r1.compare(r2);
+}
+
+bool ReservedMemoryRegion::add_committed_region(address addr, size_t size, const NativeCallStack& stack) {
+ assert(addr != NULL, "Invalid address");
+ assert(size > 0, "Invalid size");
+ assert(contain_region(addr, size), "Not contain this region");
+
+ if (all_committed()) return true;
+
+ CommittedMemoryRegion committed_rgn(addr, size, stack);
+ LinkedListNode<CommittedMemoryRegion>* node = _committed_regions.find_node(committed_rgn);
+ if (node != NULL) {
+ CommittedMemoryRegion* rgn = node->data();
+ if (rgn->same_region(addr, size)) {
+ return true;
+ }
+
+ if (rgn->adjacent_to(addr, size)) {
+ // check if the next region covers this committed region,
+ // the regions may not be merged due to different call stacks
+ LinkedListNode<CommittedMemoryRegion>* next =
+ node->next();
+ if (next != NULL && next->data()->contain_region(addr, size)) {
+ if (next->data()->same_region(addr, size)) {
+ next->data()->set_call_stack(stack);
+ }
+ return true;
+ }
+ if (rgn->call_stack()->equals(stack)) {
+ VirtualMemorySummary::record_uncommitted_memory(rgn->size(), flag());
+ // the two adjacent regions have the same call stack, merge them
+ rgn->expand_region(addr, size);
+ VirtualMemorySummary::record_committed_memory(rgn->size(), flag());
+ return true;
+ }
+ VirtualMemorySummary::record_committed_memory(size, flag());
+ if (rgn->base() > addr) {
+ return _committed_regions.insert_before(committed_rgn, node) != NULL;
+ } else {
+ return _committed_regions.insert_after(committed_rgn, node) != NULL;
+ }
+ }
+ assert(rgn->contain_region(addr, size), "Must cover this region");
+ return true;
+ } else {
+ // New committed region
+ VirtualMemorySummary::record_committed_memory(size, flag());
+ return add_committed_region(committed_rgn);
+ }
+}
+
+void ReservedMemoryRegion::set_all_committed(bool b) {
+ if (all_committed() != b) {
+ _all_committed = b;
+ if (b) {
+ VirtualMemorySummary::record_committed_memory(size(), flag());
+ }
+ }
+}
+
+bool ReservedMemoryRegion::remove_uncommitted_region(LinkedListNode<CommittedMemoryRegion>* node,
+ address addr, size_t size) {
+ assert(addr != NULL, "Invalid address");
+ assert(size > 0, "Invalid size");
+
+ CommittedMemoryRegion* rgn = node->data();
+ assert(rgn->contain_region(addr, size), "Has to be contained");
+ assert(!rgn->same_region(addr, size), "Can not be the same region");
+
+ if (rgn->base() == addr ||
+ rgn->end() == addr + size) {
+ rgn->exclude_region(addr, size);
+ return true;
+ } else {
+ // split this region
+ address top =rgn->end();
+ // use this region for lower part
+ size_t exclude_size = rgn->end() - addr;
+ rgn->exclude_region(addr, exclude_size);
+
+ // higher part
+ address high_base = addr + size;
+ size_t high_size = top - high_base;
+
+ CommittedMemoryRegion high_rgn(high_base, high_size, *rgn->call_stack());
+ LinkedListNode<CommittedMemoryRegion>* high_node = _committed_regions.add(high_rgn);
+ assert(high_node == NULL || node->next() == high_node, "Should be right after");
+ return (high_node != NULL);
+ }
+
+ return false;
+}
+
+bool ReservedMemoryRegion::remove_uncommitted_region(address addr, size_t sz) {
+ // uncommit stack guard pages
+ if (flag() == mtThreadStack && !same_region(addr, sz)) {
+ return true;
+ }
+
+ assert(addr != NULL, "Invalid address");
+ assert(sz > 0, "Invalid size");
+
+ if (all_committed()) {
+ assert(_committed_regions.is_empty(), "Sanity check");
+ assert(contain_region(addr, sz), "Reserved region does not contain this region");
+ set_all_committed(false);
+ VirtualMemorySummary::record_uncommitted_memory(sz, flag());
+ if (same_region(addr, sz)) {
+ return true;
+ } else {
+ CommittedMemoryRegion rgn(base(), size(), *call_stack());
+ if (rgn.base() == addr || rgn.end() == (addr + sz)) {
+ rgn.exclude_region(addr, sz);
+ return add_committed_region(rgn);
+ } else {
+ // split this region
+ // top of the whole region
+ address top =rgn.end();
+ // use this region for lower part
+ size_t exclude_size = rgn.end() - addr;
+ rgn.exclude_region(addr, exclude_size);
+ if (add_committed_region(rgn)) {
+ // higher part
+ address high_base = addr + sz;
+ size_t high_size = top - high_base;
+ CommittedMemoryRegion high_rgn(high_base, high_size, emptyStack);
+ return add_committed_region(high_rgn);
+ } else {
+ return false;
+ }
+ }
+ }
+ } else {
+ // we have to walk whole list to remove the committed regions in
+ // specified range
+ LinkedListNode<CommittedMemoryRegion>* head =
+ _committed_regions.head();
+ LinkedListNode<CommittedMemoryRegion>* prev = NULL;
+ VirtualMemoryRegion uncommitted_rgn(addr, sz);
+
+ while (head != NULL && !uncommitted_rgn.is_empty()) {
+ CommittedMemoryRegion* crgn = head->data();
+ // this committed region overlaps to region to uncommit
+ if (crgn->overlap_region(uncommitted_rgn.base(), uncommitted_rgn.size())) {
+ if (crgn->same_region(uncommitted_rgn.base(), uncommitted_rgn.size())) {
+ // find matched region, remove the node will do
+ VirtualMemorySummary::record_uncommitted_memory(uncommitted_rgn.size(), flag());
+ _committed_regions.remove_after(prev);
+ return true;
+ } else if (crgn->contain_region(uncommitted_rgn.base(), uncommitted_rgn.size())) {
+ // this committed region contains whole uncommitted region
+ VirtualMemorySummary::record_uncommitted_memory(uncommitted_rgn.size(), flag());
+ return remove_uncommitted_region(head, uncommitted_rgn.base(), uncommitted_rgn.size());
+ } else if (uncommitted_rgn.contain_region(crgn->base(), crgn->size())) {
+ // this committed region has been uncommitted
+ size_t exclude_size = crgn->end() - uncommitted_rgn.base();
+ uncommitted_rgn.exclude_region(uncommitted_rgn.base(), exclude_size);
+ VirtualMemorySummary::record_uncommitted_memory(crgn->size(), flag());
+ LinkedListNode<CommittedMemoryRegion>* tmp = head;
+ head = head->next();
+ _committed_regions.remove_after(prev);
+ continue;
+ } else if (crgn->contain_address(uncommitted_rgn.base())) {
+ size_t toUncommitted = crgn->end() - uncommitted_rgn.base();
+ crgn->exclude_region(uncommitted_rgn.base(), toUncommitted);
+ uncommitted_rgn.exclude_region(uncommitted_rgn.base(), toUncommitted);
+ VirtualMemorySummary::record_uncommitted_memory(toUncommitted, flag());
+ } else if (uncommitted_rgn.contain_address(crgn->base())) {
+ size_t toUncommitted = uncommitted_rgn.end() - crgn->base();
+ crgn->exclude_region(crgn->base(), toUncommitted);
+ uncommitted_rgn.exclude_region(uncommitted_rgn.end() - toUncommitted,
+ toUncommitted);
+ VirtualMemorySummary::record_uncommitted_memory(toUncommitted, flag());
+ }
+ }
+ prev = head;
+ head = head->next();
+ }
+ }
+
+ return true;
+}
+
+void ReservedMemoryRegion::move_committed_regions(address addr, ReservedMemoryRegion& rgn) {
+ assert(addr != NULL, "Invalid address");
+
+ // split committed regions
+ LinkedListNode<CommittedMemoryRegion>* head =
+ _committed_regions.head();
+ LinkedListNode<CommittedMemoryRegion>* prev = NULL;
+
+ while (head != NULL) {
+ if (head->data()->base() >= addr) {
+ break;
+ }
+ prev = head;
+ head = head->next();
+ }
+
+ if (head != NULL) {
+ if (prev != NULL) {
+ prev->set_next(head->next());
+ } else {
+ _committed_regions.set_head(NULL);
+ }
+ }
+
+ rgn._committed_regions.set_head(head);
+}
+
+size_t ReservedMemoryRegion::committed_size() const {
+ if (all_committed()) {
+ return size();
+ } else {
+ size_t committed = 0;
+ LinkedListNode<CommittedMemoryRegion>* head =
+ _committed_regions.head();
+ while (head != NULL) {
+ committed += head->data()->size();
+ head = head->next();
+ }
+ return committed;
+ }
+}
+
+void ReservedMemoryRegion::set_flag(MEMFLAGS f) {
+ assert((flag() == mtNone || flag() == f), "Overwrite memory type");
+ if (flag() != f) {
+ VirtualMemorySummary::move_reserved_memory(flag(), f, size());
+ VirtualMemorySummary::move_committed_memory(flag(), f, committed_size());
+ _flag = f;
+ }
+}
+
+bool VirtualMemoryTracker::initialize(NMT_TrackingLevel level) {
+ if (level >= NMT_summary) {
+ VirtualMemorySummary::initialize();
+ }
+ return true;
+}
+
+bool VirtualMemoryTracker::add_reserved_region(address base_addr, size_t size,
+ const NativeCallStack& stack, MEMFLAGS flag, bool all_committed) {
+ assert(base_addr != NULL, "Invalid address");
+ assert(size > 0, "Invalid size");
+
+ ReservedMemoryRegion rgn(base_addr, size, stack, flag);
+ ReservedMemoryRegion* reserved_rgn = _reserved_regions.find(rgn);
+ LinkedListNode<ReservedMemoryRegion>* node;
+ if (reserved_rgn == NULL) {
+ VirtualMemorySummary::record_reserved_memory(size, flag);
+ node = _reserved_regions.add(rgn);
+ if (node != NULL) {
+ node->data()->set_all_committed(all_committed);
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ if (reserved_rgn->same_region(base_addr, size)) {
+ reserved_rgn->set_call_stack(stack);
+ reserved_rgn->set_flag(flag);
+ return true;
+ } else if (reserved_rgn->adjacent_to(base_addr, size)) {
+ VirtualMemorySummary::record_reserved_memory(size, flag);
+ reserved_rgn->expand_region(base_addr, size);
+ reserved_rgn->set_call_stack(stack);
+ return true;
+ } else {
+ // Overlapped reservation.
+ // It can happen when the regions are thread stacks, as JNI
+ // thread does not detach from VM before exits, and leads to
+ // leak JavaThread object
+ if (reserved_rgn->flag() == mtThreadStack) {
+ guarantee(!CheckJNICalls, "Attached JNI thread exited without being detached");
+ // Overwrite with new region
+
+ // Release old region
+ VirtualMemorySummary::record_uncommitted_memory(reserved_rgn->committed_size(), reserved_rgn->flag());
+ VirtualMemorySummary::record_released_memory(reserved_rgn->size(), reserved_rgn->flag());
+
+ // Add new region
+ VirtualMemorySummary::record_reserved_memory(rgn.size(), flag);
+
+ *reserved_rgn = rgn;
+ return true;
+ } else {
+ ShouldNotReachHere();
+ return false;
+ }
+ }
+ }
+}
+
+void VirtualMemoryTracker::set_reserved_region_type(address addr, MEMFLAGS flag) {
+ assert(addr != NULL, "Invalid address");
+
+ ReservedMemoryRegion rgn(addr, 1);
+ ReservedMemoryRegion* reserved_rgn = _reserved_regions.find(rgn);
+ if (reserved_rgn != NULL) {
+ assert(reserved_rgn->contain_address(addr), "Containment");
+ if (reserved_rgn->flag() != flag) {
+ assert(reserved_rgn->flag() == mtNone, "Overwrite memory type");
+ reserved_rgn->set_flag(flag);
+ }
+ }
+}
+
+bool VirtualMemoryTracker::add_committed_region(address addr, size_t size,
+ const NativeCallStack& stack) {
+ assert(addr != NULL, "Invalid address");
+ assert(size > 0, "Invalid size");
+ ReservedMemoryRegion rgn(addr, size);
+ ReservedMemoryRegion* reserved_rgn = _reserved_regions.find(rgn);
+
+ assert(reserved_rgn != NULL, "No reserved region");
+ assert(reserved_rgn->contain_region(addr, size), "Not completely contained");
+ return reserved_rgn->add_committed_region(addr, size, stack);
+}
+
+bool VirtualMemoryTracker::remove_uncommitted_region(address addr, size_t size) {
+ assert(addr != NULL, "Invalid address");
+ assert(size > 0, "Invalid size");
+ ReservedMemoryRegion rgn(addr, size);
+ ReservedMemoryRegion* reserved_rgn = _reserved_regions.find(rgn);
+ assert(reserved_rgn != NULL, "No reserved region");
+ assert(reserved_rgn->contain_region(addr, size), "Not completely contained");
+ return reserved_rgn->remove_uncommitted_region(addr, size);
+}
+
+bool VirtualMemoryTracker::remove_released_region(address addr, size_t size) {
+ assert(addr != NULL, "Invalid address");
+ assert(size > 0, "Invalid size");
+
+ ReservedMemoryRegion rgn(addr, size);
+ ReservedMemoryRegion* reserved_rgn = _reserved_regions.find(rgn);
+
+ assert(reserved_rgn != NULL, "No reserved region");
+
+ // uncommit regions within the released region
+ if (!reserved_rgn->remove_uncommitted_region(addr, size)) {
+ return false;
+ }
+
+
+ VirtualMemorySummary::record_released_memory(size, reserved_rgn->flag());
+
+ if (reserved_rgn->same_region(addr, size)) {
+ return _reserved_regions.remove(rgn);
+ } else {
+ assert(reserved_rgn->contain_region(addr, size), "Not completely contained");
+ if (reserved_rgn->base() == addr ||
+ reserved_rgn->end() == addr + size) {
+ reserved_rgn->exclude_region(addr, size);
+ return true;
+ } else {
+ address top = reserved_rgn->end();
+ address high_base = addr + size;
+ ReservedMemoryRegion high_rgn(high_base, top - high_base,
+ *reserved_rgn->call_stack(), reserved_rgn->flag());
+
+ // use original region for lower region
+ reserved_rgn->exclude_region(addr, top - addr);
+ LinkedListNode<ReservedMemoryRegion>* new_rgn = _reserved_regions.add(high_rgn);
+ if (new_rgn == NULL) {
+ return false;
+ } else {
+ reserved_rgn->move_committed_regions(addr, *new_rgn->data());
+ return true;
+ }
+ }
+ }
+}
+
+
+bool VirtualMemoryTracker::walk_virtual_memory(VirtualMemoryWalker* walker) {
+ ThreadCritical tc;
+ LinkedListNode<ReservedMemoryRegion>* head = _reserved_regions.head();
+ while (head != NULL) {
+ const ReservedMemoryRegion* rgn = head->peek();
+ if (!walker->do_allocation_site(rgn)) {
+ return false;
+ }
+ head = head->next();
+ }
+ return true;
+}
+
+// Transition virtual memory tracking level.
+bool VirtualMemoryTracker::transition(NMT_TrackingLevel from, NMT_TrackingLevel to) {
+ if (from == NMT_minimal) {
+ assert(to == NMT_summary || to == NMT_detail, "Just check");
+ VirtualMemorySummary::reset();
+ } else if (to == NMT_minimal) {
+ assert(from == NMT_summary || from == NMT_detail, "Just check");
+ // Clean up virtual memory tracking data structures.
+ ThreadCritical tc;
+ _reserved_regions.clear();
+ }
+
+ return true;
+}
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/services/virtualMemoryTracker.hpp Thu Aug 07 12:18:58 2014 -0700
@@ -0,0 +1,437 @@
+/*
+ * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#ifndef SHARE_VM_SERVICES_VIRTUAL_MEMORY_TRACKER_HPP
+#define SHARE_VM_SERVICES_VIRTUAL_MEMORY_TRACKER_HPP
+
+#if INCLUDE_NMT
+
+#include "memory/allocation.hpp"
+#include "services/allocationSite.hpp"
+#include "services/nmtCommon.hpp"
+#include "utilities/linkedlist.hpp"
+#include "utilities/nativeCallStack.hpp"
+#include "utilities/ostream.hpp"
+
+
+/*
+ * Virtual memory counter
+ */
+class VirtualMemory VALUE_OBJ_CLASS_SPEC {
+ private:
+ size_t _reserved;
+ size_t _committed;
+
+ public:
+ VirtualMemory() : _reserved(0), _committed(0) { }
+
+ inline void reserve_memory(size_t sz) { _reserved += sz; }
+ inline void commit_memory (size_t sz) {
+ _committed += sz;
+ assert(_committed <= _reserved, "Sanity check");
+ }
+
+ inline void release_memory (size_t sz) {
+ assert(_reserved >= sz, "Negative amount");
+ _reserved -= sz;
+ }
+
+ inline void uncommit_memory(size_t sz) {
+ assert(_committed >= sz, "Negative amount");
+ _committed -= sz;
+ }
+
+ void reset() {
+ _reserved = 0;
+ _committed = 0;
+ }
+
+ inline size_t reserved() const { return _reserved; }
+ inline size_t committed() const { return _committed; }
+};
+
+// Virtual memory allocation site, keeps track where the virtual memory is reserved.
+class VirtualMemoryAllocationSite : public AllocationSite<VirtualMemory> {
+ public:
+ VirtualMemoryAllocationSite(const NativeCallStack& stack) :
+ AllocationSite<VirtualMemory>(stack) { }
+
+ inline void reserve_memory(size_t sz) { data()->reserve_memory(sz); }
+ inline void commit_memory (size_t sz) { data()->commit_memory(sz); }
+ inline void uncommit_memory(size_t sz) { data()->uncommit_memory(sz); }
+ inline void release_memory(size_t sz) { data()->release_memory(sz); }
+ inline size_t reserved() const { return peek()->reserved(); }
+ inline size_t committed() const { return peek()->committed(); }
+};
+
+class VirtualMemorySummary;
+
+// This class represents a snapshot of virtual memory at a given time.
+// The latest snapshot is saved in a static area.
+class VirtualMemorySnapshot : public ResourceObj {
+ friend class VirtualMemorySummary;
+
+ private:
+ VirtualMemory _virtual_memory[mt_number_of_types];
+
+ public:
+ inline VirtualMemory* by_type(MEMFLAGS flag) {
+ int index = NMTUtil::flag_to_index(flag);
+ return &_virtual_memory[index];
+ }
+
+ inline VirtualMemory* by_index(int index) {
+ assert(index >= 0, "Index out of bound");
+ assert(index < mt_number_of_types, "Index out of bound");
+ return &_virtual_memory[index];
+ }
+
+ inline size_t total_reserved() const {
+ size_t amount = 0;
+ for (int index = 0; index < mt_number_of_types; index ++) {
+ amount += _virtual_memory[index].reserved();
+ }
+ return amount;
+ }
+
+ inline size_t total_committed() const {
+ size_t amount = 0;
+ for (int index = 0; index < mt_number_of_types; index ++) {
+ amount += _virtual_memory[index].committed();
+ }
+ return amount;
+ }
+
+ inline void reset() {
+ for (int index = 0; index < mt_number_of_types; index ++) {
+ _virtual_memory[index].reset();
+ }
+ }
+
+ void copy_to(VirtualMemorySnapshot* s) {
+ for (int index = 0; index < mt_number_of_types; index ++) {
+ s->_virtual_memory[index] = _virtual_memory[index];
+ }
+ }
+};
+
+class VirtualMemorySummary : AllStatic {
+ public:
+ static void initialize();
+
+ static inline void record_reserved_memory(size_t size, MEMFLAGS flag) {
+ as_snapshot()->by_type(flag)->reserve_memory(size);
+ }
+
+ static inline void record_committed_memory(size_t size, MEMFLAGS flag) {
+ as_snapshot()->by_type(flag)->commit_memory(size);
+ }
+
+ static inline void record_uncommitted_memory(size_t size, MEMFLAGS flag) {
+ as_snapshot()->by_type(flag)->uncommit_memory(size);
+ }
+
+ static inline void record_released_memory(size_t size, MEMFLAGS flag) {
+ as_snapshot()->by_type(flag)->release_memory(size);
+ }
+
+ // Move virtual memory from one memory type to another.
+ // Virtual memory can be reserved before it is associated with a memory type, and tagged
+ // as 'unknown'. Once the memory is tagged, the virtual memory will be moved from 'unknown'
+ // type to specified memory type.
+ static inline void move_reserved_memory(MEMFLAGS from, MEMFLAGS to, size_t size) {
+ as_snapshot()->by_type(from)->release_memory(size);
+ as_snapshot()->by_type(to)->reserve_memory(size);
+ }
+
+ static inline void move_committed_memory(MEMFLAGS from, MEMFLAGS to, size_t size) {
+ as_snapshot()->by_type(from)->uncommit_memory(size);
+ as_snapshot()->by_type(to)->commit_memory(size);
+ }
+
+ static inline void snapshot(VirtualMemorySnapshot* s) {
+ as_snapshot()->copy_to(s);
+ }
+
+ static inline void reset() {
+ as_snapshot()->reset();
+ }
+
+ static VirtualMemorySnapshot* as_snapshot() {
+ return (VirtualMemorySnapshot*)_snapshot;
+ }
+
+ private:
+ static size_t _snapshot[CALC_OBJ_SIZE_IN_TYPE(VirtualMemorySnapshot, size_t)];
+};
+
+
+
+/*
+ * A virtual memory region
+ */
+class VirtualMemoryRegion VALUE_OBJ_CLASS_SPEC {
+ private:
+ address _base_address;
+ size_t _size;
+
+ public:
+ VirtualMemoryRegion(address addr, size_t size) :
+ _base_address(addr), _size(size) {
+ assert(addr != NULL, "Invalid address");
+ assert(size > 0, "Invalid size");
+ }
+
+ inline address base() const { return _base_address; }
+ inline address end() const { return base() + size(); }
+ inline size_t size() const { return _size; }
+
+ inline bool is_empty() const { return size() == 0; }
+
+ inline bool contain_address(address addr) const {
+ return (addr >= base() && addr < end());
+ }
+
+
+ inline bool contain_region(address addr, size_t size) const {
+ return contain_address(addr) && contain_address(addr + size - 1);
+ }
+
+ inline bool same_region(address addr, size_t sz) const {
+ return (addr == base() && sz == size());
+ }
+
+
+ inline bool overlap_region(address addr, size_t sz) const {
+ VirtualMemoryRegion rgn(addr, sz);
+ return contain_address(addr) ||
+ contain_address(addr + sz - 1) ||
+ rgn.contain_address(base()) ||
+ rgn.contain_address(end() - 1);
+ }
+
+ inline bool adjacent_to(address addr, size_t sz) const {
+ return (addr == end() || (addr + sz) == base());
+ }
+
+ void exclude_region(address addr, size_t sz) {
+ assert(contain_region(addr, sz), "Not containment");
+ assert(addr == base() || addr + sz == end(), "Can not exclude from middle");
+ size_t new_size = size() - sz;
+
+ if (addr == base()) {
+ set_base(addr + sz);
+ }
+ set_size(new_size);
+ }
+
+ void expand_region(address addr, size_t sz) {
+ assert(adjacent_to(addr, sz), "Not adjacent regions");
+ if (base() == addr + sz) {
+ set_base(addr);
+ }
+ set_size(size() + sz);
+ }
+
+ protected:
+ void set_base(address base) {
+ assert(base != NULL, "Sanity check");
+ _base_address = base;
+ }
+
+ void set_size(size_t size) {
+ assert(size > 0, "Sanity check");
+ _size = size;
+ }
+};
+
+
+class CommittedMemoryRegion : public VirtualMemoryRegion {
+ private:
+ NativeCallStack _stack;
+
+ public:
+ CommittedMemoryRegion(address addr, size_t size, const NativeCallStack& stack) :
+ VirtualMemoryRegion(addr, size), _stack(stack) { }
+
+ inline int compare(const CommittedMemoryRegion& rgn) const {
+ if (overlap_region(rgn.base(), rgn.size()) ||
+ adjacent_to (rgn.base(), rgn.size())) {
+ return 0;
+ } else {
+ if (base() == rgn.base()) {
+ return 0;
+ } else if (base() > rgn.base()) {
+ return 1;
+ } else {
+ return -1;
+ }
+ }
+ }
+
+ inline bool equals(const CommittedMemoryRegion& rgn) const {
+ return compare(rgn) == 0;
+ }
+
+ inline void set_call_stack(const NativeCallStack& stack) { _stack = stack; }
+ inline const NativeCallStack* call_stack() const { return &_stack; }
+};
+
+
+typedef LinkedListIterator<CommittedMemoryRegion> CommittedRegionIterator;
+
+int compare_committed_region(const CommittedMemoryRegion&, const CommittedMemoryRegion&);
+class ReservedMemoryRegion : public VirtualMemoryRegion {
+ private:
+ SortedLinkedList<CommittedMemoryRegion, compare_committed_region>
+ _committed_regions;
+
+ NativeCallStack _stack;
+ MEMFLAGS _flag;
+
+ bool _all_committed;
+
+ public:
+ ReservedMemoryRegion(address base, size_t size, const NativeCallStack& stack,
+ MEMFLAGS flag = mtNone) :
+ VirtualMemoryRegion(base, size), _stack(stack), _flag(flag),
+ _all_committed(false) { }
+
+
+ ReservedMemoryRegion(address base, size_t size) :
+ VirtualMemoryRegion(base, size), _stack(emptyStack), _flag(mtNone),
+ _all_committed(false) { }
+
+ // Copy constructor
+ ReservedMemoryRegion(const ReservedMemoryRegion& rr) :
+ VirtualMemoryRegion(rr.base(), rr.size()) {
+ *this = rr;
+ }
+
+ inline void set_call_stack(const NativeCallStack& stack) { _stack = stack; }
+ inline const NativeCallStack* call_stack() const { return &_stack; }
+
+ void set_flag(MEMFLAGS flag);
+ inline MEMFLAGS flag() const { return _flag; }
+
+ inline int compare(const ReservedMemoryRegion& rgn) const {
+ if (overlap_region(rgn.base(), rgn.size())) {
+ return 0;
+ } else {
+ if (base() == rgn.base()) {
+ return 0;
+ } else if (base() > rgn.base()) {
+ return 1;
+ } else {
+ return -1;
+ }
+ }
+ }
+
+ inline bool equals(const ReservedMemoryRegion& rgn) const {
+ return compare(rgn) == 0;
+ }
+
+ bool add_committed_region(address addr, size_t size, const NativeCallStack& stack);
+ bool remove_uncommitted_region(address addr, size_t size);
+
+ size_t committed_size() const;
+
+ // move committed regions that higher than specified address to
+ // the new region
+ void move_committed_regions(address addr, ReservedMemoryRegion& rgn);
+
+ inline bool all_committed() const { return _all_committed; }
+ void set_all_committed(bool b);
+
+ CommittedRegionIterator iterate_committed_regions() const {
+ return CommittedRegionIterator(_committed_regions.head());
+ }
+
+ ReservedMemoryRegion& operator= (const ReservedMemoryRegion& other) {
+ set_base(other.base());
+ set_size(other.size());
+
+ _stack = *other.call_stack();
+ _flag = other.flag();
+ _all_committed = other.all_committed();
+ if (other.all_committed()) {
+ set_all_committed(true);
+ } else {
+ CommittedRegionIterator itr = other.iterate_committed_regions();
+ const CommittedMemoryRegion* rgn = itr.next();
+ while (rgn != NULL) {
+ _committed_regions.add(*rgn);
+ rgn = itr.next();
+ }
+ }
+ return *this;
+ }
+
+ private:
+ // The committed region contains the uncommitted region, subtract the uncommitted
+ // region from this committed region
+ bool remove_uncommitted_region(LinkedListNode<CommittedMemoryRegion>* node,
+ address addr, size_t sz);
+
+ bool add_committed_region(const CommittedMemoryRegion& rgn) {
+ assert(rgn.base() != NULL, "Invalid base address");
+ assert(size() > 0, "Invalid size");
+ return _committed_regions.add(rgn) != NULL;
+ }
+};
+
+int compare_reserved_region_base(const ReservedMemoryRegion& r1, const ReservedMemoryRegion& r2);
+
+class VirtualMemoryWalker : public StackObj {
+ public:
+ virtual bool do_allocation_site(const ReservedMemoryRegion* rgn) { return false; }
+};
+
+// Main class called from MemTracker to track virtual memory allocations, commits and releases.
+class VirtualMemoryTracker : AllStatic {
+ public:
+ static bool initialize(NMT_TrackingLevel level);
+
+ static bool add_reserved_region (address base_addr, size_t size, const NativeCallStack& stack,
+ MEMFLAGS flag = mtNone, bool all_committed = false);
+
+ static bool add_committed_region (address base_addr, size_t size, const NativeCallStack& stack);
+ static bool remove_uncommitted_region (address base_addr, size_t size);
+ static bool remove_released_region (address base_addr, size_t size);
+ static void set_reserved_region_type (address addr, MEMFLAGS flag);
+
+ // Walk virtual memory data structure for creating baseline, etc.
+ static bool walk_virtual_memory(VirtualMemoryWalker* walker);
+
+ static bool transition(NMT_TrackingLevel from, NMT_TrackingLevel to);
+
+ private:
+ static SortedLinkedList<ReservedMemoryRegion, compare_reserved_region_base> _reserved_regions;
+};
+
+
+#endif // INCLUDE_NMT
+
+#endif // SHARE_VM_SERVICES_VIRTUAL_MEMORY_TRACKER_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/utilities/linkedlist.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "precompiled.hpp"
+
+/////////////// Unit tests ///////////////
+
+#ifndef PRODUCT
+
+#include "runtime/os.hpp"
+#include "utilities/linkedlist.hpp"
+#include "memory/allocation.hpp"
+#include "memory/allocation.inline.hpp"
+
+class Integer : public StackObj {
+ private:
+ int _value;
+ public:
+ Integer(int i) : _value(i) { }
+
+ int value() const { return _value; }
+ bool equals(const Integer& i) const {
+ return _value == i.value();
+ }
+};
+
+int compare_Integer(const Integer& i1, const Integer& i2) {
+ return i1.value() - i2.value();
+}
+
+void check_list_values(const int* expected, const LinkedList<Integer>* list) {
+ LinkedListNode<Integer>* head = list->head();
+ int index = 0;
+ while (head != NULL) {
+ assert(head->peek()->value() == expected[index], "Unexpected value");
+ head = head->next();
+ index ++;
+ }
+}
+
+void Test_linked_list() {
+ LinkedListImpl<Integer, ResourceObj::C_HEAP, mtTest> ll;
+
+
+ // Test regular linked list
+ assert(ll.is_empty(), "Start with empty list");
+ Integer one(1), two(2), three(3), four(4), five(5), six(6);
+
+ ll.add(six);
+ assert(!ll.is_empty(), "Should not be empty");
+
+ Integer* i = ll.find(six);
+ assert(i != NULL, "Should find it");
+
+ i = ll.find(three);
+ assert(i == NULL, "Not in the list");
+
+ LinkedListNode<Integer>* node = ll.find_node(six);
+ assert(node != NULL, "6 is in the list");
+
+ ll.insert_after(three, node);
+ ll.insert_before(one, node);
+ int expected[3] = {1, 6, 3};
+ check_list_values(expected, &ll);
+
+ ll.add(two);
+ ll.add(four);
+ ll.add(five);
+
+ // Test sorted linked list
+ SortedLinkedList<Integer, compare_Integer, ResourceObj::C_HEAP, mtTest> sl;
+ assert(sl.is_empty(), "Start with empty list");
+
+ size_t ll_size = ll.size();
+ sl.move(&ll);
+ size_t sl_size = sl.size();
+
+ assert(ll_size == sl_size, "Should be the same size");
+ assert(ll.is_empty(), "No more entires");
+
+ // sorted result
+ int sorted_result[] = {1, 2, 3, 4, 5, 6};
+ check_list_values(sorted_result, &sl);
+
+ node = sl.find_node(four);
+ assert(node != NULL, "4 is in the list");
+ sl.remove_before(node);
+ sl.remove_after(node);
+ int remains[] = {1, 2, 4, 6};
+ check_list_values(remains, &sl);
+}
+#endif // PRODUCT
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/utilities/linkedlist.hpp Thu Aug 07 12:18:58 2014 -0700
@@ -0,0 +1,416 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#ifndef SHARE_VM_UTILITIES_LINKED_LIST_HPP
+#define SHARE_VM_UTILITIES_LINKED_LIST_HPP
+
+#include "memory/allocation.hpp"
+
+/*
+ * The implementation of a generic linked list, which uses various
+ * backing storages, such as C heap, arena and resource, etc.
+ */
+
+
+// An entry in a linked list. It should use the same backing storage
+// as the linked list that contains this entry.
+template <class E> class LinkedListNode : public ResourceObj {
+ private:
+ E _data; // embedded content
+ LinkedListNode<E>* _next; // next entry
+
+ protected:
+ LinkedListNode() : _next(NULL) { }
+
+ public:
+ LinkedListNode(const E& e): _data(e), _next(NULL) { }
+
+ inline void set_next(LinkedListNode<E>* node) { _next = node; }
+ inline LinkedListNode<E> * next() const { return _next; }
+
+ E* data() { return &_data; }
+ const E* peek() const { return &_data; }
+};
+
+// A linked list interface. It does not specify
+// any storage type it uses, so all methods involving
+// memory allocation or deallocation are pure virtual
+template <class E> class LinkedList : public ResourceObj {
+ protected:
+ LinkedListNode<E>* _head;
+
+ public:
+ LinkedList() : _head(NULL) { }
+
+ inline void set_head(LinkedListNode<E>* h) { _head = h; }
+ inline LinkedListNode<E>* head() const { return _head; }
+ inline bool is_empty() const { return head() == NULL; }
+
+ inline size_t size() const {
+ LinkedListNode<E>* p;
+ size_t count = 0;
+ for (p = head(); p != NULL; count++, p = p->next());
+ return count;
+ }
+
+ // Move all entries from specified linked list to this one
+ virtual void move(LinkedList<E>* list) = 0;
+
+ // Add an entry to this linked list
+ virtual LinkedListNode<E>* add(const E& e) = 0;
+ // Add all entries from specified linked list to this one,
+ virtual void add(LinkedListNode<E>* node) = 0;
+
+ // Add a linked list to this linked list
+ virtual bool add(const LinkedList<E>* list) = 0;
+
+ // Search entry in the linked list
+ virtual LinkedListNode<E>* find_node(const E& e) = 0;
+ virtual E* find(const E& e) = 0;
+
+ // Insert entry to the linked list
+ virtual LinkedListNode<E>* insert_before(const E& e, LinkedListNode<E>* ref) = 0;
+ virtual LinkedListNode<E>* insert_after (const E& e, LinkedListNode<E>* ref) = 0;
+
+ // Remove entry from the linked list
+ virtual bool remove(const E& e) = 0;
+ virtual bool remove(LinkedListNode<E>* node) = 0;
+ virtual bool remove_before(LinkedListNode<E>* ref) = 0;
+ virtual bool remove_after(LinkedListNode<E>* ref) = 0;
+
+ LinkedListNode<E>* unlink_head() {
+ LinkedListNode<E>* h = this->head();
+ if (h != NULL) {
+ this->set_head(h->next());
+ }
+ return h;
+ }
+
+ DEBUG_ONLY(virtual ResourceObj::allocation_type storage_type() = 0;)
+};
+
+// A linked list implementation.
+// The linked list can be allocated in various type of memory: C heap, arena and resource area, etc.
+template <class E, ResourceObj::allocation_type T = ResourceObj::C_HEAP,
+ MEMFLAGS F = mtNMT, AllocFailType alloc_failmode = AllocFailStrategy::RETURN_NULL>
+ class LinkedListImpl : public LinkedList<E> {
+ protected:
+ Arena* _arena;
+ public:
+ LinkedListImpl() : _arena(NULL) { }
+ LinkedListImpl(Arena* a) : _arena(a) { }
+
+ virtual ~LinkedListImpl() {
+ clear();
+ }
+
+ virtual void clear() {
+ LinkedListNode<E>* p = this->head();
+ this->set_head(NULL);
+ while (p != NULL) {
+ LinkedListNode<E>* to_delete = p;
+ p = p->next();
+ delete_node(to_delete);
+ }
+ }
+
+ // Add an entry to the linked list
+ virtual LinkedListNode<E>* add(const E& e) {
+ LinkedListNode<E>* node = this->new_node(e);
+ if (node != NULL) {
+ this->add(node);
+ }
+
+ return node;
+ }
+
+ virtual void add(LinkedListNode<E>* node) {
+ assert(node != NULL, "NULL pointer");
+ node->set_next(this->head());
+ this->set_head(node);
+ }
+
+ // Move a linked list to this linked list, both have to be allocated on the same
+ // storage type.
+ virtual void move(LinkedList<E>* list) {
+ assert(list->storage_type() == this->storage_type(), "Different storage type");
+ LinkedListNode<E>* node = this->head();
+ while (node != NULL && node->next() != NULL) {
+ node = node->next();
+ }
+ if (node == NULL) {
+ this->set_head(list->head());
+ } else {
+ node->set_next(list->head());
+ }
+ // All entries are moved
+ list->set_head(NULL);
+ }
+
+ virtual bool add(const LinkedList<E>* list) {
+ LinkedListNode<E>* node = list->head();
+ while (node != NULL) {
+ if (this->add(*node->peek()) == NULL) {
+ return false;
+ }
+ node = node->next();
+ }
+ return true;
+ }
+
+
+ virtual LinkedListNode<E>* find_node(const E& e) {
+ LinkedListNode<E>* p = this->head();
+ while (p != NULL && !p->peek()->equals(e)) {
+ p = p->next();
+ }
+ return p;
+ }
+
+ E* find(const E& e) {
+ LinkedListNode<E>* node = find_node(e);
+ return (node == NULL) ? NULL : node->data();
+ }
+
+
+ // Add an entry in front of the reference entry
+ LinkedListNode<E>* insert_before(const E& e, LinkedListNode<E>* ref_node) {
+ LinkedListNode<E>* node = this->new_node(e);
+ if (node == NULL) return NULL;
+ if (ref_node == this->head()) {
+ node->set_next(ref_node);
+ this->set_head(node);
+ } else {
+ LinkedListNode<E>* p = this->head();
+ while (p != NULL && p->next() != ref_node) {
+ p = p->next();
+ }
+ assert(p != NULL, "ref_node not in the list");
+ node->set_next(ref_node);
+ p->set_next(node);
+ }
+ return node;
+ }
+
+ // Add an entry behind the reference entry
+ LinkedListNode<E>* insert_after(const E& e, LinkedListNode<E>* ref_node) {
+ LinkedListNode<E>* node = this->new_node(e);
+ if (node == NULL) return NULL;
+ node->set_next(ref_node->next());
+ ref_node->set_next(node);
+ return node;
+ }
+
+ // Remove an entry from the linked list.
+ // Return true if the entry is successfully removed
+ virtual bool remove(const E& e) {
+ LinkedListNode<E>* tmp = this->head();
+ LinkedListNode<E>* prev = NULL;
+
+ while (tmp != NULL) {
+ if (tmp->peek()->equals(e)) {
+ return remove_after(prev);
+ }
+ prev = tmp;
+ tmp = tmp->next();
+ }
+ return false;
+ }
+
+ // Remove the node after the reference entry
+ virtual bool remove_after(LinkedListNode<E>* prev) {
+ LinkedListNode<E>* to_delete;
+ if (prev == NULL) {
+ to_delete = this->unlink_head();
+ } else {
+ to_delete = prev->next();
+ if (to_delete != NULL) {
+ prev->set_next(to_delete->next());
+ }
+ }
+
+ if (to_delete != NULL) {
+ delete_node(to_delete);
+ return true;
+ }
+ return false;
+ }
+
+ virtual bool remove(LinkedListNode<E>* node) {
+ LinkedListNode<E>* p = this->head();
+ while (p != NULL && p->next() != node) {
+ p = p->next();
+ }
+ if (p != NULL) {
+ p->set_next(node->next());
+ delete_node(node);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ virtual bool remove_before(LinkedListNode<E>* ref) {
+ assert(ref != NULL, "NULL pointer");
+ LinkedListNode<E>* p = this->head();
+ LinkedListNode<E>* to_delete = NULL; // to be deleted
+ LinkedListNode<E>* prev = NULL; // node before the node to be deleted
+ while (p != NULL && p != ref) {
+ prev = to_delete;
+ to_delete = p;
+ p = p->next();
+ }
+ if (p == NULL || to_delete == NULL) return false;
+ assert(to_delete->next() == ref, "Wrong node to delete");
+ assert(prev == NULL || prev->next() == to_delete,
+ "Sanity check");
+ if (prev == NULL) {
+ assert(to_delete == this->head(), "Must be head");
+ this->set_head(to_delete->next());
+ } else {
+ prev->set_next(to_delete->next());
+ }
+ delete_node(to_delete);
+ return true;
+ }
+
+ DEBUG_ONLY(ResourceObj::allocation_type storage_type() { return T; })
+ protected:
+ // Create new linked list node object in specified storage
+ LinkedListNode<E>* new_node(const E& e) const {
+ switch(T) {
+ case ResourceObj::ARENA: {
+ assert(_arena != NULL, "Arena not set");
+ return new(_arena) LinkedListNode<E>(e);
+ }
+ case ResourceObj::RESOURCE_AREA:
+ case ResourceObj::C_HEAP: {
+ if (alloc_failmode == AllocFailStrategy::RETURN_NULL) {
+ return new(std::nothrow, T, F) LinkedListNode<E>(e);
+ } else {
+ return new(T, F) LinkedListNode<E>(e);
+ }
+ }
+ default:
+ ShouldNotReachHere();
+ }
+ return NULL;
+ }
+
+ // Delete linked list node object
+ void delete_node(LinkedListNode<E>* node) {
+ if (T == ResourceObj::C_HEAP) {
+ delete node;
+ }
+ }
+};
+
+// Sorted linked list. The linked list maintains sorting order specified by the comparison
+// function
+template <class E, int (*FUNC)(const E&, const E&),
+ ResourceObj::allocation_type T = ResourceObj::C_HEAP,
+ MEMFLAGS F = mtNMT, AllocFailType alloc_failmode = AllocFailStrategy::RETURN_NULL>
+ class SortedLinkedList : public LinkedListImpl<E, T, F, alloc_failmode> {
+ public:
+ SortedLinkedList() { }
+ SortedLinkedList(Arena* a) : LinkedListImpl<E, T, F, alloc_failmode>(a) { }
+
+ virtual LinkedListNode<E>* add(const E& e) {
+ return LinkedListImpl<E, T, F, alloc_failmode>::add(e);
+ }
+
+ virtual void move(LinkedList<E>* list) {
+ assert(list->storage_type() == this->storage_type(), "Different storage type");
+ LinkedListNode<E>* node;
+ while ((node = list->unlink_head()) != NULL) {
+ this->add(node);
+ }
+ assert(list->is_empty(), "All entries are moved");
+ }
+
+ virtual void add(LinkedListNode<E>* node) {
+ assert(node != NULL, "NULL pointer");
+ LinkedListNode<E>* tmp = this->head();
+ LinkedListNode<E>* prev = NULL;
+
+ int cmp_val;
+ while (tmp != NULL) {
+ cmp_val = FUNC(*tmp->peek(), *node->peek());
+ if (cmp_val >= 0) {
+ break;
+ }
+ prev = tmp;
+ tmp = tmp->next();
+ }
+
+ if (prev != NULL) {
+ node->set_next(prev->next());
+ prev->set_next(node);
+ } else {
+ node->set_next(this->head());
+ this->set_head(node);
+ }
+ }
+
+ virtual bool add(const LinkedList<E>* list) {
+ return LinkedListImpl<E, T, F, alloc_failmode>::add(list);
+ }
+
+ virtual LinkedListNode<E>* find_node(const E& e) {
+ LinkedListNode<E>* p = this->head();
+
+ while (p != NULL) {
+ int comp_val = FUNC(*p->peek(), e);
+ if (comp_val == 0) {
+ return p;
+ } else if (comp_val > 0) {
+ return NULL;
+ }
+ p = p->next();
+ }
+ return NULL;
+ }
+};
+
+// Iterates all entries in the list
+template <class E> class LinkedListIterator : public StackObj {
+ private:
+ LinkedListNode<E>* _p;
+ bool _is_empty;
+ public:
+ LinkedListIterator(LinkedListNode<E>* head) : _p(head) {
+ _is_empty = (head == NULL);
+ }
+
+ bool is_empty() const { return _is_empty; }
+
+ const E* next() {
+ if (_p == NULL) return NULL;
+ const E* e = _p->peek();
+ _p = _p->next();
+ return e;
+ }
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/utilities/nativeCallStack.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "runtime/os.hpp"
+#include "utilities/globalDefinitions.hpp"
+#include "utilities/nativeCallStack.hpp"
+
+
+NativeCallStack::NativeCallStack(int toSkip, bool fillStack) :
+ _hash_value(0) {
+
+#if !PLATFORM_NATIVE_STACK_WALKING_SUPPORTED
+ fillStack = false;
+#endif
+
+ if (fillStack) {
+ os::get_native_stack(_stack, NMT_TrackingStackDepth, toSkip);
+ } else {
+ for (int index = 0; index < NMT_TrackingStackDepth; index ++) {
+ _stack[index] = NULL;
+ }
+ }
+}
+
+NativeCallStack::NativeCallStack(address* pc, int frameCount) {
+ int frameToCopy = (frameCount < NMT_TrackingStackDepth) ?
+ frameCount : NMT_TrackingStackDepth;
+ int index;
+ for (index = 0; index < frameToCopy; index ++) {
+ _stack[index] = pc[index];
+ }
+ for (; index < NMT_TrackingStackDepth; index ++) {
+ _stack[index] = NULL;
+ }
+}
+
+// number of stack frames captured
+int NativeCallStack::frames() const {
+ int index;
+ for (index = 0; index < NMT_TrackingStackDepth; index ++) {
+ if (_stack[index] == NULL) {
+ break;
+ }
+ }
+ return index;
+}
+
+// Hash code. Any better algorithm?
+int NativeCallStack::hash() const {
+ long hash_val = _hash_value;
+ if (hash_val == 0) {
+ long pc;
+ int index;
+ for (index = 0; index < NMT_TrackingStackDepth; index ++) {
+ pc = (long)_stack[index];
+ if (pc == 0) break;
+ hash_val += pc;
+ }
+
+ NativeCallStack* p = const_cast<NativeCallStack*>(this);
+ p->_hash_value = (int)(hash_val & 0xFFFFFFFF);
+ }
+ return _hash_value;
+}
+
+void NativeCallStack::print_on(outputStream* out) const {
+ print_on(out, 0);
+}
+
+// Decode and print this call path
+void NativeCallStack::print_on(outputStream* out, int indent) const {
+ address pc;
+ char buf[1024];
+ int offset;
+ if (is_empty()) {
+ for (int index = 0; index < indent; index ++) out->print(" ");
+#if PLATFORM_NATIVE_STACK_WALKING_SUPPORTED
+ out->print("[BOOTSTRAP]");
+#else
+ out->print("[No stack]");
+#endif
+ } else {
+ for (int frame = 0; frame < NMT_TrackingStackDepth; frame ++) {
+ pc = get_frame(frame);
+ if (pc == NULL) break;
+ // Print indent
+ for (int index = 0; index < indent; index ++) out->print(" ");
+ if (os::dll_address_to_function_name(pc, buf, sizeof(buf), &offset)) {
+ out->print_cr("[" PTR_FORMAT "] %s+0x%x", p2i(pc), buf, offset);
+ } else {
+ out->print_cr("[" PTR_FORMAT "]", p2i(pc));
+ }
+ }
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/utilities/nativeCallStack.hpp Thu Aug 07 12:18:58 2014 -0700
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#ifndef SHARE_VM_UTILITIES_NATIVE_CALL_STACK_HPP
+#define SHARE_VM_UTILITIES_NATIVE_CALL_STACK_HPP
+
+#include "memory/allocation.hpp"
+#include "services/nmtCommon.hpp"
+#include "utilities/ostream.hpp"
+
+/*
+ * This class represents a native call path (does not include Java frame)
+ *
+ * This class is developed in the context of native memory tracking, it can
+ * be an useful tool for debugging purpose.
+ *
+ * For example, following code should print out native call path:
+ *
+ * ....
+ * NativeCallStack here;
+ * here.print_on(tty);
+ * ....
+ *
+ * However, there are a couple of restrictions on this class. If the restrictions are
+ * not strictly followed, it may break native memory tracking badly.
+ *
+ * 1. Number of stack frames to capture, is defined by native memory tracking.
+ * This number has impacts on how much memory to be used by native
+ * memory tracking.
+ * 2. The class is strict stack object, no heap or virtual memory can be allocated
+ * from it.
+ */
+class NativeCallStack : public StackObj {
+ private:
+ address _stack[NMT_TrackingStackDepth];
+ int _hash_value;
+
+ public:
+ NativeCallStack(int toSkip = 0, bool fillStack = false);
+ NativeCallStack(address* pc, int frameCount);
+
+
+ // if it is an empty stack
+ inline bool is_empty() const {
+ return _stack[0] == NULL;
+ }
+
+ // number of stack frames captured
+ int frames() const;
+
+ inline int compare(const NativeCallStack& other) const {
+ return memcmp(_stack, other._stack, sizeof(_stack));
+ }
+
+ inline bool equals(const NativeCallStack& other) const {
+ // compare hash values
+ if (hash() != other.hash()) return false;
+ // compare each frame
+ return compare(other) == 0;
+ }
+
+ inline address get_frame(int index) const {
+ assert(index >= 0 && index < NMT_TrackingStackDepth, "Index out of bound");
+ return _stack[index];
+ }
+
+ // Hash code. Any better algorithm?
+ int hash() const;
+
+ void print_on(outputStream* out) const;
+ void print_on(outputStream* out, int indent) const;
+};
+
+#endif
--- a/hotspot/src/share/vm/utilities/vmError.cpp Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/src/share/vm/utilities/vmError.cpp Thu Aug 07 12:18:58 2014 -0700
@@ -774,6 +774,11 @@
st->cr();
}
+ STEP(228, "(Native Memory Tracking)" )
+ if (_verbose) {
+ MemTracker::final_report(st);
+ }
+
STEP(230, "" )
if (_verbose) {
@@ -897,9 +902,6 @@
static bool log_done = false; // done saving error log
static bool transmit_report_done = false; // done error reporting
- // disble NMT to avoid further exception
- MemTracker::shutdown(MemTracker::NMT_error_reporting);
-
if (SuppressFatalErrorMessage) {
os::abort();
}
--- a/hotspot/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java Tue Aug 05 14:18:44 2014 +0000
+++ b/hotspot/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java Thu Aug 07 12:18:58 2014 -0700
@@ -95,7 +95,8 @@
public native void NMTCommitMemory(long addr, long size);
public native void NMTUncommitMemory(long addr, long size);
public native void NMTReleaseMemory(long addr, long size);
- public native boolean NMTWaitForDataMerge();
+ public native void NMTOverflowHashBucket(long num);
+ public native long NMTMallocWithPseudoStack(long size, int index);
public native boolean NMTIsDetailSupported();
// Compiler