Remove blacklist

Removes the class initialization blacklist and use transaction to detect and
revert class initialization attempting to invoke native method. This only
concerns class initialization happening at compilation time when generating an
image (like boot.art for the system).

In transactional mode, we log every object's field assignment and array update.
Therefore we're able to abort a transaction to restore values of fields and
array as they were before the transaction starts. We also log changes to the
intern string table so we can restore its state prior to transaction start.

Since transactional mode only happens at compilation time, we don't need to log
all these changes at runtime. In order to reduce the overhead of testing if
transactional mode is on/off, we templatize interfaces of mirror::Object and
mirror::Array, respectively responsible for setting a field and setting an
array element.

For various reasons, we skip some specific fields from transaction:
- Object's class and array's length must remain unchanged so garbage collector
can compute object's size.
- Immutable fields only set during class loading: list of fields, method,
dex caches, vtables, ... as all classes have been loaded and verified before a
transaction occurs.
- Object's monitor for performance reason.

Before generating the image, we browse the heap to collect objects that need to
be written into it. Since the heap may still holds references to unreachable
objects due to aborted transactions, we trigger one collection at the end of
the class preinitialization phase.

Since the transaction is held by the runtime and all compilation threads share
the same runtime, we need to ensure only one compilation thread has exclusive
access to the runtime. To workaround this issue, we force class initialization
phase to run with only one thread. Note this is only done when generating image
so application compilation is not impacted. This issue will be addressed in a
separate CL.

Bug: 9676614
Change-Id: I221910a9183a5ba6c2b99a277f5a5a68bc69b5f9
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 223ae7c..b6b4ff9 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -136,6 +136,7 @@
 	thread_pool.cc \
 	throw_location.cc \
 	trace.cc \
+	transaction.cc \
 	profiler.cc \
 	utf.cc \
 	utils.cc \
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index fac1e14..3863ee5 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -2832,8 +2832,8 @@
       return nullptr;
     }
 
-    interfaces_sfield->SetObject(klass.get(), soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces));
-    throws_sfield->SetObject(klass.get(), soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class> >*>(throws));
+    interfaces_sfield->SetObject<false>(klass.get(), soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces));
+    throws_sfield->SetObject<false>(klass.get(), soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class> >*>(throws));
     klass->SetStatus(mirror::Class::kStatusInitialized, self);
   }
 
@@ -3144,7 +3144,11 @@
       SafeMap<uint32_t, mirror::ArtField*> field_map;
       ConstructFieldMap(dex_file, *dex_class_def, klass.get(), field_map);
       for (size_t i = 0; it.HasNext(); i++, it.Next()) {
-        it.ReadValueToField(field_map.Get(i));
+        if (Runtime::Current()->IsActiveTransaction()) {
+          it.ReadValueToField<true>(field_map.Get(i));
+        } else {
+          it.ReadValueToField<false>(field_map.Get(i));
+        }
       }
     }
   }
@@ -3530,7 +3534,7 @@
                                 super_mh.GetDeclaringClassDescriptor());
               return false;
             }
-            vtable->Set(j, local_method);
+            vtable->Set<false>(j, local_method);
             local_method->SetMethodIndex(j);
             break;
           } else {
@@ -3542,7 +3546,7 @@
       }
       if (j == actual_count) {
         // Not overriding, append.
-        vtable->Set(actual_count, local_method);
+        vtable->Set<false>(actual_count, local_method);
         local_method->SetMethodIndex(actual_count);
         actual_count += 1;
       }
@@ -3576,7 +3580,7 @@
     }
     for (size_t i = 0; i < num_virtual_methods; ++i) {
       mirror::ArtMethod* virtual_method = klass->GetVirtualMethodDuringLinking(i);
-      vtable->Set(i, virtual_method);
+      vtable->Set<false>(i, virtual_method);
       virtual_method->SetMethodIndex(i & 0xFFFF);
     }
     klass->SetVTable(vtable.get());
@@ -3745,14 +3749,14 @@
                                       PrettyMethod(interface_method).c_str());
               return false;
             }
-            method_array->Set(j, vtable_method);
+            method_array->Set<false>(j, vtable_method);
             // Place method in imt if entry is empty, place conflict otherwise.
             uint32_t imt_index = interface_method->GetDexMethodIndex() % kImtSize;
             if (imtable->Get(imt_index) == NULL) {
-              imtable->Set(imt_index, vtable_method);
+              imtable->Set<false>(imt_index, vtable_method);
               imtable_changed = true;
             } else {
-              imtable->Set(imt_index, Runtime::Current()->GetImtConflictMethod());
+              imtable->Set<false>(imt_index, Runtime::Current()->GetImtConflictMethod());
             }
             break;
           }
@@ -3777,7 +3781,7 @@
             // TODO: If a methods move then the miranda_list may hold stale references.
             miranda_list.push_back(miranda_method.get());
           }
-          method_array->Set(j, miranda_method.get());
+          method_array->Set<false>(j, miranda_method.get());
         }
       }
     }
@@ -3787,7 +3791,7 @@
     mirror::ArtMethod* imt_conflict_method = Runtime::Current()->GetImtConflictMethod();
     for (size_t i = 0; i < kImtSize; i++) {
       if (imtable->Get(i) == NULL) {
-        imtable->Set(i, imt_conflict_method);
+        imtable->Set<false>(i, imt_conflict_method);
       }
     }
     klass->SetImTable(imtable.get());
@@ -3823,7 +3827,7 @@
       method->SetAccessFlags(method->GetAccessFlags() | kAccMiranda);
       method->SetMethodIndex(0xFFFF & (old_vtable_count + i));
       klass->SetVirtualMethod(old_method_count + i, method);
-      vtable->Set(old_vtable_count + i, method);
+      vtable->Set<false>(old_vtable_count + i, method);
     }
     // TODO: do not assign to the vtable field until it is fully constructed.
     klass->SetVTable(vtable.get());
@@ -3929,7 +3933,7 @@
     }
     grouped_and_sorted_fields.pop_front();
     num_reference_fields++;
-    fields->Set(current_field, field);
+    fields->Set<false>(current_field, field);
     field->SetOffset(field_offset);
     field_offset = MemberOffset(field_offset.Uint32Value() + sizeof(uint32_t));
   }
@@ -3946,7 +3950,7 @@
       if (type == Primitive::kPrimLong || type == Primitive::kPrimDouble) {
         continue;
       }
-      fields->Set(current_field++, field);
+      fields->Set<false>(current_field++, field);
       field->SetOffset(field_offset);
       // drop the consumed field
       grouped_and_sorted_fields.erase(grouped_and_sorted_fields.begin() + i);
@@ -3965,7 +3969,7 @@
     FieldHelper fh(field);
     Primitive::Type type = fh.GetTypeAsPrimitiveType();
     CHECK(type != Primitive::kPrimNot);  // should only be working on primitive types
-    fields->Set(current_field, field);
+    fields->Set<false>(current_field, field);
     field->SetOffset(field_offset);
     field_offset = MemberOffset(field_offset.Uint32Value() +
                                 ((type == Primitive::kPrimLong || type == Primitive::kPrimDouble)
@@ -4418,7 +4422,7 @@
 
   DCHECK(class_roots_ != NULL);
   DCHECK(class_roots_->Get(class_root) == NULL);
-  class_roots_->Set(class_root, klass);
+  class_roots_->Set<false>(class_root, klass);
 }
 
 }  // namespace art
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index d9ef0c1..28ed6c4 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -867,55 +867,56 @@
   EXPECT_STREQ(ClassHelper(s0->GetClass()).GetDescriptor(), "Ljava/lang/reflect/ArtField;");
   EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimBoolean);
   EXPECT_EQ(true, s0->GetBoolean(statics.get()));
-  s0->SetBoolean(statics.get(), false);
+  s0->SetBoolean<false>(statics.get(), false);
 
   mirror::ArtField* s1 = statics->FindStaticField("s1", "B");
   fh.ChangeField(s1);
   EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimByte);
   EXPECT_EQ(5, s1->GetByte(statics.get()));
-  s1->SetByte(statics.get(), 6);
+  s1->SetByte<false>(statics.get(), 6);
 
   mirror::ArtField* s2 = statics->FindStaticField("s2", "C");
   fh.ChangeField(s2);
   EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimChar);
   EXPECT_EQ('a', s2->GetChar(statics.get()));
-  s2->SetChar(statics.get(), 'b');
+  s2->SetChar<false>(statics.get(), 'b');
 
   mirror::ArtField* s3 = statics->FindStaticField("s3", "S");
   fh.ChangeField(s3);
   EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimShort);
   EXPECT_EQ(-536, s3->GetShort(statics.get()));
-  s3->SetShort(statics.get(), -535);
+  s3->SetShort<false>(statics.get(), -535);
 
   mirror::ArtField* s4 = statics->FindStaticField("s4", "I");
   fh.ChangeField(s4);
   EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimInt);
   EXPECT_EQ(2000000000, s4->GetInt(statics.get()));
-  s4->SetInt(statics.get(), 2000000001);
+  s4->SetInt<false>(statics.get(), 2000000001);
 
   mirror::ArtField* s5 = statics->FindStaticField("s5", "J");
   fh.ChangeField(s5);
   EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimLong);
   EXPECT_EQ(0x1234567890abcdefLL, s5->GetLong(statics.get()));
-  s5->SetLong(statics.get(), 0x34567890abcdef12LL);
+  s5->SetLong<false>(statics.get(), 0x34567890abcdef12LL);
 
   mirror::ArtField* s6 = statics->FindStaticField("s6", "F");
   fh.ChangeField(s6);
   EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimFloat);
   EXPECT_EQ(0.5, s6->GetFloat(statics.get()));
-  s6->SetFloat(statics.get(), 0.75);
+  s6->SetFloat<false>(statics.get(), 0.75);
 
   mirror::ArtField* s7 = statics->FindStaticField("s7", "D");
   fh.ChangeField(s7);
   EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimDouble);
   EXPECT_EQ(16777217, s7->GetDouble(statics.get()));
-  s7->SetDouble(statics.get(), 16777219);
+  s7->SetDouble<false>(statics.get(), 16777219);
 
   mirror::ArtField* s8 = statics->FindStaticField("s8", "Ljava/lang/String;");
   fh.ChangeField(s8);
   EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimNot);
   EXPECT_TRUE(s8->GetObject(statics.get())->AsString()->Equals("android"));
-  s8->SetObject(s8->GetDeclaringClass(), mirror::String::AllocFromModifiedUtf8(soa.Self(), "robot"));
+  s8->SetObject<false>(s8->GetDeclaringClass(),
+                       mirror::String::AllocFromModifiedUtf8(soa.Self(), "robot"));
 
   // TODO: Remove EXPECT_FALSE when GCC can handle EXPECT_EQ
   // http://code.google.com/p/googletest/issues/detail?id=322
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 733e843..20ad372 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -1149,7 +1149,7 @@
       if (o == ObjectRegistry::kInvalidObject) {
         return JDWP::ERR_INVALID_OBJECT;
       }
-      oa->Set(offset + i, o);
+      oa->Set<false>(offset + i, o);
     }
   }
 
@@ -1582,10 +1582,12 @@
   if (IsPrimitiveTag(tag)) {
     if (tag == JDWP::JT_DOUBLE || tag == JDWP::JT_LONG) {
       CHECK_EQ(width, 8);
-      f->Set64(o, value);
+      // Debugging can't use transactional mode (runtime only).
+      f->Set64<false>(o, value);
     } else {
       CHECK_LE(width, 4);
-      f->Set32(o, value);
+      // Debugging can't use transactional mode (runtime only).
+      f->Set32<false>(o, value);
     }
   } else {
     mirror::Object* v = gRegistry->Get<mirror::Object*>(value);
@@ -1598,7 +1600,8 @@
         return JDWP::ERR_INVALID_OBJECT;
       }
     }
-    f->SetObject(o, v);
+    // Debugging can't use transactional mode (runtime only).
+    f->SetObject<false>(o, v);
   }
 
   return JDWP::ERR_NONE;
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index eaba7eb..5e2b9ff 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -1082,33 +1082,36 @@
   ptr_ += width;
 }
 
+template<bool kTransactionActive>
 void EncodedStaticFieldValueIterator::ReadValueToField(mirror::ArtField* field) const {
   switch (type_) {
-    case kBoolean: field->SetBoolean(field->GetDeclaringClass(), jval_.z); break;
-    case kByte:    field->SetByte(field->GetDeclaringClass(), jval_.b); break;
-    case kShort:   field->SetShort(field->GetDeclaringClass(), jval_.s); break;
-    case kChar:    field->SetChar(field->GetDeclaringClass(), jval_.c); break;
-    case kInt:     field->SetInt(field->GetDeclaringClass(), jval_.i); break;
-    case kLong:    field->SetLong(field->GetDeclaringClass(), jval_.j); break;
-    case kFloat:   field->SetFloat(field->GetDeclaringClass(), jval_.f); break;
-    case kDouble:  field->SetDouble(field->GetDeclaringClass(), jval_.d); break;
-    case kNull:    field->SetObject(field->GetDeclaringClass(), NULL); break;
+    case kBoolean: field->SetBoolean<kTransactionActive>(field->GetDeclaringClass(), jval_.z); break;
+    case kByte:    field->SetByte<kTransactionActive>(field->GetDeclaringClass(), jval_.b); break;
+    case kShort:   field->SetShort<kTransactionActive>(field->GetDeclaringClass(), jval_.s); break;
+    case kChar:    field->SetChar<kTransactionActive>(field->GetDeclaringClass(), jval_.c); break;
+    case kInt:     field->SetInt<kTransactionActive>(field->GetDeclaringClass(), jval_.i); break;
+    case kLong:    field->SetLong<kTransactionActive>(field->GetDeclaringClass(), jval_.j); break;
+    case kFloat:   field->SetFloat<kTransactionActive>(field->GetDeclaringClass(), jval_.f); break;
+    case kDouble:  field->SetDouble<kTransactionActive>(field->GetDeclaringClass(), jval_.d); break;
+    case kNull:    field->SetObject<kTransactionActive>(field->GetDeclaringClass(), NULL); break;
     case kString: {
       CHECK(!kMovingFields);
       mirror::String* resolved = linker_->ResolveString(dex_file_, jval_.i, *dex_cache_);
-      field->SetObject(field->GetDeclaringClass(), resolved);
+      field->SetObject<kTransactionActive>(field->GetDeclaringClass(), resolved);
       break;
     }
     case kType: {
       CHECK(!kMovingFields);
       mirror::Class* resolved = linker_->ResolveType(dex_file_, jval_.i, *dex_cache_,
                                                      *class_loader_);
-      field->SetObject(field->GetDeclaringClass(), resolved);
+      field->SetObject<kTransactionActive>(field->GetDeclaringClass(), resolved);
       break;
     }
     default: UNIMPLEMENTED(FATAL) << ": type " << type_;
   }
 }
+template void EncodedStaticFieldValueIterator::ReadValueToField<true>(mirror::ArtField* field) const;
+template void EncodedStaticFieldValueIterator::ReadValueToField<false>(mirror::ArtField* field) const;
 
 CatchHandlerIterator::CatchHandlerIterator(const DexFile::CodeItem& code_item, uint32_t address) {
   handler_.address_ = -1;
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 46df455..e9d18b5 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -1142,6 +1142,7 @@
                                   ClassLinker* linker, const DexFile::ClassDef& class_def)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  template<bool kTransactionActive>
   void ReadValueToField(mirror::ArtField* field) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   bool HasNext() { return pos_ < array_size_; }
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index 4e58a72..4078cac 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -162,7 +162,7 @@
           CHECK(soa.Self()->IsExceptionPending());
           return zero;
         }
-        soa.Decode<mirror::ObjectArray<mirror::Object>* >(args_jobj)->Set(i, val);
+        soa.Decode<mirror::ObjectArray<mirror::Object>* >(args_jobj)->Set<false>(i, val);
       }
     }
   }
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index c3deba5..8b94b5a 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -136,6 +136,7 @@
     gc::Heap* heap = Runtime::Current()->GetHeap();
     return klass->Alloc<kInstrumented>(self, heap->GetCurrentAllocator());
   }
+  DCHECK(klass != nullptr);
   return klass->Alloc<kInstrumented>(self, allocator_type);
 }
 
diff --git a/runtime/entrypoints/portable/portable_field_entrypoints.cc b/runtime/entrypoints/portable/portable_field_entrypoints.cc
index 0b54b9c..f48f1a9 100644
--- a/runtime/entrypoints/portable/portable_field_entrypoints.cc
+++ b/runtime/entrypoints/portable/portable_field_entrypoints.cc
@@ -30,13 +30,15 @@
                                StaticPrimitiveWrite,
                                sizeof(uint32_t));
   if (LIKELY(field != NULL)) {
-    field->Set32(field->GetDeclaringClass(), new_value);
+    // Compiled code can't use transactional mode.
+    field->Set32<false>(field->GetDeclaringClass(), new_value);
     return 0;
   }
   field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, Thread::Current(),
                                                         sizeof(uint32_t));
   if (LIKELY(field != NULL)) {
-    field->Set32(field->GetDeclaringClass(), new_value);
+    // Compiled code can't use transactional mode.
+    field->Set32<false>(field->GetDeclaringClass(), new_value);
     return 0;
   }
   return -1;
@@ -48,13 +50,15 @@
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite, sizeof(uint64_t));
   if (LIKELY(field != NULL)) {
-    field->Set64(field->GetDeclaringClass(), new_value);
+    // Compiled code can't use transactional mode.
+    field->Set64<false>(field->GetDeclaringClass(), new_value);
     return 0;
   }
   field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, Thread::Current(),
                                                         sizeof(uint64_t));
   if (LIKELY(field != NULL)) {
-    field->Set64(field->GetDeclaringClass(), new_value);
+    // Compiled code can't use transactional mode.
+    field->Set64<false>(field->GetDeclaringClass(), new_value);
     return 0;
   }
   return -1;
@@ -67,13 +71,15 @@
   mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticObjectWrite,
                                           sizeof(mirror::HeapReference<mirror::Object>));
   if (LIKELY(field != NULL)) {
-    field->SetObj(field->GetDeclaringClass(), new_value);
+    // Compiled code can't use transactional mode.
+    field->SetObj<false>(field->GetDeclaringClass(), new_value);
     return 0;
   }
   field = FindFieldFromCode<StaticObjectWrite, true>(field_idx, referrer, Thread::Current(),
                                                      sizeof(mirror::HeapReference<mirror::Object>));
   if (LIKELY(field != NULL)) {
-    field->SetObj(field->GetDeclaringClass(), new_value);
+    // Compiled code can't use transactional mode.
+    field->SetObj<false>(field->GetDeclaringClass(), new_value);
     return 0;
   }
   return -1;
@@ -131,13 +137,15 @@
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite, sizeof(uint32_t));
   if (LIKELY(field != NULL)) {
-    field->Set32(obj, new_value);
+    // Compiled code can't use transactional mode.
+    field->Set32<false>(obj, new_value);
     return 0;
   }
   field = FindFieldFromCode<InstancePrimitiveWrite, true>(field_idx, referrer, Thread::Current(),
                                                           sizeof(uint32_t));
   if (LIKELY(field != NULL)) {
-    field->Set32(obj, new_value);
+    // Compiled code can't use transactional mode.
+    field->Set32<false>(obj, new_value);
     return 0;
   }
   return -1;
@@ -149,13 +157,15 @@
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite, sizeof(uint64_t));
   if (LIKELY(field != NULL)) {
-    field->Set64(obj, new_value);
+    // Compiled code can't use transactional mode.
+    field->Set64<false>(obj, new_value);
     return 0;
   }
   field = FindFieldFromCode<InstancePrimitiveWrite, true>(field_idx, referrer, Thread::Current(),
                                                           sizeof(uint64_t));
   if (LIKELY(field != NULL)) {
-    field->Set64(obj, new_value);
+    // Compiled code can't use transactional mode.
+    field->Set64<false>(obj, new_value);
     return 0;
   }
   return -1;
@@ -169,13 +179,15 @@
   mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstanceObjectWrite,
                                           sizeof(mirror::HeapReference<mirror::Object>));
   if (LIKELY(field != NULL)) {
-    field->SetObj(obj, new_value);
+    // Compiled code can't use transactional mode.
+    field->SetObj<false>(obj, new_value);
     return 0;
   }
   field = FindFieldFromCode<InstanceObjectWrite, true>(field_idx, referrer, Thread::Current(),
                                                        sizeof(mirror::HeapReference<mirror::Object>));
   if (LIKELY(field != NULL)) {
-    field->SetObj(obj, new_value);
+    // Compiled code can't use transactional mode.
+    field->SetObj<false>(obj, new_value);
     return 0;
   }
   return -1;
diff --git a/runtime/entrypoints/quick/quick_field_entrypoints.cc b/runtime/entrypoints/quick/quick_field_entrypoints.cc
index 93ff7aa..2d5c07d 100644
--- a/runtime/entrypoints/quick/quick_field_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_field_entrypoints.cc
@@ -154,13 +154,15 @@
   mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite,
                                           sizeof(int32_t));
   if (LIKELY(field != NULL)) {
-    field->Set32(field->GetDeclaringClass(), new_value);
+    // Compiled code can't use transactional mode.
+    field->Set32<false>(field->GetDeclaringClass(), new_value);
     return 0;  // success
   }
   FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
   field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, self, sizeof(int32_t));
   if (LIKELY(field != NULL)) {
-    field->Set32(field->GetDeclaringClass(), new_value);
+    // Compiled code can't use transactional mode.
+    field->Set32<false>(field->GetDeclaringClass(), new_value);
     return 0;  // success
   }
   return -1;  // failure
@@ -172,13 +174,15 @@
   mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite,
                                           sizeof(int64_t));
   if (LIKELY(field != NULL)) {
-    field->Set64(field->GetDeclaringClass(), new_value);
+    // Compiled code can't use transactional mode.
+    field->Set64<false>(field->GetDeclaringClass(), new_value);
     return 0;  // success
   }
   FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
   field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, self, sizeof(int64_t));
   if (LIKELY(field != NULL)) {
-    field->Set64(field->GetDeclaringClass(), new_value);
+    // Compiled code can't use transactional mode.
+    field->Set64<false>(field->GetDeclaringClass(), new_value);
     return 0;  // success
   }
   return -1;  // failure
@@ -192,7 +196,8 @@
                                           sizeof(mirror::HeapReference<mirror::Object>));
   if (LIKELY(field != NULL)) {
     if (LIKELY(!FieldHelper(field).IsPrimitiveType())) {
-      field->SetObj(field->GetDeclaringClass(), new_value);
+      // Compiled code can't use transactional mode.
+      field->SetObj<false>(field->GetDeclaringClass(), new_value);
       return 0;  // success
     }
   }
@@ -200,7 +205,8 @@
   field = FindFieldFromCode<StaticObjectWrite, true>(field_idx, referrer, self,
                                                      sizeof(mirror::HeapReference<mirror::Object>));
   if (LIKELY(field != NULL)) {
-    field->SetObj(field->GetDeclaringClass(), new_value);
+    // Compiled code can't use transactional mode.
+    field->SetObj<false>(field->GetDeclaringClass(), new_value);
     return 0;  // success
   }
   return -1;  // failure
@@ -213,7 +219,8 @@
   mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite,
                                           sizeof(int32_t));
   if (LIKELY(field != NULL && obj != NULL)) {
-    field->Set32(obj, new_value);
+    // Compiled code can't use transactional mode.
+    field->Set32<false>(obj, new_value);
     return 0;  // success
   }
   FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
@@ -224,7 +231,8 @@
       ThrowLocation throw_location = self->GetCurrentLocationForThrow();
       ThrowNullPointerExceptionForFieldAccess(throw_location, field, false);
     } else {
-      field->Set32(obj, new_value);
+      // Compiled code can't use transactional mode.
+      field->Set32<false>(obj, new_value);
       return 0;  // success
     }
   }
@@ -240,7 +248,8 @@
   mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite,
                                           sizeof(int64_t));
   if (LIKELY(field != NULL  && obj != NULL)) {
-    field->Set64(obj, new_value);
+    // Compiled code can't use transactional mode.
+    field->Set64<false>(obj, new_value);
     return 0;  // success
   }
   *sp = callee_save;
@@ -252,7 +261,8 @@
       ThrowLocation throw_location = self->GetCurrentLocationForThrow();
       ThrowNullPointerExceptionForFieldAccess(throw_location, field, false);
     } else {
-      field->Set64(obj, new_value);
+      // Compiled code can't use transactional mode.
+      field->Set64<false>(obj, new_value);
       return 0;  // success
     }
   }
@@ -267,7 +277,8 @@
   mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstanceObjectWrite,
                                           sizeof(mirror::HeapReference<mirror::Object>));
   if (LIKELY(field != NULL && obj != NULL)) {
-    field->SetObj(obj, new_value);
+    // Compiled code can't use transactional mode.
+    field->SetObj<false>(obj, new_value);
     return 0;  // success
   }
   FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
@@ -278,7 +289,8 @@
       ThrowLocation throw_location = self->GetCurrentLocationForThrow();
       ThrowNullPointerExceptionForFieldAccess(throw_location, field, false);
     } else {
-      field->SetObj(obj, new_value);
+      // Compiled code can't use transactional mode.
+      field->SetObj<false>(obj, new_value);
       return 0;  // success
     }
   }
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index f9486c3..012dabb 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -558,7 +558,7 @@
       // We came here because of sharpening. Ensure the dex cache is up-to-date on the method index
       // of the sharpened method.
       if (called->GetDexCacheResolvedMethods() == caller->GetDexCacheResolvedMethods()) {
-        caller->GetDexCacheResolvedMethods()->Set(called->GetDexMethodIndex(), called);
+        caller->GetDexCacheResolvedMethods()->Set<false>(called->GetDexMethodIndex(), called);
       } else {
         // Calling from one dex file to another, need to compute the method index appropriate to
         // the caller's dex file. Since we get here only if the original called was a runtime
@@ -567,7 +567,7 @@
         uint32_t method_index =
             MethodHelper(called).FindDexMethodIndexInOtherDexFile(*dex_file, dex_method_idx);
         if (method_index != DexFile::kDexNoIndex) {
-          caller->GetDexCacheResolvedMethods()->Set(method_index, called);
+          caller->GetDexCacheResolvedMethods()->Set<false>(method_index, called);
         }
       }
     }
diff --git a/runtime/gc/accounting/mod_union_table.cc b/runtime/gc/accounting/mod_union_table.cc
index aad214a..6258070 100644
--- a/runtime/gc/accounting/mod_union_table.cc
+++ b/runtime/gc/accounting/mod_union_table.cc
@@ -84,7 +84,11 @@
       if (new_ref != ref) {
         // Use SetFieldObjectWithoutWriteBarrier to avoid card mark as an optimization which
         // reduces dirtied pages and improves performance.
-        obj->SetFieldObjectWithoutWriteBarrier(offset, new_ref, true);
+        if (Runtime::Current()->IsActiveTransaction()) {
+          obj->SetFieldObjectWithoutWriteBarrier<true>(offset, new_ref, true);
+        } else {
+          obj->SetFieldObjectWithoutWriteBarrier<false>(offset, new_ref, true);
+        }
       }
     }
   }
diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc
index b1122b9..6d9496e 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -662,7 +662,9 @@
       // Don't need to mark the card since we updating the object address and not changing the
       // actual objects its pointing to. Using SetFieldObjectWithoutWriteBarrier is better in this
       // case since it does not dirty cards and use additional memory.
-      obj->SetFieldObjectWithoutWriteBarrier(offset, new_address, false);
+      // Since we do not change the actual object, we can safely use non-transactional mode. Also
+      // disable check as we could run inside a transaction.
+      obj->SetFieldObjectWithoutWriteBarrier<false, false>(offset, new_address, false);
     }
   }, kMovingClasses);
   mirror::Class* klass = obj->GetClass();
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index f1126ef..7613a31 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -2335,7 +2335,7 @@
 void Heap::SetReferenceReferent(mirror::Object* reference, mirror::Object* referent) {
   DCHECK(reference != NULL);
   DCHECK_NE(reference_referent_offset_.Uint32Value(), 0U);
-  reference->SetFieldObject(reference_referent_offset_, referent, true);
+  reference->SetFieldObject<false, false>(reference_referent_offset_, referent, true);
 }
 
 mirror::Object* Heap::GetReferenceReferent(mirror::Object* reference) {
diff --git a/runtime/gc/heap_test.cc b/runtime/gc/heap_test.cc
index 8af2725..b02b8bb 100644
--- a/runtime/gc/heap_test.cc
+++ b/runtime/gc/heap_test.cc
@@ -50,7 +50,7 @@
       for (size_t j = 0; j < 2048; ++j) {
         mirror::String* string = mirror::String::AllocFromModifiedUtf8(soa.Self(), "hello, world!");
         // SIRT operator -> deferences the SIRT before running the method.
-        array->Set(j, string);
+        array->Set<false>(j, string);
       }
     }
   }
diff --git a/runtime/gc/reference_queue.cc b/runtime/gc/reference_queue.cc
index fae4cac..203701f 100644
--- a/runtime/gc/reference_queue.cc
+++ b/runtime/gc/reference_queue.cc
@@ -49,12 +49,21 @@
   DCHECK_NE(pending_next_offset.Uint32Value(), 0U);
   if (IsEmpty()) {
     // 1 element cyclic queue, ie: Reference ref = ..; ref.pendingNext = ref;
-    ref->SetFieldObject(pending_next_offset, ref, false);
+    if (Runtime::Current()->IsActiveTransaction()) {
+      ref->SetFieldObject<true>(pending_next_offset, ref, false);
+    } else {
+      ref->SetFieldObject<false>(pending_next_offset, ref, false);
+    }
     list_ = ref;
   } else {
     mirror::Object* head = list_->GetFieldObject<mirror::Object>(pending_next_offset, false);
-    ref->SetFieldObject(pending_next_offset, head, false);
-    list_->SetFieldObject(pending_next_offset, ref, false);
+    if (Runtime::Current()->IsActiveTransaction()) {
+      ref->SetFieldObject<true>(pending_next_offset, head, false);
+      list_->SetFieldObject<true>(pending_next_offset, ref, false);
+    } else {
+      ref->SetFieldObject<false>(pending_next_offset, head, false);
+      list_->SetFieldObject<false>(pending_next_offset, ref, false);
+    }
   }
 }
 
@@ -71,10 +80,18 @@
     list_ = nullptr;
   } else {
     mirror::Object* next = head->GetFieldObject<mirror::Object>(pending_next_offset, false);
-    list_->SetFieldObject(pending_next_offset, next, false);
+    if (Runtime::Current()->IsActiveTransaction()) {
+      list_->SetFieldObject<true>(pending_next_offset, next, false);
+    } else {
+      list_->SetFieldObject<false>(pending_next_offset, next, false);
+    }
     ref = head;
   }
-  ref->SetFieldObject(pending_next_offset, nullptr, false);
+  if (Runtime::Current()->IsActiveTransaction()) {
+    ref->SetFieldObject<true>(pending_next_offset, nullptr, false);
+  } else {
+    ref->SetFieldObject<false>(pending_next_offset, nullptr, false);
+  }
   return ref;
 }
 
@@ -131,7 +148,11 @@
         // If the referent is non-null the reference must queuable.
         DCHECK(heap_->IsEnqueuable(ref));
         // Move the updated referent to the zombie field.
-        ref->SetFieldObject(heap_->GetFinalizerReferenceZombieOffset(), forward_address, false);
+        if (Runtime::Current()->IsActiveTransaction()) {
+          ref->SetFieldObject<true>(heap_->GetFinalizerReferenceZombieOffset(), forward_address, false);
+        } else {
+          ref->SetFieldObject<false>(heap_->GetFinalizerReferenceZombieOffset(), forward_address, false);
+        }
         heap_->ClearReferenceReferent(ref);
         cleared_references.EnqueueReference(ref);
       } else if (referent != forward_address) {
diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc
index 5693747..cc49d67 100644
--- a/runtime/intern_table.cc
+++ b/runtime/intern_table.cc
@@ -28,24 +28,24 @@
 namespace art {
 
 InternTable::InternTable()
-    : intern_table_lock_("InternTable lock"), is_dirty_(false), allow_new_interns_(true),
-      new_intern_condition_("New intern condition", intern_table_lock_) {
+    : is_dirty_(false), allow_new_interns_(true),
+      new_intern_condition_("New intern condition", *Locks::intern_table_lock_) {
 }
 
 size_t InternTable::Size() const {
-  MutexLock mu(Thread::Current(), intern_table_lock_);
+  MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
   return strong_interns_.size() + weak_interns_.size();
 }
 
 void InternTable::DumpForSigQuit(std::ostream& os) const {
-  MutexLock mu(Thread::Current(), intern_table_lock_);
+  MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
   os << "Intern table: " << strong_interns_.size() << " strong; "
      << weak_interns_.size() << " weak\n";
 }
 
 void InternTable::VisitRoots(RootCallback* callback, void* arg,
                              bool only_dirty, bool clean_dirty) {
-  MutexLock mu(Thread::Current(), intern_table_lock_);
+  MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
   if (!only_dirty || is_dirty_) {
     for (auto& strong_intern : strong_interns_) {
       strong_intern.second =
@@ -61,7 +61,7 @@
 }
 
 mirror::String* InternTable::Lookup(Table& table, mirror::String* s, uint32_t hash_code) {
-  intern_table_lock_.AssertHeld(Thread::Current());
+  Locks::intern_table_lock_->AssertHeld(Thread::Current());
   for (auto it = table.find(hash_code), end = table.end(); it != end; ++it) {
     mirror::String* existing_string = it->second;
     if (existing_string->Equals(s)) {
@@ -71,15 +71,38 @@
   return NULL;
 }
 
+mirror::String* InternTable::InsertStrong(mirror::String* s, uint32_t hash_code) {
+  Runtime* runtime = Runtime::Current();
+  if (runtime->IsActiveTransaction()) {
+    runtime->RecordStrongStringInsertion(s, hash_code);
+  }
+  return Insert(strong_interns_, s, hash_code);
+}
+
+mirror::String* InternTable::InsertWeak(mirror::String* s, uint32_t hash_code) {
+  Runtime* runtime = Runtime::Current();
+  if (runtime->IsActiveTransaction()) {
+    runtime->RecordWeakStringInsertion(s, hash_code);
+  }
+  return Insert(weak_interns_, s, hash_code);
+}
+
 mirror::String* InternTable::Insert(Table& table, mirror::String* s, uint32_t hash_code) {
-  intern_table_lock_.AssertHeld(Thread::Current());
+  Locks::intern_table_lock_->AssertHeld(Thread::Current());
   table.insert(std::make_pair(hash_code, s));
   return s;
 }
 
-void InternTable::Remove(Table& table, const mirror::String* s,
-                         uint32_t hash_code) {
-  intern_table_lock_.AssertHeld(Thread::Current());
+void InternTable::RemoveWeak(mirror::String* s, uint32_t hash_code) {
+  Runtime* runtime = Runtime::Current();
+  if (runtime->IsActiveTransaction()) {
+    runtime->RecordWeakStringRemoval(s, hash_code);
+  }
+  Remove(weak_interns_, s, hash_code);
+}
+
+void InternTable::Remove(Table& table, mirror::String* s, uint32_t hash_code) {
+  Locks::intern_table_lock_->AssertHeld(Thread::Current());
   for (auto it = table.find(hash_code), end = table.end(); it != end; ++it) {
     if (it->second == s) {
       table.erase(it);
@@ -88,6 +111,24 @@
   }
 }
 
+// Insert/remove methods used to undo changes made during an aborted transaction.
+mirror::String* InternTable::InsertStrongFromTransaction(mirror::String* s, uint32_t hash_code) {
+  DCHECK(!Runtime::Current()->IsActiveTransaction());
+  return InsertStrong(s, hash_code);
+}
+mirror::String* InternTable::InsertWeakFromTransaction(mirror::String* s, uint32_t hash_code) {
+  DCHECK(!Runtime::Current()->IsActiveTransaction());
+  return InsertWeak(s, hash_code);
+}
+void InternTable::RemoveStrongFromTransaction(mirror::String* s, uint32_t hash_code) {
+  DCHECK(!Runtime::Current()->IsActiveTransaction());
+  Remove(strong_interns_, s, hash_code);
+}
+void InternTable::RemoveWeakFromTransaction(mirror::String* s, uint32_t hash_code) {
+  DCHECK(!Runtime::Current()->IsActiveTransaction());
+  Remove(weak_interns_, s, hash_code);
+}
+
 static mirror::String* LookupStringFromImage(mirror::String* s)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   gc::space::ImageSpace* image = Runtime::Current()->GetHeap()->GetImageSpace();
@@ -115,20 +156,20 @@
 
 void InternTable::AllowNewInterns() {
   Thread* self = Thread::Current();
-  MutexLock mu(self, intern_table_lock_);
+  MutexLock mu(self, *Locks::intern_table_lock_);
   allow_new_interns_ = true;
   new_intern_condition_.Broadcast(self);
 }
 
 void InternTable::DisallowNewInterns() {
   Thread* self = Thread::Current();
-  MutexLock mu(self, intern_table_lock_);
+  MutexLock mu(self, *Locks::intern_table_lock_);
   allow_new_interns_ = false;
 }
 
 mirror::String* InternTable::Insert(mirror::String* s, bool is_strong) {
   Thread* self = Thread::Current();
-  MutexLock mu(self, intern_table_lock_);
+  MutexLock mu(self, *Locks::intern_table_lock_);
 
   DCHECK(s != NULL);
   uint32_t hash_code = s->GetHashCode();
@@ -150,20 +191,20 @@
     // Check the image for a match.
     mirror::String* image = LookupStringFromImage(s);
     if (image != NULL) {
-      return Insert(strong_interns_, image, hash_code);
+      return InsertStrong(image, hash_code);
     }
 
     // There is no match in the strong table, check the weak table.
     mirror::String* weak = Lookup(weak_interns_, s, hash_code);
     if (weak != NULL) {
       // A match was found in the weak table. Promote to the strong table.
-      Remove(weak_interns_, weak, hash_code);
-      return Insert(strong_interns_, weak, hash_code);
+      RemoveWeak(weak, hash_code);
+      return InsertStrong(weak, hash_code);
     }
 
     // No match in the strong table or the weak table. Insert into the strong
     // table.
-    return Insert(strong_interns_, s, hash_code);
+    return InsertStrong(s, hash_code);
   }
 
   // Check the strong table for a match.
@@ -174,7 +215,7 @@
   // Check the image for a match.
   mirror::String* image = LookupStringFromImage(s);
   if (image != NULL) {
-    return Insert(weak_interns_, image, hash_code);
+    return InsertWeak(image, hash_code);
   }
   // Check the weak table for a match.
   mirror::String* weak = Lookup(weak_interns_, s, hash_code);
@@ -182,7 +223,7 @@
     return weak;
   }
   // Insert into the weak table.
-  return Insert(weak_interns_, s, hash_code);
+  return InsertWeak(s, hash_code);
 }
 
 mirror::String* InternTable::InternStrong(int32_t utf16_length,
@@ -211,13 +252,13 @@
 }
 
 bool InternTable::ContainsWeak(mirror::String* s) {
-  MutexLock mu(Thread::Current(), intern_table_lock_);
+  MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
   const mirror::String* found = Lookup(weak_interns_, s, s->GetHashCode());
   return found == s;
 }
 
 void InternTable::SweepInternTableWeaks(IsMarkedCallback* callback, void* arg) {
-  MutexLock mu(Thread::Current(), intern_table_lock_);
+  MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
   for (auto it = weak_interns_.begin(), end = weak_interns_.end(); it != end;) {
     mirror::Object* object = it->second;
     mirror::Object* new_object = callback(object, arg);
diff --git a/runtime/intern_table.h b/runtime/intern_table.h
index 9f09fb9..cc48480 100644
--- a/runtime/intern_table.h
+++ b/runtime/intern_table.h
@@ -18,6 +18,7 @@
 #define ART_RUNTIME_INTERN_TABLE_H_
 
 #include "base/mutex.h"
+#include "locks.h"
 #include "object_callbacks.h"
 
 #include <map>
@@ -26,6 +27,7 @@
 namespace mirror {
 class String;
 }  // namespace mirror
+class Transaction;
 
 /**
  * Used to intern strings.
@@ -72,19 +74,38 @@
   typedef std::multimap<int32_t, mirror::String*> Table;
 
   mirror::String* Insert(mirror::String* s, bool is_strong)
+      LOCKS_EXCLUDED(Locks::intern_table_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   mirror::String* Lookup(Table& table, mirror::String* s, uint32_t hash_code)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  mirror::String* Insert(Table& table, mirror::String* s, uint32_t hash_code);
-  void Remove(Table& table, const mirror::String* s, uint32_t hash_code);
+  mirror::String* InsertStrong(mirror::String* s, uint32_t hash_code)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+  mirror::String* InsertWeak(mirror::String* s, uint32_t hash_code)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+  mirror::String* Insert(Table& table, mirror::String* s, uint32_t hash_code)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+  void RemoveWeak(mirror::String* s, uint32_t hash_code)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+  void Remove(Table& table, mirror::String* s, uint32_t hash_code)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
 
-  mutable Mutex intern_table_lock_;
-  bool is_dirty_ GUARDED_BY(intern_table_lock_);
-  bool allow_new_interns_ GUARDED_BY(intern_table_lock_);
-  ConditionVariable new_intern_condition_ GUARDED_BY(intern_table_lock_);
-  Table strong_interns_ GUARDED_BY(intern_table_lock_);
-  Table weak_interns_ GUARDED_BY(intern_table_lock_);
+  // Transaction rollback access.
+  mirror::String* InsertStrongFromTransaction(mirror::String* s, uint32_t hash_code)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+  mirror::String* InsertWeakFromTransaction(mirror::String* s, uint32_t hash_code)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+  void RemoveStrongFromTransaction(mirror::String* s, uint32_t hash_code)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+  void RemoveWeakFromTransaction(mirror::String* s, uint32_t hash_code)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+  friend class Transaction;
+
+  bool is_dirty_ GUARDED_BY(Locks::intern_table_lock_);
+  bool allow_new_interns_ GUARDED_BY(Locks::intern_table_lock_);
+  ConditionVariable new_intern_condition_ GUARDED_BY(Locks::intern_table_lock_);
+  Table strong_interns_ GUARDED_BY(Locks::intern_table_lock_);
+  Table weak_interns_ GUARDED_BY(Locks::intern_table_lock_);
 };
 
 }  // namespace art
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index c6faf44..a674571 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -15,6 +15,7 @@
  */
 
 #include "interpreter_common.h"
+#include <limits>
 
 namespace art {
 namespace interpreter {
@@ -23,6 +24,10 @@
 static void UnstartedRuntimeJni(Thread* self, ArtMethod* method,
                                 Object* receiver, uint32_t* args, JValue* result)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  DCHECK(Runtime::Current()->IsActiveTransaction()) << "Calling native method "
+                                                    << PrettyMethod(method)
+                                                    << " in unstarted runtime should only happen"
+                                                    << " in a transaction";
   std::string name(PrettyMethod(method));
   if (name == "java.lang.ClassLoader dalvik.system.VMStack.getCallingClassLoader()") {
     result->SetL(NULL);
@@ -73,13 +78,18 @@
     jint newValue = args[4];
     byte* raw_addr = reinterpret_cast<byte*>(obj) + offset;
     volatile int32_t* address = reinterpret_cast<volatile int32_t*>(raw_addr);
+    // Check offset is 32bits to fit in MemberOffset.
+    CHECK_GE(offset, static_cast<jlong>(std::numeric_limits<int32_t>::min()));
+    CHECK_LE(offset, static_cast<jlong>(std::numeric_limits<int32_t>::max()));
+    Runtime::Current()->RecordWriteField32(obj, MemberOffset(offset), *address, true);
     // Note: android_atomic_release_cas() returns 0 on success, not failure.
     int r = android_atomic_release_cas(expectedValue, newValue, address);
     result->SetZ(r == 0);
   } else if (name == "void sun.misc.Unsafe.putObject(java.lang.Object, long, java.lang.Object)") {
     Object* obj = reinterpret_cast<Object*>(args[0]);
     Object* newValue = reinterpret_cast<Object*>(args[3]);
-    obj->SetFieldObject(MemberOffset((static_cast<uint64_t>(args[2]) << 32) | args[1]), newValue, false);
+    obj->SetFieldObject<true>(MemberOffset((static_cast<uint64_t>(args[2]) << 32) | args[1]),
+                              newValue, false);
   } else if (name == "int sun.misc.Unsafe.getArrayBaseOffsetForComponentType(java.lang.Class)") {
     mirror::Class* component = reinterpret_cast<Object*>(args[0])->AsClass();
     Primitive::Type primitive_type = component->GetPrimitiveType();
@@ -89,7 +99,11 @@
     Primitive::Type primitive_type = component->GetPrimitiveType();
     result->SetI(Primitive::ComponentSize(primitive_type));
   } else {
-    LOG(FATAL) << "Attempt to invoke native method in non-started runtime: " << name;
+    // Throw an exception so we can abort the transaction and undo every change.
+    ThrowLocation throw_location;
+    self->ThrowNewExceptionF(throw_location, "Ljava/lang/InternalError;",
+                             "Attempt to invoke native method in non-started runtime: %s",
+                             name.c_str());
   }
 }
 
@@ -293,21 +307,38 @@
   DCHECK(!shadow_frame.GetMethod()->IsAbstract());
   DCHECK(!shadow_frame.GetMethod()->IsNative());
 
+  bool transaction_active = Runtime::Current()->IsActiveTransaction();
   if (LIKELY(shadow_frame.GetMethod()->IsPreverified())) {
     // Enter the "without access check" interpreter.
     if (kInterpreterImplKind == kSwitchImpl) {
-      return ExecuteSwitchImpl<false>(self, mh, code_item, shadow_frame, result_register);
+      if (transaction_active) {
+        return ExecuteSwitchImpl<false, true>(self, mh, code_item, shadow_frame, result_register);
+      } else {
+        return ExecuteSwitchImpl<false, false>(self, mh, code_item, shadow_frame, result_register);
+      }
     } else {
       DCHECK_EQ(kInterpreterImplKind, kComputedGotoImplKind);
-      return ExecuteGotoImpl<false>(self, mh, code_item, shadow_frame, result_register);
+      if (transaction_active) {
+        return ExecuteGotoImpl<false, true>(self, mh, code_item, shadow_frame, result_register);
+      } else {
+        return ExecuteGotoImpl<false, false>(self, mh, code_item, shadow_frame, result_register);
+      }
     }
   } else {
     // Enter the "with access check" interpreter.
     if (kInterpreterImplKind == kSwitchImpl) {
-      return ExecuteSwitchImpl<true>(self, mh, code_item, shadow_frame, result_register);
+      if (transaction_active) {
+        return ExecuteSwitchImpl<true, true>(self, mh, code_item, shadow_frame, result_register);
+      } else {
+        return ExecuteSwitchImpl<true, false>(self, mh, code_item, shadow_frame, result_register);
+      }
     } else {
       DCHECK_EQ(kInterpreterImplKind, kComputedGotoImplKind);
-      return ExecuteGotoImpl<true>(self, mh, code_item, shadow_frame, result_register);
+      if (transaction_active) {
+        return ExecuteGotoImpl<true, true>(self, mh, code_item, shadow_frame, result_register);
+      } else {
+        return ExecuteGotoImpl<true, false>(self, mh, code_item, shadow_frame, result_register);
+      }
     }
   }
 }
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 0b959fb..e37fb61 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -15,6 +15,7 @@
  */
 
 #include "interpreter_common.h"
+#include "mirror/array-inl.h"
 
 namespace art {
 namespace interpreter {
@@ -161,7 +162,7 @@
   return !self->IsExceptionPending();
 }
 
-template <bool is_range, bool do_access_check>
+template <bool is_range, bool do_access_check, bool transaction_active>
 bool DoFilledNewArray(const Instruction* inst, const ShadowFrame& shadow_frame,
                       Thread* self, JValue* result) {
   DCHECK(inst->Opcode() == Instruction::FILLED_NEW_ARRAY ||
@@ -212,9 +213,9 @@
   for (int32_t i = 0; i < length; ++i) {
     size_t src_reg = is_range ? vregC + i : arg[i];
     if (is_primitive_int_component) {
-      newArray->AsIntArray()->SetWithoutChecks(i, shadow_frame.GetVReg(src_reg));
+      newArray->AsIntArray()->SetWithoutChecks<transaction_active>(i, shadow_frame.GetVReg(src_reg));
     } else {
-      newArray->AsObjectArray<Object>()->SetWithoutChecks(i, shadow_frame.GetVRegReference(src_reg));
+      newArray->AsObjectArray<Object>()->SetWithoutChecks<transaction_active>(i, shadow_frame.GetVRegReference(src_reg));
     }
   }
 
@@ -222,6 +223,50 @@
   return true;
 }
 
+// TODO fix thread analysis: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_).
+template<typename T>
+static void RecordArrayElementsInTransactionImpl(mirror::PrimitiveArray<T>* array, int32_t count)
+    NO_THREAD_SAFETY_ANALYSIS {
+  Runtime* runtime = Runtime::Current();
+  for (int32_t i = 0; i < count; ++i) {
+    runtime->RecordWriteArray(array, i, array->GetWithoutChecks(i));
+  }
+}
+
+void RecordArrayElementsInTransaction(mirror::Array* array, int32_t count)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  DCHECK(Runtime::Current()->IsActiveTransaction());
+  DCHECK(array != nullptr);
+  DCHECK_LE(count, array->GetLength());
+  Primitive::Type primitive_component_type = array->GetClass()->GetComponentType()->GetPrimitiveType();
+  switch (primitive_component_type) {
+    case Primitive::kPrimBoolean:
+      RecordArrayElementsInTransactionImpl(array->AsBooleanArray(), count);
+      break;
+    case Primitive::kPrimByte:
+      RecordArrayElementsInTransactionImpl(array->AsByteArray(), count);
+      break;
+    case Primitive::kPrimChar:
+      RecordArrayElementsInTransactionImpl(array->AsCharArray(), count);
+      break;
+    case Primitive::kPrimShort:
+      RecordArrayElementsInTransactionImpl(array->AsShortArray(), count);
+      break;
+    case Primitive::kPrimInt:
+    case Primitive::kPrimFloat:
+      RecordArrayElementsInTransactionImpl(array->AsIntArray(), count);
+      break;
+    case Primitive::kPrimLong:
+    case Primitive::kPrimDouble:
+      RecordArrayElementsInTransactionImpl(array->AsLongArray(), count);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported primitive type " << primitive_component_type
+                 << " in fill-array-data";
+      break;
+  }
+}
+
 static void UnstartedRuntimeInvoke(Thread* self, MethodHelper& mh,
                                    const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame,
                                    JValue* result, size_t arg_offset) {
@@ -341,15 +386,19 @@
 #undef EXPLICIT_DO_CALL_TEMPLATE_DECL
 
 // Explicit DoFilledNewArray template function declarations.
-#define EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(_is_range_, _check)                \
-  template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)                                \
-  bool DoFilledNewArray<_is_range_, _check>(const Instruction* inst,                  \
-                                                     const ShadowFrame& shadow_frame, \
-                                                     Thread* self, JValue* result)
-EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(false, false);
-EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(false, true);
-EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(true, false);
-EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(true, true);
+#define EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(_is_range_, _check, _transaction_active)       \
+  template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)                                            \
+  bool DoFilledNewArray<_is_range_, _check, _transaction_active>(const Instruction* inst,         \
+                                                                 const ShadowFrame& shadow_frame, \
+                                                                 Thread* self, JValue* result)
+#define EXPLICIT_DO_FILLED_NEW_ARRAY_ALL_TEMPLATE_DECL(_transaction_active)       \
+  EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(false, false, _transaction_active);  \
+  EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(false, true, _transaction_active);   \
+  EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(true, false, _transaction_active);   \
+  EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(true, true, _transaction_active)
+EXPLICIT_DO_FILLED_NEW_ARRAY_ALL_TEMPLATE_DECL(false);
+EXPLICIT_DO_FILLED_NEW_ARRAY_ALL_TEMPLATE_DECL(true);
+#undef EXPLICIT_DO_FILLED_NEW_ARRAY_ALL_TEMPLATE_DECL
 #undef EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL
 
 }  // namespace interpreter
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 768ca33..a03e420 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -65,12 +65,12 @@
 
 // External references to both interpreter implementations.
 
-template<bool do_access_check>
+template<bool do_access_check, bool transaction_active>
 extern JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh,
                                 const DexFile::CodeItem* code_item,
                                 ShadowFrame& shadow_frame, JValue result_register);
 
-template<bool do_access_check>
+template<bool do_access_check, bool transaction_active>
 extern JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh,
                               const DexFile::CodeItem* code_item,
                               ShadowFrame& shadow_frame, JValue result_register);
@@ -83,6 +83,9 @@
   ref->MonitorExit(self);
 }
 
+void RecordArrayElementsInTransaction(mirror::Array* array, int32_t count)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
 // Invokes the given method. This is part of the invocation support and is used by DoInvoke and
 // DoInvokeVirtualQuick functions.
 // Returns true on success, otherwise throws an exception and returns false.
@@ -228,7 +231,7 @@
 
 // Handles iput-XXX and sput-XXX instructions.
 // Returns true on success, otherwise throws an exception and returns false.
-template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check>
+template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check, bool transaction_active>
 static inline bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame,
                               const Instruction* inst, uint16_t inst_data) {
   bool do_assignability_check = do_access_check;
@@ -254,22 +257,22 @@
   uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data);
   switch (field_type) {
     case Primitive::kPrimBoolean:
-      f->SetBoolean(obj, shadow_frame.GetVReg(vregA));
+      f->SetBoolean<transaction_active>(obj, shadow_frame.GetVReg(vregA));
       break;
     case Primitive::kPrimByte:
-      f->SetByte(obj, shadow_frame.GetVReg(vregA));
+      f->SetByte<transaction_active>(obj, shadow_frame.GetVReg(vregA));
       break;
     case Primitive::kPrimChar:
-      f->SetChar(obj, shadow_frame.GetVReg(vregA));
+      f->SetChar<transaction_active>(obj, shadow_frame.GetVReg(vregA));
       break;
     case Primitive::kPrimShort:
-      f->SetShort(obj, shadow_frame.GetVReg(vregA));
+      f->SetShort<transaction_active>(obj, shadow_frame.GetVReg(vregA));
       break;
     case Primitive::kPrimInt:
-      f->SetInt(obj, shadow_frame.GetVReg(vregA));
+      f->SetInt<transaction_active>(obj, shadow_frame.GetVReg(vregA));
       break;
     case Primitive::kPrimLong:
-      f->SetLong(obj, shadow_frame.GetVRegLong(vregA));
+      f->SetLong<transaction_active>(obj, shadow_frame.GetVRegLong(vregA));
       break;
     case Primitive::kPrimNot: {
       Object* reg = shadow_frame.GetVRegReference(vregA);
@@ -286,7 +289,7 @@
           return false;
         }
       }
-      f->SetObj(obj, reg);
+      f->SetObj<transaction_active>(obj, reg);
       break;
     }
     default:
@@ -297,7 +300,7 @@
 
 // Handles iput-quick, iput-wide-quick and iput-object-quick instructions.
 // Returns true on success, otherwise throws an exception and returns false.
-template<Primitive::Type field_type>
+template<Primitive::Type field_type, bool transaction_active>
 static inline bool DoIPutQuick(const ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data) {
   Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
   if (UNLIKELY(obj == nullptr)) {
@@ -311,13 +314,15 @@
   const uint32_t vregA = inst->VRegA_22c(inst_data);
   switch (field_type) {
     case Primitive::kPrimInt:
-      obj->SetField32(field_offset, shadow_frame.GetVReg(vregA), is_volatile);
+      obj->SetField32<transaction_active>(field_offset, shadow_frame.GetVReg(vregA), is_volatile);
       break;
     case Primitive::kPrimLong:
-      obj->SetField64(field_offset, shadow_frame.GetVRegLong(vregA), is_volatile);
+      obj->SetField64<transaction_active>(field_offset, shadow_frame.GetVRegLong(vregA),
+                                          is_volatile);
       break;
     case Primitive::kPrimNot:
-      obj->SetFieldObject(field_offset, shadow_frame.GetVRegReference(vregA), is_volatile);
+      obj->SetFieldObject<transaction_active>(field_offset, shadow_frame.GetVRegReference(vregA),
+                                              is_volatile);
       break;
     default:
       LOG(FATAL) << "Unreachable: " << field_type;
@@ -416,7 +421,7 @@
 
 // Handles filled-new-array and filled-new-array-range instructions.
 // Returns true on success, otherwise throws an exception and returns false.
-template <bool is_range, bool do_access_check>
+template <bool is_range, bool do_access_check, bool transaction_active>
 bool DoFilledNewArray(const Instruction* inst, const ShadowFrame& shadow_frame,
                       Thread* self, JValue* result);
 
@@ -604,14 +609,16 @@
 #undef EXPLICIT_DO_FIELD_GET_TEMPLATE_DECL
 
 // Explicitly instantiate all DoFieldPut functions.
-#define EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, _do_check)                      \
+#define EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, _do_check, _transaction_active)                      \
   template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE                                 \
-  bool DoFieldPut<_find_type, _field_type, _do_check>(Thread* self, const ShadowFrame& shadow_frame, \
-                                                      const Instruction* inst, uint16_t inst_data)
+  bool DoFieldPut<_find_type, _field_type, _do_check, _transaction_active>(Thread* self, const ShadowFrame& shadow_frame, \
+                                                                           const Instruction* inst, uint16_t inst_data)
 
 #define EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(_find_type, _field_type)  \
-    EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, false);  \
-    EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, true);
+    EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, false, false);  \
+    EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, true, false);  \
+    EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, false, true);  \
+    EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, true, true);
 
 // iput-XXX
 EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimBoolean);
