diff options
author | 2018-10-31 23:13:32 +0000 | |
---|---|---|
committer | 2018-10-31 23:13:32 +0000 | |
commit | 61afd95d12ed53e776b8ea847d94dedf98c78146 (patch) | |
tree | df6ab853427ab64353933b50637bffb584938274 | |
parent | 97d40f9c7b1157783d09861ce76d3a0b3da73cbe (diff) | |
parent | 4e7077d795ab94d40c76ae7b9245fd5dcbb7716c (diff) |
Merge "Revert^2 "Implement LockSupport.park with a futex""
-rw-r--r-- | runtime/native/sun_misc_Unsafe.cc | 31 | ||||
-rw-r--r-- | runtime/thread.cc | 143 | ||||
-rw-r--r-- | runtime/thread.h | 7 | ||||
-rw-r--r-- | runtime/well_known_classes.cc | 2 | ||||
-rw-r--r-- | runtime/well_known_classes.h | 1 | ||||
-rw-r--r-- | test/004-ThreadStress/src-art/Main.java | 36 | ||||
-rw-r--r-- | test/913-heaps/expected.txt | 52 |
7 files changed, 238 insertions, 34 deletions
diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc index e021b77dae..a739c2d16e 100644 --- a/runtime/native/sun_misc_Unsafe.cc +++ b/runtime/native/sun_misc_Unsafe.cc @@ -31,8 +31,10 @@ #include "mirror/array.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" +#include "art_field-inl.h" #include "native_util.h" #include "scoped_fast_native_object_access-inl.h" +#include "well_known_classes.h" namespace art { @@ -504,6 +506,33 @@ static void Unsafe_fullFence(JNIEnv*, jobject) { std::atomic_thread_fence(std::memory_order_seq_cst); } +static void Unsafe_park(JNIEnv* env, jobject, jboolean isAbsolute, jlong time) { + ScopedObjectAccess soa(env); + Thread::Current()->Park(isAbsolute, time); +} + +static void Unsafe_unpark(JNIEnv* env, jobject, jobject jthread) { + art::ScopedFastNativeObjectAccess soa(env); + if (jthread == nullptr || !env->IsInstanceOf(jthread, WellKnownClasses::java_lang_Thread)) { + ThrowIllegalArgumentException("Argument to unpark() was not a Thread"); + return; + } + art::MutexLock mu(soa.Self(), *art::Locks::thread_list_lock_); + art::Thread* thread = art::Thread::FromManagedThread(soa, jthread); + if (thread != nullptr) { + thread->Unpark(); + } else { + // If thread is null, that means that either the thread is not started yet, + // or the thread has already terminated. Setting the field to true will be + // respected when the thread does start, and is harmless if the thread has + // already terminated. + ArtField* unparked = + jni::DecodeArtField(WellKnownClasses::java_lang_Thread_unparkedBeforeStart); + // JNI must use non transactional mode. + unparked->SetBoolean<false>(soa.Decode<mirror::Object>(jthread), JNI_TRUE); + } +} + static JNINativeMethod gMethods[] = { FAST_NATIVE_METHOD(Unsafe, compareAndSwapInt, "(Ljava/lang/Object;JII)Z"), FAST_NATIVE_METHOD(Unsafe, compareAndSwapLong, "(Ljava/lang/Object;JJJ)Z"), @@ -546,6 +575,8 @@ static JNINativeMethod gMethods[] = { FAST_NATIVE_METHOD(Unsafe, putShort, "(Ljava/lang/Object;JS)V"), FAST_NATIVE_METHOD(Unsafe, putFloat, "(Ljava/lang/Object;JF)V"), FAST_NATIVE_METHOD(Unsafe, putDouble, "(Ljava/lang/Object;JD)V"), + FAST_NATIVE_METHOD(Unsafe, unpark, "(Ljava/lang/Object;)V"), + NATIVE_METHOD(Unsafe, park, "(ZJ)V"), // Each of the getFoo variants are overloaded with a call that operates // directively on a native pointer. diff --git a/runtime/thread.cc b/runtime/thread.cc index b5d214def4..12ec5a99fd 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -44,6 +44,7 @@ #include "arch/context.h" #include "art_field-inl.h" #include "art_method-inl.h" +#include "base/atomic.h" #include "base/bit_utils.h" #include "base/casts.h" #include "base/file_utils.h" @@ -285,6 +286,116 @@ void Thread::AssertHasDeoptimizationContext() { << "No deoptimization context for thread " << *this; } +enum { + kPermitAvailable = 0, // Incrementing consumes the permit + kNoPermit = 1, // Incrementing marks as waiter waiting + kNoPermitWaiterWaiting = 2 +}; + +void Thread::Park(bool is_absolute, int64_t time) { + DCHECK(this == Thread::Current()); +#if ART_USE_FUTEXES + // Consume the permit, or mark as waiting. This cannot cause park_state to go + // outside of its valid range (0, 1, 2), because in all cases where 2 is + // assigned it is set back to 1 before returning, and this method cannot run + // concurrently with itself since it operates on the current thread. + int old_state = tls32_.park_state_.fetch_add(1, std::memory_order_relaxed); + if (old_state == kNoPermit) { + // no permit was available. block thread until later. + // TODO: Call to signal jvmti here + int result = 0; + if (!is_absolute && time == 0) { + // Thread.getState() is documented to return waiting for untimed parks. + ScopedThreadSuspension sts(this, ThreadState::kWaiting); + DCHECK_EQ(NumberOfHeldMutexes(), 0u); + result = futex(tls32_.park_state_.Address(), + FUTEX_WAIT_PRIVATE, + /* sleep if val = */ kNoPermitWaiterWaiting, + /* timeout */ nullptr, + nullptr, + 0); + } else if (time > 0) { + // Only actually suspend and futex_wait if we're going to wait for some + // positive amount of time - the kernel will reject negative times with + // EINVAL, and a zero time will just noop. + + // Thread.getState() is documented to return timed wait for timed parks. + ScopedThreadSuspension sts(this, ThreadState::kTimedWaiting); + DCHECK_EQ(NumberOfHeldMutexes(), 0u); + timespec timespec; + if (is_absolute) { + // Time is millis when scheduled for an absolute time + timespec.tv_nsec = (time % 1000) * 1000000; + timespec.tv_sec = time / 1000; + // This odd looking pattern is recommended by futex documentation to + // wait until an absolute deadline, with otherwise identical behavior to + // FUTEX_WAIT_PRIVATE. This also allows parkUntil() to return at the + // correct time when the system clock changes. + result = futex(tls32_.park_state_.Address(), + FUTEX_WAIT_BITSET_PRIVATE | FUTEX_CLOCK_REALTIME, + /* sleep if val = */ kNoPermitWaiterWaiting, + ×pec, + nullptr, + FUTEX_BITSET_MATCH_ANY); + } else { + // Time is nanos when scheduled for a relative time + timespec.tv_sec = time / 1000000000; + timespec.tv_nsec = time % 1000000000; + result = futex(tls32_.park_state_.Address(), + FUTEX_WAIT_PRIVATE, + /* sleep if val = */ kNoPermitWaiterWaiting, + ×pec, + nullptr, + 0); + } + } + if (result == -1) { + switch (errno) { + case EAGAIN: + case ETIMEDOUT: + case EINTR: break; // park() is allowed to spuriously return + default: PLOG(FATAL) << "Failed to park"; + } + } + // Mark as no longer waiting, and consume permit if there is one. + tls32_.park_state_.store(kNoPermit, std::memory_order_relaxed); + // TODO: Call to signal jvmti here + } else { + // the fetch_add has consumed the permit. immediately return. + DCHECK_EQ(old_state, kPermitAvailable); + } +#else + #pragma clang diagnostic push + #pragma clang diagnostic warning "-W#warnings" + #warning "LockSupport.park/unpark implemented as noops without FUTEX support." + #pragma clang diagnostic pop + UNUSED(is_absolute, time); + UNIMPLEMENTED(WARNING); + sched_yield(); +#endif +} + +void Thread::Unpark() { +#if ART_USE_FUTEXES + // Set permit available; will be consumed either by fetch_add (when the thread + // tries to park) or store (when the parked thread is woken up) + if (tls32_.park_state_.exchange(kPermitAvailable, std::memory_order_relaxed) + == kNoPermitWaiterWaiting) { + int result = futex(tls32_.park_state_.Address(), + FUTEX_WAKE_PRIVATE, + /* number of waiters = */ 1, + nullptr, + nullptr, + 0); + if (result == -1) { + PLOG(FATAL) << "Failed to unpark"; + } + } +#else + UNIMPLEMENTED(WARNING); +#endif +} + void Thread::PushStackedShadowFrame(ShadowFrame* sf, StackedShadowFrameType type) { StackedShadowFrameRecord* record = new StackedShadowFrameRecord( sf, type, tlsPtr_.stacked_shadow_frame_record); @@ -489,6 +600,22 @@ void* Thread::CreateCallback(void* arg) { runtime->GetRuntimeCallbacks()->ThreadStart(self); + // Unpark ourselves if the java peer was unparked before it started (see + // b/28845097#comment49 for more information) + + ArtField* unparkedField = jni::DecodeArtField( + WellKnownClasses::java_lang_Thread_unparkedBeforeStart); + bool should_unpark = false; + { + // Hold the lock here, so that if another thread calls unpark before the thread starts + // we don't observe the unparkedBeforeStart field before the unparker writes to it, + // which could cause a lost unpark. + art::MutexLock mu(soa.Self(), *art::Locks::thread_list_lock_); + should_unpark = unparkedField->GetBoolean(self->tlsPtr_.opeer) == JNI_TRUE; + } + if (should_unpark) { + self->Unpark(); + } // Invoke the 'run' method of our java.lang.Thread. ObjPtr<mirror::Object> receiver = self->tlsPtr_.opeer; jmethodID mid = WellKnownClasses::java_lang_Thread_run; @@ -2133,6 +2260,9 @@ Thread::Thread(bool daemon) tls32_.state_and_flags.as_struct.flags = 0; tls32_.state_and_flags.as_struct.state = kNative; tls32_.interrupted.store(false, std::memory_order_relaxed); + // Initialize with no permit; if the java Thread was unparked before being + // started, it will unpark itself before calling into java code. + tls32_.park_state_.store(kNoPermit, std::memory_order_relaxed); memset(&tlsPtr_.held_mutexes[0], 0, sizeof(tlsPtr_.held_mutexes)); std::fill(tlsPtr_.rosalloc_runs, tlsPtr_.rosalloc_runs + kNumRosAllocThreadLocalSizeBracketsInThread, @@ -2449,12 +2579,15 @@ bool Thread::IsInterrupted() { } void Thread::Interrupt(Thread* self) { - MutexLock mu(self, *wait_mutex_); - if (tls32_.interrupted.load(std::memory_order_seq_cst)) { - return; + { + MutexLock mu(self, *wait_mutex_); + if (tls32_.interrupted.load(std::memory_order_seq_cst)) { + return; + } + tls32_.interrupted.store(true, std::memory_order_seq_cst); + NotifyLocked(self); } - tls32_.interrupted.store(true, std::memory_order_seq_cst); - NotifyLocked(self); + Unpark(); } void Thread::Notify() { diff --git a/runtime/thread.h b/runtime/thread.h index 941867ce2d..b304cef74d 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -581,6 +581,11 @@ class Thread { return poison_object_cookie_; } + // Parking for 0ns of relative time means an untimed park, negative (though + // should be handled in java code) returns immediately + void Park(bool is_absolute, int64_t time) REQUIRES_SHARED(Locks::mutator_lock_); + void Unpark(); + private: void NotifyLocked(Thread* self) REQUIRES(wait_mutex_); @@ -1543,6 +1548,8 @@ class Thread { // Thread "interrupted" status; stays raised until queried or thrown. Atomic<bool32_t> interrupted; + AtomicInteger park_state_; + // True if the thread is allowed to access a weak ref (Reference::GetReferent() and system // weaks) and to potentially mark an object alive/gray. This is used for concurrent reference // processing of the CC collector only. This is thread local so that we can enable/disable weak diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 206418fbc6..94faa626f6 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -128,6 +128,7 @@ jfieldID WellKnownClasses::java_lang_Thread_lock; jfieldID WellKnownClasses::java_lang_Thread_name; jfieldID WellKnownClasses::java_lang_Thread_priority; jfieldID WellKnownClasses::java_lang_Thread_nativePeer; +jfieldID WellKnownClasses::java_lang_Thread_unparkedBeforeStart; jfieldID WellKnownClasses::java_lang_ThreadGroup_groups; jfieldID WellKnownClasses::java_lang_ThreadGroup_ngroups; jfieldID WellKnownClasses::java_lang_ThreadGroup_mainThreadGroup; @@ -376,6 +377,7 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_Thread_name = CacheField(env, java_lang_Thread, false, "name", "Ljava/lang/String;"); java_lang_Thread_priority = CacheField(env, java_lang_Thread, false, "priority", "I"); java_lang_Thread_nativePeer = CacheField(env, java_lang_Thread, false, "nativePeer", "J"); + java_lang_Thread_unparkedBeforeStart = CacheField(env, java_lang_Thread, false, "unparkedBeforeStart", "Z"); java_lang_ThreadGroup_groups = CacheField(env, java_lang_ThreadGroup, false, "groups", "[Ljava/lang/ThreadGroup;"); java_lang_ThreadGroup_ngroups = CacheField(env, java_lang_ThreadGroup, false, "ngroups", "I"); java_lang_ThreadGroup_mainThreadGroup = CacheField(env, java_lang_ThreadGroup, true, "mainThreadGroup", "Ljava/lang/ThreadGroup;"); diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index ce5ab1df84..8c85228dfc 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -137,6 +137,7 @@ struct WellKnownClasses { static jfieldID java_lang_Thread_name; static jfieldID java_lang_Thread_priority; static jfieldID java_lang_Thread_nativePeer; + static jfieldID java_lang_Thread_unparkedBeforeStart; static jfieldID java_lang_ThreadGroup_groups; static jfieldID java_lang_ThreadGroup_ngroups; static jfieldID java_lang_ThreadGroup_mainThreadGroup; diff --git a/test/004-ThreadStress/src-art/Main.java b/test/004-ThreadStress/src-art/Main.java index 3a89f4f166..b8bfafb2d3 100644 --- a/test/004-ThreadStress/src-art/Main.java +++ b/test/004-ThreadStress/src-art/Main.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Semaphore; +import java.util.concurrent.locks.LockSupport; // Run on host with: // javac ThreadTest.java && java ThreadStress && rm *.class @@ -251,6 +252,31 @@ public class Main implements Runnable { } } + private final static class TimedPark extends Operation { + private final static int SLEEP_TIME = 100; + + public TimedPark() {} + + @Override + public boolean perform() { + LockSupport.parkNanos(this, 100*1000000); + return true; + } + } + + private final static class UnparkAllThreads extends Operation { + public UnparkAllThreads() {} + + @Override + public boolean perform() { + Set<Thread> threads = Thread.getAllStackTraces().keySet(); + for (Thread candidate : threads) { + LockSupport.unpark(candidate); + } + return true; + } + } + private final static class SyncAndWork extends Operation { private final Object lock; @@ -320,7 +346,9 @@ public class Main implements Runnable { frequencyMap.put(new NonMovingAlloc(), 0.025); // 5/200 frequencyMap.put(new StackTrace(), 0.1); // 20/200 frequencyMap.put(new Exit(), 0.225); // 45/200 - frequencyMap.put(new Sleep(), 0.125); // 25/200 + frequencyMap.put(new Sleep(), 0.125); // 15/200 + frequencyMap.put(new TimedPark(), 0.025); // 5/200 + frequencyMap.put(new UnparkAllThreads(), 0.025); // 5/200 frequencyMap.put(new TimedWait(lock), 0.05); // 10/200 frequencyMap.put(new Wait(lock), 0.075); // 15/200 frequencyMap.put(new QueuedWait(semaphore), 0.05); // 10/200 @@ -341,9 +369,11 @@ public class Main implements Runnable { private final static Map<Operation, Double> createLockFrequencyMap(Object lock) { Map<Operation, Double> frequencyMap = new HashMap<Operation, Double>(); frequencyMap.put(new Sleep(), 0.2); // 40/200 - frequencyMap.put(new TimedWait(lock), 0.2); // 40/200 - frequencyMap.put(new Wait(lock), 0.2); // 40/200 + frequencyMap.put(new TimedWait(lock), 0.1); // 20/200 + frequencyMap.put(new Wait(lock), 0.1); // 20/200 frequencyMap.put(new SyncAndWork(lock), 0.4); // 80/200 + frequencyMap.put(new TimedPark(), 0.1); // 20/200 + frequencyMap.put(new UnparkAllThreads(), 0.1); // 20/200 return frequencyMap; } diff --git a/test/913-heaps/expected.txt b/test/913-heaps/expected.txt index 065b854a6a..1bd56d1a53 100644 --- a/test/913-heaps/expected.txt +++ b/test/913-heaps/expected.txt @@ -1,9 +1,9 @@ --- true true -root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=132, length=-1] +root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=124, length=-1] root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=8,location= 31])--> 1@1000 [size=16, length=-1] -root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1] -root@root --(thread)--> 3000@0 [size=132, length=-1] +root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=124, length=-1] +root@root --(thread)--> 3000@0 [size=124, length=-1] 1001@0 --(superclass)--> 1000@0 [size=123456780000, length=-1] 1002@0 --(interface)--> 2001@0 [size=123456780004, length=-1] 1002@0 --(superclass)--> 1001@0 [size=123456780001, length=-1] @@ -44,14 +44,14 @@ root@root --(thread)--> 3000@0 [size=132, length=-1] --- root@root --(jni-global)--> 1@1000 [size=16, length=-1] root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1] -root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=132, length=-1] +root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=124, length=-1] root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=10,location= 8])--> 1@1000 [size=16, length=-1] root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 8])--> 1@1000 [size=16, length=-1] root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1] root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 20])--> 1@1000 [size=16, length=-1] -root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1] +root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=124, length=-1] root@root --(thread)--> 1@1000 [size=16, length=-1] -root@root --(thread)--> 3000@0 [size=132, length=-1] +root@root --(thread)--> 3000@0 [size=124, length=-1] 1001@0 --(superclass)--> 1000@0 [size=123456780005, length=-1] 1002@0 --(interface)--> 2001@0 [size=123456780009, length=-1] 1002@0 --(superclass)--> 1001@0 [size=123456780006, length=-1] @@ -90,18 +90,18 @@ root@root --(thread)--> 3000@0 [size=132, length=-1] 5@1002 --(field@9)--> 6@1000 [size=16, length=-1] 6@1000 --(class)--> 1000@0 [size=123456780005, length=-1] --- -root@root --(thread)--> 3000@0 [size=132, length=-1] +root@root --(thread)--> 3000@0 [size=124, length=-1] --- 3@1001 --(class)--> 1001@0 [size=123456780011, length=-1] --- -root@root --(thread)--> 3000@0 [size=132, length=-1] +root@root --(thread)--> 3000@0 [size=124, length=-1] --- 3@1001 --(class)--> 1001@0 [size=123456780016, length=-1] --- -root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=132, length=-1] +root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=124, length=-1] root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=8,location= 31])--> 1@1000 [size=16, length=-1] -root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1] -root@root --(thread)--> 3000@0 [size=132, length=-1] +root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=124, length=-1] +root@root --(thread)--> 3000@0 [size=124, length=-1] --- 1001@0 --(superclass)--> 1000@0 [size=123456780020, length=-1] 3@1001 --(class)--> 1001@0 [size=123456780021, length=-1] @@ -110,14 +110,14 @@ root@root --(thread)--> 3000@0 [size=132, length=-1] --- root@root --(jni-global)--> 1@1000 [size=16, length=-1] root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1] -root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=132, length=-1] +root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=124, length=-1] root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=10,location= 8])--> 1@1000 [size=16, length=-1] root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 8])--> 1@1000 [size=16, length=-1] root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1] root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 20])--> 1@1000 [size=16, length=-1] -root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1] +root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=124, length=-1] root@root --(thread)--> 1@1000 [size=16, length=-1] -root@root --(thread)--> 3000@0 [size=132, length=-1] +root@root --(thread)--> 3000@0 [size=124, length=-1] --- 1001@0 --(superclass)--> 1000@0 [size=123456780025, length=-1] 3@1001 --(class)--> 1001@0 [size=123456780026, length=-1] @@ -198,10 +198,10 @@ root@root --(thread)--> 1@1000 [size=16, length=-1] --- --- ---- untagged objects -root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=132, length=-1] +root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=124, length=-1] root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=8,location= 31])--> 1@1000 [size=16, length=-1] -root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1] -root@root --(thread)--> 3000@0 [size=132, length=-1] +root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=124, length=-1] +root@root --(thread)--> 3000@0 [size=124, length=-1] 1001@0 --(superclass)--> 1000@0 [size=123456780050, length=-1] 1002@0 --(interface)--> 2001@0 [size=123456780054, length=-1] 1002@0 --(superclass)--> 1001@0 [size=123456780051, length=-1] @@ -242,14 +242,14 @@ root@root --(thread)--> 3000@0 [size=132, length=-1] --- root@root --(jni-global)--> 1@1000 [size=16, length=-1] root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1] -root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=132, length=-1] +root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=124, length=-1] root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=10,location= 8])--> 1@1000 [size=16, length=-1] root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 8])--> 1@1000 [size=16, length=-1] root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1] root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 20])--> 1@1000 [size=16, length=-1] -root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1] +root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=124, length=-1] root@root --(thread)--> 1@1000 [size=16, length=-1] -root@root --(thread)--> 3000@0 [size=132, length=-1] +root@root --(thread)--> 3000@0 [size=124, length=-1] 1001@0 --(superclass)--> 1000@0 [size=123456780055, length=-1] 1002@0 --(interface)--> 2001@0 [size=123456780059, length=-1] 1002@0 --(superclass)--> 1001@0 [size=123456780056, length=-1] @@ -289,9 +289,9 @@ root@root --(thread)--> 3000@0 [size=132, length=-1] 6@1000 --(class)--> 1000@0 [size=123456780055, length=-1] --- ---- tagged classes -root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=132, length=-1] -root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1] -root@root --(thread)--> 3000@0 [size=132, length=-1] +root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=124, length=-1] +root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=124, length=-1] +root@root --(thread)--> 3000@0 [size=124, length=-1] 1001@0 --(superclass)--> 1000@0 [size=123456780060, length=-1] 1002@0 --(interface)--> 2001@0 [size=123456780064, length=-1] 1002@0 --(superclass)--> 1001@0 [size=123456780061, length=-1] @@ -316,9 +316,9 @@ root@root --(thread)--> 3000@0 [size=132, length=-1] 5@1002 --(field@8)--> 500@0 [size=20, length=2] 6@1000 --(class)--> 1000@0 [size=123456780060, length=-1] --- -root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=132, length=-1] -root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1] -root@root --(thread)--> 3000@0 [size=132, length=-1] +root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=124, length=-1] +root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=124, length=-1] +root@root --(thread)--> 3000@0 [size=124, length=-1] 1001@0 --(superclass)--> 1000@0 [size=123456780065, length=-1] 1002@0 --(interface)--> 2001@0 [size=123456780069, length=-1] 1002@0 --(superclass)--> 1001@0 [size=123456780066, length=-1] |