ART: Add object reclaim support
Add support for the object free callback.
Refactor code in support. Add test.
Bug: 31384923
Test: m test-art-host
Test: m test-art-host-run-test-905-object-free
Test: m ART_USE_READ_BARRIER=true test-art-host-run-test-905-object-free
Change-Id: Icde40ad464c723cfe2f7660c07cd2f79b6dc2032
diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp
index e1f9ba2..2d9149a 100644
--- a/runtime/openjdkjvmti/Android.bp
+++ b/runtime/openjdkjvmti/Android.bp
@@ -18,6 +18,7 @@
defaults: ["art_defaults"],
host_supported: true,
srcs: ["events.cc",
+ "object_tagging.cc",
"OpenjdkJvmTi.cc",
"transform.cc"],
include_dirs: ["art/runtime"],
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 6812a92..36be2a0 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -54,8 +54,8 @@
namespace openjdkjvmti {
-ObjectTagTable gObjectTagTable;
EventHandler gEventHandler;
+ObjectTagTable gObjectTagTable(&gEventHandler);
class JvmtiFunctions {
private:
diff --git a/runtime/openjdkjvmti/object_tagging.cc b/runtime/openjdkjvmti/object_tagging.cc
new file mode 100644
index 0000000..bb17cac
--- /dev/null
+++ b/runtime/openjdkjvmti/object_tagging.cc
@@ -0,0 +1,127 @@
+/* Copyright (C) 2016 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 "object_tagging.h"
+
+#include "art_jvmti.h"
+#include "base/logging.h"
+#include "events-inl.h"
+#include "gc/allocation_listener.h"
+#include "instrumentation.h"
+#include "jni_env_ext-inl.h"
+#include "mirror/class.h"
+#include "mirror/object.h"
+#include "runtime.h"
+#include "ScopedLocalRef.h"
+
+namespace openjdkjvmti {
+
+void ObjectTagTable::Add(art::mirror::Object* obj, jlong tag) {
+ art::Thread* self = art::Thread::Current();
+ art::MutexLock mu(self, allow_disallow_lock_);
+ Wait(self);
+
+ if (first_free_ == tagged_objects_.size()) {
+ tagged_objects_.push_back(Entry(art::GcRoot<art::mirror::Object>(obj), tag));
+ first_free_++;
+ } else {
+ DCHECK_LT(first_free_, tagged_objects_.size());
+ DCHECK(tagged_objects_[first_free_].first.IsNull());
+ tagged_objects_[first_free_] = Entry(art::GcRoot<art::mirror::Object>(obj), tag);
+ // TODO: scan for free elements.
+ first_free_ = tagged_objects_.size();
+ }
+}
+
+bool ObjectTagTable::Remove(art::mirror::Object* obj, jlong* tag) {
+ art::Thread* self = art::Thread::Current();
+ art::MutexLock mu(self, allow_disallow_lock_);
+ Wait(self);
+
+ for (auto it = tagged_objects_.begin(); it != tagged_objects_.end(); ++it) {
+ if (it->first.Read(nullptr) == obj) {
+ if (tag != nullptr) {
+ *tag = it->second;
+ }
+ it->first = art::GcRoot<art::mirror::Object>(nullptr);
+
+ size_t index = it - tagged_objects_.begin();
+ if (index < first_free_) {
+ first_free_ = index;
+ }
+
+ // TODO: compaction.
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void ObjectTagTable::Sweep(art::IsMarkedVisitor* visitor) {
+ if (event_handler_->IsEventEnabledAnywhere(JVMTI_EVENT_OBJECT_FREE)) {
+ SweepImpl<true>(visitor);
+ } else {
+ SweepImpl<false>(visitor);
+ }
+}
+
+template <bool kHandleNull>
+void ObjectTagTable::SweepImpl(art::IsMarkedVisitor* visitor) {
+ art::Thread* self = art::Thread::Current();
+ art::MutexLock mu(self, allow_disallow_lock_);
+
+ for (auto it = tagged_objects_.begin(); it != tagged_objects_.end(); ++it) {
+ if (!it->first.IsNull()) {
+ art::mirror::Object* original_obj = it->first.Read<art::kWithoutReadBarrier>();
+ art::mirror::Object* target_obj = visitor->IsMarked(original_obj);
+ if (original_obj != target_obj) {
+ it->first = art::GcRoot<art::mirror::Object>(target_obj);
+
+ if (kHandleNull && target_obj == nullptr) {
+ HandleNullSweep(it->second);
+ }
+ }
+ } else {
+ size_t index = it - tagged_objects_.begin();
+ if (index < first_free_) {
+ first_free_ = index;
+ }
+ }
+ }
+}
+
+void ObjectTagTable::HandleNullSweep(jlong tag) {
+ event_handler_->DispatchEvent(nullptr, JVMTI_EVENT_OBJECT_FREE, tag);
+}
+
+} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/object_tagging.h b/runtime/openjdkjvmti/object_tagging.h
index 523e15f..45f3e4f 100644
--- a/runtime/openjdkjvmti/object_tagging.h
+++ b/runtime/openjdkjvmti/object_tagging.h
@@ -25,29 +25,22 @@
namespace openjdkjvmti {
+class EventHandler;
+
class ObjectTagTable : public art::gc::SystemWeakHolder {
public:
- ObjectTagTable() : art::gc::SystemWeakHolder(art::LockLevel::kAllocTrackerLock) {
+ explicit ObjectTagTable(EventHandler* event_handler)
+ : art::gc::SystemWeakHolder(art::LockLevel::kAllocTrackerLock),
+ event_handler_(event_handler) {
}
void Add(art::mirror::Object* obj, jlong tag)
REQUIRES_SHARED(art::Locks::mutator_lock_)
- REQUIRES(!allow_disallow_lock_) {
- art::Thread* self = art::Thread::Current();
- art::MutexLock mu(self, allow_disallow_lock_);
- Wait(self);
+ REQUIRES(!allow_disallow_lock_);
- if (first_free_ == tagged_objects_.size()) {
- tagged_objects_.push_back(Entry(art::GcRoot<art::mirror::Object>(obj), tag));
- first_free_++;
- } else {
- DCHECK_LT(first_free_, tagged_objects_.size());
- DCHECK(tagged_objects_[first_free_].first.IsNull());
- tagged_objects_[first_free_] = Entry(art::GcRoot<art::mirror::Object>(obj), tag);
- // TODO: scan for free elements.
- first_free_ = tagged_objects_.size();
- }
- }
+ bool Remove(art::mirror::Object* obj, jlong* tag)
+ REQUIRES_SHARED(art::Locks::mutator_lock_)
+ REQUIRES(!allow_disallow_lock_);
bool GetTag(art::mirror::Object* obj, jlong* result)
REQUIRES_SHARED(art::Locks::mutator_lock_)
@@ -66,70 +59,23 @@
return false;
}
- bool Remove(art::mirror::Object* obj, jlong* tag)
- REQUIRES_SHARED(art::Locks::mutator_lock_)
- REQUIRES(!allow_disallow_lock_) {
- art::Thread* self = art::Thread::Current();
- art::MutexLock mu(self, allow_disallow_lock_);
- Wait(self);
-
- for (auto it = tagged_objects_.begin(); it != tagged_objects_.end(); ++it) {
- if (it->first.Read(nullptr) == obj) {
- if (tag != nullptr) {
- *tag = it->second;
- }
- it->first = art::GcRoot<art::mirror::Object>(nullptr);
-
- size_t index = it - tagged_objects_.begin();
- if (index < first_free_) {
- first_free_ = index;
- }
-
- // TODO: compaction.
-
- return true;
- }
- }
-
- return false;
- }
-
void Sweep(art::IsMarkedVisitor* visitor)
REQUIRES_SHARED(art::Locks::mutator_lock_)
- REQUIRES(!allow_disallow_lock_) {
- art::Thread* self = art::Thread::Current();
- art::MutexLock mu(self, allow_disallow_lock_);
-
- for (auto it = tagged_objects_.begin(); it != tagged_objects_.end(); ++it) {
- if (!it->first.IsNull()) {
- art::mirror::Object* original_obj = it->first.Read<art::kWithoutReadBarrier>();
- art::mirror::Object* target_obj = visitor->IsMarked(original_obj);
- if (original_obj != target_obj) {
- it->first = art::GcRoot<art::mirror::Object>(target_obj);
-
- if (target_obj == nullptr) {
- HandleNullSweep(it->second);
- }
- }
- } else {
- size_t index = it - tagged_objects_.begin();
- if (index < first_free_) {
- first_free_ = index;
- }
- }
- }
- }
+ REQUIRES(!allow_disallow_lock_);
private:
using Entry = std::pair<art::GcRoot<art::mirror::Object>, jlong>;
- void HandleNullSweep(jlong tag ATTRIBUTE_UNUSED) {
- // TODO: Handle deallocation reporting here. We'll have to enqueue tags temporarily, as we
- // probably shouldn't call the callbacks directly (to avoid any issues).
- }
+ template <bool kHandleNull>
+ void SweepImpl(art::IsMarkedVisitor* visitor)
+ REQUIRES_SHARED(art::Locks::mutator_lock_)
+ REQUIRES(!allow_disallow_lock_);
+ void HandleNullSweep(jlong tag);
std::vector<Entry> tagged_objects_ GUARDED_BY(allow_disallow_lock_);
size_t first_free_ = 0;
+
+ EventHandler* event_handler_;
};
} // namespace openjdkjvmti