@@ -657,14 +664,20 @@
 #undef EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL
 
 // Explicitly instantiate all DoIPutQuick functions.
-#define EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(_field_type)                                  \
-  template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE                       \
-  bool DoIPutQuick<_field_type>(const ShadowFrame& shadow_frame, const Instruction* inst,  \
-                                uint16_t inst_data)
+#define EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(_field_type, _transaction_active)        \
+  template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE                  \
+  bool DoIPutQuick<_field_type, _transaction_active>(const ShadowFrame& shadow_frame, \
+                                                     const Instruction* inst,         \
+                                                     uint16_t inst_data)
 
-EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(Primitive::kPrimInt);    // iget-quick.
-EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(Primitive::kPrimLong);   // iget-wide-quick.
-EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(Primitive::kPrimNot);    // iget-object-quick.
+#define EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL(_field_type)   \
+  EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(_field_type, false);     \
+  EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(_field_type, true);
+
+EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL(Primitive::kPrimInt);    // iget-quick.
+EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL(Primitive::kPrimLong);   // iget-wide-quick.
+EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL(Primitive::kPrimNot);    // iget-object-quick.
+#undef EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL
 #undef EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL
 
 }  // namespace interpreter
diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc
index e8504b7..d0bb001 100644
--- a/runtime/interpreter/interpreter_goto_table_impl.cc
+++ b/runtime/interpreter/interpreter_goto_table_impl.cc
@@ -109,7 +109,7 @@
  * ---------------------+---------------+
  *
  */
-template<bool do_access_check>
+template<bool do_access_check, bool transaction_active>
 JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item,
                        ShadowFrame& shadow_frame, JValue result_register) {
   // Define handler tables:
@@ -536,15 +536,17 @@
   HANDLE_INSTRUCTION_END();
 
   HANDLE_INSTRUCTION_START(FILLED_NEW_ARRAY) {
-    bool success = DoFilledNewArray<false, do_access_check>(inst, shadow_frame,
-                                                            self, &result_register);
+    bool success =
+        DoFilledNewArray<false, do_access_check, transaction_active>(inst, shadow_frame,
+                                                                     self, &result_register);
     POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
   }
   HANDLE_INSTRUCTION_END();
 
   HANDLE_INSTRUCTION_START(FILLED_NEW_ARRAY_RANGE) {
-    bool success = DoFilledNewArray<true, do_access_check>(inst, shadow_frame,
-                                                           self, &result_register);
+    bool success =
+        DoFilledNewArray<true, do_access_check, transaction_active>(inst, shadow_frame,
+                                                                    self, &result_register);
     POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
   }
   HANDLE_INSTRUCTION_END();
@@ -567,6 +569,9 @@
                                  array->GetLength(), payload->element_count);
         HANDLE_PENDING_EXCEPTION();
       } else {
+        if (transaction_active) {
+          RecordArrayElementsInTransaction(array, payload->element_count);
+        }
         uint32_t size_in_bytes = payload->element_count * payload->element_width;
         memcpy(array->GetRawData(payload->element_width, 0), payload->data, size_in_bytes);
         ADVANCE(3);
@@ -1060,7 +1065,7 @@
       int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
       BooleanArray* array = a->AsBooleanArray();
       if (LIKELY(array->CheckIsValidIndex(index))) {
-        array->SetWithoutChecks(index, val);
+        array->SetWithoutChecks<transaction_active>(index, val);
         ADVANCE(2);
       } else {
         HANDLE_PENDING_EXCEPTION();
@@ -1079,7 +1084,7 @@
       int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
       ByteArray* array = a->AsByteArray();
       if (LIKELY(array->CheckIsValidIndex(index))) {
-        array->SetWithoutChecks(index, val);
+        array->SetWithoutChecks<transaction_active>(index, val);
         ADVANCE(2);
       } else {
         HANDLE_PENDING_EXCEPTION();
@@ -1098,7 +1103,7 @@
       int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
       CharArray* array = a->AsCharArray();
       if (LIKELY(array->CheckIsValidIndex(index))) {
-        array->SetWithoutChecks(index, val);
+        array->SetWithoutChecks<transaction_active>(index, val);
         ADVANCE(2);
       } else {
         HANDLE_PENDING_EXCEPTION();
@@ -1117,7 +1122,7 @@
       int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
       ShortArray* array = a->AsShortArray();
       if (LIKELY(array->CheckIsValidIndex(index))) {
-        array->SetWithoutChecks(index, val);
+        array->SetWithoutChecks<transaction_active>(index, val);
         ADVANCE(2);
       } else {
         HANDLE_PENDING_EXCEPTION();
@@ -1136,7 +1141,7 @@
       int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
       IntArray* array = a->AsIntArray();
       if (LIKELY(array->CheckIsValidIndex(index))) {
-        array->SetWithoutChecks(index, val);
+        array->SetWithoutChecks<transaction_active>(index, val);
         ADVANCE(2);
       } else {
         HANDLE_PENDING_EXCEPTION();
@@ -1155,7 +1160,7 @@
       int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
       LongArray* array = a->AsLongArray();
       if (LIKELY(array->CheckIsValidIndex(index))) {
-        array->SetWithoutChecks(index, val);
+        array->SetWithoutChecks<transaction_active>(index, val);
         ADVANCE(2);
       } else {
         HANDLE_PENDING_EXCEPTION();
@@ -1174,7 +1179,7 @@
       Object* val = shadow_frame.GetVRegReference(inst->VRegA_23x(inst_data));
       ObjectArray<Object>* array = a->AsObjectArray<Object>();
       if (LIKELY(array->CheckIsValidIndex(index) && array->CheckAssignable(val))) {
-        array->SetWithoutChecks(index, val);
+        array->SetWithoutChecks<transaction_active>(index, val);
         ADVANCE(2);
       } else {
         HANDLE_PENDING_EXCEPTION();
@@ -1286,103 +1291,103 @@
   HANDLE_INSTRUCTION_END();
 
   HANDLE_INSTRUCTION_START(IPUT_BOOLEAN) {
-    bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst, inst_data);
+    bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimBoolean, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
     POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
   }
   HANDLE_INSTRUCTION_END();
 
   HANDLE_INSTRUCTION_START(IPUT_BYTE) {
-    bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst, inst_data);
+    bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimByte, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
     POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
   }
   HANDLE_INSTRUCTION_END();
 
   HANDLE_INSTRUCTION_START(IPUT_CHAR) {
-    bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst, inst_data);
+    bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimChar, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
     POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
   }
   HANDLE_INSTRUCTION_END();
 
   HANDLE_INSTRUCTION_START(IPUT_SHORT) {
-    bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst, inst_data);
+    bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimShort, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
     POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
   }
   HANDLE_INSTRUCTION_END();
 
   HANDLE_INSTRUCTION_START(IPUT) {
-    bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst, inst_data);
+    bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimInt, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
     POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
   }
   HANDLE_INSTRUCTION_END();
 
   HANDLE_INSTRUCTION_START(IPUT_WIDE) {
-    bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst, inst_data);
+    bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimLong, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
     POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
   }
   HANDLE_INSTRUCTION_END();
 
   HANDLE_INSTRUCTION_START(IPUT_OBJECT) {
-    bool success = DoFieldPut<InstanceObjectWrite, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst, inst_data);
+    bool success = DoFieldPut<InstanceObjectWrite, Primitive::kPrimNot, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
     POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
   }
   HANDLE_INSTRUCTION_END();
 
   HANDLE_INSTRUCTION_START(IPUT_QUICK) {
-    bool success = DoIPutQuick<Primitive::kPrimInt>(shadow_frame, inst, inst_data);
+    bool success = DoIPutQuick<Primitive::kPrimInt, transaction_active>(shadow_frame, inst, inst_data);
     POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
   }
   HANDLE_INSTRUCTION_END();
 
   HANDLE_INSTRUCTION_START(IPUT_WIDE_QUICK) {
-    bool success = DoIPutQuick<Primitive::kPrimLong>(shadow_frame, inst, inst_data);
+    bool success = DoIPutQuick<Primitive::kPrimLong, transaction_active>(shadow_frame, inst, inst_data);
     POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
   }
   HANDLE_INSTRUCTION_END();
 
   HANDLE_INSTRUCTION_START(IPUT_OBJECT_QUICK) {
-    bool success = DoIPutQuick<Primitive::kPrimNot>(shadow_frame, inst, inst_data);
+    bool success = DoIPutQuick<Primitive::kPrimNot, transaction_active>(shadow_frame, inst, inst_data);
     POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
   }
   HANDLE_INSTRUCTION_END();
 
   HANDLE_INSTRUCTION_START(SPUT_BOOLEAN) {
-    bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst, inst_data);
+    bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimBoolean, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
     POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
   }
   HANDLE_INSTRUCTION_END();
 
   HANDLE_INSTRUCTION_START(SPUT_BYTE) {
-    bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst, inst_data);
+    bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimByte, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
     POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
   }
   HANDLE_INSTRUCTION_END();
 
   HANDLE_INSTRUCTION_START(SPUT_CHAR) {
-    bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst, inst_data);
+    bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimChar, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
     POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
   }
   HANDLE_INSTRUCTION_END();
 
   HANDLE_INSTRUCTION_START(SPUT_SHORT) {
-    bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst, inst_data);
+    bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimShort, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
     POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
   }
   HANDLE_INSTRUCTION_END();
 
   HANDLE_INSTRUCTION_START(SPUT) {
-    bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst, inst_data);
+    bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimInt, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
     POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
   }
   HANDLE_INSTRUCTION_END();
 
   HANDLE_INSTRUCTION_START(SPUT_WIDE) {
-    bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst, inst_data);
+    bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimLong, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
     POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
   }
   HANDLE_INSTRUCTION_END();
 
   HANDLE_INSTRUCTION_START(SPUT_OBJECT) {
-    bool success = DoFieldPut<StaticObjectWrite, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst, inst_data);
+    bool success = DoFieldPut<StaticObjectWrite, Primitive::kPrimNot, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
     POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
   }
   HANDLE_INSTRUCTION_END();
@@ -2390,13 +2395,21 @@
 
 // Explicit definitions of ExecuteGotoImpl.
 template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) HOT_ATTR
-JValue ExecuteGotoImpl<true>(Thread* self, MethodHelper& mh,
-                             const DexFile::CodeItem* code_item,
-                             ShadowFrame& shadow_frame, JValue result_register);
+JValue ExecuteGotoImpl<true, false>(Thread* self, MethodHelper& mh,
+                                    const DexFile::CodeItem* code_item,
+                                    ShadowFrame& shadow_frame, JValue result_register);
 template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) HOT_ATTR
-JValue ExecuteGotoImpl<false>(Thread* self, MethodHelper& mh,
-                              const DexFile::CodeItem* code_item,
-                              ShadowFrame& shadow_frame, JValue result_register);
+JValue ExecuteGotoImpl<false, false>(Thread* self, MethodHelper& mh,
+                                     const DexFile::CodeItem* code_item,
+                                     ShadowFrame& shadow_frame, JValue result_register);
+template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+JValue ExecuteGotoImpl<true, true>(Thread* self, MethodHelper& mh,
+                                    const DexFile::CodeItem* code_item,
+                                    ShadowFrame& shadow_frame, JValue result_register);
+template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+JValue ExecuteGotoImpl<false, true>(Thread* self, MethodHelper& mh,
+                                     const DexFile::CodeItem* code_item,
+                                     ShadowFrame& shadow_frame, JValue result_register);
 
 }  // namespace interpreter
 }  // namespace art
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index e5d15b1..abee1db 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -50,7 +50,7 @@
 // Code to run before each dex instruction.
 #define PREAMBLE()
 
-template<bool do_access_check>
+template<bool do_access_check, bool transaction_active>
 JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item,
                                 ShadowFrame& shadow_frame, JValue result_register) {
   bool do_assignability_check = do_access_check;
@@ -449,15 +449,17 @@
       }
       case Instruction::FILLED_NEW_ARRAY: {
         PREAMBLE();
-        bool success = DoFilledNewArray<false, do_access_check>(inst, shadow_frame,
-                                                                self, &result_register);
+        bool success =
+            DoFilledNewArray<false, do_access_check, transaction_active>(inst, shadow_frame, self,
+                                                                         &result_register);
         POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
         break;
       }
       case Instruction::FILLED_NEW_ARRAY_RANGE: {
         PREAMBLE();
-        bool success = DoFilledNewArray<true, do_access_check>(inst, shadow_frame,
-                                                               self, &result_register);
+        bool success =
+            DoFilledNewArray<true, do_access_check, transaction_active>(inst, shadow_frame,
+                                                                        self, &result_register);
         POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
         break;
       }
@@ -482,6 +484,9 @@
           HANDLE_PENDING_EXCEPTION();
           break;
         }
+        if (transaction_active) {
+          RecordArrayElementsInTransaction(array, payload->element_count);
+        }
         uint32_t size_in_bytes = payload->element_count * payload->element_width;
         memcpy(array->GetRawData(payload->element_width, 0), payload->data, size_in_bytes);
         inst = inst->Next_3xx();
@@ -958,7 +963,7 @@
         int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
         BooleanArray* array = a->AsBooleanArray();
         if (LIKELY(array->CheckIsValidIndex(index))) {
-          array->SetWithoutChecks(index, val);
+          array->SetWithoutChecks<transaction_active>(index, val);
           inst = inst->Next_2xx();
         } else {
           HANDLE_PENDING_EXCEPTION();
@@ -977,7 +982,7 @@
         int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
         ByteArray* array = a->AsByteArray();
         if (LIKELY(array->CheckIsValidIndex(index))) {
-          array->SetWithoutChecks(index, val);
+          array->SetWithoutChecks<transaction_active>(index, val);
           inst = inst->Next_2xx();
         } else {
           HANDLE_PENDING_EXCEPTION();
@@ -996,7 +1001,7 @@
         int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
         CharArray* array = a->AsCharArray();
         if (LIKELY(array->CheckIsValidIndex(index))) {
-          array->SetWithoutChecks(index, val);
+          array->SetWithoutChecks<transaction_active>(index, val);
           inst = inst->Next_2xx();
         } else {
           HANDLE_PENDING_EXCEPTION();
@@ -1015,7 +1020,7 @@
         int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
         ShortArray* array = a->AsShortArray();
         if (LIKELY(array->CheckIsValidIndex(index))) {
-          array->SetWithoutChecks(index, val);
+          array->SetWithoutChecks<transaction_active>(index, val);
           inst = inst->Next_2xx();
         } else {
           HANDLE_PENDING_EXCEPTION();
@@ -1034,7 +1039,7 @@
         int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
         IntArray* array = a->AsIntArray();
         if (LIKELY(array->CheckIsValidIndex(index))) {
-          array->SetWithoutChecks(index, val);
+          array->SetWithoutChecks<transaction_active>(index, val);
           inst = inst->Next_2xx();
         } else {
           HANDLE_PENDING_EXCEPTION();
@@ -1053,7 +1058,7 @@
         int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
         LongArray* array = a->AsLongArray();
         if (LIKELY(array->CheckIsValidIndex(index))) {
-          array->SetWithoutChecks(index, val);
+          array->SetWithoutChecks<transaction_active>(index, val);
           inst = inst->Next_2xx();
         } else {
           HANDLE_PENDING_EXCEPTION();
@@ -1072,7 +1077,7 @@
         Object* val = shadow_frame.GetVRegReference(inst->VRegA_23x(inst_data));
         ObjectArray<Object>* array = a->AsObjectArray<Object>();
         if (LIKELY(array->CheckIsValidIndex(index) && array->CheckAssignable(val))) {
-          array->SetWithoutChecks(index, val);
+          array->SetWithoutChecks<transaction_active>(index, val);
           inst = inst->Next_2xx();
         } else {
           HANDLE_PENDING_EXCEPTION();
@@ -1183,103 +1188,103 @@
       }
       case Instruction::IPUT_BOOLEAN: {
         PREAMBLE();
-        bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst, inst_data);
+        bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimBoolean, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
         POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
         break;
       }
       case Instruction::IPUT_BYTE: {
         PREAMBLE();
-        bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst, inst_data);
+        bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimByte, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
         POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
         break;
       }
       case Instruction::IPUT_CHAR: {
         PREAMBLE();
-        bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst, inst_data);
+        bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimChar, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
         POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
         break;
       }
       case Instruction::IPUT_SHORT: {
         PREAMBLE();
-        bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst, inst_data);
+        bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimShort, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
         POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
         break;
       }
       case Instruction::IPUT: {
         PREAMBLE();
-        bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst, inst_data);
+        bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimInt, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
         POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
         break;
       }
       case Instruction::IPUT_WIDE: {
         PREAMBLE();
-        bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst, inst_data);
+        bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimLong, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
         POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
         break;
       }
       case Instruction::IPUT_OBJECT: {
         PREAMBLE();
-        bool success = DoFieldPut<InstanceObjectWrite, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst, inst_data);
+        bool success = DoFieldPut<InstanceObjectWrite, Primitive::kPrimNot, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
         POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
         break;
       }
       case Instruction::IPUT_QUICK: {
         PREAMBLE();
-        bool success = DoIPutQuick<Primitive::kPrimInt>(shadow_frame, inst, inst_data);
+        bool success = DoIPutQuick<Primitive::kPrimInt, transaction_active>(shadow_frame, inst, inst_data);
         POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
         break;
       }
       case Instruction::IPUT_WIDE_QUICK: {
         PREAMBLE();
-        bool success = DoIPutQuick<Primitive::kPrimLong>(shadow_frame, inst, inst_data);
+        bool success = DoIPutQuick<Primitive::kPrimLong, transaction_active>(shadow_frame, inst, inst_data);
         POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
         break;
       }
       case Instruction::IPUT_OBJECT_QUICK: {
         PREAMBLE();
-        bool success = DoIPutQuick<Primitive::kPrimNot>(shadow_frame, inst, inst_data);
+        bool success = DoIPutQuick<Primitive::kPrimNot, transaction_active>(shadow_frame, inst, inst_data);
         POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
         break;
       }
       case Instruction::SPUT_BOOLEAN: {
         PREAMBLE();
-        bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst, inst_data);
+        bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimBoolean, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
         POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
         break;
       }
       case Instruction::SPUT_BYTE: {
         PREAMBLE();
-        bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst, inst_data);
+        bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimByte, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
         POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
         break;
       }
       case Instruction::SPUT_CHAR: {
         PREAMBLE();
-        bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst, inst_data);
+        bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimChar, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
         POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
         break;
       }
       case Instruction::SPUT_SHORT: {
         PREAMBLE();
-        bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst, inst_data);
+        bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimShort, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
         POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
         break;
       }
       case Instruction::SPUT: {
         PREAMBLE();
-        bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst, inst_data);
+        bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimInt, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
         POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
         break;
       }
       case Instruction::SPUT_WIDE: {
         PREAMBLE();
-        bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst, inst_data);
+        bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimLong, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
         POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
         break;
       }
       case Instruction::SPUT_OBJECT: {
         PREAMBLE();
-        bool success = DoFieldPut<StaticObjectWrite, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst, inst_data);
+        bool success = DoFieldPut<StaticObjectWrite, Primitive::kPrimNot, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data);
         POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
         break;
       }
@@ -2137,13 +2142,21 @@
 
 // Explicit definitions of ExecuteSwitchImpl.
 template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) HOT_ATTR
-JValue ExecuteSwitchImpl<true>(Thread* self, MethodHelper& mh,
-                               const DexFile::CodeItem* code_item,
-                               ShadowFrame& shadow_frame, JValue result_register);
+JValue ExecuteSwitchImpl<true, false>(Thread* self, MethodHelper& mh,
+                                      const DexFile::CodeItem* code_item,
+                                      ShadowFrame& shadow_frame, JValue result_register);
 template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) HOT_ATTR
-JValue ExecuteSwitchImpl<false>(Thread* self, MethodHelper& mh,
-                                const DexFile::CodeItem* code_item,
-                                ShadowFrame& shadow_frame, JValue result_register);
+JValue ExecuteSwitchImpl<false, false>(Thread* self, MethodHelper& mh,
+                                       const DexFile::CodeItem* code_item,
+                                       ShadowFrame& shadow_frame, JValue result_register);
+template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+JValue ExecuteSwitchImpl<true, true>(Thread* self, MethodHelper& mh,
+                                     const DexFile::CodeItem* code_item,
+                                     ShadowFrame& shadow_frame, JValue result_register);
+template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+JValue ExecuteSwitchImpl<false, true>(Thread* self, MethodHelper& mh,
+                                      const DexFile::CodeItem* code_item,
+                                      ShadowFrame& shadow_frame, JValue result_register);
 
 }  // namespace interpreter
 }  // namespace art
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index f9bcc71..876de05 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -1551,7 +1551,7 @@
     mirror::Object* o = soa.Decode<mirror::Object*>(java_object);
     mirror::Object* v = soa.Decode<mirror::Object*>(java_value);
     mirror::ArtField* f = soa.DecodeField(fid);
-    f->SetObject(o, v);
+    f->SetObject<false>(o, v);
   }
 
   static void SetStaticObjectField(JNIEnv* env, jclass, jfieldID fid, jobject java_value) {
@@ -1559,7 +1559,7 @@
     ScopedObjectAccess soa(env);
     mirror::Object* v = soa.Decode<mirror::Object*>(java_value);
     mirror::ArtField* f = soa.DecodeField(fid);
-    f->SetObject(f->GetDeclaringClass(), v);
+    f->SetObject<false>(f->GetDeclaringClass(), v);
   }
 
 #define GET_PRIMITIVE_FIELD(fn, instance) \
@@ -1582,13 +1582,13 @@
   ScopedObjectAccess soa(env); \
   mirror::Object* o = soa.Decode<mirror::Object*>(instance); \
   mirror::ArtField* f = soa.DecodeField(fid); \
-  f->Set ##fn(o, value)
+  f->Set ##fn <false>(o, value)
 
 #define SET_STATIC_PRIMITIVE_FIELD(fn, value) \
   CHECK_NON_NULL_ARGUMENT(SetStatic #fn Field, fid); \
   ScopedObjectAccess soa(env); \
   mirror::ArtField* f = soa.DecodeField(fid); \
-  f->Set ##fn(f->GetDeclaringClass(), value)
+  f->Set ##fn <false>(f->GetDeclaringClass(), value)
 
   static jboolean GetBooleanField(JNIEnv* env, jobject obj, jfieldID fid) {
     GET_PRIMITIVE_FIELD(Boolean, obj);
@@ -2085,7 +2085,7 @@
     mirror::ObjectArray<mirror::Object>* array =
         soa.Decode<mirror::ObjectArray<mirror::Object>*>(java_array);
     mirror::Object* value = soa.Decode<mirror::Object*>(java_value);
-    array->Set(index, value);
+    array->Set<false>(index, value);
   }
 
   static jbooleanArray NewBooleanArray(JNIEnv* env, jsize length) {
@@ -2166,7 +2166,7 @@
 
         } else {
           for (jsize i = 0; i < length; ++i) {
-            result->SetWithoutChecks(i, initial_object);
+            result->SetWithoutChecks<false>(i, initial_object);
           }
         }
       }
diff --git a/runtime/locks.cc b/runtime/locks.cc
index d08206a..246e339 100644
--- a/runtime/locks.cc
+++ b/runtime/locks.cc
@@ -33,6 +33,7 @@
 Mutex* Locks::trace_lock_ = NULL;
 Mutex* Locks::profiler_lock_ = NULL;
 Mutex* Locks::unexpected_signal_lock_ = NULL;
