mirror of
https://github.com/corda/corda.git
synced 2025-01-21 03:55:00 +00:00
implement primitive heap dump facility for memory profiling, accessible via Runtime.dumpHeap
The proper way to do this is to implement a subset of JVMTI, but this will do the job for now.
This commit is contained in:
parent
f38a55cbb2
commit
6a5116e7a7
@ -75,6 +75,8 @@ public class Runtime {
|
|||||||
|
|
||||||
public native long totalMemory();
|
public native long totalMemory();
|
||||||
|
|
||||||
|
public static native void dumpHeap(String outputFile);
|
||||||
|
|
||||||
private static class MyProcess extends Process {
|
private static class MyProcess extends Process {
|
||||||
private long pid;
|
private long pid;
|
||||||
private final int in;
|
private final int in;
|
||||||
|
@ -111,8 +111,8 @@ public class Method<T> extends AccessibleObject implements Member {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static native Object invoke(Method method, Object instance,
|
private static native Object invoke(Method method, Object instance,
|
||||||
Object ... arguments)
|
Object ... arguments)
|
||||||
throws InvocationTargetException, IllegalAccessException;
|
throws InvocationTargetException, IllegalAccessException;
|
||||||
|
|
||||||
public Class getReturnType() {
|
public Class getReturnType() {
|
||||||
|
5
makefile
5
makefile
@ -204,6 +204,11 @@ vm-sources = \
|
|||||||
$(src)/process.cpp \
|
$(src)/process.cpp \
|
||||||
$(src)/$(asm).cpp
|
$(src)/$(asm).cpp
|
||||||
|
|
||||||
|
ifeq ($(heapdump),true)
|
||||||
|
vm-sources += $(src)/heapdump.cpp
|
||||||
|
cflags += -DAVIAN_HEAPDUMP
|
||||||
|
endif
|
||||||
|
|
||||||
vm-asm-sources = $(src)/$(asm).S
|
vm-asm-sources = $(src)/$(asm).S
|
||||||
|
|
||||||
ifeq ($(process),compile)
|
ifeq ($(process),compile)
|
||||||
|
@ -585,6 +585,26 @@ Java_java_lang_Runtime_gc(Thread* t, jobject)
|
|||||||
collect(t, Heap::MajorCollection);
|
collect(t, Heap::MajorCollection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef AVIAN_HEAPDUMP
|
||||||
|
|
||||||
|
extern "C" JNIEXPORT void JNICALL
|
||||||
|
Java_java_lang_Runtime_dumpHeap(Thread* t, jclass, jstring outputFile)
|
||||||
|
{
|
||||||
|
unsigned length = stringLength(t, *outputFile);
|
||||||
|
char n[length + 1];
|
||||||
|
stringChars(t, *outputFile, n);
|
||||||
|
FILE* out = fopen(n, "wb");
|
||||||
|
if (out) {
|
||||||
|
dumpHeap(t, out);
|
||||||
|
fclose(out);
|
||||||
|
} else {
|
||||||
|
object message = makeString(t, "file not found: %s", n);
|
||||||
|
t->exception = makeRuntimeException(t, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif//AVIAN_HEAPDUMP
|
||||||
|
|
||||||
extern "C" JNIEXPORT void JNICALL
|
extern "C" JNIEXPORT void JNICALL
|
||||||
Java_java_lang_Runtime_exit(Thread* t, jobject, jint code)
|
Java_java_lang_Runtime_exit(Thread* t, jobject, jint code)
|
||||||
{
|
{
|
||||||
|
331
src/heapdump.cpp
Normal file
331
src/heapdump.cpp
Normal file
@ -0,0 +1,331 @@
|
|||||||
|
/* Copyright (c) 2008, Avian Contributors
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software
|
||||||
|
for any purpose with or without fee is hereby granted, provided
|
||||||
|
that the above copyright notice and this permission notice appear
|
||||||
|
in all copies.
|
||||||
|
|
||||||
|
There is NO WARRANTY for this software. See license.txt for
|
||||||
|
details. */
|
||||||
|
|
||||||
|
#include "machine.h"
|
||||||
|
|
||||||
|
using namespace vm;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
const uintptr_t PointerShift = log(BytesPerWord);
|
||||||
|
|
||||||
|
class Set {
|
||||||
|
public:
|
||||||
|
class Entry {
|
||||||
|
public:
|
||||||
|
object value;
|
||||||
|
uint32_t number;
|
||||||
|
int next;
|
||||||
|
};
|
||||||
|
|
||||||
|
static unsigned footprint(unsigned capacity) {
|
||||||
|
return sizeof(Set)
|
||||||
|
+ pad(sizeof(int) * capacity)
|
||||||
|
+ pad(sizeof(Set::Entry) * capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
Set(unsigned capacity):
|
||||||
|
size(0),
|
||||||
|
capacity(capacity),
|
||||||
|
index(reinterpret_cast<int*>
|
||||||
|
(reinterpret_cast<uint8_t*>(this)
|
||||||
|
+ sizeof(Set))),
|
||||||
|
entries(reinterpret_cast<Entry*>
|
||||||
|
(reinterpret_cast<uint8_t*>(index)
|
||||||
|
+ pad(sizeof(int) * capacity)))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
unsigned size;
|
||||||
|
unsigned capacity;
|
||||||
|
int* index;
|
||||||
|
Entry* entries;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Stack {
|
||||||
|
public:
|
||||||
|
class Entry {
|
||||||
|
public:
|
||||||
|
object value;
|
||||||
|
int offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned Capacity = 4096;
|
||||||
|
|
||||||
|
Stack(Stack* next): next(next), entryCount(0) { }
|
||||||
|
|
||||||
|
Stack* next;
|
||||||
|
unsigned entryCount;
|
||||||
|
Entry entries[Capacity];
|
||||||
|
};
|
||||||
|
|
||||||
|
class Context {
|
||||||
|
public:
|
||||||
|
Context(Thread* thread, FILE* out):
|
||||||
|
thread(thread), out(out), objects(0), stack(0), nextNumber(1)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
~Context() {
|
||||||
|
if (objects) {
|
||||||
|
thread->m->heap->free(objects, Set::footprint(objects->capacity));
|
||||||
|
}
|
||||||
|
while (stack) {
|
||||||
|
Stack* dead = stack;
|
||||||
|
stack = dead->next;
|
||||||
|
thread->m->heap->free(stack, sizeof(Stack));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread* thread;
|
||||||
|
FILE* out;
|
||||||
|
Set* objects;
|
||||||
|
Stack* stack;
|
||||||
|
uint32_t nextNumber;
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
push(Context* c, object p, int offset)
|
||||||
|
{
|
||||||
|
if (c->stack == 0 or c->stack->entryCount == Stack::Capacity) {
|
||||||
|
c->stack = new (c->thread->m->heap->allocate(sizeof(Stack)))
|
||||||
|
Stack(c->stack);
|
||||||
|
}
|
||||||
|
Stack::Entry* e = c->stack->entries + (c->stack->entryCount++);
|
||||||
|
e->value = p;
|
||||||
|
e->offset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
pop(Context* c, object* p, int* offset)
|
||||||
|
{
|
||||||
|
if (c->stack) {
|
||||||
|
if (c->stack->entryCount == 0) {
|
||||||
|
if (c->stack->next) {
|
||||||
|
Stack* dead = c->stack;
|
||||||
|
c->stack = dead->next;
|
||||||
|
c->thread->m->heap->free(dead, sizeof(Stack));
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Stack::Entry* e = c->stack->entries + (--c->stack->entryCount);
|
||||||
|
*p = e->value;
|
||||||
|
*offset = e->offset;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned
|
||||||
|
hash(object p, unsigned capacity)
|
||||||
|
{
|
||||||
|
return (reinterpret_cast<uintptr_t>(p) >> PointerShift)
|
||||||
|
& (capacity - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Set::Entry*
|
||||||
|
find(Context* c, object p)
|
||||||
|
{
|
||||||
|
if (c->objects == 0) return false;
|
||||||
|
|
||||||
|
for (int i = c->objects->index[hash(p, c->objects->capacity)]; i >= 0;) {
|
||||||
|
Set::Entry* e = c->objects->entries + i;
|
||||||
|
if (e->value == p) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
i = e->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Set::Entry*
|
||||||
|
add(Context* c UNUSED, Set* set, object p)
|
||||||
|
{
|
||||||
|
assert(c->thread, set->size < set->capacity);
|
||||||
|
|
||||||
|
unsigned index = hash(p, set->capacity);
|
||||||
|
|
||||||
|
int offset = set->size++;
|
||||||
|
Set::Entry* e = set->entries + offset;
|
||||||
|
e->value = p;
|
||||||
|
e->next = set->index[index];
|
||||||
|
set->index[index] = offset;
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
Set::Entry*
|
||||||
|
add(Context* c, object p)
|
||||||
|
{
|
||||||
|
if (c->objects == 0 or c->objects->size == c->objects->capacity) {
|
||||||
|
unsigned capacity;
|
||||||
|
if (c->objects) {
|
||||||
|
capacity = c->objects->capacity * 2;
|
||||||
|
} else {
|
||||||
|
capacity = 4096; // must be power of two
|
||||||
|
}
|
||||||
|
|
||||||
|
Set* set = new (c->thread->m->heap->allocate(Set::footprint(capacity)))
|
||||||
|
Set(capacity);
|
||||||
|
|
||||||
|
memset(set->index, 0xFF, sizeof(int) * capacity);
|
||||||
|
|
||||||
|
if (c->objects) {
|
||||||
|
for (unsigned i = 0; i < c->objects->capacity; ++i) {
|
||||||
|
for (int j = c->objects->index[i]; j >= 0;) {
|
||||||
|
Set::Entry* e = c->objects->entries + j;
|
||||||
|
add(c, set, e->value);
|
||||||
|
j = e->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c->thread->m->heap->free
|
||||||
|
(c->objects, Set::footprint(c->objects->capacity));
|
||||||
|
}
|
||||||
|
|
||||||
|
c->objects = set;
|
||||||
|
}
|
||||||
|
|
||||||
|
return add(c, c->objects, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum {
|
||||||
|
Root,
|
||||||
|
ClassName,
|
||||||
|
Push,
|
||||||
|
LastChild,
|
||||||
|
Pop,
|
||||||
|
Size
|
||||||
|
};
|
||||||
|
|
||||||
|
inline object
|
||||||
|
get(object o, unsigned offsetInWords)
|
||||||
|
{
|
||||||
|
return static_cast<object>
|
||||||
|
(mask(cast<void*>(o, offsetInWords * BytesPerWord)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
write1(Context* c, uint8_t v)
|
||||||
|
{
|
||||||
|
fwrite(&v, 1, 1, c->out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
write4(Context* c, uint32_t v)
|
||||||
|
{
|
||||||
|
uint8_t b[] = { v >> 24, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF };
|
||||||
|
fwrite(b, 4, 1, c->out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
writeString(Context* c, int8_t* p, unsigned size)
|
||||||
|
{
|
||||||
|
write4(c, size);
|
||||||
|
fwrite(p, size, 1, c->out);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned
|
||||||
|
objectSize(Thread* t, object o)
|
||||||
|
{
|
||||||
|
unsigned n = baseSize(t, o, objectClass(t, o));
|
||||||
|
if (objectExtended(t, o)) {
|
||||||
|
++ n;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
visit(Context* c, object p)
|
||||||
|
{
|
||||||
|
Thread* t = c->thread;
|
||||||
|
int nextChildOffset;
|
||||||
|
|
||||||
|
write1(c, Root);
|
||||||
|
|
||||||
|
visit: {
|
||||||
|
Set::Entry* e = find(c, p);
|
||||||
|
if (e) {
|
||||||
|
write4(c, e->number);
|
||||||
|
} else {
|
||||||
|
e = add(c, p);
|
||||||
|
e->number = c->nextNumber++;
|
||||||
|
|
||||||
|
write4(c, e->number);
|
||||||
|
|
||||||
|
write1(c, Size);
|
||||||
|
write4(c, objectSize(t, p));
|
||||||
|
|
||||||
|
if (objectClass(t, p) == arrayBody(t, t->m->types, Machine::ClassType)) {
|
||||||
|
object name = className(t, p);
|
||||||
|
if (name) {
|
||||||
|
write1(c, ClassName);
|
||||||
|
writeString(c, &byteArrayBody(t, name, 0),
|
||||||
|
byteArrayLength(t, name) - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nextChildOffset = walkNext(t, p, -1);
|
||||||
|
if (nextChildOffset != -1) {
|
||||||
|
goto children;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
goto pop;
|
||||||
|
|
||||||
|
children: {
|
||||||
|
int next = walkNext(t, p, nextChildOffset);
|
||||||
|
if (next >= 0) {
|
||||||
|
write1(c, Push);
|
||||||
|
push(c, p, next);
|
||||||
|
} else {
|
||||||
|
write1(c, LastChild);
|
||||||
|
}
|
||||||
|
p = get(p, nextChildOffset);
|
||||||
|
goto visit;
|
||||||
|
}
|
||||||
|
|
||||||
|
pop: {
|
||||||
|
if (pop(c, &p, &nextChildOffset)) {
|
||||||
|
write1(c, Pop);
|
||||||
|
goto children;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace vm {
|
||||||
|
|
||||||
|
void
|
||||||
|
dumpHeap(Thread* t, FILE* out)
|
||||||
|
{
|
||||||
|
Context context(t, out);
|
||||||
|
|
||||||
|
class Visitor : public Heap::Visitor {
|
||||||
|
public:
|
||||||
|
Visitor(Context* c): c(c) { }
|
||||||
|
|
||||||
|
virtual void visit(void* p) {
|
||||||
|
::visit(c, static_cast<object>(mask(*static_cast<void**>(p))));
|
||||||
|
}
|
||||||
|
|
||||||
|
Context* c;
|
||||||
|
} v(&context);
|
||||||
|
|
||||||
|
add(&context, 0)->number = 0;
|
||||||
|
|
||||||
|
visitRoots(t, &v);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace vm
|
@ -177,34 +177,15 @@ footprint(Thread* t)
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
visitRoots(Thread* t, Heap::Visitor* v)
|
|
||||||
{
|
|
||||||
if (t->state != Thread::ZombieState) {
|
|
||||||
v->visit(&(t->javaThread));
|
|
||||||
v->visit(&(t->exception));
|
|
||||||
|
|
||||||
t->m->processor->visitObjects(t, v);
|
|
||||||
|
|
||||||
for (Thread::Protector* p = t->protector; p; p = p->next) {
|
|
||||||
p->visit(v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Thread* c = t->child; c; c = c->peer) {
|
|
||||||
visitRoots(c, v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
walk(Thread*, Heap::Walker* w, uint32_t* mask, unsigned fixedSize,
|
walk(Thread*, Heap::Walker* w, uint32_t* mask, unsigned fixedSize,
|
||||||
unsigned arrayElementSize, unsigned arrayLength)
|
unsigned arrayElementSize, unsigned arrayLength, unsigned start)
|
||||||
{
|
{
|
||||||
unsigned fixedSizeInWords = ceiling(fixedSize, BytesPerWord);
|
unsigned fixedSizeInWords = ceiling(fixedSize, BytesPerWord);
|
||||||
unsigned arrayElementSizeInWords
|
unsigned arrayElementSizeInWords
|
||||||
= ceiling(arrayElementSize, BytesPerWord);
|
= ceiling(arrayElementSize, BytesPerWord);
|
||||||
|
|
||||||
for (unsigned i = 0; i < fixedSizeInWords; ++i) {
|
for (unsigned i = start; i < fixedSizeInWords; ++i) {
|
||||||
if (mask[i / 32] & (static_cast<uint32_t>(1) << (i % 32))) {
|
if (mask[i / 32] & (static_cast<uint32_t>(1) << (i % 32))) {
|
||||||
if (not w->visit(i)) {
|
if (not w->visit(i)) {
|
||||||
return;
|
return;
|
||||||
@ -222,8 +203,19 @@ walk(Thread*, Heap::Walker* w, uint32_t* mask, unsigned fixedSize,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (arrayObjectElements) {
|
if (arrayObjectElements) {
|
||||||
for (unsigned i = 0; i < arrayLength; ++i) {
|
unsigned arrayStart;
|
||||||
for (unsigned j = 0; j < arrayElementSizeInWords; ++j) {
|
unsigned elementStart;
|
||||||
|
if (start > fixedSizeInWords) {
|
||||||
|
unsigned s = start - fixedSizeInWords;
|
||||||
|
arrayStart = s / arrayElementSizeInWords;
|
||||||
|
elementStart = s % arrayElementSizeInWords;
|
||||||
|
} else {
|
||||||
|
arrayStart = 0;
|
||||||
|
elementStart = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned i = arrayStart; i < arrayLength; ++i) {
|
||||||
|
for (unsigned j = elementStart; j < arrayElementSizeInWords; ++j) {
|
||||||
unsigned k = fixedSizeInWords + j;
|
unsigned k = fixedSizeInWords + j;
|
||||||
if (mask[k / 32] & (static_cast<uint32_t>(1) << (k % 32))) {
|
if (mask[k / 32] & (static_cast<uint32_t>(1) << (k % 32))) {
|
||||||
if (not w->visit
|
if (not w->visit
|
||||||
@ -238,7 +230,7 @@ walk(Thread*, Heap::Walker* w, uint32_t* mask, unsigned fixedSize,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
walk(Thread* t, Heap::Walker* w, object o)
|
walk(Thread* t, Heap::Walker* w, object o, unsigned start)
|
||||||
{
|
{
|
||||||
object class_ = static_cast<object>(t->m->heap->follow(objectClass(t, o)));
|
object class_ = static_cast<object>(t->m->heap->follow(objectClass(t, o)));
|
||||||
object objectMask = static_cast<object>
|
object objectMask = static_cast<object>
|
||||||
@ -255,16 +247,16 @@ walk(Thread* t, Heap::Walker* w, object o)
|
|||||||
memcpy(mask, &intArrayBody(t, objectMask, 0),
|
memcpy(mask, &intArrayBody(t, objectMask, 0),
|
||||||
intArrayLength(t, objectMask) * 4);
|
intArrayLength(t, objectMask) * 4);
|
||||||
|
|
||||||
walk(t, w, mask, fixedSize, arrayElementSize, arrayLength);
|
walk(t, w, mask, fixedSize, arrayElementSize, arrayLength, start);
|
||||||
} else if (classVmFlags(t, class_) & SingletonFlag) {
|
} else if (classVmFlags(t, class_) & SingletonFlag) {
|
||||||
unsigned length = singletonLength(t, o);
|
unsigned length = singletonLength(t, o);
|
||||||
if (length) {
|
if (length) {
|
||||||
walk(t, w, singletonMask(t, o),
|
walk(t, w, singletonMask(t, o),
|
||||||
(singletonCount(t, o) + 2) * BytesPerWord, 0, 0);
|
(singletonCount(t, o) + 2) * BytesPerWord, 0, 0, start);
|
||||||
} else {
|
} else if (start == 0) {
|
||||||
w->visit(0);
|
w->visit(0);
|
||||||
}
|
}
|
||||||
} else {
|
} else if (start == 0) {
|
||||||
w->visit(0);
|
w->visit(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1616,7 +1608,7 @@ class HeapClient: public Heap::Client {
|
|||||||
|
|
||||||
virtual void walk(void* p, Heap::Walker* w) {
|
virtual void walk(void* p, Heap::Walker* w) {
|
||||||
object o = static_cast<object>(m->heap->follow(mask(p)));
|
object o = static_cast<object>(m->heap->follow(mask(p)));
|
||||||
::walk(m->rootThread, w, o);
|
::walk(m->rootThread, w, o, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
@ -2772,6 +2764,44 @@ collect(Thread* t, Heap::CollectionType type)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
walkNext(Thread* t, object o, int previous)
|
||||||
|
{
|
||||||
|
class Walker: public Heap::Walker {
|
||||||
|
public:
|
||||||
|
Walker(): value(-1) { }
|
||||||
|
|
||||||
|
bool visit(unsigned offset) {
|
||||||
|
value = offset;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int value;
|
||||||
|
} walker;
|
||||||
|
|
||||||
|
walk(t, &walker, o, previous + 1);
|
||||||
|
return walker.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
visitRoots(Thread* t, Heap::Visitor* v)
|
||||||
|
{
|
||||||
|
if (t->state != Thread::ZombieState) {
|
||||||
|
v->visit(&(t->javaThread));
|
||||||
|
v->visit(&(t->exception));
|
||||||
|
|
||||||
|
t->m->processor->visitObjects(t, v);
|
||||||
|
|
||||||
|
for (Thread::Protector* p = t->protector; p; p = p->next) {
|
||||||
|
p->visit(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Thread* c = t->child; c; c = c->peer) {
|
||||||
|
visitRoots(c, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
printTrace(Thread* t, object exception)
|
printTrace(Thread* t, object exception)
|
||||||
{
|
{
|
||||||
|
@ -2204,6 +2204,12 @@ intern(Thread* t, object s);
|
|||||||
void
|
void
|
||||||
exit(Thread* t);
|
exit(Thread* t);
|
||||||
|
|
||||||
|
int
|
||||||
|
walkNext(Thread* t, object o, int previous);
|
||||||
|
|
||||||
|
void
|
||||||
|
visitRoots(Thread* t, Heap::Visitor* v);
|
||||||
|
|
||||||
inline jobject
|
inline jobject
|
||||||
makeLocalReference(Thread* t, object o)
|
makeLocalReference(Thread* t, object o)
|
||||||
{
|
{
|
||||||
@ -2297,6 +2303,9 @@ makeSingleton(Thread* t, unsigned count)
|
|||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
dumpHeap(Thread* t, FILE* out);
|
||||||
|
|
||||||
} // namespace vm
|
} // namespace vm
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
public class Hello {
|
public class Hello {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
System.out.println("hello, world!");
|
System.out.println("hello, world!");
|
||||||
|
try {
|
||||||
|
Runtime.class.getMethod("dumpHeap", String.class)
|
||||||
|
.invoke(null, "/tmp/heap.bin");
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user