+Mutex* Locks::intern_table_lock_ = NULL;
 
 void Locks::Init() {
   if (logging_lock_ != NULL) {
@@ -49,6 +50,7 @@
     DCHECK(trace_lock_ != NULL);
     DCHECK(profiler_lock_ != NULL);
     DCHECK(unexpected_signal_lock_ != NULL);
+    DCHECK(intern_table_lock_ != NULL);
   } else {
     logging_lock_ = new Mutex("logging lock", kLoggingLock, true);
     abort_lock_ = new Mutex("abort lock", kAbortLock, true);
@@ -76,6 +78,8 @@
     profiler_lock_ = new Mutex("profiler lock", kProfilerLock);
     DCHECK(unexpected_signal_lock_ == NULL);
     unexpected_signal_lock_ = new Mutex("unexpected signal lock", kUnexpectedSignalLock, true);
+    DCHECK(intern_table_lock_ == NULL);
+    intern_table_lock_ = new Mutex("InternTable lock", kInternTableLock);
   }
 }
 
diff --git a/runtime/locks.h b/runtime/locks.h
index d4fbd91..2f9810d 100644
--- a/runtime/locks.h
+++ b/runtime/locks.h
@@ -44,6 +44,8 @@
   kDexFileMethodInlinerLock,
   kDexFileToMethodInlinerMapLock,
   kMarkSweepMarkStackLock,
+  kTransactionLogLock,
+  kInternTableLock,
   kDefaultMutexLevel,
   kMarkSweepLargeObjectLock,
   kPinTableLock,
@@ -163,6 +165,9 @@
   // doesn't try to hold a higher level Mutex.
   #define DEFAULT_MUTEX_ACQUIRED_AFTER ACQUIRED_AFTER(classlinker_classes_lock_)
 
+  // Guards intern table.
+  static Mutex* intern_table_lock_ ACQUIRED_AFTER(classlinker_classes_lock_);
+
   // Have an exclusive aborting thread.
   static Mutex* abort_lock_ ACQUIRED_AFTER(classlinker_classes_lock_);
 
diff --git a/runtime/mirror/array.cc b/runtime/mirror/array.cc
index c23234e..2180857 100644
--- a/runtime/mirror/array.cc
+++ b/runtime/mirror/array.cc
@@ -60,7 +60,8 @@
         CHECK(self->IsExceptionPending());
         return nullptr;
       }
-      new_array->AsObjectArray<Array>()->Set(i, sub_array);
+      // Use non-transactional mode without check.
+      new_array->AsObjectArray<Array>()->Set<false, false>(i, sub_array);
     }
   }
   return new_array.get();
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index 2e123ef..7555975 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -20,6 +20,7 @@
 #include "object.h"
 #include "object_callbacks.h"
 #include "gc/heap.h"
+#include "runtime.h"
 #include "thread.h"
 
 namespace art {
@@ -60,7 +61,9 @@
 
   void SetLength(int32_t length) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     CHECK_GE(length, 0);
-    SetField32(OFFSET_OF_OBJECT_MEMBER(Array, length_), length, false, false);
+    // We use non transactional version since we can't undo this write. We also disable checking
+    // since it would fail during a transaction.
+    SetField32<false, false>(OFFSET_OF_OBJECT_MEMBER(Array, length_), length, false, false);
   }
 
   static MemberOffset LengthOffset() {
@@ -144,14 +147,34 @@
   }
 
   void Set(int32_t i, T value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    if (Runtime::Current()->IsActiveTransaction()) {
+      Set<true>(i, value);
+    } else {
+      Set<false>(i, value);
+    }
+  }
+
+  // TODO fix thread safety analysis broken by the use of template. This should be
+  // SHARED_LOCKS_REQUIRED(Locks::mutator_lock_).
+  template<bool kTransactionActive, bool kCheckTransaction = true>
+  void Set(int32_t i, T value) NO_THREAD_SAFETY_ANALYSIS {
     if (LIKELY(CheckIsValidIndex(i))) {
-      SetWithoutChecks(i, value);
+      SetWithoutChecks<kTransactionActive, kCheckTransaction>(i, value);
     } else {
       DCHECK(Thread::Current()->IsExceptionPending());
     }
   }
 
-  void SetWithoutChecks(int32_t i, T value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  // TODO fix thread safety analysis broken by the use of template. This should be
+  // SHARED_LOCKS_REQUIRED(Locks::mutator_lock_).
+  template<bool kTransactionActive, bool kCheckTransaction = true>
+  void SetWithoutChecks(int32_t i, T value) NO_THREAD_SAFETY_ANALYSIS {
+    if (kCheckTransaction) {
+      DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+    }
+    if (kTransactionActive) {
+      Runtime::Current()->RecordWriteArray(this, i, GetWithoutChecks(i));
+    }
     DCHECK(CheckIsValidIndex(i));
     GetData()[i] = value;
   }
diff --git a/runtime/mirror/art_field-inl.h b/runtime/mirror/art_field-inl.h
index 530226b..6253edd 100644
--- a/runtime/mirror/art_field-inl.h
+++ b/runtime/mirror/art_field-inl.h
@@ -37,7 +37,8 @@
 }
 
 inline void ArtField::SetDeclaringClass(Class *new_declaring_class) {
-  SetFieldObject(OFFSET_OF_OBJECT_MEMBER(ArtField, declaring_class_), new_declaring_class, false);
+  SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ArtField, declaring_class_),
+                        new_declaring_class, false);
 }
 
 inline uint32_t ArtField::GetAccessFlags() {
@@ -61,10 +62,11 @@
   return object->GetField32(GetOffset(), IsVolatile());
 }
 
+template<bool kTransactionActive>
 inline void ArtField::Set32(Object* object, uint32_t new_value) {
   DCHECK(object != NULL) << PrettyField(this);
   DCHECK(!IsStatic() || (object == GetDeclaringClass()) || !Runtime::Current()->IsStarted());
-  object->SetField32(GetOffset(), new_value, IsVolatile());
+  object->SetField32<kTransactionActive>(GetOffset(), new_value, IsVolatile());
 }
 
 inline uint64_t ArtField::Get64(Object* object) {
@@ -73,10 +75,11 @@
   return object->GetField64(GetOffset(), IsVolatile());
 }
 
+template<bool kTransactionActive>
 inline void ArtField::Set64(Object* object, uint64_t new_value) {
   DCHECK(object != NULL) << PrettyField(this);
   DCHECK(!IsStatic() || (object == GetDeclaringClass()) || !Runtime::Current()->IsStarted());
-  object->SetField64(GetOffset(), new_value, IsVolatile());
+  object->SetField64<kTransactionActive>(GetOffset(), new_value, IsVolatile());
 }
 
 inline Object* ArtField::GetObj(Object* object) {
@@ -85,10 +88,11 @@
   return object->GetFieldObject<Object>(GetOffset(), IsVolatile());
 }
 
+template<bool kTransactionActive>
 inline void ArtField::SetObj(Object* object, Object* new_value) {
   DCHECK(object != NULL) << PrettyField(this);
   DCHECK(!IsStatic() || (object == GetDeclaringClass()) || !Runtime::Current()->IsStarted());
-  object->SetFieldObject(GetOffset(), new_value, IsVolatile());
+  object->SetFieldObject<kTransactionActive>(GetOffset(), new_value, IsVolatile());
 }
 
 inline bool ArtField::GetBoolean(Object* object) {
@@ -97,10 +101,11 @@
   return Get32(object);
 }
 
+template<bool kTransactionActive>
 inline void ArtField::SetBoolean(Object* object, bool z) {
   DCHECK_EQ(Primitive::kPrimBoolean, FieldHelper(this).GetTypeAsPrimitiveType())
       << PrettyField(this);
-  Set32(object, z);
+  Set32<kTransactionActive>(object, z);
 }
 
 inline int8_t ArtField::GetByte(Object* object) {
@@ -109,10 +114,11 @@
   return Get32(object);
 }
 
+template<bool kTransactionActive>
 inline void ArtField::SetByte(Object* object, int8_t b) {
   DCHECK_EQ(Primitive::kPrimByte, FieldHelper(this).GetTypeAsPrimitiveType())
       << PrettyField(this);
-  Set32(object, b);
+  Set32<kTransactionActive>(object, b);
 }
 
 inline uint16_t ArtField::GetChar(Object* object) {
@@ -121,10 +127,11 @@
   return Get32(object);
 }
 
+template<bool kTransactionActive>
 inline void ArtField::SetChar(Object* object, uint16_t c) {
   DCHECK_EQ(Primitive::kPrimChar, FieldHelper(this).GetTypeAsPrimitiveType())
        << PrettyField(this);
-  Set32(object, c);
+  Set32<kTransactionActive>(object, c);
 }
 
 inline int16_t ArtField::GetShort(Object* object) {
@@ -133,42 +140,45 @@
   return Get32(object);
 }
 
+template<bool kTransactionActive>
 inline void ArtField::SetShort(Object* object, int16_t s) {
   DCHECK_EQ(Primitive::kPrimShort, FieldHelper(this).GetTypeAsPrimitiveType())
        << PrettyField(this);
-  Set32(object, s);
+  Set32<kTransactionActive>(object, s);
 }
 
 inline int32_t ArtField::GetInt(Object* object) {
-#ifndef NDEBUG
-  Primitive::Type type = FieldHelper(this).GetTypeAsPrimitiveType();
-  CHECK(type == Primitive::kPrimInt || type == Primitive::kPrimFloat) << PrettyField(this);
-#endif
+  if (kIsDebugBuild) {
+    Primitive::Type type = FieldHelper(this).GetTypeAsPrimitiveType();
+    CHECK(type == Primitive::kPrimInt || type == Primitive::kPrimFloat) << PrettyField(this);
+  }
   return Get32(object);
 }
 
+template<bool kTransactionActive>
 inline void ArtField::SetInt(Object* object, int32_t i) {
-#ifndef NDEBUG
-  Primitive::Type type = FieldHelper(this).GetTypeAsPrimitiveType();
-  CHECK(type == Primitive::kPrimInt || type == Primitive::kPrimFloat) << PrettyField(this);
-#endif
-  Set32(object, i);
+  if (kIsDebugBuild) {
+    Primitive::Type type = FieldHelper(this).GetTypeAsPrimitiveType();
+    CHECK(type == Primitive::kPrimInt || type == Primitive::kPrimFloat) << PrettyField(this);
+  }
+  Set32<kTransactionActive>(object, i);
 }
 
 inline int64_t ArtField::GetLong(Object* object) {
-#ifndef NDEBUG
-  Primitive::Type type = FieldHelper(this).GetTypeAsPrimitiveType();
-  CHECK(type == Primitive::kPrimLong || type == Primitive::kPrimDouble) << PrettyField(this);
-#endif
+  if (kIsDebugBuild) {
+    Primitive::Type type = FieldHelper(this).GetTypeAsPrimitiveType();
+    CHECK(type == Primitive::kPrimLong || type == Primitive::kPrimDouble) << PrettyField(this);
+  }
   return Get64(object);
 }
 
+template<bool kTransactionActive>
 inline void ArtField::SetLong(Object* object, int64_t j) {
-#ifndef NDEBUG
-  Primitive::Type type = FieldHelper(this).GetTypeAsPrimitiveType();
-  CHECK(type == Primitive::kPrimLong || type == Primitive::kPrimDouble) << PrettyField(this);
-#endif
-  Set64(object, j);
+  if (kIsDebugBuild) {
+    Primitive::Type type = FieldHelper(this).GetTypeAsPrimitiveType();
+    CHECK(type == Primitive::kPrimLong || type == Primitive::kPrimDouble) << PrettyField(this);
+  }
+  Set64<kTransactionActive>(object, j);
 }
 
 inline float ArtField::GetFloat(Object* object) {
@@ -179,12 +189,13 @@
   return bits.GetF();
 }
 
+template<bool kTransactionActive>
 inline void ArtField::SetFloat(Object* object, float f) {
   DCHECK_EQ(Primitive::kPrimFloat, FieldHelper(this).GetTypeAsPrimitiveType())
        << PrettyField(this);
   JValue bits;
   bits.SetF(f);
-  Set32(object, bits.GetI());
+  Set32<kTransactionActive>(object, bits.GetI());
 }
 
 inline double ArtField::GetDouble(Object* object) {
@@ -195,12 +206,13 @@
   return bits.GetD();
 }
 
+template<bool kTransactionActive>
 inline void ArtField::SetDouble(Object* object, double d) {
   DCHECK_EQ(Primitive::kPrimDouble, FieldHelper(this).GetTypeAsPrimitiveType())
        << PrettyField(this);
   JValue bits;
   bits.SetD(d);
-  Set64(object, bits.GetJ());
+  Set64<kTransactionActive>(object, bits.GetJ());
 }
 
 inline Object* ArtField::GetObject(Object* object) {
@@ -209,10 +221,11 @@
   return GetObj(object);
 }
 
+template<bool kTransactionActive>
 inline void ArtField::SetObject(Object* object, Object* l) {
   DCHECK_EQ(Primitive::kPrimNot, FieldHelper(this).GetTypeAsPrimitiveType())
        << PrettyField(this);
-  SetObj(object, l);
+  SetObj<kTransactionActive>(object, l);
 }
 
 }  // namespace mirror
diff --git a/runtime/mirror/art_field.cc b/runtime/mirror/art_field.cc
index 29aade9..dd628ea 100644
--- a/runtime/mirror/art_field.cc
+++ b/runtime/mirror/art_field.cc
@@ -42,14 +42,15 @@
 
 void ArtField::SetOffset(MemberOffset num_bytes) {
   DCHECK(GetDeclaringClass()->IsLoaded() || GetDeclaringClass()->IsErroneous());
-#if 0  // TODO enable later in boot and under !NDEBUG
-  FieldHelper fh(this);
-  Primitive::Type type = fh.GetTypeAsPrimitiveType();
-  if (type == Primitive::kPrimDouble || type == Primitive::kPrimLong) {
-    DCHECK_ALIGNED(num_bytes.Uint32Value(), 8);
+  if (kIsDebugBuild && Runtime::Current()->IsCompiler() &&
+      !Runtime::Current()->UseCompileTimeClassPath()) {
+    Primitive::Type type = FieldHelper(this).GetTypeAsPrimitiveType();
+    if (type == Primitive::kPrimDouble || type == Primitive::kPrimLong) {
+      DCHECK_ALIGNED(num_bytes.Uint32Value(), 8);
+    }
   }
-#endif
-  SetField32(OFFSET_OF_OBJECT_MEMBER(ArtField, offset_), num_bytes.Uint32Value(), false);
+  // Not called within a transaction.
+  SetField32<false>(OFFSET_OF_OBJECT_MEMBER(ArtField, offset_), num_bytes.Uint32Value(), false);
 }
 
 void ArtField::VisitRoots(RootCallback* callback, void* arg) {
diff --git a/runtime/mirror/art_field.h b/runtime/mirror/art_field.h
index 716b736..46287c3 100644
--- a/runtime/mirror/art_field.h
+++ b/runtime/mirror/art_field.h
@@ -38,7 +38,8 @@
   uint32_t GetAccessFlags() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void SetAccessFlags(uint32_t new_access_flags) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    SetField32(OFFSET_OF_OBJECT_MEMBER(ArtField, access_flags_), new_access_flags, false);
+    // Not called within a transaction.
+    SetField32<false>(OFFSET_OF_OBJECT_MEMBER(ArtField, access_flags_), new_access_flags, false);
   }
 
   bool IsPublic() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -58,7 +59,8 @@
   }
 
   void SetDexFieldIndex(uint32_t new_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    SetField32(OFFSET_OF_OBJECT_MEMBER(ArtField, field_dex_idx_), new_idx, false);
+    // Not called within a transaction.
+    SetField32<false>(OFFSET_OF_OBJECT_MEMBER(ArtField, field_dex_idx_), new_idx, false);
   }
 
   // Offset to field within an Object.
@@ -74,30 +76,42 @@
 
   // field access, null object for static fields
   bool GetBoolean(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  template<bool kTransactionActive>
   void SetBoolean(Object* object, bool z) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   int8_t GetByte(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  template<bool kTransactionActive>
   void SetByte(Object* object, int8_t b) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   uint16_t GetChar(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  template<bool kTransactionActive>
   void SetChar(Object* object, uint16_t c) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   int16_t GetShort(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  template<bool kTransactionActive>
   void SetShort(Object* object, int16_t s) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   int32_t GetInt(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  template<bool kTransactionActive>
   void SetInt(Object* object, int32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   int64_t GetLong(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  template<bool kTransactionActive>
   void SetLong(Object* object, int64_t j) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   float GetFloat(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  template<bool kTransactionActive>
   void SetFloat(Object* object, float f) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   double GetDouble(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  template<bool kTransactionActive>
   void SetDouble(Object* object, double d) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   Object* GetObject(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  template<bool kTransactionActive>
   void SetObject(Object* object, Object* l) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Raw field accesses.
   uint32_t Get32(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  template<bool kTransactionActive>
   void Set32(Object* object, uint32_t new_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   uint64_t Get64(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  template<bool kTransactionActive>
   void Set64(Object* object, uint64_t new_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   Object* GetObj(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  template<bool kTransactionActive>
   void SetObj(Object* object, Object* new_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   static Class* GetJavaLangReflectArtField() {
diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h
index 8ef3be8..d347724 100644
--- a/runtime/mirror/art_method-inl.h
+++ b/runtime/mirror/art_method-inl.h
@@ -36,7 +36,8 @@
 }
 
 inline void ArtMethod::SetDeclaringClass(Class *new_declaring_class) {
-  SetFieldObject(OFFSET_OF_OBJECT_MEMBER(ArtMethod, declaring_class_), new_declaring_class, false);
+  SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, declaring_class_),
+                        new_declaring_class, false);
 }
 
 inline uint32_t ArtMethod::GetAccessFlags() {
diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc
index 67e6c7d..b5c87ad 100644
--- a/runtime/mirror/art_method.cc
+++ b/runtime/mirror/art_method.cc
@@ -73,18 +73,18 @@
 }
 
 void ArtMethod::SetDexCacheStrings(ObjectArray<String>* new_dex_cache_strings) {
-  SetFieldObject(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_strings_),
-                 new_dex_cache_strings, false);
+  SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_strings_),
+                        new_dex_cache_strings, false);
 }
 
 void ArtMethod::SetDexCacheResolvedMethods(ObjectArray<ArtMethod>* new_dex_cache_methods) {
-  SetFieldObject(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_resolved_methods_),
-                 new_dex_cache_methods, false);
+  SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_resolved_methods_),
+                        new_dex_cache_methods, false);
 }
 
 void ArtMethod::SetDexCacheResolvedTypes(ObjectArray<Class>* new_dex_cache_classes) {
-  SetFieldObject(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_resolved_types_),
-                 new_dex_cache_classes, false);
+  SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_resolved_types_),
+                        new_dex_cache_classes, false);
 }
 
 size_t ArtMethod::NumArgRegisters(const StringPiece& shorty) {
@@ -337,8 +337,8 @@
 #else
     SetNativeMethod(reinterpret_cast<void*>(art_work_around_app_jni_bugs));
 #endif
-    SetFieldPtr<const uint8_t*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, gc_map_),
-        reinterpret_cast<const uint8_t*>(native_method), false);
+    SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, gc_map_),
+                       reinterpret_cast<const uint8_t*>(native_method), false);
   }
 }
 
@@ -349,8 +349,8 @@
 }
 
 void ArtMethod::SetNativeMethod(const void* native_method) {
-  SetFieldPtr<const void*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_jni_),
-      native_method, false);
+  SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_jni_),
+                     native_method, false);
 }
 
 }  // namespace mirror
diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h
index e678503..71cc7af 100644
--- a/runtime/mirror/art_method.h
+++ b/runtime/mirror/art_method.h
@@ -56,7 +56,8 @@
   uint32_t GetAccessFlags() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void SetAccessFlags(uint32_t new_access_flags) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    SetField32(OFFSET_OF_OBJECT_MEMBER(ArtMethod, access_flags_), new_access_flags, false);
+    // Not called within a transaction.
+    SetField32<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, access_flags_), new_access_flags, false);
   }
 
   // Approximate what kind of method call would be used for this method.
@@ -156,7 +157,8 @@
   }
 
   void SetMethodIndex(uint16_t new_method_index) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    SetField32(OFFSET_OF_OBJECT_MEMBER(ArtMethod, method_index_), new_method_index, false);
+    // Not called within a transaction.
+    SetField32<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, method_index_), new_method_index, false);
   }
 
   static MemberOffset MethodIndexOffset() {
@@ -168,7 +170,8 @@
   }
 
   void SetCodeItemOffset(uint32_t new_code_off) {
-    SetField32(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_code_item_offset_), new_code_off, false);
+    // Not called within a transaction.
+    SetField32<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_code_item_offset_), new_code_off, false);
   }
 
   // Number of 32bit registers that would be required to hold all the arguments
@@ -177,7 +180,8 @@
   uint32_t GetDexMethodIndex() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void SetDexMethodIndex(uint32_t new_idx) {
-    SetField32(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_method_index_), new_idx, false);
+    // Not called within a transaction.
+    SetField32<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_method_index_), new_idx, false);
   }
 
   ObjectArray<String>* GetDexCacheStrings() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -216,9 +220,8 @@
   }
 
   void SetEntryPointFromInterpreter(EntryPointFromInterpreter* entry_point_from_interpreter) {
-    SetFieldPtr<EntryPointFromInterpreter*>(
-        OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_interpreter_),
-        entry_point_from_interpreter, false);
+    SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_interpreter_),
+                       entry_point_from_interpreter, false);
   }
 
   static MemberOffset EntryPointFromPortableCompiledCodeOffset() {
@@ -230,8 +233,8 @@
   }
 
   void SetEntryPointFromPortableCompiledCode(const void* entry_point_from_portable_compiled_code) {
-    SetFieldPtr<const void*>(EntryPointFromPortableCompiledCodeOffset(),
-        entry_point_from_portable_compiled_code, false);
+    SetFieldPtr<false>(EntryPointFromPortableCompiledCodeOffset(),
+                       entry_point_from_portable_compiled_code, false);
   }
 
   static MemberOffset EntryPointFromQuickCompiledCodeOffset() {
@@ -243,8 +246,8 @@
   }
 
   void SetEntryPointFromQuickCompiledCode(const void* entry_point_from_quick_compiled_code) {
-    SetFieldPtr<const void*>(EntryPointFromQuickCompiledCodeOffset(),
-        entry_point_from_quick_compiled_code, false);
+    SetFieldPtr<false>(EntryPointFromQuickCompiledCodeOffset(),
+                       entry_point_from_quick_compiled_code, false);
   }
 
 
@@ -277,8 +280,8 @@
   }
 
   void SetMappingTable(const uint8_t* mapping_table) {
-    SetFieldPtr<const uint8_t*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_mapping_table_),
-                                mapping_table, false);
+    SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_mapping_table_),
+                       mapping_table, false);
   }
 
   uint32_t GetOatMappingTableOffset();
@@ -292,8 +295,7 @@
   }
 
   void SetVmapTable(const uint8_t* vmap_table) {
-    SetFieldPtr<const uint8_t*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_vmap_table_), vmap_table,
-        false);
+    SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_vmap_table_), vmap_table, false);
   }
 
   uint32_t GetOatVmapTableOffset();
@@ -304,7 +306,7 @@
     return GetFieldPtr<uint8_t*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, gc_map_), false);
   }
   void SetNativeGcMap(const uint8_t* data) {
-    SetFieldPtr<const uint8_t*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, gc_map_), data, false);
+    SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, gc_map_), data, false);
   }
 
   // When building the oat need a convenient place to stuff the offset of the native GC map.
@@ -319,8 +321,9 @@
   }
 
   void SetFrameSizeInBytes(size_t new_frame_size_in_bytes) {
-    SetField32(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_frame_size_in_bytes_),
-               new_frame_size_in_bytes, false);
+    // Not called within a transaction.
+    SetField32<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_frame_size_in_bytes_),
+                      new_frame_size_in_bytes, false);
   }
 
   size_t GetReturnPcOffsetInBytes() {
@@ -358,8 +361,9 @@
   }
 
   void SetCoreSpillMask(uint32_t core_spill_mask) {
-    // Computed during compilation
-    SetField32(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_core_spill_mask_), core_spill_mask, false);
+    // Computed during compilation.
+    // Not called within a transaction.
+    SetField32<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_core_spill_mask_), core_spill_mask, false);
   }
 
   uint32_t GetFpSpillMask() {
@@ -367,8 +371,9 @@
   }
 
   void SetFpSpillMask(uint32_t fp_spill_mask) {
-    // Computed during compilation
-    SetField32(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_fp_spill_mask_), fp_spill_mask, false);
+    // Computed during compilation.
+    // Not called within a transaction.
+    SetField32<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_fp_spill_mask_), fp_spill_mask, false);
   }
 
   // Is this a CalleSaveMethod or ResolutionMethod and therefore doesn't adhere to normal
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index a5f743b..4c2bdb0 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -64,8 +64,8 @@
   DCHECK(NULL == GetFieldObject<ObjectArray<ArtMethod> >(
       OFFSET_OF_OBJECT_MEMBER(Class, direct_methods_), false));
   DCHECK_NE(0, new_direct_methods->GetLength());
-  SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, direct_methods_),
-                 new_direct_methods, false);
+  SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, direct_methods_),
+                        new_direct_methods, false);
 }
 
 inline ArtMethod* Class::GetDirectMethod(int32_t i) {
@@ -77,7 +77,7 @@
   ObjectArray<ArtMethod>* direct_methods =
       GetFieldObject<ObjectArray<ArtMethod> >(
           OFFSET_OF_OBJECT_MEMBER(Class, direct_methods_), false);
-  direct_methods->Set(i, f);
+  direct_methods->Set<false>(i, f);
 }
 
 // Returns the number of static, private, and constructor methods.
@@ -93,10 +93,10 @@
 
 inline void Class::SetVirtualMethods(ObjectArray<ArtMethod>* new_virtual_methods) {
   // TODO: we reassign virtual methods to grow the table for miranda
-  // methods.. they should really just be assigned once
+  // methods.. they should really just be assigned once.
   DCHECK_NE(0, new_virtual_methods->GetLength());
-  SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, virtual_methods_),
-                 new_virtual_methods, false);
+  SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, virtual_methods_),
+                        new_virtual_methods, false);
 }
 
 inline uint32_t Class::NumVirtualMethods() {
@@ -118,7 +118,7 @@
   ObjectArray<ArtMethod>* virtual_methods =
       GetFieldObject<ObjectArray<ArtMethod> >(
           OFFSET_OF_OBJECT_MEMBER(Class, virtual_methods_), false);
-  virtual_methods->Set(i, f);
+  virtual_methods->Set<false>(i, f);
 }
 
 inline ObjectArray<ArtMethod>* Class::GetVTable() {
@@ -132,7 +132,7 @@
 }
 
 inline void Class::SetVTable(ObjectArray<ArtMethod>* new_vtable) {
-  SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, vtable_), new_vtable, false);
+  SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, vtable_), new_vtable, false);
 }
 
 inline ObjectArray<ArtMethod>* Class::GetImTable() {
@@ -140,7 +140,7 @@
 }
 
 inline void Class::SetImTable(ObjectArray<ArtMethod>* new_imtable) {
-  SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, imtable_), new_imtable, false);
+  SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, imtable_), new_imtable, false);
 }
 
 inline bool Class::Implements(Class* klass) {
@@ -347,7 +347,7 @@
 }
 
 inline void Class::SetIfTable(IfTable* new_iftable) {
-  SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, iftable_), new_iftable, false);
+  SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, iftable_), new_iftable, false);
 }
 
 inline ObjectArray<ArtField>* Class::GetIFields() {
@@ -359,7 +359,7 @@
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   DCHECK(NULL == GetFieldObject<ObjectArray<ArtField> >(
       OFFSET_OF_OBJECT_MEMBER(Class, ifields_), false));
-  SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, ifields_), new_ifields, false);
+  SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, ifields_), new_ifields, false);
 }
 
 inline ObjectArray<ArtField>* Class::GetSFields() {
@@ -371,7 +371,7 @@
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   DCHECK(NULL == GetFieldObject<ObjectArray<ArtField> >(
       OFFSET_OF_OBJECT_MEMBER(Class, sfields_), false));
-  SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, sfields_), new_sfields, false);
+  SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, sfields_), new_sfields, false);
 }
 
 inline uint32_t Class::NumStaticFields() {
@@ -387,7 +387,7 @@
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   ObjectArray<ArtField>* sfields= GetFieldObject<ObjectArray<ArtField> >(
       OFFSET_OF_OBJECT_MEMBER(Class, sfields_), false);
-  sfields->Set(i, f);
+  sfields->Set<false>(i, f);
 }
 
 inline uint32_t Class::NumInstanceFields() {
@@ -403,12 +403,16 @@
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   ObjectArray<ArtField>* ifields= GetFieldObject<ObjectArray<ArtField> >(
       OFFSET_OF_OBJECT_MEMBER(Class, ifields_), false);
-  ifields->Set(i, f);
+  ifields->Set<false>(i, f);
 }
 
 inline void Class::SetVerifyErrorClass(Class* klass) {
   CHECK(klass != NULL) << PrettyClass(this);
-  SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, verify_error_class_), klass, false);
+  if (Runtime::Current()->IsActiveTransaction()) {
+    SetFieldObject<true>(OFFSET_OF_OBJECT_MEMBER(Class, verify_error_class_), klass, false);
+  } else {
+    SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, verify_error_class_), klass, false);
+  }
 }
 
 inline uint32_t Class::GetAccessFlags() {
@@ -425,7 +429,11 @@
   return GetFieldObject<String>(OFFSET_OF_OBJECT_MEMBER(Class, name_), false);
 }
 inline void Class::SetName(String* name) {
-  SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, name_), name, false);
+  if (Runtime::Current()->IsActiveTransaction()) {
+    SetFieldObject<true>(OFFSET_OF_OBJECT_MEMBER(Class, name_), name, false);
+  } else {
+    SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, name_), name, false);
+  }
 }
 
 inline void Class::CheckObjectAlloc() {
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 99a35e3..6446d02 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -113,7 +113,11 @@
     self->SetException(gc_safe_throw_location, old_exception.get());
   }
   CHECK(sizeof(Status) == sizeof(uint32_t)) << PrettyClass(this);
-  SetField32(OFFSET_OF_OBJECT_MEMBER(Class, status_), new_status, false);
+  if (Runtime::Current()->IsActiveTransaction()) {
+    SetField32<true>(OFFSET_OF_OBJECT_MEMBER(Class, status_), new_status, false);
+  } else {
+    SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, status_), new_status, false);
+  }
   // Classes that are being resolved or initialized need to notify waiters that the class status
   // changed. See ClassLinker::EnsureResolved and ClassLinker::WaitForInitializeClass.
   if ((old_status >= kStatusResolved || new_status >= kStatusResolved) &&
@@ -123,7 +127,7 @@
 }
 
 void Class::SetDexCache(DexCache* new_dex_cache) {
-  SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, dex_cache_), new_dex_cache, false);
+  SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, dex_cache_), new_dex_cache, false);
 }
 
 void Class::SetClassSize(uint32_t new_class_size) {
@@ -131,7 +135,8 @@
     DumpClass(LOG(ERROR), kDumpClassFullDetail);
     CHECK_GE(new_class_size, GetClassSize()) << " class=" << PrettyTypeOf(this);
   }
-  SetField32(OFFSET_OF_OBJECT_MEMBER(Class, class_size_), new_class_size, false);
+  // Not called within a transaction.
+  SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, class_size_), new_class_size, false);
 }
 
 // Return the class' name. The exact format is bizarre, but it's the specified behavior for
@@ -254,8 +259,9 @@
     }
     CHECK_EQ((size_t)__builtin_popcount(new_reference_offsets), count);
   }
-  SetField32(OFFSET_OF_OBJECT_MEMBER(Class, reference_instance_offsets_),
-             new_reference_offsets, false);
+  // Not called within a transaction.
+  SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, reference_instance_offsets_),
+                    new_reference_offsets, false);
 }
 
 void Class::SetReferenceStaticOffsets(uint32_t new_reference_offsets) {
@@ -265,8 +271,9 @@
     CHECK_EQ((size_t)__builtin_popcount(new_reference_offsets),
              NumReferenceStaticFieldsDuringLinking());
   }
-  SetField32(OFFSET_OF_OBJECT_MEMBER(Class, reference_static_offsets_),
-             new_reference_offsets, false);
+  // Not called within a transaction.
+  SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, reference_static_offsets_),
+                    new_reference_offsets, false);
 }
 
 bool Class::IsInSamePackage(const StringPiece& descriptor1, const StringPiece& descriptor2) {
@@ -332,7 +339,11 @@
 }
 
 void Class::SetClassLoader(ClassLoader* new_class_loader) {
-  SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader, false);
+  if (Runtime::Current()->IsActiveTransaction()) {
+    SetFieldObject<true>(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader, false);
+  } else {
+    SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader, false);
+  }
 }
 
 ArtMethod* Class::FindInterfaceMethod(const StringPiece& name, const Signature& signature) {
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 82c8264..cd8504b 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -174,7 +174,8 @@
   uint32_t GetAccessFlags() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void SetAccessFlags(uint32_t new_access_flags) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    SetField32(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_), new_access_flags, false);
+    // Not called within a transaction.
+    SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_), new_access_flags, false);
   }
 
   // Returns true if the class is an interface.
@@ -275,7 +276,7 @@
 
   void SetPrimitiveType(Primitive::Type new_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     DCHECK_EQ(sizeof(Primitive::Type), sizeof(int32_t));
-    SetField32(OFFSET_OF_OBJECT_MEMBER(Class, primitive_type_), new_type, false);
+    SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, primitive_type_), new_type, false);
   }
 
   // Returns true if the class is a primitive type.
@@ -357,7 +358,11 @@
   void SetComponentType(Class* new_component_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     DCHECK(GetComponentType() == NULL);
     DCHECK(new_component_type != NULL);
-    SetFieldObject(ComponentTypeOffset(), new_component_type, false);
+    if (Runtime::Current()->IsActiveTransaction()) {
+      SetFieldObject<true>(ComponentTypeOffset(), new_component_type, false);
+    } else {
+      SetFieldObject<false>(ComponentTypeOffset(), new_component_type, false);
+    }
   }
 
   size_t GetComponentSize() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -406,7 +411,8 @@
 
   void SetObjectSize(uint32_t new_object_size) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     DCHECK(!IsVariableSize());
-    return SetField32(OFFSET_OF_OBJECT_MEMBER(Class, object_size_), new_object_size, false);
+    // Not called within a transaction.
+    return SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, object_size_), new_object_size, false);
   }
 
   // Returns true if this class is in the same packages as that class.
@@ -499,7 +505,7 @@
                                                    false);
     DCHECK(old_super_class == nullptr || old_super_class == new_super_class);
     DCHECK(new_super_class != nullptr);
-    SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, super_class_), new_super_class, false);
+    SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, super_class_), new_super_class, false);
   }
 
   bool HasSuperClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -672,7 +678,9 @@
   }
 
   void SetNumReferenceInstanceFields(uint32_t new_num) {
-    SetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_instance_fields_), new_num, false);
+    // Not called within a transaction.
+    SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_instance_fields_), new_num,
+                      false);
   }
 
   uint32_t GetReferenceInstanceOffsets() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -700,7 +708,8 @@
   }
 
   void SetNumReferenceStaticFields(uint32_t new_num) {
-    SetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_static_fields_), new_num, false);
+    // Not called within a transaction.
+    SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_static_fields_), new_num, false);
   }
 
   // Gets the static fields of the class.
@@ -763,7 +772,13 @@
   }
 
   void SetClinitThreadId(pid_t new_clinit_thread_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    SetField32(OFFSET_OF_OBJECT_MEMBER(Class, clinit_thread_id_), new_clinit_thread_id, false);
+    if (Runtime::Current()->IsActiveTransaction()) {
+      SetField32<true>(OFFSET_OF_OBJECT_MEMBER(Class, clinit_thread_id_), new_clinit_thread_id,
+                       false);
+    } else {
+      SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, clinit_thread_id_), new_clinit_thread_id,
+                        false);
+    }
   }
 
   Class* GetVerifyErrorClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -776,7 +791,8 @@
   }
 
   void SetDexClassDefIndex(uint16_t class_def_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    SetField32(OFFSET_OF_OBJECT_MEMBER(Class, dex_class_def_idx_), class_def_idx, false);
+    // Not called within a transaction.
+    SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, dex_class_def_idx_), class_def_idx, false);
   }
 
   uint16_t GetDexTypeIndex() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -784,7 +800,8 @@
   }
 
   void SetDexTypeIndex(uint16_t type_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    SetField32(OFFSET_OF_OBJECT_MEMBER(Class, dex_type_idx_), type_idx, false);
+    // Not called within a transaction.
+    SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, dex_type_idx_), type_idx, false);
   }
 
   static Class* GetJavaLangClass() {
diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc
index fa0900c..0a77db3 100644
--- a/runtime/mirror/dex_cache.cc
+++ b/runtime/mirror/dex_cache.cc
@@ -44,12 +44,12 @@
   CHECK(resolved_methods != nullptr);
   CHECK(resolved_fields != nullptr);
 
-  SetFieldPtr(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_), dex_file, false);
-  SetFieldObject(OFFSET_OF_OBJECT_MEMBER(DexCache, location_), location, false);
-  SetFieldObject(StringsOffset(), strings, false);
-  SetFieldObject(OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_types_), resolved_types, false);
-  SetFieldObject(ResolvedMethodsOffset(), resolved_methods, false);
-  SetFieldObject(ResolvedFieldsOffset(), resolved_fields, false);
+  SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_), dex_file, false);
+  SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, location_), location, false);
+  SetFieldObject<false>(StringsOffset(), strings, false);
+  SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_types_), resolved_types, false);
+  SetFieldObject<false>(ResolvedMethodsOffset(), resolved_methods, false);
+  SetFieldObject<false>(ResolvedFieldsOffset(), resolved_fields, false);
 
   Runtime* runtime = Runtime::Current();
   if (runtime->HasResolutionMethod()) {
@@ -57,7 +57,7 @@
     ArtMethod* trampoline = runtime->GetResolutionMethod();
     size_t length = resolved_methods->GetLength();
     for (size_t i = 0; i < length; i++) {
-      resolved_methods->SetWithoutChecks(i, trampoline);
+      resolved_methods->SetWithoutChecks<false>(i, trampoline);
     }
   }
 }
@@ -69,7 +69,7 @@
   size_t length = resolved_methods->GetLength();
   for (size_t i = 0; i < length; i++) {
     if (resolved_methods->GetWithoutChecks(i) == nullptr) {
-      resolved_methods->SetWithoutChecks(i, trampoline);
+      resolved_methods->SetWithoutChecks<false>(i, trampoline);
     }
   }
 }
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index 99529f0..843f860 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -90,6 +90,7 @@
 
   void SetResolvedString(uint32_t string_idx, String* resolved)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    // TODO default transaction support.
     GetStrings()->Set(string_idx, resolved);
   }
 
@@ -99,6 +100,7 @@
 
   void SetResolvedType(uint32_t type_idx, Class* resolved)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    // TODO default transaction support.
     GetResolvedTypes()->Set(type_idx, resolved);
   }
 
@@ -140,7 +142,7 @@
   }
 
   void SetDexFile(const DexFile* dex_file) {
-    return SetFieldPtr(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_), dex_file, false);
+    return SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_), dex_file, false);
   }
 
  private:
diff --git a/runtime/mirror/iftable-inl.h b/runtime/mirror/iftable-inl.h
index 9d5fa74..ec3e514 100644
--- a/runtime/mirror/iftable-inl.h
+++ b/runtime/mirror/iftable-inl.h
@@ -26,7 +26,7 @@
   DCHECK(interface != NULL);
   DCHECK(interface->IsInterface());
   DCHECK(Get((i * kMax) + kInterface) == NULL);
-  Set((i * kMax) + kInterface, interface);
+  Set<false>((i * kMax) + kInterface, interface);
 }
 
 }  // namespace mirror
diff --git a/runtime/mirror/iftable.h b/runtime/mirror/iftable.h
index be83d03..bb4cd41 100644
--- a/runtime/mirror/iftable.h
+++ b/runtime/mirror/iftable.h
@@ -52,7 +52,7 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     DCHECK(new_ma != NULL);
     DCHECK(Get((i * kMax) + kMethodArray) == NULL);
-    Set((i * kMax) + kMethodArray, new_ma);
+    Set<false>((i * kMax) + kMethodArray, new_ma);
   }
 
   size_t Count() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 2392561..70291c1 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -40,7 +40,10 @@
   // new_klass may be NULL prior to class linker initialization.
   // We don't mark the card as this occurs as part of object allocation. Not all objects have
   // backing cards, such as large objects.
-  SetFieldObjectWithoutWriteBarrier(OFFSET_OF_OBJECT_MEMBER(Object, klass_), new_klass, false, false);
+  // We use non transactional version since we can't undo this write. We also disable checking as
+  // we may run in transaction mode here.
+  SetFieldObjectWithoutWriteBarrier<false, false>(OFFSET_OF_OBJECT_MEMBER(Object, klass_),
+                                                  new_klass, false, false);
 }
 
 inline LockWord Object::GetLockWord() {
@@ -48,12 +51,14 @@
 }
 
 inline void Object::SetLockWord(LockWord new_val) {
-  SetField32(OFFSET_OF_OBJECT_MEMBER(Object, monitor_), new_val.GetValue(), true);
+  // Force use of non-transactional mode and do not check.
+  SetField32<false, false>(OFFSET_OF_OBJECT_MEMBER(Object, monitor_), new_val.GetValue(), true);
 }
 
 inline bool Object::CasLockWord(LockWord old_val, LockWord new_val) {
-  return CasField32(OFFSET_OF_OBJECT_MEMBER(Object, monitor_), old_val.GetValue(),
-                    new_val.GetValue());
+  // Force use of non-transactional mode and do not check.
+  return CasField32<false, false>(OFFSET_OF_OBJECT_MEMBER(Object, monitor_), old_val.GetValue(),
+                                  new_val.GetValue());
 }
 
 inline uint32_t Object::GetLockOwnerThreadId() {
@@ -199,6 +204,18 @@
   return down_cast<LongArray*>(this);
 }
 
+inline FloatArray* Object::AsFloatArray() {
+  DCHECK(GetClass()->IsArrayClass());
+  DCHECK(GetClass()->GetComponentType()->IsPrimitiveFloat());
+  return down_cast<FloatArray*>(this);
+}
+
+inline DoubleArray* Object::AsDoubleArray() {
+  DCHECK(GetClass()->IsArrayClass());
+  DCHECK(GetClass()->GetComponentType()->IsPrimitiveDouble());
+  return down_cast<DoubleArray*>(this);
+}
+
 inline String* Object::AsString() {
   DCHECK(GetClass()->IsStringClass());
   return down_cast<String*>(this);
@@ -253,8 +270,16 @@
   }
 }
 
+template<bool kTransactionActive, bool kCheckTransaction>
 inline void Object::SetField32(MemberOffset field_offset, int32_t new_value, bool is_volatile,
                                bool this_is_valid) {
+  if (kCheckTransaction) {
+    DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+  }
+  if (kTransactionActive) {
+    Runtime::Current()->RecordWriteField32(this, field_offset, GetField32(field_offset, is_volatile),
+                                           is_volatile);
+  }
   if (this_is_valid) {
     VerifyObject(this);
   }
@@ -269,7 +294,14 @@
   }
 }
 
+template<bool kTransactionActive, bool kCheckTransaction>
 inline bool Object::CasField32(MemberOffset field_offset, int32_t old_value, int32_t new_value) {
+  if (kCheckTransaction) {
+    DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+  }
+  if (kTransactionActive) {
+    Runtime::Current()->RecordWriteField32(this, field_offset, old_value, true);
+  }
   VerifyObject(this);
   byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
   volatile int32_t* addr = reinterpret_cast<volatile int32_t*>(raw_addr);
@@ -289,8 +321,16 @@
   }
 }
 
+template<bool kTransactionActive, bool kCheckTransaction>
 inline void Object::SetField64(MemberOffset field_offset, int64_t new_value, bool is_volatile,
                                bool this_is_valid) {
+  if (kCheckTransaction) {
+    DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+  }
+  if (kTransactionActive) {
+    Runtime::Current()->RecordWriteField64(this, field_offset, GetField64(field_offset, is_volatile),
+                                           is_volatile);
+  }
   if (this_is_valid) {
     VerifyObject(this);
   }
@@ -309,7 +349,14 @@
   }
 }
 
+template<bool kTransactionActive, bool kCheckTransaction>
 inline bool Object::CasField64(MemberOffset field_offset, int64_t old_value, int64_t new_value) {
+  if (kCheckTransaction) {
+    DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+  }
+  if (kTransactionActive) {
+    Runtime::Current()->RecordWriteField64(this, field_offset, old_value, true);
+  }
   VerifyObject(this);
   byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
   volatile int64_t* addr = reinterpret_cast<volatile int64_t*>(raw_addr);
@@ -331,8 +378,17 @@
   return result;
 }
 
+template<bool kTransactionActive, bool kCheckTransaction>
 inline void Object::SetFieldObjectWithoutWriteBarrier(MemberOffset field_offset, Object* new_value,
                                                       bool is_volatile, bool this_is_valid) {
+  if (kCheckTransaction) {
+    DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+  }
+  if (kTransactionActive) {
+    Runtime::Current()->RecordWriteFieldReference(this, field_offset,
+                                                  GetFieldObject<Object>(field_offset, is_volatile),
+                                                  true);
+  }
   if (this_is_valid) {
     VerifyObject(this);
   }
@@ -349,16 +405,26 @@
   }
 }
 
+template<bool kTransactionActive, bool kCheckTransaction>
 inline void Object::SetFieldObject(MemberOffset field_offset, Object* new_value, bool is_volatile,
                                    bool this_is_valid) {
-  SetFieldObjectWithoutWriteBarrier(field_offset, new_value, is_volatile, this_is_valid);
+  SetFieldObjectWithoutWriteBarrier<kTransactionActive, kCheckTransaction>(field_offset, new_value,
+                                                                           is_volatile,
+                                                                           this_is_valid);
   if (new_value != nullptr) {
     CheckFieldAssignment(field_offset, new_value);
     Runtime::Current()->GetHeap()->WriteBarrierField(this, field_offset, new_value);
   }
 }
 
+template<bool kTransactionActive, bool kCheckTransaction>
 inline bool Object::CasFieldObject(MemberOffset field_offset, Object* old_value, Object* new_value) {
+  if (kCheckTransaction) {
+    DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+  }
+  if (kTransactionActive) {
+    Runtime::Current()->RecordWriteFieldReference(this, field_offset, old_value, true);
+  }
   VerifyObject(this);
   byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
   volatile int32_t* addr = reinterpret_cast<volatile int32_t*>(raw_addr);
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index 5874976..eb118c7 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -23,6 +23,7 @@
 #include "cutils/atomic-inline.h"
 #include "object_reference.h"
 #include "offsets.h"
+#include "runtime.h"
 
 namespace art {
 
@@ -130,6 +131,9 @@
   IntArray* AsIntArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   LongArray* AsLongArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  FloatArray* AsFloatArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  DoubleArray* AsDoubleArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   String* AsString() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   Throwable* AsThrowable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -155,12 +159,15 @@
   // Accessor for Java type fields.
   template<class T> T* GetFieldObject(MemberOffset field_offset, bool is_volatile)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  template<bool kTransactionActive, bool kCheckTransaction = true>
   void SetFieldObjectWithoutWriteBarrier(MemberOffset field_offset, Object* new_value,
                                          bool is_volatile, bool this_is_valid = true)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  template<bool kTransactionActive, bool kCheckTransaction = true>
   void SetFieldObject(MemberOffset field_offset, Object* new_value, bool is_volatile,
                       bool this_is_valid = true)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  template<bool kTransactionActive, bool kCheckTransaction = true>
   bool CasFieldObject(MemberOffset field_offset, Object* old_value, Object* new_value)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -172,27 +179,35 @@
 
   int32_t GetField32(MemberOffset field_offset, bool is_volatile);
 
+  template<bool kTransactionActive, bool kCheckTransaction = true>
   void SetField32(MemberOffset field_offset, int32_t new_value, bool is_volatile,
                   bool this_is_valid = true);
 
+  template<bool kTransactionActive, bool kCheckTransaction = true>
   bool CasField32(MemberOffset field_offset, int32_t old_value, int32_t new_value)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   int64_t GetField64(MemberOffset field_offset, bool is_volatile);
 
+  template<bool kTransactionActive, bool kCheckTransaction = true>
   void SetField64(MemberOffset field_offset, int64_t new_value, bool is_volatile,
                   bool this_is_valid = true);
 
+  template<bool kTransactionActive, bool kCheckTransaction = true>
   bool CasField64(MemberOffset field_offset, int64_t old_value, int64_t new_value)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  template<typename T>
+  template<bool kTransactionActive, bool kCheckTransaction = true, typename T>
   void SetFieldPtr(MemberOffset field_offset, T new_value, bool is_volatile,
                    bool this_is_valid = true) {
 #ifndef __LP64__
-    SetField32(field_offset, reinterpret_cast<int32_t>(new_value), is_volatile, this_is_valid);
+    SetField32<kTransactionActive, kCheckTransaction>(field_offset,
+                                                      reinterpret_cast<int32_t>(new_value),
+                                                      is_volatile, this_is_valid);
 #else
-    SetField64(field_offset, reinterpret_cast<int64_t>(new_value), is_volatile, this_is_valid);
+    SetField64<kTransactionActive, kCheckTransaction>(field_offset,
+                                                      reinterpret_cast<int64_t>(new_value),
+                                                      is_volatile, this_is_valid);
 #endif
   }
 
diff --git a/runtime/mirror/object_array-inl.h b/runtime/mirror/object_array-inl.h
index c342479..521b6ce 100644
--- a/runtime/mirror/object_array-inl.h
+++ b/runtime/mirror/object_array-inl.h
@@ -72,26 +72,39 @@
 
 template<class T>
 inline void ObjectArray<T>::Set(int32_t i, T* object) {
+  if (Runtime::Current()->IsActiveTransaction()) {
+    Set<true>(i, object);
+  } else {
+    Set<false>(i, object);
+  }
+}
+
+template<class T>
+template<bool kTransactionActive, bool kCheckTransaction>
+inline void ObjectArray<T>::Set(int32_t i, T* object) {
   if (LIKELY(CheckIsValidIndex(i) && CheckAssignable(object))) {
-    SetFieldObject(OffsetOfElement(i), object, false);
+    SetFieldObject<kTransactionActive, kCheckTransaction>(OffsetOfElement(i), object, false);
   } else {
     DCHECK(Thread::Current()->IsExceptionPending());
   }
 }
 
 template<class T>
+template<bool kTransactionActive, bool kCheckTransaction>
 inline void ObjectArray<T>::SetWithoutChecks(int32_t i, T* object) {
   DCHECK(CheckIsValidIndex(i));
   DCHECK(CheckAssignable(object));
-  SetFieldObject(OffsetOfElement(i), object, false);
+  SetFieldObject<kTransactionActive, kCheckTransaction>(OffsetOfElement(i), object, false);
 }
 
 template<class T>
+template<bool kTransactionActive, bool kCheckTransaction>
 inline void ObjectArray<T>::SetWithoutChecksAndWriteBarrier(int32_t i, T* object) {
   DCHECK(CheckIsValidIndex(i));
   // TODO:  enable this check. It fails when writing the image in ImageWriter::FixupObjectArray.
   // DCHECK(CheckAssignable(object));
-  SetFieldObjectWithoutWriteBarrier(OffsetOfElement(i), object, false);
+  SetFieldObjectWithoutWriteBarrier<kTransactionActive, kCheckTransaction>(OffsetOfElement(i),
+                                                                           object, false);
 }
 
 template<class T>
@@ -164,15 +177,15 @@
     o = src->GetWithoutChecks(src_pos + i);
     if (o == nullptr) {
       // Null is always assignable.
-      SetWithoutChecks(dst_pos + i, nullptr);
+      SetWithoutChecks<false>(dst_pos + i, nullptr);
     } else {
       // TODO: use the underlying class reference to avoid uncompression when not necessary.
       Class* o_class = o->GetClass();
       if (LIKELY(lastAssignableElementClass == o_class)) {
-        SetWithoutChecks(dst_pos + i, o);
+        SetWithoutChecks<false>(dst_pos + i, o);
       } else if (LIKELY(dst_class->IsAssignableFrom(o_class))) {
         lastAssignableElementClass = o_class;
-        SetWithoutChecks(dst_pos + i, o);
+        SetWithoutChecks<false>(dst_pos + i, o);
       } else {
         // Can't put this element into the array, break to perform write-barrier and throw
         // exception.
diff --git a/runtime/mirror/object_array.h b/runtime/mirror/object_array.h
index 347494e..668b276 100644
--- a/runtime/mirror/object_array.h
+++ b/runtime/mirror/object_array.h
@@ -40,12 +40,20 @@
   bool CheckAssignable(T* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void Set(int32_t i, T* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  // TODO fix thread safety analysis: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_).
+  template<bool kTransactionActive, bool kCheckTransaction = true>
+  void Set(int32_t i, T* object) NO_THREAD_SAFETY_ANALYSIS;
 
   // Set element without bound and element type checks, to be used in limited
-  // circumstances, such as during boot image writing
-  void SetWithoutChecks(int32_t i, T* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  void SetWithoutChecksAndWriteBarrier(int32_t i, T* object)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  // circumstances, such as during boot image writing.
+  // TODO fix thread safety analysis broken by the use of template. This should be
+  // SHARED_LOCKS_REQUIRED(Locks::mutator_lock_).
+  template<bool kTransactionActive, bool kCheckTransaction = true>
+  void SetWithoutChecks(int32_t i, T* object) NO_THREAD_SAFETY_ANALYSIS;
+  // TODO fix thread safety analysis broken by the use of template. This should be
+  // SHARED_LOCKS_REQUIRED(Locks::mutator_lock_).
+  template<bool kTransactionActive, bool kCheckTransaction = true>
+  void SetWithoutChecksAndWriteBarrier(int32_t i, T* object) NO_THREAD_SAFETY_ANALYSIS;
 
   T* GetWithoutChecks(int32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index db9723b..40c3748 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -116,10 +116,10 @@
   EXPECT_EQ(2, oa->GetLength());
   EXPECT_TRUE(oa->Get(0) == NULL);
   EXPECT_TRUE(oa->Get(1) == NULL);
-  oa->Set(0, oa.get());
+  oa->Set<false>(0, oa.get());
   EXPECT_TRUE(oa->Get(0) == oa.get());
   EXPECT_TRUE(oa->Get(1) == NULL);
-  oa->Set(1, oa.get());
+  oa->Set<false>(1, oa.get());
   EXPECT_TRUE(oa->Get(0) == oa.get());
   EXPECT_TRUE(oa->Get(1) == oa.get());
 
@@ -235,12 +235,12 @@
 
   SirtRef<Class> c(soa.Self(), class_linker_->FindSystemClass("I"));
   SirtRef<IntArray> dims(soa.Self(), IntArray::Alloc(soa.Self(), 1));
-  dims->Set(0, 1);
+  dims->Set<false>(0, 1);
   Array* multi = Array::CreateMultiArray(soa.Self(), c, dims);
   EXPECT_TRUE(multi->GetClass() == class_linker_->FindSystemClass("[I"));
   EXPECT_EQ(1, multi->GetLength());
 
-  dims->Set(0, -1);
+  dims->Set<false>(0, -1);
   multi = Array::CreateMultiArray(soa.Self(), c, dims);
   EXPECT_TRUE(soa.Self()->IsExceptionPending());
   EXPECT_EQ(PrettyDescriptor(soa.Self()->GetException(NULL)->GetClass()),
@@ -250,8 +250,8 @@
   dims.reset(IntArray::Alloc(soa.Self(), 2));
   for (int i = 1; i < 20; ++i) {
     for (int j = 0; j < 20; ++j) {
-      dims->Set(0, i);
-      dims->Set(1, j);
+      dims->Set<false>(0, i);
+      dims->Set<false>(1, j);
       multi = Array::CreateMultiArray(soa.Self(), c, dims);
       EXPECT_TRUE(multi->GetClass() == class_linker_->FindSystemClass("[[I"));
       EXPECT_EQ(i, multi->GetLength());
@@ -301,10 +301,10 @@
   EXPECT_TRUE(s0 != NULL);
 
   SirtRef<CharArray> char_array(soa.Self(), CharArray::Alloc(soa.Self(), 0));
-  field->SetObj(field->GetDeclaringClass(), char_array.get());
+  field->SetObj<false>(field->GetDeclaringClass(), char_array.get());
   EXPECT_EQ(char_array.get(), field->GetObj(klass));
 
-  field->SetObj(field->GetDeclaringClass(), NULL);
+  field->SetObj<false>(field->GetDeclaringClass(), NULL);
   EXPECT_EQ(NULL, field->GetObj(klass));
 
   // TODO: more exhaustive tests of all 6 cases of ArtField::*FromCode
@@ -387,8 +387,8 @@
   EXPECT_EQ(string->GetLength(), 7);
   EXPECT_EQ(string->GetUtfLength(), 7);
 
-  string->SetOffset(2);
-  string->SetCount(5);
+  string->SetOffset<false>(2);
+  string->SetCount<false>(5);
   EXPECT_TRUE(string->Equals("droid"));
   EXPECT_EQ(string->GetLength(), 5);
   EXPECT_EQ(string->GetUtfLength(), 5);
diff --git a/runtime/mirror/stack_trace_element.cc b/runtime/mirror/stack_trace_element.cc
index 2e33198..6bc695d 100644
--- a/runtime/mirror/stack_trace_element.cc
+++ b/runtime/mirror/stack_trace_element.cc
@@ -46,18 +46,28 @@
   StackTraceElement* trace =
       down_cast<StackTraceElement*>(GetStackTraceElement()->AllocObject(self));
   if (LIKELY(trace != NULL)) {
-    trace->SetFieldObject(OFFSET_OF_OBJECT_MEMBER(StackTraceElement, declaring_class_),
-                          declaring_class.get(), false);
-    trace->SetFieldObject(OFFSET_OF_OBJECT_MEMBER(StackTraceElement, method_name_),
-                          method_name.get(), false);
-    trace->SetFieldObject(OFFSET_OF_OBJECT_MEMBER(StackTraceElement, file_name_),
-                          file_name.get(), false);
-    trace->SetField32(OFFSET_OF_OBJECT_MEMBER(StackTraceElement, line_number_),
-                      line_number, false);
+    if (Runtime::Current()->IsActiveTransaction()) {
+      trace->Init<true>(declaring_class, method_name, file_name, line_number);
+    } else {
+      trace->Init<false>(declaring_class, method_name, file_name, line_number);
+    }
   }
   return trace;
 }
 
+template<bool kTransactionActive>
+void StackTraceElement::Init(SirtRef<String>& declaring_class, SirtRef<String>& method_name,
+                             SirtRef<String>& file_name, int32_t line_number) {
+  SetFieldObject<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(StackTraceElement, declaring_class_),
+                                     declaring_class.get(), false);
+  SetFieldObject<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(StackTraceElement, method_name_),
+                                     method_name.get(), false);
+  SetFieldObject<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(StackTraceElement, file_name_),
+                                     file_name.get(), false);
+  SetField32<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(StackTraceElement, line_number_),
+                                 line_number, false);
+}
+
 void StackTraceElement::VisitRoots(RootCallback* callback, void* arg) {
   if (java_lang_StackTraceElement_ != nullptr) {
     java_lang_StackTraceElement_ = down_cast<Class*>(
diff --git a/runtime/mirror/stack_trace_element.h b/runtime/mirror/stack_trace_element.h
index 51817f6..779ec4b 100644
--- a/runtime/mirror/stack_trace_element.h
+++ b/runtime/mirror/stack_trace_element.h
@@ -67,6 +67,11 @@
   HeapReference<String> method_name_;
   int32_t line_number_;
 
+  template<bool kTransactionActive>
+  void Init(SirtRef<String>& declaring_class, SirtRef<String>& method_name,
+            SirtRef<String>& file_name, int32_t line_number)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   static Class* GetStackTraceElement() {
     DCHECK(java_lang_StackTraceElement_ != NULL);
     return java_lang_StackTraceElement_;
diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc
index 6f4ead9..cd63c39 100644
--- a/runtime/mirror/string.cc
+++ b/runtime/mirror/string.cc
@@ -33,7 +33,7 @@
   return GetFieldObject<CharArray>(ValueOffset(), false);
 }
 
-void String::ComputeHashCode() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+void String::ComputeHashCode() {
   SetHashCode(ComputeUtf16Hash(GetCharArray(), GetOffset(), GetLength()));
 }
 
@@ -59,9 +59,10 @@
   return -1;
 }
 
-void String::SetArray(CharArray* new_array) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+template<bool kTransactionActive>
+void String::SetArray(CharArray* new_array) {
   DCHECK(new_array != NULL);
-  SetFieldObject(OFFSET_OF_OBJECT_MEMBER(String, array_), new_array, false);
+  SetFieldObject<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(String, array_), new_array, false);
 }
 
 // TODO: get global references for these
@@ -169,8 +170,13 @@
   // Hold reference in case AllocObject causes GC.
   String* string = down_cast<String*>(GetJavaLangString()->AllocObject(self));
   if (LIKELY(string != nullptr)) {
-    string->SetArray(array.get());
-    string->SetCount(array->GetLength());
+    if (Runtime::Current()->IsActiveTransaction()) {
+      string->SetArray<true>(array.get());
+      string->SetCount<true>(array->GetLength());
+    } else {
+      string->SetArray<false>(array.get());
+      string->SetCount<false>(array->GetLength());
+    }
   }
   return string;
 }
diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h
index 57ec314..1340e7d 100644
--- a/runtime/mirror/string.h
+++ b/runtime/mirror/string.h
@@ -112,19 +112,23 @@
 
  private:
   void SetHashCode(int32_t new_hash_code) {
+    // Hash code is invariant so use non-transactional mode. Also disable check as we may run inside
+    // a transaction.
     DCHECK_EQ(0, GetField32(OFFSET_OF_OBJECT_MEMBER(String, hash_code_), false));
-    SetField32(OFFSET_OF_OBJECT_MEMBER(String, hash_code_), new_hash_code, false);
+    SetField32<false, false>(OFFSET_OF_OBJECT_MEMBER(String, hash_code_), new_hash_code, false);
   }
 
+  template<bool kTransactionActive>
   void SetCount(int32_t new_count) {
     DCHECK_LE(0, new_count);
-    SetField32(OFFSET_OF_OBJECT_MEMBER(String, count_), new_count, false);
+    SetField32<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(String, count_), new_count, false);
   }
 
+  template<bool kTransactionActive>
   void SetOffset(int32_t new_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     DCHECK_LE(0, new_offset);
     DCHECK_GE(GetLength(), new_offset);
-    SetField32(OFFSET_OF_OBJECT_MEMBER(String, offset_), new_offset, false);
+    SetField32<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(String, offset_), new_offset, false);
   }
 
   static String* Alloc(Thread* self, int32_t utf16_length)
@@ -133,6 +137,7 @@
   static String* Alloc(Thread* self, const SirtRef<CharArray>& array)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  template<bool kTransactionActive>
   void SetArray(CharArray* new_array) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
diff --git a/runtime/mirror/throwable.cc b/runtime/mirror/throwable.cc
index a57bd43..fef7d36 100644
--- a/runtime/mirror/throwable.cc
+++ b/runtime/mirror/throwable.cc
@@ -38,7 +38,11 @@
   Throwable* current_cause = GetFieldObject<Throwable>(OFFSET_OF_OBJECT_MEMBER(Throwable, cause_),
                                                        false);
   CHECK(current_cause == NULL || current_cause == this);
-  SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Throwable, cause_), cause, false);
+  if (Runtime::Current()->IsActiveTransaction()) {
+    SetFieldObject<true>(OFFSET_OF_OBJECT_MEMBER(Throwable, cause_), cause, false);
+  } else {
+    SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Throwable, cause_), cause, false);
+  }
 }
 
 bool Throwable::IsCheckedException() {
diff --git a/runtime/mirror/throwable.h b/runtime/mirror/throwable.h
index de71957..c1438d7 100644
--- a/runtime/mirror/throwable.h
+++ b/runtime/mirror/throwable.h
@@ -31,7 +31,13 @@
 class MANAGED Throwable : public Object {
  public:
   void SetDetailMessage(String* new_detail_message) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Throwable, detail_message_), new_detail_message, false);
+    if (Runtime::Current()->IsActiveTransaction()) {
+      SetFieldObject<true>(OFFSET_OF_OBJECT_MEMBER(Throwable, detail_message_), new_detail_message,
+                           false);
+    } else {
+      SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Throwable, detail_message_), new_detail_message,
+                            false);
+    }
   }
   String* GetDetailMessage() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return GetFieldObject<String>(OFFSET_OF_OBJECT_MEMBER(Throwable, detail_message_), false);
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index 269a4a3..40aebfa 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -183,32 +183,32 @@
   o = sirt_obj.get();
   switch (FieldHelper(f).GetTypeAsPrimitiveType()) {
   case Primitive::kPrimBoolean:
-    f->SetBoolean(o, new_value.GetZ());
+    f->SetBoolean<false>(o, new_value.GetZ());
     break;
   case Primitive::kPrimByte:
-    f->SetByte(o, new_value.GetB());
+    f->SetByte<false>(o, new_value.GetB());
     break;
   case Primitive::kPrimChar:
-    f->SetChar(o, new_value.GetC());
+    f->SetChar<false>(o, new_value.GetC());
     break;
   case Primitive::kPrimDouble:
-    f->SetDouble(o, new_value.GetD());
+    f->SetDouble<false>(o, new_value.GetD());
     break;
   case Primitive::kPrimFloat:
-    f->SetFloat(o, new_value.GetF());
+    f->SetFloat<false>(o, new_value.GetF());
     break;
   case Primitive::kPrimInt:
-    f->SetInt(o, new_value.GetI());
+    f->SetInt<false>(o, new_value.GetI());
     break;
   case Primitive::kPrimLong:
-    f->SetLong(o, new_value.GetJ());
+    f->SetLong<false>(o, new_value.GetJ());
     break;
   case Primitive::kPrimShort:
-    f->SetShort(o, new_value.GetS());
+    f->SetShort<false>(o, new_value.GetS());
     break;
   case Primitive::kPrimNot:
     if (allow_references) {
-      f->SetObject(o, new_value.GetL());
+      f->SetObject<false>(o, new_value.GetL());
       break;
     }
     // Else fall through to report an error.
diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc
index 6727862..ad0f317 100644
--- a/runtime/native/sun_misc_Unsafe.cc
+++ b/runtime/native/sun_misc_Unsafe.cc
@@ -27,7 +27,8 @@
                                          jint expectedValue, jint newValue) {
   ScopedFastNativeObjectAccess soa(env);
   mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
-  bool success = obj->CasField32(MemberOffset(offset), expectedValue, newValue);
+  // JNI must use non transactional mode.
+  bool success = obj->CasField32<false>(MemberOffset(offset), expectedValue, newValue);
   return success ? JNI_TRUE : JNI_FALSE;
 }
 
@@ -35,7 +36,8 @@
                                           jlong expectedValue, jlong newValue) {
   ScopedFastNativeObjectAccess soa(env);
   mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
-  bool success = obj->CasField64(MemberOffset(offset), expectedValue, newValue);
+  // JNI must use non transactional mode.
+  bool success = obj->CasField64<false>(MemberOffset(offset), expectedValue, newValue);
   return success ? JNI_TRUE : JNI_FALSE;
 }
 
@@ -45,7 +47,8 @@
   mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
   mirror::Object* expectedValue = soa.Decode<mirror::Object*>(javaExpectedValue);
   mirror::Object* newValue = soa.Decode<mirror::Object*>(javaNewValue);
-  bool success = obj->CasFieldObject(MemberOffset(offset), expectedValue, newValue);
+  // JNI must use non transactional mode.
+  bool success = obj->CasFieldObject<false>(MemberOffset(offset), expectedValue, newValue);
   return success ? JNI_TRUE : JNI_FALSE;
 }
 
@@ -64,14 +67,16 @@
 static void Unsafe_putInt(JNIEnv* env, jobject, jobject javaObj, jlong offset, jint newValue) {
   ScopedFastNativeObjectAccess soa(env);
   mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
-  obj->SetField32(MemberOffset(offset), newValue, false);
+  // JNI must use non transactional mode.
+  obj->SetField32<false>(MemberOffset(offset), newValue, false);
 }
 
 static void Unsafe_putIntVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset,
                                   jint newValue) {
   ScopedFastNativeObjectAccess soa(env);
   mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
-  obj->SetField32(MemberOffset(offset), newValue, true);
+  // JNI must use non transactional mode.
+  obj->SetField32<false>(MemberOffset(offset), newValue, true);
 }
 
 static void Unsafe_putOrderedInt(JNIEnv* env, jobject, jobject javaObj, jlong offset,
@@ -79,7 +84,8 @@
   ScopedFastNativeObjectAccess soa(env);
   mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
   QuasiAtomic::MembarStoreStore();
-  obj->SetField32(MemberOffset(offset), newValue, false);
+  // JNI must use non transactional mode.
+  obj->SetField32<false>(MemberOffset(offset), newValue, false);
 }
 
 static jlong Unsafe_getLong(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
@@ -97,14 +103,16 @@
 static void Unsafe_putLong(JNIEnv* env, jobject, jobject javaObj, jlong offset, jlong newValue) {
   ScopedFastNativeObjectAccess soa(env);
   mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
-  obj->SetField64(MemberOffset(offset), newValue, false);
+  // JNI must use non transactional mode.
+  obj->SetField64<false>(MemberOffset(offset), newValue, false);
 }
 
 static void Unsafe_putLongVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset,
                                    jlong newValue) {
   ScopedFastNativeObjectAccess soa(env);
   mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
-  obj->SetField64(MemberOffset(offset), newValue, true);
+  // JNI must use non transactional mode.
+  obj->SetField64<false>(MemberOffset(offset), newValue, true);
 }
 
 static void Unsafe_putOrderedLong(JNIEnv* env, jobject, jobject javaObj, jlong offset,
@@ -112,7 +120,8 @@
   ScopedFastNativeObjectAccess soa(env);
   mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
   QuasiAtomic::MembarStoreStore();
-  obj->SetField64(MemberOffset(offset), newValue, false);
+  // JNI must use non transactional mode.
+  obj->SetField64<false>(MemberOffset(offset), newValue, false);
 }
 
 static jobject Unsafe_getObjectVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
@@ -134,7 +143,8 @@
   ScopedFastNativeObjectAccess soa(env);
   mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
   mirror::Object* newValue = soa.Decode<mirror::Object*>(javaNewValue);
-  obj->SetFieldObject(MemberOffset(offset), newValue, false);
+  // JNI must use non transactional mode.
+  obj->SetFieldObject<false>(MemberOffset(offset), newValue, false);
 }
 
 static void Unsafe_putObjectVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset,
@@ -142,7 +152,8 @@
   ScopedFastNativeObjectAccess soa(env);
   mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
   mirror::Object* newValue = soa.Decode<mirror::Object*>(javaNewValue);
-  obj->SetFieldObject(MemberOffset(offset), newValue, true);
+  // JNI must use non transactional mode.
+  obj->SetFieldObject<false>(MemberOffset(offset), newValue, true);
 }
 
 static void Unsafe_putOrderedObject(JNIEnv* env, jobject, jobject javaObj, jlong offset,
@@ -151,7 +162,8 @@
   mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
   mirror::Object* newValue = soa.Decode<mirror::Object*>(javaNewValue);
   QuasiAtomic::MembarStoreStore();
-  obj->SetFieldObject(MemberOffset(offset), newValue, false);
+  // JNI must use non transactional mode.
+  obj->SetFieldObject<false>(MemberOffset(offset), newValue, false);
 }
 
 static jint Unsafe_getArrayBaseOffsetForComponentType(JNIEnv* env, jclass, jobject component_class) {
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 6ca45e8..e1b0ed4 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -60,6 +60,7 @@
 #include "thread.h"
 #include "thread_list.h"
 #include "trace.h"
+#include "transaction.h"
 #include "profiler.h"
 #include "UniquePtr.h"
 #include "verifier/method_verifier.h"
@@ -120,7 +121,8 @@
       main_thread_group_(nullptr),
       system_thread_group_(nullptr),
       system_class_loader_(nullptr),
-      dump_gc_performance_on_shutdown_(false) {
+      dump_gc_performance_on_shutdown_(false),
+      preinitialization_transaction(nullptr) {
   for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
     callee_save_methods_[i] = nullptr;
   }
@@ -826,7 +828,8 @@
       thread_class->FindDeclaredInstanceField("contextClassLoader", "Ljava/lang/ClassLoader;");
   CHECK(contextClassLoader != NULL);
 
-  contextClassLoader->SetObject(soa.Self()->GetPeer(), class_loader.get());
+  // We can't run in a transaction yet.
+  contextClassLoader->SetObject<false>(soa.Self()->GetPeer(), class_loader.get());
 
   return env->NewGlobalRef(system_class_loader.get());
 }
@@ -1305,6 +1308,10 @@
                                    bool clean_dirty) {
   intern_table_->VisitRoots(callback, arg, only_dirty, clean_dirty);
   class_linker_->VisitRoots(callback, arg, only_dirty, clean_dirty);
+  // TODO: is it the right place ?
+  if (preinitialization_transaction != nullptr) {
+    preinitialization_transaction->VisitRoots(callback, arg);
+  }
 }
 
 void Runtime::VisitNonThreadRoots(RootCallback* callback, void* arg) {
@@ -1371,7 +1378,7 @@
   SirtRef<mirror::ObjectArray<mirror::ArtMethod> > imtable(self, cl->AllocArtMethodArray(self, 64));
   mirror::ArtMethod* imt_conflict_method = Runtime::Current()->GetImtConflictMethod();
   for (size_t i = 0; i < static_cast<size_t>(imtable->GetLength()); i++) {
-    imtable->Set(i, imt_conflict_method);
+    imtable->Set<false>(i, imt_conflict_method);
   }
   return imtable.get();
 }
@@ -1547,4 +1554,74 @@
   BackgroundMethodSamplingProfiler::Start(profile_period_s_, profile_duration_s_, appDir, profile_interval_us_,
       profile_backoff_coefficient_, startImmediately);
 }
+
+// Transaction support.
+// TODO move them to header file for inlining.
+bool Runtime::IsActiveTransaction() const {
+  return preinitialization_transaction != nullptr;
+}
+
+void Runtime::EnterTransactionMode(Transaction* transaction) {
+  DCHECK(IsCompiler());
+  DCHECK(transaction != nullptr);
+  DCHECK(!IsActiveTransaction());
+  preinitialization_transaction = transaction;
+}
+
+void Runtime::ExitTransactionMode() {
+  DCHECK(IsCompiler());
+  DCHECK(IsActiveTransaction());
+  preinitialization_transaction = nullptr;
+}
+
+void Runtime::RecordWriteField32(mirror::Object* obj, MemberOffset field_offset,
+                                 uint32_t value, bool is_volatile) const {
+  DCHECK(IsCompiler());
+  DCHECK(IsActiveTransaction());
+  preinitialization_transaction->RecordWriteField32(obj, field_offset, value, is_volatile);
+}
+
+void Runtime::RecordWriteField64(mirror::Object* obj, MemberOffset field_offset,
+                                 uint64_t value, bool is_volatile) const {
+  DCHECK(IsCompiler());
+  DCHECK(IsActiveTransaction());
+  preinitialization_transaction->RecordWriteField64(obj, field_offset, value, is_volatile);
+}
+
+void Runtime::RecordWriteFieldReference(mirror::Object* obj, MemberOffset field_offset,
+                                        mirror::Object* value, bool is_volatile) const {
+  DCHECK(IsCompiler());
+  DCHECK(IsActiveTransaction());
+  preinitialization_transaction->RecordWriteFieldReference(obj, field_offset, value, is_volatile);
+}
+
+void Runtime::RecordWriteArray(mirror::Array* array, size_t index, uint64_t value) const {
+  DCHECK(IsCompiler());
+  DCHECK(IsActiveTransaction());
+  preinitialization_transaction->RecordWriteArray(array, index, value);
+}
+
+void Runtime::RecordStrongStringInsertion(mirror::String* s, uint32_t hash_code) const {
+  DCHECK(IsCompiler());
+  DCHECK(IsActiveTransaction());
+  preinitialization_transaction->RecordStrongStringInsertion(s, hash_code);
+}
+
+void Runtime::RecordWeakStringInsertion(mirror::String* s, uint32_t hash_code) const {
+  DCHECK(IsCompiler());
+  DCHECK(IsActiveTransaction());
+  preinitialization_transaction->RecordWeakStringInsertion(s, hash_code);
+}
+
+void Runtime::RecordStrongStringRemoval(mirror::String* s, uint32_t hash_code) const {
+  DCHECK(IsCompiler());
+  DCHECK(IsActiveTransaction());
+  preinitialization_transaction->RecordStrongStringRemoval(s, hash_code);
+}
+
+void Runtime::RecordWeakStringRemoval(mirror::String* s, uint32_t hash_code) const {
+  DCHECK(IsCompiler());
+  DCHECK(IsActiveTransaction());
+  preinitialization_transaction->RecordWeakStringRemoval(s, hash_code);
+}
 }  // namespace art
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 07f3d7d..159de2e 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -46,6 +46,7 @@
 namespace mirror {
   class ArtMethod;
   class ClassLoader;
+  class Array;
   template<class T> class ObjectArray;
   template<class T> class PrimitiveArray;
   typedef PrimitiveArray<int8_t> ByteArray;
@@ -65,6 +66,7 @@
 class SignalCatcher;
 class ThreadList;
 class Trace;
+class Transaction;
 
 class Runtime {
  public:
@@ -474,6 +476,27 @@
 
   void StartProfiler(const char *appDir, bool startImmediately = false);
 
+  // Transaction support.
+  bool IsActiveTransaction() const;
+  void EnterTransactionMode(Transaction* transaction);
+  void ExitTransactionMode();
+  void RecordWriteField32(mirror::Object* obj, MemberOffset field_offset, uint32_t value,
+                          bool is_volatile) const;
+  void RecordWriteField64(mirror::Object* obj, MemberOffset field_offset, uint64_t value,
+                          bool is_volatile) const;
+  void RecordWriteFieldReference(mirror::Object* obj, MemberOffset field_offset,
+                                 mirror::Object* value, bool is_volatile) const;
+  void RecordWriteArray(mirror::Array* array, size_t index, uint64_t value) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void RecordStrongStringInsertion(mirror::String* s, uint32_t hash_code) const
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+  void RecordWeakStringInsertion(mirror::String* s, uint32_t hash_code) const
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+  void RecordStrongStringRemoval(mirror::String* s, uint32_t hash_code) const
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+  void RecordWeakStringRemoval(mirror::String* s, uint32_t hash_code) const
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+
  private:
   static void InitPlatformSignalHandlers();
 
@@ -612,6 +635,9 @@
   // If true, then we dump the GC cumulative timings on shutdown.
   bool dump_gc_performance_on_shutdown_;
 
+  // Transaction used for pre-initializing classes at compilation time.
+  Transaction* preinitialization_transaction;
+
   DISALLOW_COPY_AND_ASSIGN(Runtime);
 };
 
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 3382811..5728391 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -398,14 +398,11 @@
     // non-null value. However, because we can run without code
     // available (in the compiler, in tests), we manually assign the
     // fields the constructor should have set.
-    soa.DecodeField(WellKnownClasses::java_lang_Thread_daemon)->
-        SetBoolean(opeer_, thread_is_daemon);
-    soa.DecodeField(WellKnownClasses::java_lang_Thread_group)->
-        SetObject(opeer_, soa.Decode<mirror::Object*>(thread_group));
-    soa.DecodeField(WellKnownClasses::java_lang_Thread_name)->
-        SetObject(opeer_, soa.Decode<mirror::Object*>(thread_name.get()));
-    soa.DecodeField(WellKnownClasses::java_lang_Thread_priority)->
-        SetInt(opeer_, thread_priority);
+    if (runtime->IsActiveTransaction()) {
+      InitPeer<true>(soa, thread_is_daemon, thread_group, thread_name.get(), thread_priority);
+    } else {
+      InitPeer<false>(soa, thread_is_daemon, thread_group, thread_name.get(), thread_priority);
+    }
     peer_thread_name.reset(GetThreadName(soa));
   }
   // 'thread_name' may have been null, so don't trust 'peer_thread_name' to be non-null.
@@ -414,6 +411,19 @@
   }
 }
 
+template<bool kTransactionActive>
+void Thread::InitPeer(ScopedObjectAccess& soa, jboolean thread_is_daemon, jobject thread_group,
+                      jobject thread_name, jint thread_priority) {
+  soa.DecodeField(WellKnownClasses::java_lang_Thread_daemon)->
+      SetBoolean<kTransactionActive>(opeer_, thread_is_daemon);
+  soa.DecodeField(WellKnownClasses::java_lang_Thread_group)->
+      SetObject<kTransactionActive>(opeer_, soa.Decode<mirror::Object*>(thread_group));
+  soa.DecodeField(WellKnownClasses::java_lang_Thread_name)->
+      SetObject<kTransactionActive>(opeer_, soa.Decode<mirror::Object*>(thread_name));
+  soa.DecodeField(WellKnownClasses::java_lang_Thread_priority)->
+      SetInt<kTransactionActive>(opeer_, thread_priority);
+}
+
 void Thread::SetThreadName(const char* name) {
   name_->assign(name);
   ::art::SetThreadName(name);
@@ -1020,7 +1030,11 @@
     RemoveFromThreadGroup(soa);
 
     // this.nativePeer = 0;
-    soa.DecodeField(WellKnownClasses::java_lang_Thread_nativePeer)->SetLong(opeer_, 0);
+    if (Runtime::Current()->IsActiveTransaction()) {
+      soa.DecodeField(WellKnownClasses::java_lang_Thread_nativePeer)->SetLong<true>(opeer_, 0);
+    } else {
+      soa.DecodeField(WellKnownClasses::java_lang_Thread_nativePeer)->SetLong<false>(opeer_, 0);
+    }
     Dbg::PostThreadDeath(self);
 
     // Thread.join() is implemented as an Object.wait() on the Thread.lock object. Signal anyone
@@ -1309,7 +1323,8 @@
     }
     // Save PC trace in last element of method trace, also places it into the
     // object graph.
-    method_trace->Set(depth, dex_pc_trace);
+    // We are called from native: use non-transactional mode.
+    method_trace->Set<false>(depth, dex_pc_trace);
     // Set the Object*s and assert that no thread suspension is now possible.
     const char* last_no_suspend_cause =
         self_->StartAssertNoThreadSuspension("Building internal stack trace");
@@ -1337,8 +1352,14 @@
     if (m->IsRuntimeMethod()) {
       return true;  // Ignore runtime frames (in particular callee save).
     }
-    method_trace_->Set(count_, m);
-    dex_pc_trace_->Set(count_, m->IsProxyMethod() ? DexFile::kDexNoIndex : GetDexPc());
+    // TODO dedup this code.
+    if (Runtime::Current()->IsActiveTransaction()) {
+      method_trace_->Set<true>(count_, m);
+      dex_pc_trace_->Set<true>(count_, m->IsProxyMethod() ? DexFile::kDexNoIndex : GetDexPc());
+    } else {
+      method_trace_->Set<false>(count_, m);
+      dex_pc_trace_->Set<false>(count_, m->IsProxyMethod() ? DexFile::kDexNoIndex : GetDexPc());
+    }
     ++count_;
     return true;
   }
@@ -1461,7 +1482,8 @@
     if (obj == nullptr) {
       return nullptr;
     }
-    soa.Decode<mirror::ObjectArray<mirror::StackTraceElement>*>(result)->Set(i, obj);
+    // We are called from native: use non-transactional mode.
+    soa.Decode<mirror::ObjectArray<mirror::StackTraceElement>*>(result)->Set<false>(i, obj);
   }
   return result;
 }
diff --git a/runtime/thread.h b/runtime/thread.h
index 6c072ba..48912d1 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -591,6 +591,11 @@
 
   void CreatePeer(const char* name, bool as_daemon, jobject thread_group);
 
+  template<bool kTransactionActive>
+  void InitPeer(ScopedObjectAccess& soa, jboolean thread_is_daemon, jobject thread_group,
+                jobject thread_name, jint thread_priority)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   // Avoid use, callers should use SetState. Used only by SignalCatcher::HandleSigQuit, ~Thread and
   // Dbg::Disconnected.
   ThreadState SetStateUnsafe(ThreadState new_state) {
diff --git a/runtime/transaction.cc b/runtime/transaction.cc
new file mode 100644
index 0000000..6adcfec
--- /dev/null
+++ b/runtime/transaction.cc
@@ -0,0 +1,419 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "transaction.h"
+
+#include "base/stl_util.h"
+#include "base/logging.h"
+#include "gc/accounting/card_table-inl.h"
+#include "intern_table.h"
+#include "mirror/object-inl.h"
+#include "mirror/object_array-inl.h"
+
+#include <list>
+
+namespace art {
+
+// TODO: remove (only used for debugging purpose).
+static constexpr bool kEnableTransactionStats = false;
+
+Transaction::Transaction() : log_lock_("transaction log lock", kTransactionLogLock) {
+  CHECK(Runtime::Current()->IsCompiler());
+}
+
+Transaction::~Transaction() {
+  if (kEnableTransactionStats) {
+    MutexLock mu(Thread::Current(), log_lock_);
+    size_t objects_count = object_logs_.size();
+    size_t field_values_count = 0;
+    for (auto it : object_logs_) {
+      field_values_count += it.second.Size();
+    }
+    size_t array_count = array_logs_.size();
+    size_t array_values_count = 0;
+    for (auto it : array_logs_) {
+      array_values_count += it.second.Size();
+    }
+    size_t string_count = intern_string_logs_.size();
+    LOG(INFO) << "Transaction::~Transaction"
+              << ": objects_count=" << objects_count
+              << ", field_values_count=" << field_values_count
+              << ", array_count=" << array_count
+              << ", array_values_count=" << array_values_count
+              << ", string_count=" << string_count;
+  }
+}
+
+void Transaction::RecordWriteField32(mirror::Object* obj, MemberOffset field_offset, uint32_t value,
+                                     bool is_volatile) {
+  DCHECK(obj != nullptr);
+  MutexLock mu(Thread::Current(), log_lock_);
+  ObjectLog& object_log = object_logs_[obj];
+  object_log.Log32BitsValue(field_offset, value, is_volatile);
+}
+
+void Transaction::RecordWriteField64(mirror::Object* obj, MemberOffset field_offset, uint64_t value,
+                                     bool is_volatile) {
+  DCHECK(obj != nullptr);
+  MutexLock mu(Thread::Current(), log_lock_);
+  ObjectLog& object_log = object_logs_[obj];
+  object_log.Log64BitsValue(field_offset, value, is_volatile);
+}
+
+void Transaction::RecordWriteFieldReference(mirror::Object* obj, MemberOffset field_offset,
+                                            mirror::Object* value, bool is_volatile) {
+  DCHECK(obj != nullptr);
+  MutexLock mu(Thread::Current(), log_lock_);
+  ObjectLog& object_log = object_logs_[obj];
+  object_log.LogReferenceValue(field_offset, value, is_volatile);
+}
+
+void Transaction::RecordWriteArray(mirror::Array* array, size_t index, uint64_t value) {
+  DCHECK(array != nullptr);
+  DCHECK(array->IsArrayInstance());
+  MutexLock mu(Thread::Current(), log_lock_);
+  ArrayLog& array_log = array_logs_[array];
+  array_log.LogValue(index, value);
+}
+
+void Transaction::RecordStrongStringInsertion(mirror::String* s, uint32_t hash_code) {
+  DCHECK(s != nullptr);
+  InternStringLog log(s, hash_code, InternStringLog::kStrongString, InternStringLog::kInsert);
+  LogInternedString(log);
+}
+
+void Transaction::RecordWeakStringInsertion(mirror::String* s, uint32_t hash_code) {
+  DCHECK(s != nullptr);
+  InternStringLog log(s, hash_code, InternStringLog::kWeakString, InternStringLog::kInsert);
+  LogInternedString(log);
+}
+
+void Transaction::RecordStrongStringRemoval(mirror::String* s, uint32_t hash_code) {
+  InternStringLog log(s, hash_code, InternStringLog::kStrongString, InternStringLog::kRemove);
+  LogInternedString(log);
+}
+
+void Transaction::RecordWeakStringRemoval(mirror::String* s, uint32_t hash_code) {
+  InternStringLog log(s, hash_code, InternStringLog::kWeakString, InternStringLog::kRemove);
+  LogInternedString(log);
+}
+
+void Transaction::LogInternedString(InternStringLog& log) {
+  Locks::intern_table_lock_->AssertExclusiveHeld(Thread::Current());
+  MutexLock mu(Thread::Current(), log_lock_);
+  intern_string_logs_.push_front(log);
+}
+
+void Transaction::Abort() {
+  CHECK(!Runtime::Current()->IsActiveTransaction());
+  Thread* self = Thread::Current();
+  self->AssertNoPendingException();
+  MutexLock mu1(self, *Locks::intern_table_lock_);
+  MutexLock mu2(self, log_lock_);
+  UndoObjectModifications();
+  UndoArrayModifications();
+  UndoInternStringTableModifications();
+}
+
+void Transaction::UndoObjectModifications() {
+  // TODO we may not need to restore objects allocated during this transaction. Or we could directly
+  // remove them from the heap.
+  for (auto it : object_logs_) {
+    it.second.Undo(it.first);
+  }
+  object_logs_.clear();
+}
+
+void Transaction::UndoArrayModifications() {
+  // TODO we may not need to restore array allocated during this transaction. Or we could directly
+  // remove them from the heap.
+  for (auto it : array_logs_) {
+    it.second.Undo(it.first);
+  }
+  array_logs_.clear();
+}
+
+void Transaction::UndoInternStringTableModifications() {
+  InternTable* const intern_table = Runtime::Current()->GetInternTable();
+  // We want to undo each operation from the most recent to the oldest. List has been filled so the
+  // most recent operation is at list begin so just have to iterate over it.
+  for (InternStringLog& string_log : intern_string_logs_) {
+    string_log.Undo(intern_table);
+  }
+  intern_string_logs_.clear();
+}
+
+void Transaction::VisitRoots(RootCallback* callback, void* arg) {
+  LOG(INFO) << "Transaction::VisitRoots";
+  MutexLock mu(Thread::Current(), log_lock_);
+  VisitObjectLogs(callback, arg);
+  VisitArrayLogs(callback, arg);
+  VisitStringLogs(callback, arg);
+}
+
+void Transaction::VisitObjectLogs(RootCallback* callback, void* arg) {
+  // List of moving roots.
+  typedef std::pair<mirror::Object*, mirror::Object*> ObjectPair;
+  std::list<ObjectPair> moving_roots;
+
+  // Visit roots.
+  for (auto it : object_logs_) {
+    it.second.VisitRoots(callback, arg);
+    mirror::Object* old_root = it.first;
+    mirror::Object* new_root = callback(old_root, arg, 0, kRootUnknown);
+    if (new_root != old_root) {
+      moving_roots.push_back(std::make_pair(old_root, new_root));
+    }
+  }
+
+  // Update object logs with moving roots.
+  for (const ObjectPair& pair : moving_roots) {
+    mirror::Object* old_root = pair.first;
+    mirror::Object* new_root = pair.second;
+    auto old_root_it = object_logs_.find(old_root);
+    CHECK(old_root_it != object_logs_.end());
+    CHECK(object_logs_.find(new_root) == object_logs_.end());
+    object_logs_.insert(std::make_pair(new_root, old_root_it->second));
+    object_logs_.erase(old_root_it);
+  }
+}
+
+void Transaction::VisitArrayLogs(RootCallback* callback, void* arg) {
+  // List of moving roots.
+  typedef std::pair<mirror::Array*, mirror::Array*> ArrayPair;
+  std::list<ArrayPair> moving_roots;
+
+  for (auto it : array_logs_) {
+    mirror::Array* old_root = it.first;
+    if (old_root->IsObjectArray()) {
+      it.second.VisitRoots(callback, arg);
+    }
+    mirror::Array* new_root = down_cast<mirror::Array*>(callback(old_root, arg, 0, kRootUnknown));
+    if (new_root != old_root) {
+      moving_roots.push_back(std::make_pair(old_root, new_root));
+    }
+  }
+
+  // Update array logs with moving roots.
+  for (const ArrayPair& pair : moving_roots) {
+    mirror::Array* old_root = pair.first;
+    mirror::Array* new_root = pair.second;
+    auto old_root_it = array_logs_.find(old_root);
+    CHECK(old_root_it != array_logs_.end());
+    CHECK(array_logs_.find(new_root) == array_logs_.end());
+    array_logs_.insert(std::make_pair(new_root, old_root_it->second));
+    array_logs_.erase(old_root_it);
+  }
+}
+
+void Transaction::VisitStringLogs(RootCallback* callback, void* arg) {
+  for (InternStringLog& log : intern_string_logs_) {
+    log.VisitRoots(callback, arg);
+  }
+}
+
+void Transaction::ObjectLog::Log32BitsValue(MemberOffset offset, uint32_t value, bool is_volatile) {
+  auto it = field_values_.find(offset.Uint32Value());
+  if (it == field_values_.end()) {
+    ObjectLog::FieldValue field_value;
+    field_value.value = value;
+    field_value.is_volatile = is_volatile;
+    field_value.kind = ObjectLog::k32Bits;
+    field_values_.insert(std::make_pair(offset.Uint32Value(), field_value));
+  }
+}
+
+void Transaction::ObjectLog::Log64BitsValue(MemberOffset offset, uint64_t value, bool is_volatile) {
+  auto it = field_values_.find(offset.Uint32Value());
+  if (it == field_values_.end()) {
+    ObjectLog::FieldValue field_value;
+    field_value.value = value;
+    field_value.is_volatile = is_volatile;
+    field_value.kind = ObjectLog::k64Bits;
+    field_values_.insert(std::make_pair(offset.Uint32Value(), field_value));
+  }
+}
+
+void Transaction::ObjectLog::LogReferenceValue(MemberOffset offset, mirror::Object* obj, bool is_volatile) {
+  auto it = field_values_.find(offset.Uint32Value());
+  if (it == field_values_.end()) {
+    ObjectLog::FieldValue field_value;
+    field_value.value = reinterpret_cast<uintptr_t>(obj);
+    field_value.is_volatile = is_volatile;
+    field_value.kind = ObjectLog::kReference;
+    field_values_.insert(std::make_pair(offset.Uint32Value(), field_value));
+  }
+}
+
+void Transaction::ObjectLog::Undo(mirror::Object* obj) {
+  for (auto& it : field_values_) {
+    // Garbage collector needs to access object's class and array's length. So we don't rollback
+    // these values.
+    MemberOffset field_offset(it.first);
+    if (field_offset.Uint32Value() == mirror::Class::ClassOffset().Uint32Value()) {
+      // Skip Object::class field.
+      continue;
+    }
+    if (obj->IsArrayInstance() &&
+        field_offset.Uint32Value() == mirror::Array::LengthOffset().Uint32Value()) {
+      // Skip Array::length field.
+      continue;
+    }
+    FieldValue& field_value = it.second;
+    UndoFieldWrite(obj, field_offset, field_value);
+  }
+}
+
+void Transaction::ObjectLog::UndoFieldWrite(mirror::Object* obj, MemberOffset field_offset,
+                                            const FieldValue& field_value) {
+  // TODO We may want to abort a transaction while still being in transaction mode. In this case,
+  // we'd need to disable the check.
+  constexpr bool kCheckTransaction = true;
+  switch (field_value.kind) {
+    case k32Bits:
+      obj->SetField32<false, kCheckTransaction>(field_offset, static_cast<uint32_t>(field_value.value),
+                                                field_value.is_volatile);
+      break;
+    case k64Bits:
+      obj->SetField64<false, kCheckTransaction>(field_offset, field_value.value,
+                                                field_value.is_volatile);
+      break;
+    case kReference:
+      obj->SetFieldObject<false, kCheckTransaction>(field_offset,
+                                                    reinterpret_cast<mirror::Object*>(field_value.value),
+                                                    field_value.is_volatile);
+      break;
+    default:
+      LOG(FATAL) << "Unknown value kind " << field_value.kind;
+      break;
+  }
+}
+
+void Transaction::ObjectLog::VisitRoots(RootCallback* callback, void* arg) {
+  for (auto it : field_values_) {
+    FieldValue& field_value = it.second;
+    if (field_value.kind == ObjectLog::kReference) {
+      mirror::Object* obj = reinterpret_cast<mirror::Object*>(static_cast<uintptr_t>(field_value.value));
+      field_value.value = reinterpret_cast<uintptr_t>(callback(obj, arg, 0, kRootUnknown));
+    }
+  }
+}
+
+void Transaction::InternStringLog::Undo(InternTable* intern_table) {
+  DCHECK(intern_table != nullptr);
+  switch (string_op_) {
+      case InternStringLog::kInsert: {
+        switch (string_kind_) {
+          case InternStringLog::kStrongString:
+            intern_table->RemoveStrongFromTransaction(str_, hash_code_);
+            break;
+          case InternStringLog::kWeakString:
+            intern_table->RemoveWeakFromTransaction(str_, hash_code_);
+            break;
+          default:
+            LOG(FATAL) << "Unknown interned string kind";
+            break;
+        }
+        break;
+      }
+      case InternStringLog::kRemove: {
+        switch (string_kind_) {
+          case InternStringLog::kStrongString:
+            intern_table->InsertStrongFromTransaction(str_, hash_code_);
+            break;
+          case InternStringLog::kWeakString:
+            intern_table->InsertWeakFromTransaction(str_, hash_code_);
+            break;
+          default:
+            LOG(FATAL) << "Unknown interned string kind";
+            break;
+        }
+        break;
+      }
+      default:
+        LOG(FATAL) << "Unknown interned string op";
+        break;
+    }
+}
+
+void Transaction::InternStringLog::VisitRoots(RootCallback* callback, void* arg) {
+  str_ = down_cast<mirror::String*>(callback(str_, arg, 0, kRootInternedString));
+}
+
+void Transaction::ArrayLog::LogValue(size_t index, uint64_t value) {
+  auto it = array_values_.find(index);
+  if (it == array_values_.end()) {
+    array_values_.insert(std::make_pair(index, value));
+  }
+}
+
+void Transaction::ArrayLog::Undo(mirror::Array* array) {
+  DCHECK(array != nullptr);
+  DCHECK(array->IsArrayInstance());
+  Primitive::Type type = array->GetClass()->GetComponentType()->GetPrimitiveType();
+  for (auto it : array_values_) {
+    UndoArrayWrite(array, type, it.first, it.second);
+  }
+}
+
+void Transaction::ArrayLog::UndoArrayWrite(mirror::Array* array, Primitive::Type array_type,
+                                           size_t index, uint64_t value) {
+  // TODO We may want to abort a transaction while still being in transaction mode. In this case,
+  // we'd need to disable the check.
+  switch (array_type) {
+    case Primitive::kPrimBoolean:
+      array->AsBooleanArray()->SetWithoutChecks<false>(index, static_cast<uint8_t>(value));
+      break;
+    case Primitive::kPrimByte:
+      array->AsByteArray()->SetWithoutChecks<false>(index, static_cast<int8_t>(value));
+      break;
+    case Primitive::kPrimChar:
+      array->AsCharArray()->SetWithoutChecks<false>(index, static_cast<uint16_t>(value));
+      break;
+    case Primitive::kPrimShort:
+      array->AsShortArray()->SetWithoutChecks<false>(index, static_cast<int16_t>(value));
+      break;
+    case Primitive::kPrimInt:
+      array->AsIntArray()->SetWithoutChecks<false>(index, static_cast<int32_t>(value));
+      break;
+    case Primitive::kPrimFloat:
+      array->AsFloatArray()->SetWithoutChecks<false>(index, static_cast<float>(value));
+      break;
+    case Primitive::kPrimLong:
+      array->AsLongArray()->SetWithoutChecks<false>(index, static_cast<int64_t>(value));
+      break;
+    case Primitive::kPrimDouble:
+      array->AsDoubleArray()->SetWithoutChecks<false>(index, static_cast<double>(value));
+      break;
+    case Primitive::kPrimNot: {
+      mirror::ObjectArray<mirror::Object>* obj_array = array->AsObjectArray<mirror::Object>();
+      obj_array->SetWithoutChecks<false>(index, reinterpret_cast<mirror::Object*>(
+          static_cast<uintptr_t>(value)));
+      break;
+    }
+    default:
+      LOG(FATAL) << "Unsupported type " << array_type;
+  }
+}
+
+void Transaction::ArrayLog::VisitRoots(RootCallback* callback, void* arg) {
+  for (auto& it : array_values_) {
+    mirror::Object* obj = reinterpret_cast<mirror::Object*>(static_cast<uintptr_t>(it.second));
+    it.second = reinterpret_cast<uintptr_t>(callback(obj, arg, 0, kRootUnknown));
+  }
+}
+
+}  // namespace art
diff --git a/runtime/transaction.h b/runtime/transaction.h
new file mode 100644
index 0000000..68f9540
--- /dev/null
+++ b/runtime/transaction.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_TRANSACTION_H_
+#define ART_RUNTIME_TRANSACTION_H_
+
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "locks.h"
+#include "offsets.h"
+#include "primitive.h"
+#include "object_callbacks.h"
+#include "safe_map.h"
+
+#include <list>
+#include <map>
+
+namespace art {
+namespace mirror {
+class Array;
+class Object;
+class String;
+}
+class InternTable;
+
+class Transaction {
+ public:
+  Transaction();
+  ~Transaction();
+
+  // Record object field changes.
+  void RecordWriteField32(mirror::Object* obj, MemberOffset field_offset, uint32_t value,
+                          bool is_volatile)
+      LOCKS_EXCLUDED(log_lock_);
+  void RecordWriteField64(mirror::Object* obj, MemberOffset field_offset, uint64_t value,
+                          bool is_volatile)
+      LOCKS_EXCLUDED(log_lock_);
+  void RecordWriteFieldReference(mirror::Object* obj, MemberOffset field_offset,
+                                 mirror::Object* value, bool is_volatile)
+      LOCKS_EXCLUDED(log_lock_);
+
+  // Record array change.
+  void RecordWriteArray(mirror::Array* array, size_t index, uint64_t value)
+      LOCKS_EXCLUDED(log_lock_)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  // Record intern string table changes.
+  void RecordStrongStringInsertion(mirror::String* s, uint32_t hash_code)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_)
+      LOCKS_EXCLUDED(log_lock_);
+  void RecordWeakStringInsertion(mirror::String* s, uint32_t hash_code)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_)
+      LOCKS_EXCLUDED(log_lock_);
+  void RecordStrongStringRemoval(mirror::String* s, uint32_t hash_code)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_)
+      LOCKS_EXCLUDED(log_lock_);
+  void RecordWeakStringRemoval(mirror::String* s, uint32_t hash_code)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_)
+      LOCKS_EXCLUDED(log_lock_);
+
+  // Abort transaction by undoing all recorded changes.
+  void Abort()
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      LOCKS_EXCLUDED(log_lock_);
+
+  void VisitRoots(RootCallback* callback, void* arg)
+      LOCKS_EXCLUDED(log_lock_)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ private:
+  class ObjectLog {
+   public:
+    void Log32BitsValue(MemberOffset offset, uint32_t value, bool is_volatile);
+    void Log64BitsValue(MemberOffset offset, uint64_t value, bool is_volatile);
+    void LogReferenceValue(MemberOffset offset, mirror::Object* obj, bool is_volatile);
+
+    void Undo(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+    void VisitRoots(RootCallback* callback, void* arg);
+
+    size_t Size() const {
+      return field_values_.size();
+    }
+
+   private:
+    enum FieldValueKind {
+      k32Bits,
+      k64Bits,
+      kReference
+    };
+    struct FieldValue {
+      // TODO use JValue instead ?
+      uint64_t value;
+      FieldValueKind kind;
+      bool is_volatile;
+    };
+
+    void UndoFieldWrite(mirror::Object* obj, MemberOffset field_offset,
+                        const FieldValue& field_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+    // Maps field's offset to its value.
+    std::map<uint32_t, FieldValue> field_values_;
+  };
+
+  class ArrayLog {
+   public:
+    void LogValue(size_t index, uint64_t value);
+
+    void Undo(mirror::Array* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+    void VisitRoots(RootCallback* callback, void* arg);
+
+    size_t Size() const {
+      return array_values_.size();
+    }
+
+   private:
+    void UndoArrayWrite(mirror::Array* array, Primitive::Type array_type, size_t index,
+                        uint64_t value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+    // Maps index to value.
+    // TODO use JValue instead ?
+    std::map<size_t, uint64_t> array_values_;
+  };
+
+  class InternStringLog {
+   public:
+    enum StringKind {
+      kStrongString,
+      kWeakString
+    };
+    enum StringOp {
+      kInsert,
+      kRemove
+    };
+    InternStringLog(mirror::String* s, uint32_t hash_code, StringKind kind, StringOp op)
+      : str_(s), hash_code_(hash_code), string_kind_(kind), string_op_(op) {
+    }
+
+    void Undo(InternTable* intern_table) EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+    void VisitRoots(RootCallback* callback, void* arg);
+
+   private:
+    mirror::String* str_;
+    uint32_t hash_code_;
+    StringKind string_kind_;
+    StringOp string_op_;
+  };
+
+  void LogInternedString(InternStringLog& log)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_)
+      LOCKS_EXCLUDED(log_lock_);
+
+  void UndoObjectModifications()
+      EXCLUSIVE_LOCKS_REQUIRED(log_lock_)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void UndoArrayModifications()
+      EXCLUSIVE_LOCKS_REQUIRED(log_lock_)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void UndoInternStringTableModifications()
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_)
+      EXCLUSIVE_LOCKS_REQUIRED(log_lock_);
+
+  void VisitObjectLogs(RootCallback* callback, void* arg)
+      EXCLUSIVE_LOCKS_REQUIRED(log_lock_)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void VisitArrayLogs(RootCallback* callback, void* arg)
+      EXCLUSIVE_LOCKS_REQUIRED(log_lock_)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void VisitStringLogs(RootCallback* callback, void* arg)
+      EXCLUSIVE_LOCKS_REQUIRED(log_lock_)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  Mutex log_lock_ ACQUIRED_AFTER(Locks::intern_table_lock_);
+  std::map<mirror::Object*, ObjectLog> object_logs_ GUARDED_BY(log_lock_);
+  std::map<mirror::Array*, ArrayLog> array_logs_  GUARDED_BY(log_lock_);
+  std::list<InternStringLog> intern_string_logs_ GUARDED_BY(log_lock_);
+
+  DISALLOW_COPY_AND_ASSIGN(Transaction);
+};
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_TRANSACTION_H_
diff --git a/runtime/transaction_test.cc b/runtime/transaction_test.cc
new file mode 100644
index 0000000..dcfa24b
--- /dev/null
+++ b/runtime/transaction_test.cc
@@ -0,0 +1,472 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "common_test.h"
+#include "invoke_arg_array_builder.h"
+#include "mirror/array-inl.h"
+#include "mirror/art_field-inl.h"
+#include "mirror/art_method-inl.h"
+#include "transaction.h"
+
+namespace art {
+
+class TransactionTest : public CommonTest {
+};
+
+TEST_F(TransactionTest, Object_class) {
+  ScopedObjectAccess soa(Thread::Current());
+  SirtRef<mirror::Class> sirt_klass(soa.Self(),
+                                    class_linker_->FindSystemClass("Ljava/lang/Object;"));
+  ASSERT_TRUE(sirt_klass.get() != nullptr);
+
+  Transaction transaction;
+  Runtime::Current()->EnterTransactionMode(&transaction);
+  SirtRef<mirror::Object> sirt_obj(soa.Self(), sirt_klass->AllocObject(soa.Self()));
+  ASSERT_TRUE(sirt_obj.get() != nullptr);
+  ASSERT_EQ(sirt_obj->GetClass(), sirt_klass.get());
+  Runtime::Current()->ExitTransactionMode();
+
+  // Aborting transaction must not clear the Object::class field.
+  transaction.Abort();
+  EXPECT_EQ(sirt_obj->GetClass(), sirt_klass.get());
+}
+
+TEST_F(TransactionTest, Object_monitor) {
+  ScopedObjectAccess soa(Thread::Current());
+  SirtRef<mirror::Class> sirt_klass(soa.Self(),
+                                    class_linker_->FindSystemClass("Ljava/lang/Object;"));
+  ASSERT_TRUE(sirt_klass.get() != nullptr);
+  SirtRef<mirror::Object> sirt_obj(soa.Self(), sirt_klass->AllocObject(soa.Self()));
+  ASSERT_TRUE(sirt_obj.get() != nullptr);
+  ASSERT_EQ(sirt_obj->GetClass(), sirt_klass.get());
+
+  // Lock object's monitor outside the transaction.
+  sirt_obj->MonitorEnter(soa.Self());
+  uint32_t old_lock_word = sirt_obj->GetLockWord().GetValue();
+
+  Transaction transaction;
+  Runtime::Current()->EnterTransactionMode(&transaction);
+  // Unlock object's monitor inside the transaction.
+  sirt_obj->MonitorExit(soa.Self());
+  uint32_t new_lock_word = sirt_obj->GetLockWord().GetValue();
+  Runtime::Current()->ExitTransactionMode();
+
+  // Aborting transaction must not clear the Object::class field.
+  transaction.Abort();
+  uint32_t aborted_lock_word = sirt_obj->GetLockWord().GetValue();
+  EXPECT_NE(old_lock_word, new_lock_word);
+  EXPECT_EQ(aborted_lock_word, new_lock_word);
+}
+
+TEST_F(TransactionTest, Array_length) {
+  ScopedObjectAccess soa(Thread::Current());
+  SirtRef<mirror::Class> sirt_klass(soa.Self(),
+                                    class_linker_->FindSystemClass("[Ljava/lang/Object;"));
+  ASSERT_TRUE(sirt_klass.get() != nullptr);
+
+  constexpr int32_t kArraySize = 2;
+
+  Transaction transaction;
+  Runtime::Current()->EnterTransactionMode(&transaction);
+
+  // Allocate an array during transaction.
+  SirtRef<mirror::Array> sirt_obj(soa.Self(),
+                                  mirror::Array::Alloc<false>(soa.Self(), sirt_klass.get(), kArraySize));
+  ASSERT_TRUE(sirt_obj.get() != nullptr);
+  ASSERT_EQ(sirt_obj->GetClass(), sirt_klass.get());
+  Runtime::Current()->ExitTransactionMode();
+
+  // Aborting transaction must not clear the Object::class field.
+  transaction.Abort();
+  EXPECT_EQ(sirt_obj->GetLength(), kArraySize);
+}
+
+TEST_F(TransactionTest, StaticFieldsTest) {
+  ScopedObjectAccess soa(Thread::Current());
+  SirtRef<mirror::ClassLoader> class_loader(
+      soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("Transaction")));
+  ASSERT_TRUE(class_loader.get() != nullptr);
+
+  SirtRef<mirror::Class> sirt_klass(soa.Self(),
+                                    class_linker_->FindClass("LStaticFieldsTest;", class_loader));
+  ASSERT_TRUE(sirt_klass.get() != nullptr);
+  class_linker_->EnsureInitialized(sirt_klass, true, true);
+  ASSERT_TRUE(sirt_klass->IsInitialized());
+
+  // Lookup fields.
+  mirror::ArtField* booleanField = sirt_klass->FindDeclaredStaticField("booleanField", "Z");
+  ASSERT_TRUE(booleanField != nullptr);
+  ASSERT_EQ(FieldHelper(booleanField).GetTypeAsPrimitiveType(), Primitive::kPrimBoolean);
+  ASSERT_EQ(booleanField->GetBoolean(sirt_klass.get()), false);
+
+  mirror::ArtField* byteField = sirt_klass->FindDeclaredStaticField("byteField", "B");
+  ASSERT_TRUE(byteField != nullptr);
+  ASSERT_EQ(FieldHelper(byteField).GetTypeAsPrimitiveType(), Primitive::kPrimByte);
+  ASSERT_EQ(byteField->GetByte(sirt_klass.get()), 0);
+
+  mirror::ArtField* charField = sirt_klass->FindDeclaredStaticField("charField", "C");
+  ASSERT_TRUE(charField != nullptr);
+  ASSERT_EQ(FieldHelper(charField).GetTypeAsPrimitiveType(), Primitive::kPrimChar);
+  ASSERT_EQ(charField->GetChar(sirt_klass.get()), 0u);
+
+  mirror::ArtField* shortField = sirt_klass->FindDeclaredStaticField("shortField", "S");
+  ASSERT_TRUE(shortField != nullptr);
+  ASSERT_EQ(FieldHelper(shortField).GetTypeAsPrimitiveType(), Primitive::kPrimShort);
+  ASSERT_EQ(shortField->GetShort(sirt_klass.get()), 0);
+
+  mirror::ArtField* intField = sirt_klass->FindDeclaredStaticField("intField", "I");
+  ASSERT_TRUE(intField != nullptr);
+  ASSERT_EQ(FieldHelper(intField).GetTypeAsPrimitiveType(), Primitive::kPrimInt);
+  ASSERT_EQ(intField->GetInt(sirt_klass.get()), 0);
+
+  mirror::ArtField* longField = sirt_klass->FindDeclaredStaticField("longField", "J");
+  ASSERT_TRUE(longField != nullptr);
+  ASSERT_EQ(FieldHelper(longField).GetTypeAsPrimitiveType(), Primitive::kPrimLong);
+  ASSERT_EQ(longField->GetLong(sirt_klass.get()), static_cast<int64_t>(0));
+
+  mirror::ArtField* floatField = sirt_klass->FindDeclaredStaticField("floatField", "F");
+  ASSERT_TRUE(floatField != nullptr);
+  ASSERT_EQ(FieldHelper(floatField).GetTypeAsPrimitiveType(), Primitive::kPrimFloat);
+  ASSERT_EQ(floatField->GetFloat(sirt_klass.get()), static_cast<float>(0.0f));
+
+  mirror::ArtField* doubleField = sirt_klass->FindDeclaredStaticField("doubleField", "D");
+  ASSERT_TRUE(doubleField != nullptr);
+  ASSERT_EQ(FieldHelper(doubleField).GetTypeAsPrimitiveType(), Primitive::kPrimDouble);
+  ASSERT_EQ(doubleField->GetDouble(sirt_klass.get()), static_cast<double>(0.0));
+
+  mirror::ArtField* objectField = sirt_klass->FindDeclaredStaticField("objectField", "Ljava/lang/Object;");
+  ASSERT_TRUE(objectField != nullptr);
+  ASSERT_EQ(FieldHelper(objectField).GetTypeAsPrimitiveType(), Primitive::kPrimNot);
+  ASSERT_EQ(objectField->GetObject(sirt_klass.get()), nullptr);
+
+  // Create a java.lang.Object instance to set objectField.
+  SirtRef<mirror::Class> object_klass(soa.Self(), class_linker_->FindSystemClass("Ljava/lang/Object;"));
+  ASSERT_TRUE(object_klass.get() != nullptr);
+  SirtRef<mirror::Object> sirt_obj(soa.Self(), sirt_klass->AllocObject(soa.Self()));
+  ASSERT_TRUE(sirt_obj.get() != nullptr);
+  ASSERT_EQ(sirt_obj->GetClass(), sirt_klass.get());
+
+  // Modify fields inside transaction and abort it.
+  Transaction transaction;
+  Runtime::Current()->EnterTransactionMode(&transaction);
+  booleanField->SetBoolean<true>(sirt_klass.get(), true);
+  byteField->SetByte<true>(sirt_klass.get(), 1);
+  charField->SetChar<true>(sirt_klass.get(), 1u);
+  shortField->SetShort<true>(sirt_klass.get(), 1);
+  intField->SetInt<true>(sirt_klass.get(), 1);
+  longField->SetLong<true>(sirt_klass.get(), 1);
+  floatField->SetFloat<true>(sirt_klass.get(), 1.0);
+  doubleField->SetDouble<true>(sirt_klass.get(), 1.0);
+  objectField->SetObject<true>(sirt_klass.get(), sirt_obj.get());
+  Runtime::Current()->ExitTransactionMode();
+  transaction.Abort();
+
+  // Check values have properly been restored to their original (default) value.
+  EXPECT_EQ(booleanField->GetBoolean(sirt_klass.get()), false);
+  EXPECT_EQ(byteField->GetByte(sirt_klass.get()), 0);
+  EXPECT_EQ(charField->GetChar(sirt_klass.get()), 0u);
+  EXPECT_EQ(shortField->GetShort(sirt_klass.get()), 0);
+  EXPECT_EQ(intField->GetInt(sirt_klass.get()), 0);
+  EXPECT_EQ(longField->GetLong(sirt_klass.get()), static_cast<int64_t>(0));
+  EXPECT_EQ(floatField->GetFloat(sirt_klass.get()), static_cast<float>(0.0f));
+  EXPECT_EQ(doubleField->GetDouble(sirt_klass.get()), static_cast<double>(0.0));
+  EXPECT_EQ(objectField->GetObject(sirt_klass.get()), nullptr);
+}
+
+TEST_F(TransactionTest, InstanceFieldsTest) {
+  ScopedObjectAccess soa(Thread::Current());
+  SirtRef<mirror::ClassLoader> class_loader(
+      soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("Transaction")));
+  ASSERT_TRUE(class_loader.get() != nullptr);
+
+  SirtRef<mirror::Class> sirt_klass(soa.Self(),
+                                    class_linker_->FindClass("LInstanceFieldsTest;", class_loader));
+  ASSERT_TRUE(sirt_klass.get() != nullptr);
+  class_linker_->EnsureInitialized(sirt_klass, true, true);
+  ASSERT_TRUE(sirt_klass->IsInitialized());
+
+  // Allocate an InstanceFieldTest object.
+  SirtRef<mirror::Object> sirt_instance(soa.Self(), sirt_klass->AllocObject(soa.Self()));
+  ASSERT_TRUE(sirt_instance.get() != nullptr);
+
+  // Lookup fields.
+  mirror::ArtField* booleanField = sirt_klass->FindDeclaredInstanceField("booleanField", "Z");
+  ASSERT_TRUE(booleanField != nullptr);
+  ASSERT_EQ(FieldHelper(booleanField).GetTypeAsPrimitiveType(), Primitive::kPrimBoolean);
+  ASSERT_EQ(booleanField->GetBoolean(sirt_instance.get()), false);
+
+  mirror::ArtField* byteField = sirt_klass->FindDeclaredInstanceField("byteField", "B");
+  ASSERT_TRUE(byteField != nullptr);
+  ASSERT_EQ(FieldHelper(byteField).GetTypeAsPrimitiveType(), Primitive::kPrimByte);
+  ASSERT_EQ(byteField->GetByte(sirt_instance.get()), 0);
+
+  mirror::ArtField* charField = sirt_klass->FindDeclaredInstanceField("charField", "C");
+  ASSERT_TRUE(charField != nullptr);
+  ASSERT_EQ(FieldHelper(charField).GetTypeAsPrimitiveType(), Primitive::kPrimChar);
+  ASSERT_EQ(charField->GetChar(sirt_instance.get()), 0u);
+
+  mirror::ArtField* shortField = sirt_klass->FindDeclaredInstanceField("shortField", "S");
+  ASSERT_TRUE(shortField != nullptr);
+  ASSERT_EQ(FieldHelper(shortField).GetTypeAsPrimitiveType(), Primitive::kPrimShort);
+  ASSERT_EQ(shortField->GetShort(sirt_instance.get()), 0);
+
+  mirror::ArtField* intField = sirt_klass->FindDeclaredInstanceField("intField", "I");
+  ASSERT_TRUE(intField != nullptr);
+  ASSERT_EQ(FieldHelper(intField).GetTypeAsPrimitiveType(), Primitive::kPrimInt);
+  ASSERT_EQ(intField->GetInt(sirt_instance.get()), 0);
+
+  mirror::ArtField* longField = sirt_klass->FindDeclaredInstanceField("longField", "J");
+  ASSERT_TRUE(longField != nullptr);
+  ASSERT_EQ(FieldHelper(longField).GetTypeAsPrimitiveType(), Primitive::kPrimLong);
+  ASSERT_EQ(longField->GetLong(sirt_instance.get()), static_cast<int64_t>(0));
+
+  mirror::ArtField* floatField = sirt_klass->FindDeclaredInstanceField("floatField", "F");
+  ASSERT_TRUE(floatField != nullptr);
+  ASSERT_EQ(FieldHelper(floatField).GetTypeAsPrimitiveType(), Primitive::kPrimFloat);
+  ASSERT_EQ(floatField->GetFloat(sirt_instance.get()), static_cast<float>(0.0f));
+
+  mirror::ArtField* doubleField = sirt_klass->FindDeclaredInstanceField("doubleField", "D");
+  ASSERT_TRUE(doubleField != nullptr);
+  ASSERT_EQ(FieldHelper(doubleField).GetTypeAsPrimitiveType(), Primitive::kPrimDouble);
+  ASSERT_EQ(doubleField->GetDouble(sirt_instance.get()), static_cast<double>(0.0));
+
+  mirror::ArtField* objectField = sirt_klass->FindDeclaredInstanceField("objectField", "Ljava/lang/Object;");
+  ASSERT_TRUE(objectField != nullptr);
+  ASSERT_EQ(FieldHelper(objectField).GetTypeAsPrimitiveType(), Primitive::kPrimNot);
+  ASSERT_EQ(objectField->GetObject(sirt_instance.get()), nullptr);
+
+  // Create a java.lang.Object instance to set objectField.
+  SirtRef<mirror::Class> object_klass(soa.Self(), class_linker_->FindSystemClass("Ljava/lang/Object;"));
+  ASSERT_TRUE(object_klass.get() != nullptr);
+  SirtRef<mirror::Object> sirt_obj(soa.Self(), sirt_klass->AllocObject(soa.Self()));
+  ASSERT_TRUE(sirt_obj.get() != nullptr);
+  ASSERT_EQ(sirt_obj->GetClass(), sirt_klass.get());
+
+  // Modify fields inside transaction and abort it.
+  Transaction transaction;
+  Runtime::Current()->EnterTransactionMode(&transaction);
+  booleanField->SetBoolean<true>(sirt_instance.get(), true);
+  byteField->SetByte<true>(sirt_instance.get(), 1);
+  charField->SetChar<true>(sirt_instance.get(), 1u);
+  shortField->SetShort<true>(sirt_instance.get(), 1);
+  intField->SetInt<true>(sirt_instance.get(), 1);
+  longField->SetLong<true>(sirt_instance.get(), 1);
+  floatField->SetFloat<true>(sirt_instance.get(), 1.0);
+  doubleField->SetDouble<true>(sirt_instance.get(), 1.0);
+  objectField->SetObject<true>(sirt_instance.get(), sirt_obj.get());
+  Runtime::Current()->ExitTransactionMode();
+  transaction.Abort();
+
+  // Check values have properly been restored to their original (default) value.
+  EXPECT_EQ(booleanField->GetBoolean(sirt_instance.get()), false);
+  EXPECT_EQ(byteField->GetByte(sirt_instance.get()), 0);
+  EXPECT_EQ(charField->GetChar(sirt_instance.get()), 0u);
+  EXPECT_EQ(shortField->GetShort(sirt_instance.get()), 0);
+  EXPECT_EQ(intField->GetInt(sirt_instance.get()), 0);
+  EXPECT_EQ(longField->GetLong(sirt_instance.get()), static_cast<int64_t>(0));
+  EXPECT_EQ(floatField->GetFloat(sirt_instance.get()), static_cast<float>(0.0f));
+  EXPECT_EQ(doubleField->GetDouble(sirt_instance.get()), static_cast<double>(0.0));
+  EXPECT_EQ(objectField->GetObject(sirt_instance.get()), nullptr);
+}
+
+
+TEST_F(TransactionTest, StaticArrayFieldsTest) {
+  ScopedObjectAccess soa(Thread::Current());
+  SirtRef<mirror::ClassLoader> class_loader(
+      soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("Transaction")));
+  ASSERT_TRUE(class_loader.get() != nullptr);
+
+  SirtRef<mirror::Class> sirt_klass(soa.Self(),
+                                    class_linker_->FindClass("LStaticArrayFieldsTest;", class_loader));
+  ASSERT_TRUE(sirt_klass.get() != nullptr);
+  class_linker_->EnsureInitialized(sirt_klass, true, true);
+  ASSERT_TRUE(sirt_klass->IsInitialized());
+
+  // Lookup fields.
+  mirror::ArtField* booleanArrayField = sirt_klass->FindDeclaredStaticField("booleanArrayField", "[Z");
+  ASSERT_TRUE(booleanArrayField != nullptr);
+  mirror::BooleanArray* booleanArray = booleanArrayField->GetObject(sirt_klass.get())->AsBooleanArray();
+  ASSERT_TRUE(booleanArray != nullptr);
+  ASSERT_EQ(booleanArray->GetLength(), 1);
+  ASSERT_EQ(booleanArray->GetWithoutChecks(0), false);
+
+  mirror::ArtField* byteArrayField = sirt_klass->FindDeclaredStaticField("byteArrayField", "[B");
+  ASSERT_TRUE(byteArrayField != nullptr);
+  mirror::ByteArray* byteArray = byteArrayField->GetObject(sirt_klass.get())->AsByteArray();
+  ASSERT_TRUE(byteArray != nullptr);
+  ASSERT_EQ(byteArray->GetLength(), 1);
+  ASSERT_EQ(byteArray->GetWithoutChecks(0), 0);
+
+  mirror::ArtField* charArrayField = sirt_klass->FindDeclaredStaticField("charArrayField", "[C");
+  ASSERT_TRUE(charArrayField != nullptr);
+  mirror::CharArray* charArray = charArrayField->GetObject(sirt_klass.get())->AsCharArray();
+  ASSERT_TRUE(charArray != nullptr);
+  ASSERT_EQ(charArray->GetLength(), 1);
+  ASSERT_EQ(charArray->GetWithoutChecks(0), 0u);
+
+  mirror::ArtField* shortArrayField = sirt_klass->FindDeclaredStaticField("shortArrayField", "[S");
+  ASSERT_TRUE(shortArrayField != nullptr);
+  mirror::ShortArray* shortArray = shortArrayField->GetObject(sirt_klass.get())->AsShortArray();
+  ASSERT_TRUE(shortArray != nullptr);
+  ASSERT_EQ(shortArray->GetLength(), 1);
+  ASSERT_EQ(shortArray->GetWithoutChecks(0), 0);
+
+  mirror::ArtField* intArrayField = sirt_klass->FindDeclaredStaticField("intArrayField", "[I");
+  ASSERT_TRUE(intArrayField != nullptr);
+  mirror::IntArray* intArray = intArrayField->GetObject(sirt_klass.get())->AsIntArray();
+  ASSERT_TRUE(intArray != nullptr);
+  ASSERT_EQ(intArray->GetLength(), 1);
+  ASSERT_EQ(intArray->GetWithoutChecks(0), 0);
+
+  mirror::ArtField* longArrayField = sirt_klass->FindDeclaredStaticField("longArrayField", "[J");
+  ASSERT_TRUE(longArrayField != nullptr);
+  mirror::LongArray* longArray = longArrayField->GetObject(sirt_klass.get())->AsLongArray();
+  ASSERT_TRUE(longArray != nullptr);
+  ASSERT_EQ(longArray->GetLength(), 1);
+  ASSERT_EQ(longArray->GetWithoutChecks(0), static_cast<int64_t>(0));
+
+  mirror::ArtField* floatArrayField = sirt_klass->FindDeclaredStaticField("floatArrayField", "[F");
+  ASSERT_TRUE(floatArrayField != nullptr);
+  mirror::FloatArray* floatArray = floatArrayField->GetObject(sirt_klass.get())->AsFloatArray();
+  ASSERT_TRUE(floatArray != nullptr);
+  ASSERT_EQ(floatArray->GetLength(), 1);
+  ASSERT_EQ(floatArray->GetWithoutChecks(0), static_cast<float>(0.0f));
+
+  mirror::ArtField* doubleArrayField = sirt_klass->FindDeclaredStaticField("doubleArrayField", "[D");
+  ASSERT_TRUE(doubleArrayField != nullptr);
+  mirror::DoubleArray* doubleArray = doubleArrayField->GetObject(sirt_klass.get())->AsDoubleArray();
+  ASSERT_TRUE(doubleArray != nullptr);
+  ASSERT_EQ(doubleArray->GetLength(), 1);
+  ASSERT_EQ(doubleArray->GetWithoutChecks(0), static_cast<double>(0.0f));
+
+  mirror::ArtField* objectArrayField = sirt_klass->FindDeclaredStaticField("objectArrayField", "[Ljava/lang/Object;");
+  ASSERT_TRUE(objectArrayField != nullptr);
+  mirror::ObjectArray<mirror::Object>* objectArray = objectArrayField->GetObject(sirt_klass.get())->AsObjectArray<mirror::Object>();
+  ASSERT_TRUE(objectArray != nullptr);
+  ASSERT_EQ(objectArray->GetLength(), 1);
+  ASSERT_EQ(objectArray->GetWithoutChecks(0), nullptr);
+
+  // Create a java.lang.Object instance to set objectField.
+  SirtRef<mirror::Class> object_klass(soa.Self(), class_linker_->FindSystemClass("Ljava/lang/Object;"));
+  ASSERT_TRUE(object_klass.get() != nullptr);
+  SirtRef<mirror::Object> sirt_obj(soa.Self(), sirt_klass->AllocObject(soa.Self()));
+  ASSERT_TRUE(sirt_obj.get() != nullptr);
+  ASSERT_EQ(sirt_obj->GetClass(), sirt_klass.get());
+
+  // Modify fields inside transaction and abort it.
+  Transaction transaction;
+  Runtime::Current()->EnterTransactionMode(&transaction);
+  booleanArray->SetWithoutChecks<true>(0, true);
+  byteArray->SetWithoutChecks<true>(0, 1);
+  charArray->SetWithoutChecks<true>(0, 1u);
+  shortArray->SetWithoutChecks<true>(0, 1);
+  intArray->SetWithoutChecks<true>(0, 1);
+  longArray->SetWithoutChecks<true>(0, 1);
+  floatArray->SetWithoutChecks<true>(0, 1.0);
+  doubleArray->SetWithoutChecks<true>(0, 1.0);
+  objectArray->SetWithoutChecks<true>(0, sirt_obj.get());
+  Runtime::Current()->ExitTransactionMode();
+  transaction.Abort();
+
+  // Check values have properly been restored to their original (default) value.
+  EXPECT_EQ(booleanArray->GetWithoutChecks(0), false);
+  EXPECT_EQ(byteArray->GetWithoutChecks(0), 0);
+  EXPECT_EQ(charArray->GetWithoutChecks(0), 0u);
+  EXPECT_EQ(shortArray->GetWithoutChecks(0), 0);
+  EXPECT_EQ(intArray->GetWithoutChecks(0), 0);
+  EXPECT_EQ(longArray->GetWithoutChecks(0), static_cast<int64_t>(0));
+  EXPECT_EQ(floatArray->GetWithoutChecks(0), static_cast<float>(0.0f));
+  EXPECT_EQ(doubleArray->GetWithoutChecks(0), static_cast<double>(0.0f));
+  EXPECT_EQ(objectArray->GetWithoutChecks(0), nullptr);
+}
+
+TEST_F(TransactionTest, EmptyClass) {
+  ScopedObjectAccess soa(Thread::Current());
+  SirtRef<mirror::ClassLoader> class_loader(
+      soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("Transaction")));
+  ASSERT_TRUE(class_loader.get() != nullptr);
+
+  SirtRef<mirror::Class> sirt_klass(soa.Self(),
+                                    class_linker_->FindClass("LTransaction$EmptyStatic;", class_loader));
+  ASSERT_TRUE(sirt_klass.get() != nullptr);
+  class_linker_->VerifyClass(sirt_klass);
+  ASSERT_TRUE(sirt_klass->IsVerified());
+
+  Transaction transaction;
+  Runtime::Current()->EnterTransactionMode(&transaction);
+  class_linker_->EnsureInitialized(sirt_klass, true, true);
+  Runtime::Current()->ExitTransactionMode();
+  ASSERT_FALSE(soa.Self()->IsExceptionPending());
+}
+
+TEST_F(TransactionTest, StaticFieldClass) {
+  ScopedObjectAccess soa(Thread::Current());
+  SirtRef<mirror::ClassLoader> class_loader(
+      soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("Transaction")));
+  ASSERT_TRUE(class_loader.get() != nullptr);
+
+  SirtRef<mirror::Class> sirt_klass(soa.Self(),
+                                    class_linker_->FindClass("LTransaction$StaticFieldClass;", class_loader));
+  ASSERT_TRUE(sirt_klass.get() != nullptr);
+  class_linker_->VerifyClass(sirt_klass);
+  ASSERT_TRUE(sirt_klass->IsVerified());
+
+  Transaction transaction;
+  Runtime::Current()->EnterTransactionMode(&transaction);
+  class_linker_->EnsureInitialized(sirt_klass, true, true);
+  Runtime::Current()->ExitTransactionMode();
+  ASSERT_FALSE(soa.Self()->IsExceptionPending());
+}
+
+TEST_F(TransactionTest, BlacklistedClass) {
+  ScopedObjectAccess soa(Thread::Current());
+  jobject jclass_loader = LoadDex("Transaction");
+  SirtRef<mirror::ClassLoader> class_loader(soa.Self(),
+                                            soa.Decode<mirror::ClassLoader*>(jclass_loader));
+  ASSERT_TRUE(class_loader.get() != nullptr);
+
+  // Load and verify java.lang.ExceptionInInitializerError and java.lang.InternalError which will
+  // be thrown by class initialization due to native call.
+  SirtRef<mirror::Class> sirt_klass(soa.Self(),
+                                    class_linker_->FindSystemClass("Ljava/lang/ExceptionInInitializerError;"));
+  ASSERT_TRUE(sirt_klass.get() != nullptr);
+  class_linker_->VerifyClass(sirt_klass);
+  ASSERT_TRUE(sirt_klass->IsVerified());
+  sirt_klass.reset(class_linker_->FindSystemClass("Ljava/lang/InternalError;"));
+  ASSERT_TRUE(sirt_klass.get() != nullptr);
+  class_linker_->VerifyClass(sirt_klass);
+  ASSERT_TRUE(sirt_klass->IsVerified());
+
+  // Load and verify Transaction$NativeSupport used in class initialization.
+  sirt_klass.reset(class_linker_->FindClass("LTransaction$NativeSupport;", class_loader));
+  ASSERT_TRUE(sirt_klass.get() != nullptr);
+  class_linker_->VerifyClass(sirt_klass);
+  ASSERT_TRUE(sirt_klass->IsVerified());
+
+  sirt_klass.reset(class_linker_->FindClass("LTransaction$BlacklistedClass;", class_loader));
+  ASSERT_TRUE(sirt_klass.get() != nullptr);
+  class_linker_->VerifyClass(sirt_klass);
+  ASSERT_TRUE(sirt_klass->IsVerified());
+
+  Transaction transaction;
+  Runtime::Current()->EnterTransactionMode(&transaction);
+  class_linker_->EnsureInitialized(sirt_klass, true, true);
+  Runtime::Current()->ExitTransactionMode();
+  ASSERT_TRUE(soa.Self()->IsExceptionPending());
+}
+
+
+}  // namespace art