changeset 13041:823e229a1ee5 mvt

Value types off-heap buffering for the interpreter
author fparain
date Thu, 29 Jun 2017 09:35:38 -0400
parents 641b106d1560
children 0c9fe8c21f50
files src/cpu/x86/vm/frame_x86.cpp src/cpu/x86/vm/frame_x86.hpp src/cpu/x86/vm/frame_x86.inline.hpp src/cpu/x86/vm/interp_masm_x86.cpp src/cpu/x86/vm/stubGenerator_x86_64.cpp src/cpu/x86/vm/templateInterpreterGenerator_x86.cpp src/cpu/x86/vm/templateTable_x86.cpp src/share/vm/classfile/classFileParser.cpp src/share/vm/interpreter/interpreterRuntime.cpp src/share/vm/interpreter/interpreterRuntime.hpp src/share/vm/interpreter/oopMapCache.cpp src/share/vm/logging/logTag.hpp src/share/vm/memory/allocation.hpp src/share/vm/memory/allocation.inline.hpp src/share/vm/memory/iterator.hpp src/share/vm/memory/vtBuffer.cpp src/share/vm/memory/vtBuffer.hpp src/share/vm/oops/instanceKlass.cpp src/share/vm/oops/instanceKlass.hpp src/share/vm/oops/method.cpp src/share/vm/oops/method.hpp src/share/vm/oops/oop.inline.hpp src/share/vm/oops/valueKlass.cpp src/share/vm/oops/valueKlass.hpp src/share/vm/runtime/frame.cpp src/share/vm/runtime/frame.hpp src/share/vm/runtime/globals.hpp src/share/vm/runtime/handles.cpp src/share/vm/runtime/mutexLocker.cpp src/share/vm/runtime/sharedRuntime.cpp src/share/vm/runtime/thread.cpp src/share/vm/runtime/thread.hpp src/share/vm/runtime/vframeArray.cpp src/share/vm/runtime/vm_operations.cpp src/share/vm/runtime/vm_operations.hpp src/share/vm/services/diagnosticCommand.cpp src/share/vm/services/diagnosticCommand.hpp src/share/vm/services/nmtCommon.cpp test/runtime/valhalla/valuetypes/UninitializedValueFieldsTest.java test/runtime/valhalla/valuetypes/VTBufferTest.java test/runtime/valhalla/valuetypes/ValueTypeGenerator.java test/runtime/valhalla/valuetypes/ValueTypeGetField.java
diffstat 42 files changed, 1937 insertions(+), 101 deletions(-) [+]
line wrap: on
line diff
--- a/src/cpu/x86/vm/frame_x86.cpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/cpu/x86/vm/frame_x86.cpp	Thu Jun 29 09:35:38 2017 -0400
@@ -642,6 +642,7 @@
     DESCRIBE_FP_OFFSET(interpreter_frame_mirror);
     DESCRIBE_FP_OFFSET(interpreter_frame_mdp);
     DESCRIBE_FP_OFFSET(interpreter_frame_cache);
+    DESCRIBE_FP_OFFSET(interpreter_frame_vt_alloc_ptr);
     DESCRIBE_FP_OFFSET(interpreter_frame_locals);
     DESCRIBE_FP_OFFSET(interpreter_frame_bcp);
     DESCRIBE_FP_OFFSET(interpreter_frame_initial_sp);
--- a/src/cpu/x86/vm/frame_x86.hpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/cpu/x86/vm/frame_x86.hpp	Thu Jun 29 09:35:38 2017 -0400
@@ -73,7 +73,8 @@
     interpreter_frame_mirror_offset                  = interpreter_frame_method_offset - 1,
     interpreter_frame_mdp_offset                     = interpreter_frame_mirror_offset - 1,
     interpreter_frame_cache_offset                   = interpreter_frame_mdp_offset - 1,
-    interpreter_frame_locals_offset                  = interpreter_frame_cache_offset - 1,
+    interpreter_frame_vt_alloc_ptr_offset            = interpreter_frame_cache_offset - 1,
+    interpreter_frame_locals_offset                  = interpreter_frame_vt_alloc_ptr_offset - 1,
     interpreter_frame_bcp_offset                     = interpreter_frame_locals_offset - 1,
     interpreter_frame_initial_sp_offset              = interpreter_frame_bcp_offset - 1,
 
--- a/src/cpu/x86/vm/frame_x86.inline.hpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/cpu/x86/vm/frame_x86.inline.hpp	Thu Jun 29 09:35:38 2017 -0400
@@ -192,6 +192,10 @@
   return (oop*)addr_at(interpreter_frame_mirror_offset);
 }
 
+inline intptr_t** frame::interpreter_frame_vt_alloc_ptr_addr() const {
+  return (intptr_t**)addr_at(interpreter_frame_vt_alloc_ptr_offset);
+}
+
 // top of expression stack
 inline intptr_t* frame::interpreter_frame_tos_address() const {
   intptr_t* last_sp = interpreter_frame_last_sp();
--- a/src/cpu/x86/vm/interp_masm_x86.cpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/cpu/x86/vm/interp_masm_x86.cpp	Thu Jun 29 09:35:38 2017 -0400
@@ -27,6 +27,7 @@
 #include "interpreter/interpreter.hpp"
 #include "interpreter/interpreterRuntime.hpp"
 #include "logging/log.hpp"
+#include "memory/vtBuffer.hpp"
 #include "oops/arrayOop.hpp"
 #include "oops/markOop.hpp"
 #include "oops/methodData.hpp"
@@ -1077,6 +1078,28 @@
     notify_method_exit(state, SkipNotifyJVMTI); // preserve TOSCA
   }
 
+  Label vtbuffer_slow, vtbuffer_done;
+  const Register thread1 = NOT_LP64(rcx) LP64_ONLY(r15_thread);
+  const uintptr_t chunk_mask = VTBufferChunk::chunk_mask();
+  movptr(rbx, Address(rbp, frame::interpreter_frame_vt_alloc_ptr_offset * wordSize));
+  NOT_LP64(get_thread(thread1));
+  movptr(rcx, Address(thread1, JavaThread::vt_alloc_ptr_offset()));
+  cmpptr(rbx, rcx);
+  jcc(Assembler::equal, vtbuffer_done);
+  andptr(rbx, chunk_mask);
+  andptr(rcx, chunk_mask);
+  cmpptr(rbx, rcx);
+  jcc(Assembler::notEqual, vtbuffer_slow);
+  movptr(rbx, Address(rbp, frame::interpreter_frame_vt_alloc_ptr_offset * wordSize));
+  movptr(Address(thread1, JavaThread::vt_alloc_ptr_offset()), rbx);
+  jmp(vtbuffer_done);
+  bind(vtbuffer_slow);
+  push(state);
+  call_VM(noreg, CAST_FROM_FN_PTR(address,
+                                  InterpreterRuntime::recycle_vtbuffer));
+  pop(state);
+  bind(vtbuffer_done);
+
   // remove activation
   // get sender sp
   movptr(rbx,
--- a/src/cpu/x86/vm/stubGenerator_x86_64.cpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/cpu/x86/vm/stubGenerator_x86_64.cpp	Thu Jun 29 09:35:38 2017 -0400
@@ -995,7 +995,7 @@
     StubCodeMark mark(this, "StubRoutines", "verify_oop");
     address start = __ pc();
 
-    Label exit, error;
+    Label exit, error, in_Java_heap;
 
     __ pushf();
     __ incrementl(ExternalAddress((address) StubRoutines::verify_oop_count_addr()));
@@ -1029,7 +1029,14 @@
     __ andptr(c_rarg2, c_rarg3);
     __ movptr(c_rarg3, (intptr_t) Universe::verify_oop_bits());
     __ cmpptr(c_rarg2, c_rarg3);
-    __ jcc(Assembler::notZero, error);
+    __ jcc(Assembler::zero, in_Java_heap);
+    // Not in Java heap, but could be valid if it's a bufferable value type
+    __ load_klass(c_rarg2, rax);
+    __ movbool(c_rarg2, Address(c_rarg2, InstanceKlass::extra_flags_offset()));
+    __ andptr(c_rarg2, InstanceKlass::_extra_is_bufferable);
+    __ testbool(c_rarg2);
+    __ jcc(Assembler::zero, error);
+    __ bind(in_Java_heap);
 
     // set r12 to heapbase for load_klass()
     __ reinit_heapbase();
--- a/src/cpu/x86/vm/templateInterpreterGenerator_x86.cpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/cpu/x86/vm/templateInterpreterGenerator_x86.cpp	Thu Jun 29 09:35:38 2017 -0400
@@ -686,6 +686,10 @@
   __ movptr(rdx, Address(rdx, ConstMethod::constants_offset()));
   __ movptr(rdx, Address(rdx, ConstantPool::cache_offset_in_bytes()));
   __ push(rdx); // set constant pool cache
+  const Register thread1 = NOT_LP64(rdx) LP64_ONLY(r15_thread);
+  NOT_LP64(__ get_thread(thread1));
+  __ movptr(rdx, Address(thread1, JavaThread::vt_alloc_ptr_offset()));
+  __ push(rdx); // value type allocation pointer when activation is created
   __ push(rlocals); // set locals pointer
   if (native_call) {
     __ push(0); // no bcp
--- a/src/cpu/x86/vm/templateTable_x86.cpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/cpu/x86/vm/templateTable_x86.cpp	Thu Jun 29 09:35:38 2017 -0400
@@ -2104,6 +2104,36 @@
 }
 
 void TemplateTable::branch(bool is_jsr, bool is_wide) {
+  if (ValueTypesThreadLocalRecycling) {
+    Label no_vt_recycling, no_fixing_required;
+    const Register thread1 = NOT_LP64(rbx) LP64_ONLY(r15_thread);
+    NOT_LP64(__ get_thread(thread1));
+    __ movptr(rbx, Address(thread1, in_bytes(JavaThread::vt_alloc_ptr_offset())));
+    __ testptr(rbx, rbx);
+    __ jcc(Assembler::zero, no_vt_recycling);
+    __ movptr(rcx, Address(rbp, frame::interpreter_frame_vt_alloc_ptr_offset * wordSize));
+    __ testptr(rcx, rcx);
+    __ jcc(Assembler::notZero, no_fixing_required);
+    // vt_alloc_ptr in JavaThread is non-null but frame vt_alloc_ptr is null
+    // which means frame vt_alloc_ptr needs to be initialized
+    __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::fix_frame_vt_alloc_ptr));
+    __ movptr(rcx, Address(rbp, frame::interpreter_frame_vt_alloc_ptr_offset * wordSize));
+    __ bind(no_fixing_required);
+    __ testptr(rcx, rbx);
+    __ jcc(Assembler::equal, no_vt_recycling);
+    __ andptr(rcx, VTBufferChunk::chunk_mask());
+    __ movl(rcx, Address(rcx, VTBufferChunk::index_offset()));
+    __ andptr(rbx, VTBufferChunk::chunk_mask());
+    __ movl(rbx, Address(rbx, VTBufferChunk::index_offset()));
+    __ subl(rbx, rcx);
+    __ get_method(rcx);
+    __ movl(rcx, Address(rcx, Method::max_vt_buffer_offset()));
+    __ cmpl(rbx, rcx);
+    __ jcc(Assembler::lessEqual, no_vt_recycling);
+    __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::recycle_buffered_values));
+    __ bind(no_vt_recycling);
+  }
+
   __ get_method(rcx); // rcx holds method
   __ profile_taken_branch(rax, rbx); // rax holds updated MDP, rbx
                                      // holds bumped taken count
@@ -2620,12 +2650,30 @@
     __ bind(skip_register_finalizer);
   }
 
+  if (state == qtos) {
+    const Register thread1 = NOT_LP64(rcx) LP64_ONLY(r15_thread);
+    __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::return_value), rax);
+    NOT_LP64(__ get_thread(thread1));
+    __ get_vm_result(rax, thread1);
+  }
   // Narrow result if state is itos but result type is smaller.
   // Need to narrow in the return bytecode rather than in generate_return_entry
   // since compiled code callers expect the result to already be narrowed.
   if (state == itos) {
     __ narrow(rax);
   }
+
+#ifdef ASSERT
+  if (EnableMVT || EnableValhalla) {
+    if (state == atos) {
+      const Register thread1 = NOT_LP64(rcx) LP64_ONLY(r15_thread);
+      __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::check_areturn), rax);
+      NOT_LP64(__ get_thread(thread1));
+      __ get_vm_result(rax, thread1);
+    }
+  }
+#endif // ASSERT
+
   __ remove_activation(state, rbcp, true, true, true, state == qtos && ValueTypeReturnedAsFields);
 
   __ jmp(rbcp);
@@ -2845,20 +2893,21 @@
   __ jcc(Assembler::notEqual, notValueType);
   // qtos
   if (is_static) {
+    Label initialized;
+    // Issue below if the static field has not been initialized yet
     __ load_heap_oop(rax, field);
+    __ testptr(rax, rax);
+    __ jcc(Assembler::notZero, initialized);
+    __ call_VM(rax, CAST_FROM_FN_PTR(address, InterpreterRuntime::initialize_static_value_field),
+         obj, off);
+    __ verify_oop(rax);
+    __ bind(initialized);
     __ push(qtos);
     //    if (!is_static && !is_vgetfield) {
     //      patch_bytecode(Bytecodes::_fast_qgetfield, bc, rbx);
     //    }
   } else {
-
-    // cp cache entry pointer
-//    __ addptr(cache, in_bytes(ConstantPoolCache::base_offset()));
-//    __ shll(index, LogBytesPerWord);
-//    __ addptr(cache, index);
-
     pop_and_check_object(obj);
-
     call_VM(rax, CAST_FROM_FN_PTR(address, InterpreterRuntime::qgetfield),
         obj, off);
     __ verify_oop(rax);
@@ -3206,7 +3255,9 @@
     } else {
       // Store into the static field
       // Value types in static fields are currently handled with indirection
-      do_oop_store(_masm, field, rax, _bs->kind(), false);
+      // but a copy to the Java heap might be required if the value is currently
+      // stored in a thread local buffer
+      call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::qputstatic), rax);
     }
     __ jmp(Done);
   }
--- a/src/share/vm/classfile/classFileParser.cpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/share/vm/classfile/classFileParser.cpp	Thu Jun 29 09:35:38 2017 -0400
@@ -5635,6 +5635,11 @@
                                              CHECK);
   }
 
+  if (is_value_type()) {
+    ValueKlass* vk = ValueKlass::cast(ik);
+    vk->set_if_bufferable();
+  }
+
   // Valhalla shady value type conversion
   if (_parsed_annotations->is_derive_value_type()) {
     ik->create_derive_value_type(Handle(THREAD, _loader_data->class_loader()),
--- a/src/share/vm/interpreter/interpreterRuntime.cpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/share/vm/interpreter/interpreterRuntime.cpp	Thu Jun 29 09:35:38 2017 -0400
@@ -38,6 +38,7 @@
 #include "memory/oopFactory.hpp"
 #include "memory/resourceArea.hpp"
 #include "memory/universe.inline.hpp"
+#include "memory/vtBuffer.hpp"
 #include "oops/constantPool.hpp"
 #include "oops/instanceKlass.hpp"
 #include "oops/methodData.hpp"
@@ -193,7 +194,7 @@
     instance()->int_field_put(offset, (jint)*((int*)addr));
     break;
   case T_LONG:
-    instance()->long_field_put(offset, (jlong)*((long*)addr)); // Is it correct on 32 and 64 bits?
+    instance()->long_field_put(offset, (jlong)*((long long*)addr));
     break;
   case T_OBJECT:
   case T_ARRAY:
@@ -214,14 +215,10 @@
   vklass->initialize(THREAD);
 
   // Creating value
-  instanceOop value = vklass->allocate_instance(CHECK);
+  bool in_heap;
+  instanceOop value = vklass->allocate_buffered_or_heap_instance(&in_heap, CHECK);
   Handle value_h = Handle(THREAD, value);
 
-  // Zeroing, already performed by allocate_instance() when allocating in the Java Heap
-  // Might need to be performed manually for off-heap allocations
-  // memset(((char*)(oopDesc*)value) + vklass_h->first_field_offset(), 0,
-  //        vklass_h->size_helper() * wordSize - vklass_h->first_field_offset());
-
   thread->set_vm_result(value_h());
 IRT_END
 
@@ -252,11 +249,13 @@
   Handle old_value_h(THREAD, old_value);
 
   // Creating new value by copying the one passed in argument
-  instanceOop new_value = vklass->allocate_instance(CHECK_0);
+  bool in_heap;
+  instanceOop new_value = vklass->allocate_buffered_or_heap_instance(&in_heap,
+      CHECK_((type2size[field_type]) * AbstractInterpreter::stackElementSize));
   Handle new_value_h = Handle(THREAD, new_value);
   int first_offset = vklass->first_field_offset();
-  vklass->value_store(((char*)(oopDesc*)old_value_h()) + first_offset,
-      ((char*)(oopDesc*)new_value_h()) + first_offset, true, false);
+  vklass->value_store(vklass->data_for_oop(old_value_h()),
+      vklass->data_for_oop(new_value_h()), in_heap, false);
 
   // Updating the field specified in arguments
   if (field_type == T_OBJECT || field_type == T_ARRAY) {
@@ -269,8 +268,8 @@
     oop vt_oop = *(oop*)f.interpreter_frame_expression_stack_at(tos_idx);
     assert(vt_oop != NULL && vt_oop->is_oop() && vt_oop->is_value(),"argument must be a value type");
     assert(field_vk == vt_oop->klass(), "Must match");
-    field_vk->value_store(((char*)(oopDesc*)vt_oop + field_vk->first_field_offset()),
-            ((char*)(oopDesc*)new_value_h()) + fd.offset(), true, false);
+    field_vk->value_store(field_vk->data_for_oop(vt_oop),
+        ((char*)(oopDesc*)new_value_h()) + fd.offset(), true, false);
   } else {
     intptr_t* addr = f.interpreter_frame_expression_stack_at(tos_idx);
     copy_primitive_argument(addr, new_value_h, fd.offset(), field_type);
@@ -300,11 +299,10 @@
   if (vtklass->get_vcc_klass() != target_klass) {
     THROW_MSG(vmSymbols::java_lang_ClassCastException(), "vbox target is not derive value type box");
   }
-
-  oop boxed = vtklass->derive_value_type_copy(Handle(THREAD, value),
-                                              InstanceKlass::cast(target_klass),
-                                              CHECK);
-  thread->set_vm_result(boxed);
+  oop box = vtklass->box(Handle(THREAD, value),
+                         InstanceKlass::cast(target_klass),
+                         CHECK);
+  thread->set_vm_result(box);
 IRT_END
 
 IRT_ENTRY(void, InterpreterRuntime::vunbox(JavaThread* thread, ConstantPool* pool, int index, oopDesc* obj))
@@ -323,9 +321,9 @@
     if (klass != InstanceKlass::cast(target_klass)->get_vcc_klass()) {
     THROW_MSG(vmSymbols::java_lang_ClassCastException(), "vunbox target is not derive value type");
   }
-  oop value = ValueKlass::cast(target_klass)->derive_value_type_copy(Handle(THREAD, obj),
-                                                                 InstanceKlass::cast(target_klass),
-                                                                 CHECK);
+  oop value = ValueKlass::cast(target_klass)->unbox(Handle(THREAD, obj),
+                                                InstanceKlass::cast(target_klass),
+                                                CHECK);
   thread->set_vm_result(value);
 IRT_END
 
@@ -337,26 +335,88 @@
   klass->find_field_from_offset(offset, false, &fd);
   Klass* field_k = klass->get_value_field_klass(fd.index());
   ValueKlass* field_vklass = ValueKlass::cast(field_k);
+  field_vklass->initialize(THREAD);
+
   // allocate instance
+  bool in_heap;
+  instanceOop res = field_vklass->allocate_buffered_or_heap_instance(&in_heap, CHECK);
+  instanceHandle res_h(THREAD, res);
+  // copy value
+  field_vklass->value_store(((char*)(oopDesc*)value_h()) + offset,
+                            field_vklass->data_for_oop(res), in_heap, false);
+  thread->set_vm_result(res_h());
+IRT_END
+
+IRT_ENTRY(void, InterpreterRuntime::initialize_static_value_field(JavaThread* thread, oopDesc* mirror, int offset))
+  instanceHandle mirror_h(THREAD, (instanceOop)mirror);
+  InstanceKlass* klass = InstanceKlass::cast(java_lang_Class::as_Klass(mirror));
+  assert(mirror->obj_field(offset) == NULL,"Field must not be initialized twice");
+
+  fieldDescriptor fd;
+  klass->find_field_from_offset(offset, true, &fd);
+  Klass* field_k = klass->get_value_field_klass(fd.index());
+  ValueKlass* field_vklass = ValueKlass::cast(field_k);
+  // allocate instance, because it is going to be assigned to a static field
+  // it must not be a buffered value
   instanceOop res = field_vklass->allocate_instance(CHECK);
-  // copy value
-  int size = field_vklass->layout_helper_size_in_bytes(field_vklass->layout_helper());
-  field_vklass->value_store(((char*)(oopDesc*)value_h()) + offset,
-    ((char*)(oopDesc*)res) + field_vklass->first_field_offset(),true, false);
-  thread->set_vm_result(res);
+  instanceHandle res_h(THREAD, res);
+  mirror_h()->obj_field_put(offset, res_h());
+  thread->set_vm_result(res_h());
 IRT_END
 
 IRT_ENTRY(void, InterpreterRuntime::qputfield(JavaThread* thread, oopDesc* obj, oopDesc* value, int offset))
   Handle value_h(THREAD, value);
   Handle obj_h(THREAD, obj);
+  assert(!obj_h()->klass()->is_value(), "obj must be an object");
+  assert(value_h()->klass()->is_value(), "value must be an value type");
 
-  InstanceKlass* klass_h = InstanceKlass::cast(obj->klass());
+  InstanceKlass* klass = InstanceKlass::cast(obj->klass());
+  fieldDescriptor fd;
+  klass->find_field_from_offset(offset, false, &fd);
+  Klass* field_k = klass->get_value_field_klass(fd.index());
   ValueKlass* field_vklass = ValueKlass::cast(value->klass());
+  assert(field_k == field_vklass, "Field descriptor and argument must match");
+  // copy value
+  field_vklass->value_store(field_vklass->data_for_oop(value_h()),
+                            ((char*)(oopDesc*)obj_h()) + offset, true, false);
+IRT_END
 
-  // copy value
-  int size = field_vklass->layout_helper_size_in_bytes(field_vklass->layout_helper());
-  field_vklass->value_store(((char*)(oopDesc*)value_h()) + field_vklass->first_field_offset(),
-                            ((char*)(oopDesc*)obj_h()) + offset, true, false);
+IRT_ENTRY(void, InterpreterRuntime::qputstatic(JavaThread* thread, oopDesc* value))
+  instanceHandle value_h(THREAD, (instanceOop)value);
+  assert(value_h()->is_value(), "qputstatic only deals with value arguments");
+  Method* m = last_frame(thread).interpreter_frame_method();
+  jint bci = last_frame(thread).interpreter_frame_bci();
+  assert(m->code_at(bci) == Bytecodes::_putstatic, "qputstatic is a particular case of putstatic");
+  ConstantPoolCache* cp_cache = last_frame(thread).interpreter_frame_method()->constants()->cache();
+  int index = ConstantPool::decode_cpcache_index(get_index_u2_cpcache(thread, Bytecodes::_putstatic));
+  ConstantPoolCacheEntry* cp_entry = cp_cache->entry_at(index);
+  assert(cp_entry->is_field_entry(), "Sanity check");
+
+  InstanceKlass* klass = InstanceKlass::cast(cp_entry->f1_as_klass());
+  int offset = cp_entry->f2_as_index();
+  oop mirror = klass->java_mirror();
+
+  if (Universe::heap()->is_in_reserved(value_h())) {
+    mirror->obj_field_put(offset, value_h());
+  } else {
+    // The argument is a buffered value, a copy must be created in the Java heap
+    // because a static field cannot point to a thread-local buffered value
+    fieldDescriptor fd;
+    klass->find_field_from_offset(offset, false, &fd);
+    Klass* field_k = klass->get_value_field_klass(fd.index());
+    ValueKlass* field_vklass = ValueKlass::cast(field_k);
+    assert(field_vklass == value->klass(), "Field descriptor and argument must match");
+    // allocate heap instance
+    instanceOop res = field_vklass->allocate_instance(CHECK);
+    assert(Universe::heap()->is_in_reserved(res), "Must be in the Java heap");
+    instanceHandle res_h(THREAD, res);
+    // copy value
+    field_vklass->value_store(field_vklass->data_for_oop(value_h()),
+                              field_vklass->data_for_oop(res), true, false);
+    // writing static field
+    mirror->obj_field_put(offset, res_h());
+    assert(mirror->obj_field(offset) != NULL,"Sanity check");
+  }
 IRT_END
 
 IRT_ENTRY(void, InterpreterRuntime::newarray(JavaThread* thread, BasicType type, jint size))
@@ -382,14 +442,14 @@
     thread->set_vm_result(((objArrayOop) array)->obj_at(index));
   }
   else {
-    // Early prototype: we don't have valorind support...just allocate aref and copy
     ValueArrayKlass* vaklass = ValueArrayKlass::cast(klass);
     ValueKlass* vklass = vaklass->element_klass();
     arrayHandle ah(THREAD, array);
-    instanceOop value_holder = vklass->allocate_instance(CHECK);
+    bool in_heap;
+    instanceOop value_holder = vklass->allocate_buffered_or_heap_instance(&in_heap, CHECK);
     void* src = ((valueArrayOop)ah())->value_at_addr(index, vaklass->layout_helper());
     vklass->value_store(src, vklass->data_for_oop(value_holder),
-                          vaklass->element_byte_size(), true, true);
+                          vaklass->element_byte_size(), in_heap, false);
     thread->set_vm_result(value_holder);
   }
 IRT_END
@@ -402,9 +462,25 @@
     THROW(vmSymbols::java_lang_ArrayStoreException());
   }
   if (klass->is_objArray_klass()) {
+    if(!Universe::heap()->is_in_reserved(val)) {
+      // A Java heap allocated copy must be made because an array cannot
+      // reference a thread-local buffered value
+      Handle val_h(THREAD, (oop)val);
+      ObjArrayKlass* aklass = ObjArrayKlass::cast(klass);
+      Klass* eklass = aklass->element_klass();
+      assert(eklass->is_value(), "Sanity check");
+      assert(eklass == ((oop)val)->klass(), "Sanity check");
+      ValueKlass* vklass = ValueKlass::cast(eklass);
+      // allocate heap instance
+      instanceOop res = vklass->allocate_instance(CHECK);
+      Handle res_h(THREAD, res);
+      // copy value
+      vklass->value_store(((char*)(oopDesc*)val_h()) + vklass->first_field_offset(),
+                            ((char*)(oopDesc*)res_h()) + vklass->first_field_offset(),true, false);
+      val = res_h();
+    }
     ((objArrayOop) array)->obj_at_put(index, (oop)val);
-  }
-  else {
+  } else {
     valueArrayOop varray = (valueArrayOop)array;
     ValueArrayKlass* vaklass = ValueArrayKlass::cast(klass);
     ValueKlass* vklass = vaklass->element_klass();
@@ -444,6 +520,58 @@
   thread->set_vm_result(obj);
 IRT_END
 
+IRT_ENTRY(void, InterpreterRuntime::recycle_vtbuffer(JavaThread* thread))
+  VTBuffer::recycle_vtbuffer(thread, last_frame(thread));
+IRT_END
+
+IRT_ENTRY(void, InterpreterRuntime::recycle_buffered_values(JavaThread* thread))
+  frame f = thread->last_frame();
+  assert(f.is_interpreted_frame(), "recycling can only be triggered from interpreted frames");
+  VTBuffer::recycle_vt_in_frame(thread, &f);
+IRT_END
+
+IRT_ENTRY(void, InterpreterRuntime::fix_frame_vt_alloc_ptr(JavaThread* thread))
+  frame f = thread->last_frame();
+  VTBuffer::fix_frame_vt_alloc_ptr(f, VTBufferChunk::chunk(thread->vt_alloc_ptr()));
+IRT_END
+
+IRT_ENTRY(void, InterpreterRuntime::return_value(JavaThread* thread, oopDesc* obj))
+  if (Universe::heap()->is_in_reserved(obj)) {
+    thread->set_vm_result(obj);
+    return;
+  }
+  assert(obj->klass()->is_value(), "Sanity check");
+  ValueKlass* vk = ValueKlass::cast(obj->klass());
+  RegisterMap reg_map(thread, false);
+  frame current_frame = last_frame(thread);
+  frame caller_frame = current_frame.sender(&reg_map);
+  if (!caller_frame.is_interpreted_frame()) {
+    // caller is not an interpreted frame, creating a new value in Java heap
+    Handle obj_h(THREAD, obj);
+    instanceOop res = vk->allocate_instance(CHECK);
+    Handle res_h(THREAD, res);
+    // copy value
+    vk->value_store(vk->data_for_oop(obj_h()),
+                    vk->data_for_oop(res_h()), true, false);
+    thread->set_vm_result(res_h());
+    return;
+  } else {
+    oop dest = VTBuffer::relocate_return_value(thread, current_frame, obj);
+    thread->set_vm_result(dest);
+  }
+IRT_END
+
+IRT_ENTRY(void, InterpreterRuntime::check_areturn(JavaThread* thread, oopDesc* obj))
+  if (obj != NULL) {
+    Klass* k = obj->klass();
+    if (k->is_value()) {
+      ResourceMark rm(thread);
+      tty->print_cr("areturn used on a value from %s", k->name()->as_C_string());
+    }
+    assert(!k->is_value(), "areturn should never be used on values");
+  }
+  thread->set_vm_result(obj);
+IRT_END
 
 IRT_ENTRY(void, InterpreterRuntime::register_finalizer(JavaThread* thread, oopDesc* obj))
   assert(obj->is_oop(), "must be a valid oop");
@@ -974,7 +1102,8 @@
     Symbol* signature = call.signature();
     receiver = Handle(thread,
                   thread->last_frame().interpreter_callee_receiver(signature));
-    assert(Universe::heap()->is_in_reserved_or_null(receiver()),
+    assert(Universe::heap()->is_in_reserved_or_null(receiver())
+           || VTBuffer::is_in_vt_buffer(receiver()),
            "sanity check");
     assert(receiver.is_null() ||
            !Universe::heap()->is_in_reserved(receiver->klass()),
--- a/src/share/vm/interpreter/interpreterRuntime.hpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/share/vm/interpreter/interpreterRuntime.hpp	Thu Jun 29 09:35:38 2017 -0400
@@ -91,6 +91,15 @@
   static int     vwithfield    (JavaThread* thread, ConstantPoolCache* cp_cache);
   static void    qgetfield     (JavaThread* thread, oopDesc* value, int offset);
   static void    qputfield     (JavaThread* thread, oopDesc* obj, oopDesc* value, int offset);
+  static void    qputstatic    (JavaThread* thread, oopDesc* value);
+  static void    initialize_static_value_field(JavaThread*, oopDesc* mirror, int offset);
+
+  // Value Buffers support
+  static void    recycle_vtbuffer(JavaThread* thread);
+  static void    recycle_buffered_values(JavaThread* thread);
+  static void    return_value(JavaThread* thread, oopDesc* obj);
+  static void    check_areturn(JavaThread* thread, oopDesc* obj);
+  static void    fix_frame_vt_alloc_ptr(JavaThread* thread);
 
   // vaload/vastore
   static void value_array_load(JavaThread* thread, arrayOopDesc* array, int index);
--- a/src/share/vm/interpreter/oopMapCache.cpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/share/vm/interpreter/oopMapCache.cpp	Thu Jun 29 09:35:38 2017 -0400
@@ -439,7 +439,7 @@
 
 
 OopMapCache::OopMapCache() :
-  _mut(Mutex::leaf, "An OopMapCache lock", true)
+  _mut(Mutex::leaf, "An OopMapCache lock", true, Monitor::_safepoint_check_never)
 {
   _array  = NEW_C_HEAP_ARRAY(OopMapCacheEntry, _size, mtClass);
   // Cannot call flush for initialization, since flush
@@ -484,7 +484,7 @@
 void OopMapCache::lookup(const methodHandle& method,
                          int bci,
                          InterpreterOopMap* entry_for) const {
-  MutexLocker x(&_mut);
+  MutexLockerEx x(&_mut, Mutex::_no_safepoint_check_flag);
 
   OopMapCacheEntry* entry = NULL;
   int probe = hash_value_for(method, bci);
--- a/src/share/vm/logging/logTag.hpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/share/vm/logging/logTag.hpp	Thu Jun 29 09:35:38 2017 -0400
@@ -141,6 +141,7 @@
   LOG_TAG(vmoperation) \
   LOG_TAG(vtables) \
   LOG_TAG(workgang) \
+  LOG_TAG(valuetypes) \
   LOG_TAG_LIST_EXT
 
 #define PREFIX_LOG_TAG(T) (LogTag::_##T)
--- a/src/share/vm/memory/allocation.hpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/share/vm/memory/allocation.hpp	Thu Jun 29 09:35:38 2017 -0400
@@ -145,8 +145,9 @@
   mtLogging           = 0x0F,  // memory for logging
   mtArguments         = 0x10,  // memory for argument processing
   mtModule            = 0x11,  // memory for module processing
-  mtNone              = 0x12,  // undefined
-  mt_number_of_types  = 0x13   // number of memory types (mtDontTrack
+  mtValueTypes        = 0x12,  // memory for buffered value types
+  mtNone              = 0x13,  // undefined
+  mt_number_of_types  = 0x14   // number of memory types (mtDontTrack
                                  // is not included as validate type)
 };
 
@@ -753,4 +754,10 @@
   static void free(E* addr, size_t length);
 };
 
+template <class E, MEMFLAGS F> class CMmapObj ALLOCATION_SUPER_CLASS_SPEC {
+public:
+  void* operator new(size_t size);
+  void  operator delete(void* address, size_t length);
+};
+
 #endif // SHARE_VM_MEMORY_ALLOCATION_HPP
--- a/src/share/vm/memory/allocation.inline.hpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/share/vm/memory/allocation.inline.hpp	Thu Jun 29 09:35:38 2017 -0400
@@ -268,4 +268,29 @@
   }
 }
 
+template <class E, MEMFLAGS F> void* CMmapObj<E,F>::operator new(size_t size) {
+  int alignment = os::vm_allocation_granularity();
+
+  int pages = (int)(size / os::vm_page_size());
+  if (pages == 0 || size % (pages * os::vm_page_size()) != 0) pages++;
+  size_t mmap_size = pages * os::vm_page_size();
+
+  char* addr = os::reserve_memory(mmap_size, NULL, alignment, F);
+  if (addr == NULL) {
+    vm_exit_out_of_memory(size, OOM_MMAP_ERROR, "VTBuffer (reserve)");
+  }
+
+  os::commit_memory_or_exit(addr, mmap_size, !ExecMem, "VTBuffer (commit)");
+
+  return addr;
+}
+
+template <class E, MEMFLAGS F> void CMmapObj<E, F>::operator delete(void* addr, size_t length) {
+  int pages = (int)(length / os::vm_page_size());
+  if (pages == 0 || length % (pages * os::vm_page_size()) != 0) pages++;
+  size_t mmap_size = pages * os::vm_page_size();
+  bool result = os::release_memory((char*)addr, mmap_size);
+  assert(result, "Failed to release memory");
+}
+
 #endif // SHARE_VM_MEMORY_ALLOCATION_INLINE_HPP
--- a/src/share/vm/memory/iterator.hpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/share/vm/memory/iterator.hpp	Thu Jun 29 09:35:38 2017 -0400
@@ -128,6 +128,11 @@
                                  _wrapped_closure->do_oop(p);}
 };
 
+class BufferedValueClosure : public Closure {
+public:
+  virtual void do_buffered_value(oop* p) = 0;
+};
+
 class KlassClosure : public Closure {
  public:
   virtual void do_klass(Klass* k) = 0;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/vm/memory/vtBuffer.cpp	Thu Jun 29 09:35:38 2017 -0400
@@ -0,0 +1,474 @@
+/*
+ * Copyright (c) 2016, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "gc/shared/gcLocker.hpp"
+#include "memory/vtBuffer.hpp"
+#include "oops/oop.inline.hpp"
+#include "oops/valueKlass.hpp"
+#include "runtime/frame.hpp"
+#include "runtime/thread.hpp"
+#include "utilities/globalDefinitions.hpp"
+#include "utilities/ticks.hpp"
+#include "utilities/ticks.inline.hpp"
+
+VTBufferChunk* VTBuffer::_free_list = NULL;
+Mutex* VTBuffer::_pool_lock = new Mutex(Mutex::leaf, "VTBuffer::_pool_lock", true, Monitor::_safepoint_check_never);
+int VTBuffer::_pool_counter = 0;
+int VTBuffer::_max_pool_counter = 0;
+int VTBuffer::_total_allocated = 0;
+int VTBuffer::_total_deallocated = 0;
+int VTBuffer::_total_failed = 0;
+
+oop VTBuffer::allocate_value(ValueKlass* k, TRAPS) {
+  assert(THREAD->is_Java_thread(), "Only JavaThreads have a buffer for value types");
+  JavaThread* thread = (JavaThread*)THREAD;
+  if (thread->vt_alloc_ptr() == NULL) {
+    if (!allocate_vt_chunk(thread)) {
+      return NULL; // will trigger fall back strategy: allocation in Java heap
+    }
+  }
+  assert(thread->vt_alloc_ptr() != NULL, "should not be null if chunk allocation was successful");
+  int size_in_bytes = k->size_helper() * wordSize;
+  if ((char*)thread->vt_alloc_ptr() + size_in_bytes  >= thread->vt_alloc_limit()) {
+    if (size_in_bytes > (int)VTBufferChunk::max_alloc_size()) {
+      // Too big to be allocated in a buffer
+      return NULL;
+    }
+    if (!allocate_vt_chunk(thread)) {
+      return NULL; // will trigger fall back strategy: allocation in Java heap
+    }
+  }
+  assert((char*)thread->vt_alloc_ptr() + size_in_bytes < thread->vt_alloc_limit(),"otherwise the logic above is wrong");
+  oop new_vt = (oop)thread->vt_alloc_ptr();
+  int size_in_words = k->size_helper();
+  thread->increment_vtchunk_total_memory_buffered(size_in_words * HeapWordSize);
+  int increment = align_object_size(size_in_words);
+  void* new_ptr = (char*)thread->vt_alloc_ptr() + increment * HeapWordSize;
+  new_ptr = MIN2(new_ptr, thread->vt_alloc_limit());
+  assert(VTBufferChunk::chunk(new_ptr) == VTBufferChunk::chunk(thread->vt_alloc_ptr()),
+      "old and new alloc ptr must be in the same chunk");
+  thread->set_vt_alloc_ptr(new_ptr);
+  // the value and its header must be initialized before being returned!!!
+  memset(((char*)(oopDesc*)new_vt), 0, size_in_bytes);
+  new_vt->set_klass(k);
+  new_vt->set_mark(markOop(k->java_mirror()));
+  return new_vt;
+}
+
+bool VTBuffer::allocate_vt_chunk(JavaThread* thread) {
+  VTBufferChunk* new_chunk = NULL;
+  // Trying local cache;
+  if (thread->local_free_chunk() != NULL) {
+    new_chunk = thread->local_free_chunk();
+    thread->set_local_free_chunk(NULL);
+  } else {
+    // Trying global pool
+    MutexLockerEx ml(_pool_lock, Mutex::_no_safepoint_check_flag);
+    if (_free_list != NULL) {
+      new_chunk = _free_list;
+      _free_list = new_chunk->next();
+      if (_free_list != NULL) {
+        _free_list->set_prev(NULL);
+      }
+      new_chunk->set_next(NULL);
+      _pool_counter--;
+    } else {
+      // A new chunk has to be allocated
+      // Hold _pool_lock to maintain counters
+      if ((_total_allocated + 1) <= ValueTypesBufferMaxMemory) {
+        // Allocate new chunk only if total size for buffer
+        // memory is below its max size
+        new_chunk = new VTBufferChunk(thread);
+        _total_allocated += new_chunk == NULL ? 0 : 1;
+      }
+    }
+  }
+  if (new_chunk == NULL) {
+    _total_failed++;
+    thread->increment_vtchunk_failed();
+    return false; // allocation failed
+  }
+  VTBufferChunk* current = thread->current_chunk();
+  assert(new_chunk->owner() == thread || new_chunk->owner()== NULL, "Sanity check");
+  assert(new_chunk->index() == -1, "Sanity check");
+  new_chunk->set_owner(thread);
+  if(current != NULL) {
+    new_chunk->set_prev(current);
+    new_chunk->set_index(current->index() + 1);
+    current->set_next(new_chunk);
+  } else {
+    new_chunk->set_index(0);
+  }
+  thread->increment_vtchunk_in_use();
+  thread->set_vt_alloc_ptr(new_chunk->first_alloc());
+  thread->set_vt_alloc_limit(new_chunk->alloc_limit());
+  return true; // allocation was successful
+}
+
+void VTBuffer::recycle_chunk(JavaThread* thread, VTBufferChunk* chunk) {
+  if (thread->local_free_chunk() == NULL) {
+    chunk->set_prev(NULL);
+    chunk->set_next(NULL);
+    chunk->set_index(-1);
+    thread->set_local_free_chunk(chunk);
+  } else {
+    return_vt_chunk(thread, chunk);
+  }
+  thread->decrement_vtchunk_in_use();
+}
+
+// This is the main way to recycle VTBuffer memory, it is called from
+// remove_activation() when an interpreter frame is about to be removed
+// from the stack. All memory used in the context of this frame is freed,
+// and the vt_alloc_ptr is restored to the value it had when the frame
+// was created (modulo a possible adjustment if a value is being returned)
+void VTBuffer::recycle_vtbuffer(JavaThread* thread, frame current_frame) {
+  address current_ptr = (address)thread->vt_alloc_ptr();
+  assert(current_ptr != NULL, "Should not reach here if NULL");
+  VTBufferChunk* current_chunk = VTBufferChunk::chunk(current_ptr);
+  assert(current_chunk->owner() == thread, "Sanity check");
+  address previous_ptr = (address)current_frame.interpreter_frame_vt_alloc_ptr();
+  if (previous_ptr == NULL) {
+    // vt_alloc_ptr has not been initialized in this frame
+    // let's initialize it to the first_alloc() value of the first chunk
+    VTBufferChunk* first_chunk = current_chunk;
+    while (first_chunk->prev() != NULL) {
+      first_chunk = first_chunk->prev();
+    }
+    previous_ptr = (address)first_chunk->first_alloc();
+  }
+  assert(previous_ptr != NULL, "Should not reach here if NULL");
+  VTBufferChunk* previous_chunk = VTBufferChunk::chunk(previous_ptr);
+  assert(previous_chunk->owner() == thread, "Sanity check");
+  if (current_ptr == previous_ptr) return;
+  assert(current_chunk != previous_chunk || current_ptr >= previous_ptr, "Sanity check");
+  VTBufferChunk* del = previous_chunk->next();
+  previous_chunk->set_next(NULL);
+  thread->set_vt_alloc_ptr(previous_ptr);
+  thread->set_vt_alloc_limit(previous_chunk->alloc_limit());
+  while (del != NULL) {
+    VTBufferChunk* temp = del->next();
+    VTBuffer::recycle_chunk(thread, del);
+    del = temp;
+  }
+}
+
+void VTBuffer::return_vt_chunk(JavaThread* thread, VTBufferChunk* chunk) {
+  chunk->set_prev(NULL);
+  chunk->set_owner(NULL);
+  chunk->set_index(-1);
+  MutexLockerEx ml(_pool_lock, Mutex::_no_safepoint_check_flag);
+  if (_pool_counter < _max_free_list) {
+    if (_free_list != NULL) {
+      chunk->set_next(_free_list);
+      _free_list->set_prev(chunk);
+      _free_list = chunk;
+    } else {
+      chunk->set_next(NULL);
+      _free_list = chunk;
+    }
+    _pool_counter++;
+    if (_pool_counter > _max_pool_counter) {
+      _max_pool_counter = _pool_counter;
+    }
+  } else {
+    delete chunk;
+    _total_deallocated++;
+  }
+  thread->increment_vtchunk_returned();
+}
+
+bool VTBuffer::value_belongs_to_frame(oop p, frame* f) {
+  // the code below assumes that frame f is the last interpreted frame
+  // on the execution stack
+  int p_chunk_idx = VTBufferChunk::chunk(p)->index();
+  int frame_first_chunk_idx;
+  if (f->interpreter_frame_vt_alloc_ptr() != NULL) {
+    frame_first_chunk_idx = VTBufferChunk::chunk(f->interpreter_frame_vt_alloc_ptr())->index();
+  } else {
+    frame_first_chunk_idx = 0;
+  }
+  if (p_chunk_idx == frame_first_chunk_idx) {
+    return (intptr_t*)p >= f->interpreter_frame_vt_alloc_ptr();
+  } else {
+    return  p_chunk_idx > frame_first_chunk_idx;
+  }
+
+}
+
+void VTBuffer::fix_frame_vt_alloc_ptr(frame f, VTBufferChunk* chunk) {
+  assert(f.is_interpreted_frame(), "recycling can only be triggered from interpreted frames");
+  assert(chunk != NULL, "Should not be called if null");
+  while (chunk->prev() != NULL) {
+    chunk = chunk->prev();
+  }
+  f.interpreter_frame_set_vt_alloc_ptr((intptr_t*)chunk->first_alloc());
+}
+
+extern "C" {
+  static int compare_reloc_entries(const void* void_a, const void* void_b) {
+    struct VT_relocation_entry* entry_a = (struct VT_relocation_entry*)void_a;
+    struct VT_relocation_entry* entry_b = (struct VT_relocation_entry*)void_b;
+    if (entry_a->chunk_index == entry_b->chunk_index) {
+      if (entry_a->old_ptr < entry_b->old_ptr) {
+        return -1;
+      } else {
+        return 1;
+      }
+    } else {
+      if (entry_a->chunk_index < entry_b->chunk_index) {
+        return -1;
+      } else {
+        return 1;
+      }
+    }
+  }
+}
+
+void dump_reloc_table(struct VT_relocation_entry* table, int nelem, bool print_new_ptr) {
+  ResourceMark rm;
+  for (int i = 0; i < nelem; i++) {
+	  InstanceKlass* ik = InstanceKlass::cast(((oop)table[i].old_ptr)->klass());
+    tty->print("%d:\t%p\t%d\t%s\t%x", i, table[i].old_ptr, table[i].chunk_index,
+    		ik->name()->as_C_string(), ik->size_helper() * HeapWordSize);
+    if (print_new_ptr) {
+    	tty->print_cr("\t%p\t%d\n", table[i].new_ptr, VTBufferChunk::chunk(table[i].new_ptr)->index());
+    } else {
+    	tty->print_cr("");
+    }
+  }
+}
+
+// Relocate value 'old' after value 'previous'
+address VTBuffer::relocate_value(address old, address previous, int previous_size_in_words) {
+  InstanceKlass* ik_old = InstanceKlass::cast(((oop)old)->klass());
+  assert(ik_old->is_value(), "Sanity check");
+  VTBufferChunk* chunk = VTBufferChunk::chunk(previous);
+  address next_alloc = previous + align_object_size(ik_old->size_helper());
+  if(next_alloc + ik_old->size_helper() * HeapWordSize < chunk->alloc_limit()) {
+    // relocation can be performed in the same chunk
+    return previous + align_object_size(previous_size_in_words) * HeapWordSize;
+  } else {
+    // relocation must be performed in the next chunk
+    VTBufferChunk* next_chunk = chunk->next();
+    assert(next_chunk != NULL, "Because we are compacting, there should be enough in use chunks");
+    return (address)next_chunk->first_alloc();
+  }
+}
+
+oop VTBuffer::relocate_return_value(JavaThread* thread, frame current_frame, oop obj) {
+  assert(!Universe::heap()->is_in_reserved(obj), "This method should never be called on Java heap allocated values");
+  assert(obj->klass()->is_value(), "Sanity check");
+  ValueKlass* vk = ValueKlass::cast(obj->klass());
+  address current_ptr = (address)thread->vt_alloc_ptr();
+  VTBufferChunk* current_chunk = VTBufferChunk::chunk(current_ptr);
+  address previous_ptr = (address)current_frame.interpreter_frame_vt_alloc_ptr();
+  if (previous_ptr == NULL) {
+    fix_frame_vt_alloc_ptr(current_frame, current_chunk);
+    previous_ptr = (address)current_frame.interpreter_frame_vt_alloc_ptr();
+  }
+  VTBufferChunk* previous_chunk = VTBufferChunk::chunk(previous_ptr);
+  address dest;
+  if ((address)obj != previous_ptr) {
+    if (previous_chunk == current_chunk
+        || (previous_ptr + vk->size_helper() * wordSize) < previous_chunk->alloc_limit()) {
+      dest = previous_ptr;
+    } else {
+      assert(previous_chunk->next() != NULL, "Should not happen");
+      dest = (address)previous_chunk->next()->first_alloc();
+    }
+    // Copying header
+    memcpy(dest, obj, vk->first_field_offset());
+    // Copying value content
+    vk->value_store(((char*)(address)obj) + vk->first_field_offset(),
+                    dest + vk->first_field_offset(), false, true);
+  } else {
+    dest = (address)obj;
+  }
+  address new_alloc_ptr = dest + vk->size_helper() * wordSize;
+  current_frame.interpreter_frame_set_vt_alloc_ptr((intptr_t*)new_alloc_ptr);
+  VTBufferChunk* last = VTBufferChunk::chunk(dest);
+  VTBufferChunk* del = last->next();
+  thread->set_vt_alloc_ptr(new_alloc_ptr);
+  thread->set_vt_alloc_limit(last->alloc_limit());
+  last->set_next(NULL);
+  while (del != NULL) {
+    VTBufferChunk* tmp = del->next();
+    VTBuffer::recycle_chunk(thread, del);
+    del = tmp;
+  }
+  return (oop)dest;
+}
+
+// This method is called to recycle VTBuffer memory when the VM has detected
+// that too much memory is being consumed in the current frame context. This
+// can only happen when the method contains at least one loop in which new
+// values are created.
+void VTBuffer::recycle_vt_in_frame(JavaThread* thread, frame* f) {
+  Ticks begin, end;
+  Ticks step1, step2, step3, step4, step5, step6, step7;
+  int returned_chunks = 0;
+
+  if (ReportVTBufferRecyclingTimes) {
+    begin = Ticks::now();
+  }
+  assert(f->is_interpreted_frame(), "only interpreted frames are using VT buffering so far");
+  ResourceMark rm(thread);
+
+  // 1 - allocate relocation table
+  Method* m = f->interpreter_frame_method();
+  int max_entries = m->max_locals() + m->max_stack();
+  VT_relocation_entry* reloc_table = NEW_RESOURCE_ARRAY_IN_THREAD(thread, struct VT_relocation_entry, max_entries);
+  int n_entries = 0;
+  if (ReportVTBufferRecyclingTimes) {
+    step1 = Ticks::now();
+  }
+
+  {
+    // No GC should occur during the phases 2->5
+    // either because the mark word (usually containing the pointer
+    // to the Java mirror) is used for marking, or because the values are being relocated
+    NoSafepointVerifier nsv;
+
+    // 2 - marking phase + populate relocation table
+    BufferedValuesMarking marking_closure = BufferedValuesMarking(f, reloc_table, max_entries, &n_entries);
+    f->buffered_values_interpreted_do(&marking_closure);
+    if (ReportVTBufferRecyclingTimes) {
+      step2 = Ticks::now();
+    }
+
+    if (n_entries > 0) {
+      // 3 - sort relocation table entries and compute compaction
+      qsort(reloc_table, n_entries, sizeof(struct VT_relocation_entry), compare_reloc_entries);
+      if (f->interpreter_frame_vt_alloc_ptr() == NULL) {
+        VTBufferChunk* chunk = VTBufferChunk::chunk(reloc_table[0].old_ptr);
+        while (chunk->prev() != NULL) chunk = chunk->prev();
+        //f->interpreter_frame_set_vt_alloc_ptr((intptr_t*)chunk->first_alloc());
+        reloc_table[0].new_ptr = (address)chunk->first_alloc();
+      } else {
+        reloc_table[0].new_ptr = (address)f->interpreter_frame_vt_alloc_ptr();
+      }
+      ((oop)reloc_table[0].old_ptr)->set_mark((markOop)reloc_table[0].new_ptr);
+      for (int i = 1; i < n_entries; i++) {
+        reloc_table[i].new_ptr = relocate_value(reloc_table[i].old_ptr, reloc_table[i-1].new_ptr,
+            InstanceKlass::cast(((oop)reloc_table[i-1].old_ptr)->klass())->size_helper());
+        ((oop)reloc_table[i].old_ptr)->set_mark((markOop)reloc_table[i].new_ptr);
+      }
+      if (ReportVTBufferRecyclingTimes) {
+        step3 = Ticks::now();
+      }
+
+      // 4 - update pointers
+      BufferedValuesPointersUpdate update_closure = BufferedValuesPointersUpdate(f);
+      f->buffered_values_interpreted_do(&update_closure);
+      if (ReportVTBufferRecyclingTimes) {
+        step4 = Ticks::now();
+      }
+
+      // 5 - relocate values
+      for (int i = 0; i < n_entries; i++) {
+        if (reloc_table[i].old_ptr != reloc_table[i].new_ptr) {
+          InstanceKlass* ik_old = InstanceKlass::cast(((oop)reloc_table[i].old_ptr)->klass());
+          // instead of memcpy, a value_store() might be required here
+          memcpy(reloc_table[i].new_ptr, reloc_table[i].old_ptr, ik_old->size_helper() * HeapWordSize);
+        }
+        // Resetting the mark word
+        ((oop)reloc_table[i].new_ptr)->set_mark(markOop(((oop)reloc_table[i].new_ptr)->klass()->java_mirror()));
+      }
+      if (ReportVTBufferRecyclingTimes) {
+        step5 = Ticks::now();
+      }
+
+      // 6 - update thread allocation pointer
+      oop last_oop = (oop)reloc_table[n_entries - 1].new_ptr;
+      InstanceKlass* ik = InstanceKlass::cast(last_oop->klass());
+      thread->set_vt_alloc_ptr((address)last_oop + ik->size_helper() * HeapWordSize);
+      thread->set_vt_alloc_limit(VTBufferChunk::chunk(thread->vt_alloc_ptr())->alloc_limit());
+      if (ReportVTBufferRecyclingTimes) {
+        step6 = Ticks::now();
+      }
+
+      // 7 - free/return unused chunks
+      VTBufferChunk* chunk = VTBufferChunk::chunk(reloc_table[n_entries - 1].new_ptr);
+      VTBufferChunk* temp = chunk;
+      chunk = chunk->next();
+      temp->set_next(NULL);
+      while (chunk != NULL) {
+        returned_chunks++;
+        temp = chunk->next();
+        VTBuffer::recycle_chunk(thread, chunk);
+        chunk = temp;
+      }
+      if (ReportVTBufferRecyclingTimes) {
+        step7 = Ticks::now();
+      }
+    } else {
+      f->interpreter_frame_set_vt_alloc_ptr((intptr_t*)thread->vt_alloc_ptr());
+    }
+  }
+
+  // 8 - free relocation table
+  FREE_RESOURCE_ARRAY(struct VT_relocation_entry, reloc_table, max_entries);
+  if (ReportVTBufferRecyclingTimes) {
+    end = Ticks::now();
+    ResourceMark rm(thread);
+    tty->print_cr("VTBufferRecyling: %s : %s.%s %s : %ldus",
+        thread->name(),
+        f->interpreter_frame_method()->klass_name()->as_C_string(),
+        f->interpreter_frame_method()->name()->as_C_string(),
+        f->interpreter_frame_method()->signature()->as_C_string(),
+        (end.value() - begin.value()) / 1000);
+    tty->print("Step1 : %6ldns ", step1.value() - begin.value());
+    tty->print("Step2 : %6ldns ", step2.value() - step1.value());
+    tty->print("Step3 : %6ldns ", step3.value() - step2.value());
+    tty->print("Step4 : %6ldns ", step4.value() - step3.value());
+    tty->print("Step5 : %6ldns ", step5.value() - step4.value());
+    tty->print("Step6 : %6ldns ", step6.value() - step5.value());
+    tty->print("Step7 : %6ldns ", step7.value() - step6.value());
+    tty->print("Step8 : %6ldns ", end.value() - step7.value());
+    tty->print_cr("Returned chunks: %d", returned_chunks);
+  }
+}
+
+void BufferedValuesMarking::do_buffered_value(oop* p) {
+  assert(!Universe::heap()->is_in_reserved_or_null(*p), "Sanity check");
+  if (VTBuffer::value_belongs_to_frame(*p, _frame)) {
+    if (!(*p)->mark()->is_marked()) {
+      assert(*_index < _size, "index outside of relocation table range");
+      _reloc_table[*_index].old_ptr = (address)*p;
+      _reloc_table[*_index].chunk_index = VTBufferChunk::chunk(*p)->index();
+      *_index = (*_index) + 1;
+      (*p)->set_mark((*p)->mark()->set_marked());
+    }
+  }
+}
+
+void BufferedValuesPointersUpdate::do_buffered_value(oop* p) {
+  assert(!Universe::heap()->is_in_reserved_or_null(*p), "Sanity check");
+  // might be coded more efficiently just by checking mark word is not NULL
+  if (VTBuffer::value_belongs_to_frame(*p, _frame)) {
+    *p = (oop)(*p)->mark();
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/vm/memory/vtBuffer.hpp	Thu Jun 29 09:35:38 2017 -0400
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2016, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#ifndef SHARE_VM_MEMORY_VTBUFFER_HPP
+#define SHARE_VM_MEMORY_VTBUFFER_HPP
+
+#include "memory/allocation.hpp"
+#include "runtime/globals.hpp"
+#include "runtime/os.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+class VTBufferChunk : public CMmapObj<VTBufferChunk, mtValueTypes> {
+  friend class VMStructs;
+
+  static const int MAGIC_NUMBER = 3141592;
+
+private:
+  int            _magic;
+  int            _index;
+  VTBufferChunk* _prev;
+  VTBufferChunk* _next;
+  JavaThread*    _owner;
+ public:
+
+  /* A VTBufferChunk is a 4KB page used to create a thread-local
+   * buffer to store values. They are allocated in a global pool,
+   * and then threads can get them to create their own buffer.
+   * Each thread creates a linked list of VTBufferChunk to build
+   * its buffer. Fields _prev and _next are used to link the
+   * chunks together, the _owner field indicates to which thread
+   * this chunk belongs to (if NULL, it means the chunk has been
+   * returned to the global pool). When creating the linked list,
+   * the field _index is used to store the position of the chunk
+   * in the list. The index is used to optimize the comparison
+   * of addresses of buffered values. Because the thread local
+   * buffer is made of non-contiguous chunks, it is not possible
+   * to directly compare the two addresses. The comparison requires
+   * first to compare the indexes of each address' chunk, and if
+   * they are equal, compare the addresses directly. Without
+   * the _index field, this operation would require to walk the
+   * linked list for each comparison.
+   */
+
+  VTBufferChunk(JavaThread* thread) {
+    _magic = MAGIC_NUMBER;
+    _index = -1;
+    _prev = NULL;
+    _next = NULL;
+    _owner = thread;
+  }
+
+  int            index() { return _index; }
+  void           set_index(int index) { _index = index; }
+  VTBufferChunk* prev() { return _prev; }
+  void           set_prev(VTBufferChunk* prev) { _prev = prev; }
+  VTBufferChunk* next() { return _next; }
+  void           set_next(VTBufferChunk* next) { _next = next; }
+  JavaThread*    owner() { return _owner; }
+  void           set_owner(JavaThread* thread) {
+    assert(thread == NULL || _owner == NULL || _owner == thread, "Sanity check");
+    _owner = thread;
+  }
+
+  bool is_valid() {
+    return _magic == MAGIC_NUMBER && _owner != NULL && _index != -1;
+  }
+
+  void* first_alloc() { return (void*)((char*)this + align_object_size(sizeof (VTBufferChunk))); }
+  void* alloc_limit() { return (void*)((char*)this + chunk_size() - 1); }
+
+  static int chunk_size() {
+    return os::vm_page_size();
+  }
+
+  static uintptr_t chunk_mask() {
+    return ~(chunk_size() - 1);
+  }
+
+  static ByteSize index_offset() { return byte_offset_of(VTBufferChunk, _index); }
+
+  static size_t max_alloc_size() {
+    return chunk_size() - align_object_size(sizeof (VTBufferChunk));
+  }
+
+  static VTBufferChunk* chunk(void* address) {
+    VTBufferChunk* c = (VTBufferChunk*)((intptr_t)address & chunk_mask());
+    assert(c->is_valid(), "Sanity check");
+    return c;
+  }
+
+  static bool check_buffered(void* address) {
+    assert(address != NULL, "Sanity check");
+    VTBufferChunk* c = (VTBufferChunk*)((intptr_t)address & chunk_mask());
+    return c->is_valid();
+  }
+
+  bool contains(void* address) {
+    return address > (char*)chunk(address) && address < ((char*)chunk(address) + chunk_size());
+  }
+};
+
+class VTBuffer : AllStatic {
+  friend class VMStructs;
+private:
+  static VTBufferChunk* _free_list;
+  static Mutex* _pool_lock;
+  static int _pool_counter;
+  static int _max_pool_counter;
+  static int _total_allocated;
+  static int _total_deallocated;
+  static int _total_failed;
+  static const int _max_free_list = 64;  // Should be tunable
+
+public:
+  static Mutex* lock() { return _pool_lock; }
+  static oop allocate_value(ValueKlass* k, TRAPS);
+  static bool allocate_vt_chunk(JavaThread* thread);
+  static void recycle_chunk(JavaThread* thread, VTBufferChunk* chunk);
+  static void return_vt_chunk(JavaThread* thread, VTBufferChunk* chunk);
+
+  static int in_pool() { return _pool_counter; }
+  static int max_in_pool() { return _max_pool_counter; }
+  static int total_allocated() { return _total_allocated; }
+  static int total_deallocated() { return _total_deallocated; }
+  static int total_failed() { return _total_failed; }
+
+  static bool is_in_vt_buffer(const void* p) {
+    intptr_t chunk_mask = (~(VTBufferChunk::chunk_size() - 1));
+    VTBufferChunk* c = (VTBufferChunk*)((intptr_t)p & chunk_mask);
+    return c->is_valid();
+  }
+
+  static bool value_belongs_to_frame(oop p, frame *f);
+  static void recycle_vt_in_frame(JavaThread* thread, frame* f);
+  static void recycle_vtbuffer(JavaThread *thread, frame f);
+  static address relocate_value(address old, address previous, int previous_size_in_words);
+  static oop relocate_return_value(JavaThread* thread, frame fr, oop old);
+
+  static void fix_frame_vt_alloc_ptr(frame fr, VTBufferChunk* chunk);
+
+};
+
+struct VT_relocation_entry {
+  int chunk_index;
+  address old_ptr;
+  address new_ptr;
+};
+
+
+class BufferedValuesMarking : public BufferedValueClosure {
+  frame* _frame;
+  struct VT_relocation_entry* _reloc_table;
+  int _size;
+  int* _index;
+public:
+  BufferedValuesMarking(frame* frame, struct VT_relocation_entry* reloc_table, int size, int* index) {
+    _frame = frame;
+    _reloc_table = reloc_table;
+    _size = size;
+    _index = index;
+  }
+  virtual void do_buffered_value(oop* p);
+};
+
+class BufferedValuesPointersUpdate : public BufferedValueClosure {
+  frame* _frame;
+public:
+  BufferedValuesPointersUpdate(frame* frame) {
+    _frame = frame;
+  }
+  virtual void do_buffered_value(oop* p);
+};
+
+#endif /* SHARE_VM_MEMORY_VTBUFFER_HPP */
--- a/src/share/vm/oops/instanceKlass.cpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/share/vm/oops/instanceKlass.cpp	Thu Jun 29 09:35:38 2017 -0400
@@ -1212,7 +1212,7 @@
   OopMapCache* oop_map_cache =
       static_cast<OopMapCache*>(OrderAccess::load_ptr_acquire(&_oop_map_cache));
   if (oop_map_cache == NULL) {
-    MutexLocker x(OopMapCacheAlloc_lock);
+    MutexLockerEx x(OopMapCacheAlloc_lock,  Mutex::_no_safepoint_check_flag);
     // Check if _oop_map_cache was allocated while we were waiting for this lock
     if ((oop_map_cache = _oop_map_cache) == NULL) {
       oop_map_cache = new OopMapCache();
--- a/src/share/vm/oops/instanceKlass.hpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/share/vm/oops/instanceKlass.hpp	Thu Jun 29 09:35:38 2017 -0400
@@ -207,12 +207,15 @@
   // _misc_flags.
   bool            _is_marked_dependent;  // used for marking during flushing and deoptimization
 
+ public:
   enum {
     _extra_is_being_redefined = 1 << 0, // used for locking redefinition
     _extra_has_value_fields   = 1 << 1, // has value fields and related embedded section is not empty
-    _extra_has_vcc_klass      = 1 << 2  // has a pointer to its Value Capable Class (MVT)
+    _extra_is_bufferable      = 1 << 2, // value can be buffered out side of the Java heap
+    _extra_has_vcc_klass      = 1 << 3  // has a pointer to its Value Capable Class (MVT)
   };
 
+ protected:
   u1              _extra_flags;
 
   // The low three bits of _misc_flags contains the kind field.
@@ -543,6 +546,8 @@
   bool is_marked_dependent() const         { return _is_marked_dependent; }
   void set_is_marked_dependent(bool value) { _is_marked_dependent = value; }
 
+  static ByteSize extra_flags_offset() { return in_ByteSize(offset_of(InstanceKlass, _extra_flags)); }
+
   // initialization (virtuals from Klass)
   bool should_be_initialized() const;  // means that initialize should be called
   void initialize(TRAPS);
--- a/src/share/vm/oops/method.cpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/share/vm/oops/method.cpp	Thu Jun 29 09:35:38 2017 -0400
@@ -40,6 +40,7 @@
 #include "memory/metaspaceShared.hpp"
 #include "memory/oopFactory.hpp"
 #include "memory/resourceArea.hpp"
+#include "memory/vtBuffer.hpp"
 #include "oops/constMethod.hpp"
 #include "oops/method.hpp"
 #include "oops/methodData.hpp"
@@ -104,6 +105,8 @@
     set_signature_handler(NULL);
   }
 
+  initialize_max_vt_buffer();
+
   NOT_PRODUCT(set_compiled_invocation_count(0);)
 }
 
@@ -1865,6 +1868,14 @@
   }
 }
 
+void Method::initialize_max_vt_buffer() {
+  long long max_entries = constMethod()->max_locals() + constMethod()->max_stack();
+  max_entries *= 2; // Add margin for loops
+  long long max_size = max_entries * (BigValueTypeThreshold + 8); // 8 -> header size
+  int max_chunks = (int)(max_size / VTBufferChunk::max_alloc_size()) + 1;
+  set_max_vt_buffer(MAX2(MinimumVTBufferChunkPerFrame, max_chunks));
+}
+
 int Method::highest_comp_level() const {
   const MethodCounters* mcs = method_counters();
   if (mcs != NULL) {
--- a/src/share/vm/oops/method.hpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/share/vm/oops/method.hpp	Thu Jun 29 09:35:38 2017 -0400
@@ -103,6 +103,7 @@
   // NULL only at safepoints (because of a de-opt).
   CompiledMethod* volatile _code;                       // Points to the corresponding piece of native code
   volatile address           _from_interpreted_entry; // Cache of _code ? _adapter->i2c_entry() : _i2i_entry
+  int _max_vt_buffer; // max number of VT buffer chunk to use before recycling
 
 #if INCLUDE_AOT && defined(TIERED)
   CompiledMethod* _aot_code;
@@ -263,6 +264,12 @@
   int  max_locals() const                        { return constMethod()->max_locals(); }
   void set_max_locals(int size)                  { constMethod()->set_max_locals(size); }
 
+  // value type buffering
+  void initialize_max_vt_buffer();
+  int max_vt_buffer() const                      { return _max_vt_buffer; }
+  void set_max_vt_buffer(int size)               { _max_vt_buffer = size; }
+
+
   int highest_comp_level() const;
   void set_highest_comp_level(int level);
   int highest_osr_comp_level() const;
@@ -705,6 +712,8 @@
   static int intrinsic_id_offset_in_bytes()      { return offset_of(Method, _intrinsic_id); }
   static int intrinsic_id_size_in_bytes()        { return sizeof(u2); }
 
+  static ByteSize max_vt_buffer_offset()         { return byte_offset_of(Method, _max_vt_buffer); }
+
   // Static methods that are used to implement member methods where an exposed this pointer
   // is needed due to possible GCs
   static objArrayHandle resolved_checked_exceptions_impl(Method* method, TRAPS);
--- a/src/share/vm/oops/oop.inline.hpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/share/vm/oops/oop.inline.hpp	Thu Jun 29 09:35:38 2017 -0400
@@ -31,6 +31,7 @@
 #include "gc/shared/collectedHeap.inline.hpp"
 #include "gc/shared/genCollectedHeap.hpp"
 #include "gc/shared/generation.hpp"
+#include "memory/vtBuffer.hpp"
 #include "oops/arrayKlass.hpp"
 #include "oops/arrayOop.hpp"
 #include "oops/klass.inline.hpp"
@@ -547,7 +548,12 @@
 bool oopDesc::is_oop(bool ignore_mark_word) const {
   oop obj = (oop) this;
   if (!check_obj_alignment(obj)) return false;
-  if (!Universe::heap()->is_in_reserved(obj)) return false;
+  if (!Universe::heap()->is_in_reserved(obj)) {
+    assert(obj->klass()->is_value(), "Only value type can be outside of the Java heap");
+    VTBufferChunk* chunk = VTBufferChunk::chunk(obj);
+    assert(chunk->is_valid(), "if not in the heap, should a buffered VT");
+    if (!VTBuffer::is_in_vt_buffer(obj)) return false;
+  }
   // obj is aligned and accessible in heap
   if (Universe::heap()->is_in_reserved(obj->klass_or_null())) return false;
 
@@ -561,7 +567,8 @@
   if (mark() != NULL) {
     return true;
   }
-  return !SafepointSynchronize::is_at_safepoint();
+  return !SafepointSynchronize::is_at_safepoint()
+    || (obj->klass()->is_value() && !Universe::heap()->is_in_reserved(obj)) ;
 }
 
 
--- a/src/share/vm/oops/valueKlass.cpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/share/vm/oops/valueKlass.cpp	Thu Jun 29 09:35:38 2017 -0400
@@ -25,6 +25,7 @@
 #include "precompiled.hpp"
 #include "gc/shared/gcLocker.inline.hpp"
 #include "interpreter/interpreter.hpp"
+#include "logging/log.hpp"
 #include "oops/oop.inline.hpp"
 #include "oops/fieldStreams.hpp"
 #include "oops/method.hpp"
@@ -86,6 +87,28 @@
   return 1 << upper_log2(last_offset - first_offset);
 }
 
+instanceOop ValueKlass::allocate_instance(TRAPS) {
+  int size = size_helper();  // Query before forming handle.
+
+  return (instanceOop)CollectedHeap::obj_allocate(this, size, CHECK_NULL);
+}
+
+instanceOop ValueKlass::allocate_buffered_or_heap_instance(bool* in_heap, TRAPS) {
+  assert(THREAD->is_Java_thread(), "Only Java threads can call this method");
+
+  instanceOop value = NULL;
+  if (is_bufferable()) {
+    value = (instanceOop)VTBuffer::allocate_value(this, CHECK_NULL);
+    *in_heap = false;
+  }
+  if (value == NULL) {
+    log_info(valuetypes)("Value buffering failed, allocating in the Java heap");
+    value = allocate_instance(CHECK_NULL);
+    *in_heap = true;
+  }
+  return value;
+}
+
 bool ValueKlass::is_atomic() {
   return (nonstatic_field_size() * heapOopSize) <= longSize;
 }
@@ -235,24 +258,29 @@
   }
 }
 
-oop ValueKlass::derive_value_type_copy(Handle src, InstanceKlass* target_klass, TRAPS) {
-  assert(EnableMVT, "Only supported with the MVT programming model");
-  // assert(InstanceKlass::cast(src->klass())->derive_value_type_klass() == target_klass, "Not this DVT");
-#ifdef ASSERT
-  if (InstanceKlass::cast(src->klass())->has_vcc_klass()) {
-    assert(InstanceKlass::cast(src->klass())->get_vcc_klass() == target_klass,
-           "VCC/DVT mismatch");
-  } else {
-    assert(target_klass->has_vcc_klass(), "Sanity check");
-    assert(target_klass->get_vcc_klass() == InstanceKlass::cast(src->klass()),
-           "VCC/DVT mismatch");
-  }
-#endif // ASSERT
+oop ValueKlass::box(Handle src, InstanceKlass* target_klass, TRAPS) {
+  assert(src()->klass()->is_value(), "src must be a value type");
+  assert(!target_klass->is_value(), "target_klass must not be a value type");
 
-  // Allocate new for safety, simply reinstalling the klass pointer is a little too risky
   target_klass->initialize(CHECK_0);
-  instanceOop value = target_klass->allocate_instance(CHECK_0);
-  value_store(data_for_oop(src()), data_for_oop(value), true, true);
+  instanceOop box = target_klass->allocate_instance(CHECK_0);
+  value_store(data_for_oop(src()), data_for_oop(box), true, false);
+
+  assert(!box->klass()->is_value(), "Sanity check");
+  return box;
+}
+
+oop ValueKlass::unbox(Handle src, InstanceKlass* target_klass, TRAPS) {
+  assert(!src()->klass()->is_value(), "src must not be a value type");
+  assert(target_klass->is_value(), "target_klass must be a value type");
+  ValueKlass* vtklass = ValueKlass::cast(target_klass);
+
+  vtklass->initialize(CHECK_0);
+  bool in_heap;
+  instanceOop value = vtklass->allocate_buffered_or_heap_instance(&in_heap, CHECK_0);
+  value_store(data_for_oop(src()), data_for_oop(value), in_heap, false);
+
+  assert(value->klass()->is_value(), "Sanity check");
   return value;
 }
 
@@ -511,8 +539,13 @@
   
   address loc = map.location(pair.first());
   intptr_t ptr = *(intptr_t*)loc;
-  if (Universe::heap()->is_in_reserved((void*)ptr)) {
-    return NULL;
+  if (Metaspace::contains((void*)ptr)) {
+    return (ValueKlass*)ptr;
   }
-  return (ValueKlass*)ptr;
+  return NULL;
+//  if (Universe::heap()->is_in_reserved((void*)ptr)) {
+//    return NULL;
+//  }
+//  return (ValueKlass*)ptr;
 }
+
--- a/src/share/vm/oops/valueKlass.hpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/share/vm/oops/valueKlass.hpp	Thu Jun 29 09:35:38 2017 -0400
@@ -69,6 +69,13 @@
     return layout_helper_to_size_helper(layout_helper());
   }
 
+  // allocate_instance() allocates a stand alone value in the Java heap
+  instanceOop allocate_instance(TRAPS);
+  // allocate_buffered_or_heap_instance() tries to allocate a value in the
+  // thread local value buffer, if allocation fails, it allocates it in the
+  // Java heap
+  instanceOop allocate_buffered_or_heap_instance(bool* in_heap, TRAPS);
+
   // minimum number of bytes occupied by nonstatic fields, HeapWord aligned or pow2
   int raw_value_byte_size() const;
 
@@ -84,6 +91,27 @@
     return o;
   }
 
+   void set_if_bufferable() {
+     bool bufferable;
+     if (contains_oops()) {
+       bufferable = false;
+     } else {
+       int size_in_heap_words = size_helper();
+       int base_offset = instanceOopDesc::base_offset_in_bytes();
+       size_t size_in_bytes = size_in_heap_words * HeapWordSize - base_offset;
+       bufferable = size_in_bytes <= BigValueTypeThreshold;
+     }
+     if (bufferable) {
+       _extra_flags |= _extra_is_bufferable;
+     } else {
+       _extra_flags &= ~_extra_is_bufferable;
+     }
+   }
+
+  bool is_bufferable() const          {
+    return (_extra_flags & _extra_is_bufferable) != 0;
+  }
+
   // Query if h/w provides atomic load/store
   bool is_atomic();
 
@@ -104,8 +132,8 @@
   // store the value of this klass contained with src into dst, raw data ptr
   void value_store(void* src, void* dst, size_t raw_byte_size, bool dst_is_heap, bool dst_uninitialized);
 
-
-  oop derive_value_type_copy(Handle src, InstanceKlass* target_klass, TRAPS);
+  oop unbox(Handle src, InstanceKlass* target_klass, TRAPS);
+  oop box(Handle src, InstanceKlass* target_klass, TRAPS);
 
   // GC support...
 
--- a/src/share/vm/runtime/frame.cpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/share/vm/runtime/frame.cpp	Thu Jun 29 09:35:38 2017 -0400
@@ -443,6 +443,16 @@
   *interpreter_frame_mdp_addr() = (intptr_t)mdp;
 }
 
+intptr_t* frame::interpreter_frame_vt_alloc_ptr() const {
+  assert(is_interpreted_frame(), "interpreted frame expected");
+  return (intptr_t*)*interpreter_frame_vt_alloc_ptr_addr();
+}
+
+void frame::interpreter_frame_set_vt_alloc_ptr(intptr_t* ptr) {
+  assert(is_interpreted_frame(), "interpreted frame expected");
+  *interpreter_frame_vt_alloc_ptr_addr() = ptr;
+}
+
 BasicObjectLock* frame::next_monitor_in_interpreter_frame(BasicObjectLock* current) const {
   assert(is_interpreted_frame(), "Not an interpreted frame");
 #ifdef ASSERT
@@ -747,16 +757,18 @@
  private:
   frame* _fr;
   OopClosure* _f;
+  BufferedValueClosure* _bvt_f;
   int    _max_locals;
   int    _max_stack;
 
  public:
   InterpreterFrameClosure(frame* fr, int max_locals, int max_stack,
-                          OopClosure* f) {
+                          OopClosure* f, BufferedValueClosure* bvt_f) {
     _fr         = fr;
     _max_locals = max_locals;
     _max_stack  = max_stack;
     _f          = f;
+    _bvt_f      = bvt_f;
   }
 
   void offset_do(int offset) {
@@ -764,7 +776,19 @@
     if (offset < _max_locals) {
       addr = (oop*) _fr->interpreter_frame_local_at(offset);
       assert((intptr_t*)addr >= _fr->sp(), "must be inside the frame");
-      _f->do_oop(addr);
+      if (Universe::heap()->is_in_reserved_or_null(*addr)) {
+        if (_f != NULL) {
+          _f->do_oop(addr);
+        }
+      } else { // Buffered value types case
+        if (_f != NULL) {
+          oop* addr_mirror = (oop*)(*addr)->mark_addr();
+          _f->do_oop(addr_mirror);
+        }
+        if (_bvt_f != NULL) {
+          _bvt_f->do_buffered_value(addr);
+        }
+      }
     } else {
       addr = (oop*) _fr->interpreter_frame_expression_stack_at((offset - _max_locals));
       // In case of exceptions, the expression stack is invalid and the esp will be reset to express
@@ -776,7 +800,19 @@
         in_stack = (intptr_t*)addr >= _fr->interpreter_frame_tos_address();
       }
       if (in_stack) {
-        _f->do_oop(addr);
+        if (Universe::heap()->is_in_reserved_or_null(*addr)) {
+          if (_f != NULL) {
+            _f->do_oop(addr);
+          }
+        } else { // Buffered value types case
+          if (_f != NULL) {
+            oop* addr_mirror = (oop*)(*addr)->mark_addr();
+            _f->do_oop(addr_mirror);
+          }
+          if (_bvt_f != NULL) {
+            _bvt_f->do_buffered_value(addr);
+          }
+        }
       }
     }
   }
@@ -949,7 +985,7 @@
     }
   }
 
-  InterpreterFrameClosure blk(this, max_locals, m->max_stack(), f);
+  InterpreterFrameClosure blk(this, max_locals, m->max_stack(), f, NULL);
 
   // process locals & expression stack
   InterpreterOopMap mask;
@@ -961,6 +997,23 @@
   mask.iterate_oop(&blk);
 }
 
+void frame::buffered_values_interpreted_do(BufferedValueClosure* f) {
+  assert(is_interpreted_frame(), "Not an interpreted frame");
+  Thread *thread = Thread::current();
+  methodHandle m (thread, interpreter_frame_method());
+  jint      bci = interpreter_frame_bci();
+
+  assert(m->is_method(), "checking frame value");
+  assert(!m->is_native() && bci >= 0 && bci < m->code_size(),
+         "invalid bci value");
+
+  InterpreterFrameClosure blk(this, m->max_locals(), m->max_stack(), NULL, f);
+
+  // process locals & expression stack
+  InterpreterOopMap mask;
+  m->mask_for(bci, &mask);
+  mask.iterate_oop(&blk);
+}
 
 void frame::oops_interpreted_arguments_do(Symbol* signature, bool has_receiver, OopClosure* f) {
   InterpretedArgumentOopFinder finder(signature, has_receiver, this, f);
--- a/src/share/vm/runtime/frame.hpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/share/vm/runtime/frame.hpp	Thu Jun 29 09:35:38 2017 -0400
@@ -325,6 +325,10 @@
   ConstantPoolCache** interpreter_frame_cache_addr() const;
   oop* interpreter_frame_mirror_addr() const;
 
+  intptr_t* interpreter_frame_vt_alloc_ptr() const;
+  intptr_t** interpreter_frame_vt_alloc_ptr_addr() const;
+  void interpreter_frame_set_vt_alloc_ptr(intptr_t* ptr);
+
   void interpreter_frame_set_mirror(oop mirror);
 
  public:
@@ -393,6 +397,7 @@
   // Oops-do's
   void oops_compiled_arguments_do(Symbol* signature, bool has_receiver, bool has_appendix, const RegisterMap* reg_map, OopClosure* f);
   void oops_interpreted_do(OopClosure* f, const RegisterMap* map, bool query_oop_map_cache = true);
+  void buffered_values_interpreted_do(BufferedValueClosure* f);
 
  private:
   void oops_interpreted_arguments_do(Symbol* signature, bool has_receiver, OopClosure* f);
--- a/src/share/vm/runtime/globals.hpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/share/vm/runtime/globals.hpp	Thu Jun 29 09:35:38 2017 -0400
@@ -4090,6 +4090,22 @@
                                                                             \
   develop_pd(bool, ValueTypeReturnedAsFields,                               \
             "return fields instead of a value type reference")              \
+                                                                            \
+  product(size_t, BigValueTypeThreshold, 4 * BytesPerLong,                  \
+          "Max value type size for buffering")                              \
+                                                                            \
+  product(intx, ValueTypesBufferMaxMemory, 128,                             \
+          "Max memory used for value types buffers (in pages)")             \
+                                                                            \
+  product(bool, ValueTypesThreadLocalRecycling, true,                       \
+          "Enable Thread local recycling of buffered values")               \
+                                                                            \
+  product(bool, ReportVTBufferRecyclingTimes, false,                        \
+          "Print duration of each VBuffer recycling")                       \
+                                                                            \
+  product(int, MinimumVTBufferChunkPerFrame, 2,                             \
+          "Minimum number of VT buffer chunk allowed per frame")            \
+
 
 
 /*
--- a/src/share/vm/runtime/handles.cpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/share/vm/runtime/handles.cpp	Thu Jun 29 09:35:38 2017 -0400
@@ -100,8 +100,10 @@
     // This test can be moved up but for now check every oop.
 
     assert((*bottom)->is_oop(), "handle should point to oop");
-
-    f->do_oop(bottom++);
+    if (Universe::heap()->is_in_reserved_or_null(*bottom)) {
+      f->do_oop(bottom);
+    }
+    bottom++;
   }
   return handles_visited;
 }
--- a/src/share/vm/runtime/mutexLocker.cpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/share/vm/runtime/mutexLocker.cpp	Thu Jun 29 09:35:38 2017 -0400
@@ -209,7 +209,7 @@
 #endif
   def(CodeCache_lock               , PaddedMutex  , special,     true,  Monitor::_safepoint_check_never);
   def(RawMonitor_lock              , PaddedMutex  , special,     true,  Monitor::_safepoint_check_never);
-  def(OopMapCacheAlloc_lock        , PaddedMutex  , leaf,        true,  Monitor::_safepoint_check_always);     // used for oop_map_cache allocation.
+  def(OopMapCacheAlloc_lock        , PaddedMutex  , leaf,        true,  Monitor::_safepoint_check_never);      // used for oop_map_cache allocation.
 
   def(Patching_lock                , PaddedMutex  , special,     true,  Monitor::_safepoint_check_never);      // used for safepointing and code patching.
   def(Service_lock                 , PaddedMonitor, special,     true,  Monitor::_safepoint_check_never);      // used for service thread operations
--- a/src/share/vm/runtime/sharedRuntime.cpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/share/vm/runtime/sharedRuntime.cpp	Thu Jun 29 09:35:38 2017 -0400
@@ -3468,7 +3468,7 @@
   }
 #endif
 
-  if (Universe::heap()->is_in_reserved((void*)res)) {
+    if (!Metaspace::contains((void*)res)) {
     // We're not returning with value type fields in registers (the
     // calling convention didn't allow it for this value klass)
     thread->set_vm_result((oopDesc*)res);
--- a/src/share/vm/runtime/thread.cpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/share/vm/runtime/thread.cpp	Thu Jun 29 09:35:38 2017 -0400
@@ -840,6 +840,17 @@
   }
 }
 
+void JavaThread::print_vt_buffer_stats_on(outputStream* st) const {
+  st->print_cr("%s:", this->name());
+  st->print_cr("\tChunks in use       : %d", vtchunk_in_use());
+  st->print_cr("\tCached chunk        : %d", local_free_chunk() == NULL ? 0 : 1);
+  st->print_cr("\tMax chunks          : %d", vtchunk_max());
+  st->print_cr("\tReturned chunks     : %d", vtchunk_total_returned());
+  st->print_cr("\tFailed chunk allocs : %d", vtchunk_total_failed());
+  st->print_cr("\tMemory buffered     : %ld", vtchunk_total_memory_buffered());
+  st->print_cr("");
+}
+
 #ifdef ASSERT
 void Thread::print_owned_locks_on(outputStream* st) const {
   Monitor *cur = _owned_locks;
@@ -1511,6 +1522,17 @@
   _popframe_preserved_args_size = 0;
   _frames_to_pop_failed_realloc = 0;
 
+  // Buffered value types support
+  _vt_alloc_ptr = NULL;
+  _vt_alloc_limit = NULL;
+  _local_free_chunk = NULL;
+  // Buffered value types instrumentation support
+  _vtchunk_in_use = 0;
+  _vtchunk_max = 0;
+  _vtchunk_total_returned = 0;
+  _vtchunk_total_failed = 0;
+  _vtchunk_total_memory_buffered = 0;
+
   pd_initialize();
 }
 
@@ -2821,7 +2843,10 @@
   // Traverse instance variables at the end since the GC may be moving things
   // around using this function
   f->do_oop((oop*) &_threadObj);
-  f->do_oop((oop*) &_vm_result);
+  // if (Universe::heap()->is_in_reserved_or_null((void*)_vm_result)) {
+    if (!VTBufferChunk::check_buffered(&_vm_result)) {
+    f->do_oop((oop*) &_vm_result);
+  }
   f->do_oop((oop*) &_exception_oop);
   f->do_oop((oop*) &_pending_async_exception);
 
@@ -4877,3 +4902,9 @@
   VMThread* thread = VMThread::vm_thread();
   if (thread != NULL) thread->verify();
 }
+
+void Threads::print_vt_buffer_stats_on(outputStream* st) {
+  ALL_JAVA_THREADS(p) {
+    p->print_vt_buffer_stats_on(st);
+  }
+}
--- a/src/share/vm/runtime/thread.hpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/share/vm/runtime/thread.hpp	Thu Jun 29 09:35:38 2017 -0400
@@ -27,6 +27,7 @@
 
 #include "gc/shared/threadLocalAllocBuffer.hpp"
 #include "memory/allocation.hpp"
+#include "memory/vtBuffer.hpp"
 #include "oops/oop.hpp"
 #include "prims/jni.h"
 #include "prims/jvmtiExport.hpp"
@@ -994,6 +995,18 @@
   // failed reallocations.
   int _frames_to_pop_failed_realloc;
 
+  // Buffered value types support
+  void* _vt_alloc_ptr;
+  void* _vt_alloc_limit;
+  VTBufferChunk* _local_free_chunk;
+  // Next 4 fields are used to monitor VT buffer memory consumption
+  // We may want to not support them in PRODUCT builds
+  jint _vtchunk_in_use;
+  jint _vtchunk_max;
+  jint _vtchunk_total_returned;
+  jint _vtchunk_total_failed;
+  jlong _vtchunk_total_memory_buffered;
+
 #ifndef PRODUCT
   int _jmp_ring_index;
   struct {
@@ -1673,6 +1686,7 @@
   void print_thread_state() const                      PRODUCT_RETURN;
   void print_on_error(outputStream* st, char* buf, int buflen) const;
   void print_name_on_error(outputStream* st, char* buf, int buflen) const;
+  void print_vt_buffer_stats_on(outputStream* st) const;
   void verify();
   const char* get_thread_name() const;
  private:
@@ -1874,6 +1888,37 @@
     _stack_size_at_create = value;
   }
 
+  void* vt_alloc_ptr() const { return _vt_alloc_ptr; }
+  void set_vt_alloc_ptr(void* ptr) { _vt_alloc_ptr = ptr; }
+  void* vt_alloc_limit() const { return _vt_alloc_limit; }
+  void set_vt_alloc_limit(void* ptr) { _vt_alloc_limit = ptr; }
+  VTBufferChunk* local_free_chunk() const { return _local_free_chunk; }
+  void set_local_free_chunk(VTBufferChunk* chunk) { _local_free_chunk = chunk; }
+  VTBufferChunk* current_chunk() {
+    if (_vt_alloc_ptr == NULL) return NULL;
+    VTBufferChunk* chunk = VTBufferChunk::chunk(_vt_alloc_ptr);
+    assert(chunk->owner() == this, "Sanity check");
+    return chunk;
+    // return _vt_alloc_ptr == NULL ? NULL : VTBufferChunk::chunk(_vt_alloc_ptr);
+  }
+
+  void increment_vtchunk_in_use() {
+    _vtchunk_in_use++;
+    if (_vtchunk_in_use > _vtchunk_max) _vtchunk_max = _vtchunk_in_use;
+  }
+  void decrement_vtchunk_in_use() { _vtchunk_in_use--; }
+  jint vtchunk_in_use() const { return _vtchunk_in_use; }
+  jint vtchunk_max() const { return _vtchunk_max; }
+  void increment_vtchunk_returned() { _vtchunk_total_returned++; }
+  jint vtchunk_total_returned() const { return _vtchunk_total_returned; }
+  void increment_vtchunk_failed() { _vtchunk_total_failed++; }
+  jint vtchunk_total_failed() const { return _vtchunk_total_failed; }
+  void increment_vtchunk_total_memory_buffered(jlong size) { _vtchunk_total_memory_buffered += size; }
+  jlong vtchunk_total_memory_buffered() const { return _vtchunk_total_memory_buffered; }
+
+  static ByteSize vt_alloc_ptr_offset() { return byte_offset_of(JavaThread, _vt_alloc_ptr); }
+
+
 #if INCLUDE_ALL_GCS
   // SATB marking queue support
   SATBMarkQueue& satb_mark_queue() { return _satb_mark_queue; }
@@ -2138,6 +2183,7 @@
   static void print_on_error(Thread* this_thread, outputStream* st, Thread* current, char* buf,
                              int buflen, bool* found_current);
   static void print_threads_compiling(outputStream* st, char* buf, int buflen);
+  static void print_vt_buffer_stats_on(outputStream* st);
 
   // Get Java threads that are waiting to enter a monitor. If doLock
   // is true, then Threads_lock is grabbed as needed. Otherwise, the
--- a/src/share/vm/runtime/vframeArray.cpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/share/vm/runtime/vframeArray.cpp	Thu Jun 29 09:35:38 2017 -0400
@@ -295,6 +295,8 @@
                                  is_top_frame,
                                  is_bottom_frame);
 
+  iframe()->interpreter_frame_set_vt_alloc_ptr((intptr_t*)thread->vt_alloc_ptr());
+
   // Update the pc in the frame object and overwrite the temporary pc
   // we placed in the skeletal frame now that we finally know the
   // exact interpreter address we should use.
--- a/src/share/vm/runtime/vm_operations.cpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/share/vm/runtime/vm_operations.cpp	Thu Jun 29 09:35:38 2017 -0400
@@ -501,6 +501,10 @@
   CompileBroker::print_compile_queues(_out);
 }
 
+void VM_VTBufferStats::doit() {
+  Threads::print_vt_buffer_stats_on(_out);
+}
+
 #if INCLUDE_SERVICES
 void VM_PrintClassHierarchy::doit() {
   KlassHierarchy::print_class_hierarchy(_out, _print_interfaces, _print_subclasses, _classname);
--- a/src/share/vm/runtime/vm_operations.hpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/share/vm/runtime/vm_operations.hpp	Thu Jun 29 09:35:38 2017 -0400
@@ -106,6 +106,7 @@
   template(MarkActiveNMethods)                    \
   template(PrintCompileQueue)                     \
   template(PrintClassHierarchy)                   \
+  template(VTBufferStats)                         \
 
 class VM_Operation: public CHeapObj<mtInternal> {
  public:
@@ -434,6 +435,16 @@
   void doit();
 };
 
+class VM_VTBufferStats: public VM_Operation {
+private:
+  outputStream* _out;
+public:
+  VM_VTBufferStats()                  { _out = tty; }
+  VM_VTBufferStats(outputStream* out) { _out = out; }
+  VMOp_Type type() const              {  return VMOp_VTBufferStats; }
+  void doit();
+};
+
 #if INCLUDE_SERVICES
 class VM_PrintClassHierarchy: public VM_Operation {
  private:
--- a/src/share/vm/services/diagnosticCommand.cpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/share/vm/services/diagnosticCommand.cpp	Thu Jun 29 09:35:38 2017 -0400
@@ -111,7 +111,7 @@
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JMXStartLocalDCmd>(jmx_agent_export_flags, true,false));
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JMXStopRemoteDCmd>(jmx_agent_export_flags, true,false));
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JMXStatusDCmd>(jmx_agent_export_flags, true,false));
-
+  DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<VTBufferStatsDCmd>(full_export, true, false));
 }
 
 #ifndef HAVE_EXTRA_DCMD
@@ -1013,3 +1013,32 @@
 int TouchedMethodsDCmd::num_arguments() {
   return 0;
 }
+
+VTBufferStatsDCmd::VTBufferStatsDCmd(outputStream* output, bool heap) :
+                                    DCmd(output, heap)  { }
+
+void VTBufferStatsDCmd::execute(DCmdSource source, TRAPS) {
+
+  VM_VTBufferStats op1(output());
+  VMThread::execute(&op1);
+
+  int in_pool;
+  int max_in_pool;
+  int total_allocated;
+  int total_deallocated;
+  int total_failed;
+  {
+    MutexLockerEx ml(VTBuffer::lock(), Mutex::_no_safepoint_check_flag);
+    in_pool = VTBuffer::in_pool();
+    max_in_pool = VTBuffer::max_in_pool();
+    total_allocated = VTBuffer::total_allocated();
+    total_deallocated = VTBuffer::total_deallocated();
+    total_failed = VTBuffer::total_failed();
+  }
+  output()->print_cr("Global VTBuffer Pool statistics:");
+  output()->print_cr("\tChunks in pool   : %d", in_pool);
+  output()->print_cr("\tMax in pool      : %d", max_in_pool);
+  output()->print_cr("\tTotal allocated  : %d", total_allocated);
+  output()->print_cr("\tTotal deallocated: %d", total_deallocated);
+  output()->print_cr("\tTotal failed     : %d", total_failed);
+}
--- a/src/share/vm/services/diagnosticCommand.hpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/share/vm/services/diagnosticCommand.hpp	Thu Jun 29 09:35:38 2017 -0400
@@ -724,4 +724,23 @@
   virtual void execute(DCmdSource source, TRAPS);
 };
 
+class VTBufferStatsDCmd : public DCmd {
+public:
+  VTBufferStatsDCmd(outputStream* output, bool heap);
+  static const char* name() { return "VTBuffer.stats"; }
+  static const char* description() {
+    return "[EXPERIMENTAL] Print statistics about Value Types buffering.";
+  }
+  static const char* impact() {
+    return "Medium: Depends on the number of threads.";
+  }
+  static const JavaPermission permission() {
+    JavaPermission p = {"java.lang.management.ManagementPermission",
+        "monitor", NULL};
+    return p;
+  }
+  static int num_arguments() { return 0; }
+  virtual void execute(DCmdSource source, TRAPS);
+};
+
 #endif // SHARE_VM_SERVICES_DIAGNOSTICCOMMAND_HPP
--- a/src/share/vm/services/nmtCommon.cpp	Wed Jun 28 16:38:15 2017 +0800
+++ b/src/share/vm/services/nmtCommon.cpp	Thu Jun 29 09:35:38 2017 -0400
@@ -43,6 +43,7 @@
   "Logging",
   "Arguments",
   "Module",
+  "Value Types",
   "Unknown"
 };
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/runtime/valhalla/valuetypes/UninitializedValueFieldsTest.java	Thu Jun 29 09:35:38 2017 -0400
@@ -0,0 +1,37 @@
+package runtime.valhalla.valuetypes;
+
+import jdk.test.lib.Asserts;
+
+/*
+ * @test 
+ * @summary Uninitialized value fields test
+ * @library /test/lib
+ * @compile -XDenableValueTypes Point.java UninitializedValueFieldsTest.java
+ * @run main/othervm -noverify -Xint runtime.valhalla.valuetypes.UninitializedValueFieldsTest
+ */
+
+/*
+ * disabled run main/othervm -noverify -Xcomp runtime.valhalla.valuetypes.UninitializedValueFieldsTest
+ */
+public class UninitializedValueFieldsTest {
+
+    static Point staticPoint;
+    Point instancePoint;
+
+    UninitializedValueFieldsTest() { }
+
+    public static void main(String[] args) {
+	checkUninitializedPoint(UninitializedValueFieldsTest.staticPoint, 0, 0);
+	UninitializedValueFieldsTest.staticPoint = Point.createPoint(456, 678);
+	checkUninitializedPoint(UninitializedValueFieldsTest.staticPoint, 456, 678);
+	UninitializedValueFieldsTest test = new UninitializedValueFieldsTest();
+	checkUninitializedPoint(test.instancePoint, 0, 0);
+	test.instancePoint = Point.createPoint(123, 345);
+	checkUninitializedPoint(test.instancePoint, 123, 345);
+    }
+
+    static void checkUninitializedPoint(Point p, int x, int y) {
+	Asserts.assertEquals(p.x, x, "invalid x value");
+	Asserts.assertEquals(p.y, y, "invalid y value");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/runtime/valhalla/valuetypes/VTBufferTest.java	Thu Jun 29 09:35:38 2017 -0400
@@ -0,0 +1,340 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test VTBufferTest
+ * @summary Value Type interpreter value buffering test
+ * @library /test/lib
+ * @build ValueTypeGenerator
+ * @run main/othervm -noverify -Xint VTBufferTest generate-and-run
+ * @run main/othervm -noverify -Xint -XX:ValueTypesBufferMaxMemory=0 VTBufferTest generate-and-run
+ * @run main/othervm -noverify -Xint -XX:BigValueTypeThreshold=196 VTBufferTest generate-and-run
+ */
+
+/* This test generates its source code.
+ * To reproduce a run (for instance to investigate a failure), look at
+ * the test output and search for a line starting with "Seed=". The value
+ * at the end of the line is the seed used to generate the test.
+ * It possible to re-generate the same test with the following commande
+ * line:
+ *  $ java <VMOptions> VTBufferTest generate-and-run -seed <seed>
+ * where <seed> is the seed value from the test output.
+ * The test source code is generated in the current directory with
+ * names Value[0-9][0-9].java and Loop.java.
+ * Once generated, the test can be run again without going through
+ * the generation phase with the following commande line:
+ *  $ java <VMOptions> VTBufferTest run
+ */
+
+import jvm.internal.value.*;
+
+import javax.management.*;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.ToolProvider;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.lang.management.*;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+
+public class VTBufferTest implements Runnable {
+    static Random random;
+    static boolean generate;
+    static boolean execute;
+    static long seed = 0;
+    long startTime;
+    boolean verbose = false;
+    String[] valueNames;
+    File[] valueSources;
+    File[] loopSource;
+
+
+    static void usage() {
+        System.out.println("Usage:\n");
+        System.out.println("\tVTBufferTest <command> [options]...\n");
+        System.out.println("\nWhere <command> is one of the following: generate | generate-and-run | run\n");
+        System.out.println("Where [options] can be: -seed <long value>\n");
+    }
+
+    public static void main(String[] args) {
+        if (args.length < 1) {
+            usage();
+            System.exit(-1);
+        }
+
+        if (args[0].compareTo("generate") == 0) {
+            generate = true;
+            execute = false;
+        } else if (args[0].compareTo("generate-and-run") == 0) {
+            generate = true;
+            execute = true;
+        } else if (args[0].compareTo("run") == 0) {
+            generate = false;
+            execute = true;
+        } else {
+            System.out.println("Unknown command\n");
+            usage();
+            System.exit(-1);
+        }
+
+        if (args.length > 1) {
+            int cursor = 1;
+            if (args[cursor].compareTo("-seed") == 0) {
+                if (args.length < 3) {
+                    usage();
+                    System.exit(-1);
+                }
+                seed = Long.valueOf(args[cursor+1]);
+                cursor++;
+            } else {
+                System.out.println("Unknown option\n");
+                usage();
+                System.exit(-1);
+            }
+        }
+
+        if (generate) {
+            if (seed == 0) {
+                seed = System.nanoTime();
+            }
+            random = new Random(seed);
+            System.out.println("Seed= " + seed);
+        }
+
+        VTBufferTest test = new VTBufferTest(true);
+        test.run();
+    }
+
+    public VTBufferTest(boolean verbose) {
+        this.verbose = verbose;
+    }
+
+    static private String[] generateValueNames() {
+        int nvalues = random.nextInt(16) + 4;
+        String[] names = new String[nvalues];
+        for (int i = 0; i < nvalues; i++) {
+            names[i] = new String("Value"+i);
+        }
+        return names;
+    }
+
+    static private File writeSource(String filename, String source) {
+        try{
+            PrintWriter writer = new PrintWriter(filename, "UTF-8");
+            writer.println(source);
+            writer.close();
+        } catch (IOException e) {
+            throw new RuntimeException("Writing source file failed");
+        }
+        return new File(filename);
+    }
+
+    static private File[] generateValueSources(String[] valueNames) {
+        File[] sources = new File[valueNames.length];
+        for (int i = 0; i < valueNames.length; i++) {
+            int nfields = random.nextInt(6) + 1;
+            String s = ValueTypeGenerator.generateValueTypeNoObjectRef(random, valueNames[i], nfields);
+            String filename = valueNames[i]+".java";
+            sources[i] = writeSource(filename, s);
+        }
+        return sources;
+    }
+
+    static private File[] generateLoopSource(String[] names) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("// Seed = ").append(seed).append("\n");
+        // class declaration
+        sb.append("public final class Loop {\n");
+        sb.append("\n");
+
+        sb.append("\tstatic {\n");
+        int i = 0;
+        for (String name : names) {
+            sb.append("\t\t").append(names[i]).append(" lv").append(i).append(" = ");
+            sb.append(names[i]).append(".make").append(names[i]).append("();\n");
+            sb.append("\t\tlv").append(i).append(".printLayout(System.out);\n");
+            i++;
+        }
+        sb.append("\t}\n\n");
+
+        // loop method
+        sb.append("\tstatic public void loop(int iterations) { \n");
+        i = 0;
+        for (String name : names) {
+            sb.append("\t\t").append(names[i]).append(" lv").append(i).append(" = ");
+            sb.append(names[i]).append(".make").append(names[i]).append("();\n");
+            i++;
+        }
+        sb.append("\t\tfor (int i = 0; i < iterations; i++) {\n");
+        i = 0;
+        for (String name : names) {
+            sb.append("\t\t\tif (!").append(names[i]).append(".verify(lv").append(i).append("))\n");
+            sb.append("\t\t\t\tthrow new RuntimeException(\"Error in ").append(names[i]).append("\");\n");
+            i++;
+        }
+        i = 0;
+        for (String name : names) {
+            if (i != 0) {
+                sb.append("\t\t\tif (i % ").append(i).append(" != 0) {\n");
+                sb.append("\t\t\t\tlv").append(i).append(" = ");
+                sb.append(names[i]).append(".make").append(names[i]).append("();\n");
+                sb.append("\t\t\t}\n");
+            }
+            i++;
+        }
+        sb.append("\t\t}\n");
+        sb.append("\t}\n");
+        sb.append("}\n");
+
+        String source = sb.toString();
+
+        File[] files = new File[1];
+        files[0] = writeSource("Loop.java", source);
+        return files;
+    }
+
+    public void run() {
+        if (generate) {
+            valueNames = generateValueNames();
+            valueSources = generateValueSources(valueNames);
+            loopSource = generateLoopSource(valueNames);
+
+            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+            StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
+	    List<String> optionList = new ArrayList<String>();
+	    optionList.addAll(Arrays.asList("-classpath",".","-XDenableValueTypes"));
+
+            Iterable<? extends JavaFileObject> compilationUnits1 =
+                    fileManager.getJavaFileObjectsFromFiles(Arrays.asList(valueSources));
+            compiler.getTask(null, fileManager, null, optionList, null, compilationUnits1).call();
+
+            Iterable<? extends JavaFileObject> compilationUnits2 =
+                    fileManager.getJavaFileObjectsFromFiles(Arrays.asList(loopSource));
+            compiler.getTask(null, fileManager, null, optionList, null, compilationUnits2).call();
+        }
+
+        if (execute) {
+            startTime = ManagementFactory.getRuntimeMXBean().getUptime();
+
+	    ClassLoader cl = createClassLoader();
+            try {
+                iterate(100, 5000, cl);
+            } catch(InvocationTargetException e) {
+                e.getCause().printStackTrace();
+                System.exit(-1);
+            }
+
+            if (verbose) {
+                printVTBufferStats();
+
+                System.out.println("\nGC Statistics:");
+                List<GarbageCollectorMXBean> gcs = ManagementFactory.getGarbageCollectorMXBeans();
+                for (GarbageCollectorMXBean gc : gcs) {
+                    System.out.println("Name=" + gc.getName());
+                    System.out.println("GC counts=" + gc.getCollectionCount());
+                    System.out.println("GC time=" + gc.getCollectionTime() + "ms");
+                }
+
+                System.out.println("\nHeap Statistics");
+                List<MemoryPoolMXBean> memPools = ManagementFactory.getMemoryPoolMXBeans();
+                for (MemoryPoolMXBean memPool : memPools) {
+                    if (memPool.getType() == MemoryType.HEAP) {
+                        System.out.println("\nName: " + memPool.getName());
+                        System.out.println("Usage: " + memPool.getUsage());
+                        System.out.println("Collection Usage: " + memPool.getCollectionUsage());
+                        System.out.println("Peak Usage: " + memPool.getPeakUsage());
+                    }
+                }
+            }
+        }
+    }
+
+
+    ClassLoader createClassLoader() {
+	try{
+	    File file = new File(".");
+	    URL url = file.toURI().toURL();
+	    URL[] urls = new URL[]{url};
+	    ClassLoader cl = new URLClassLoader(urls);
+	    return cl;
+	} catch(Exception ex){
+	    ex.printStackTrace();
+	}
+	return null;
+    }
+    
+    public void iterate(int n, int m, ClassLoader cl) throws InvocationTargetException {
+        for (int i = 0; i < n; i++) {
+            Class loop = null;
+            try {
+                loop = Class.forName("Loop", true, cl);
+            } catch (ClassNotFoundException e) {
+                e.printStackTrace();
+            }
+            Method method = null;
+            try {
+                method = loop.getMethod("loop", int.class);
+            } catch (NoSuchMethodException e) {
+                e.printStackTrace();
+                return;
+            }
+            try {
+                method.invoke(null, m);
+            } catch (IllegalAccessException e) {
+                e.printStackTrace();
+                return;
+            } catch (InvocationTargetException e) {
+                throw e;
+            }
+        }
+    }
+
+    public void printVTBufferStats() {
+	MBeanServerConnection mbs = ManagementFactory.getPlatformMBeanServer();
+	String MBeanName = "com.sun.management:type=DiagnosticCommand";
+	ObjectName beanName;
+	try {
+            beanName = new ObjectName(MBeanName);
+        } catch (MalformedObjectNameException e) {
+            String message = "MBean not found: " + MBeanName;
+            throw new RuntimeException(message, e);
+        }
+	String result = null;
+	try {
+	    result = (String)mbs.invoke(beanName,"vtbufferStats",new Object[0],new String[0]);
+	} catch(Exception e) {
+	    e.printStackTrace();
+	}
+	System.out.println(result);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/runtime/valhalla/valuetypes/ValueTypeGenerator.java	Thu Jun 29 09:35:38 2017 -0400
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.lang.reflect.Type;
+import java.util.Random;
+
+public class ValueTypeGenerator {
+
+    static class FieldDescriptor {
+
+        final public String name;
+        final public Type type;
+
+        public FieldDescriptor(String name, Type type) {
+            this.name = name;
+            this.type = type;
+        }
+    }
+
+    static Type[] typeArray;
+    static String[] defaultArray;
+    static int NB_TYPES = 9;
+
+    static {
+        typeArray = new Type[NB_TYPES];
+        typeArray[0] = byte.class;
+        typeArray[1] = short.class;
+        typeArray[2] = int.class;
+        typeArray[3] = long.class;
+        typeArray[4] = char.class;
+        typeArray[5] = float.class;
+        typeArray[6] = double.class;
+        typeArray[7] = boolean.class;
+        typeArray[8] = Object.class;
+
+    }
+
+    static String defaultValue(Type t) {
+        switch(t.getTypeName()) {
+            case "byte": return "(byte)123";
+            case "short": return "(short)32056";
+            case "int": return "483647";
+            case "long": return "922337203685477L";
+            case "char": return "(char)65456";
+            case "float": return "2.71828f";
+            case "double": return "3.14159d";
+            case "boolean": return "true";
+            case "java.lang.Object": return "null";
+            default:
+                throw new RuntimeException();
+        }
+    }
+    static private String generateValueTypeInternal(Random random, String name, int nfields, int typeLimit) {
+        // generate the fields
+        FieldDescriptor[] fieldDescArray = new FieldDescriptor[nfields];
+        for (int i = 0; i < nfields; i++) {
+            int idx = random.nextInt(typeLimit);
+            String fieldName = typeArray[idx].getTypeName()+"Field"+i;
+            fieldDescArray[i] = new FieldDescriptor(fieldName, typeArray[idx]);
+        }
+
+        String source = generateSource(name, fieldDescArray);
+        return source;
+    }
+
+    static public String generateValueType(Random random, String name, int nfields) {
+        return generateValueTypeInternal(random, name, nfields, NB_TYPES);
+    }
+
+    static public String generateValueTypeNoObjectRef(Random random, String name, int nfields) {
+        return generateValueTypeInternal(random, name, nfields, NB_TYPES - 1);
+    }
+
+    static String fieldsAsArgs(FieldDescriptor[] fields) {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < fields.length; i++) {
+            sb.append(fields[i].type).append(" ").append(fields[i].name);
+            if (i != fields.length - 1) {
+                sb.append(", ");
+            }
+        }
+        return sb.toString();
+    }
+
+    static String generateSource(String name, FieldDescriptor[] fields) {
+        StringBuilder sb = new StringBuilder();
+
+        // imports
+        sb.append("import java.io.PrintStream;\n\n");
+
+        // class declaration
+        sb.append("public __ByValue final class ").append(name).append(" {\n");
+
+        // field declarations
+        for (FieldDescriptor f : fields) {
+            sb.append("\tfinal public ").append(f.type).append(" ");
+            sb.append(f.name).append(";\n");
+        }
+        sb.append("\n");
+
+        // private constructor
+        sb.append("\tprivate ").append(name).append("() {\n");
+	for (int i = 0 ; i < fields.length; i++) {
+	    sb.append("\t\tthis.").append(fields[i].name).append(" = ").append(defaultValue(fields[i].type)).append(";\n");
+        }
+        sb.append("\t}\n");
+        sb.append("\n");
+
+        // factory
+        sb.append("\t__ValueFactory static public ").append(name).append(" ").append("make").append(name).append("(");
+        sb.append(fieldsAsArgs(fields));
+        sb.append(") {\n");
+        sb.append("\t\t").append(name).append(" v = ").append("__MakeDefault ").append(name).append("();\n");
+        for (int i = 0 ; i < fields.length; i++) {
+            sb.append("\t\tv.").append(fields[i].name).append(" = ").append(fields[i].name).append(";\n");
+        }
+	sb.append("\t\treturn v;\n");
+        sb.append("\t};\n");
+        sb.append("\n");
+
+        // default factory
+        sb.append("\t__ValueFactory static public ").append(name).append(" ").append("make").append(name).append("() {\n");
+	sb.append("\t\t").append(name).append(" v = ").append("__MakeDefault ").append(name).append("();\n");
+        for (int i = 0 ; i < fields.length; i++) {
+	    sb.append("\t\tv.").append(fields[i].name).append(" = ").append(defaultValue(fields[i].type)).append(";\n");
+        }
+	sb.append("\t\treturn v;\n");
+        sb.append("\t}\n");
+        sb.append("\n");
+
+        // verify method
+        sb.append("\tstatic public boolean verify(").append(name).append(" value) {\n");
+        for (FieldDescriptor f : fields) {
+            sb.append("\t\tif (value.").append(f.name).append(" != ").append(defaultValue(f.type)).append(") return false;\n");
+        }
+        sb.append("\t\treturn true;\n");
+        sb.append("\t}\n");
+
+        // printLayout method
+        sb.append("\tstatic public void printLayout(PrintStream out) {\n");
+        sb.append("\t\tout.println(\"").append(name).append(" fields: ");
+        for (int i = 0; i < fields.length; i++) {
+            sb.append(fields[i].type);
+            if (i != fields.length - 1) {
+                sb.append(", ");
+            }
+        }
+        sb.append("\");\n");
+        sb.append("\t}\n");
+
+        sb.append("}\n");
+
+        return sb.toString();
+    }
+}
--- a/test/runtime/valhalla/valuetypes/ValueTypeGetField.java	Wed Jun 28 16:38:15 2017 +0800
+++ b/test/runtime/valhalla/valuetypes/ValueTypeGetField.java	Thu Jun 29 09:35:38 2017 -0400
@@ -12,29 +12,60 @@
  */
 public class ValueTypeGetField {
 
-    static Point staticPoint;
-    Point myPoint;
+    static Point staticPoint0;
+    static Point staticPoint1;
+    Point instancePoint0;
+    Point instancePoint1;
 
+    static {
+	staticPoint0 = Point.createPoint(358, 406);
+	staticPoint1 = Point.createPoint(101, 2653);
+    }
+    
+    ValueTypeGetField() {
+	instancePoint0 = Point.createPoint(1890, 1918);
+	instancePoint1 = Point.createPoint(91, 102);
+    }
+    
     public static void main(String[] args) {
         ValueTypeGetField valueTypeGetField = new ValueTypeGetField();
+	System.gc(); // check that VTs survive GC
         valueTypeGetField.run();
     }
 
     public void run() {
-        Point p = Point.createPoint(1, 2);
-        fieldTest(p);
-        System.gc(); // check that VTs survive GC
-        fieldTest(p);
-        System.gc(); // check that VTs survive GC
+	// testing initial configuration 
+	checkPoint(staticPoint0, 358, 406);
+	checkPoint(staticPoint1, 101, 2653);
+	checkPoint(instancePoint0, 1890, 1918);
+	checkPoint(instancePoint1, 91, 102);
+	// swapping static fields
+	Point p = staticPoint1;
+	staticPoint1 = staticPoint0;
+	staticPoint0 = p;
+	System.gc();
+	checkPoint(staticPoint0, 101, 2653);
+	checkPoint(staticPoint1, 358, 406);
+	//swapping instance fields
+	p = instancePoint1;
+	instancePoint1 = instancePoint0;
+	instancePoint0 = p;
+	System.gc();
+	checkPoint(instancePoint0, 91, 102);
+	checkPoint(instancePoint1, 1890, 1918);
+	// instance to static
+	staticPoint0 = instancePoint0;
+	System.gc();
+	checkPoint(staticPoint0, 91, 102);
+	// static to instance
+	instancePoint1 = staticPoint1;
+	System.gc();
+	checkPoint(instancePoint1, 358, 406);
     }
 
-    static void fieldTest(Point p) {
-        staticPoint = p;
-        Asserts.assertEquals(staticPoint.x, 1, "invalid staticPoint x value");
-        Asserts.assertEquals(staticPoint.y, 2, "invalid staticPoint y value");
-        ValueTypeGetField valueTypeGetField = new ValueTypeGetField();
-        valueTypeGetField.myPoint = Point.createPoint(5, 6);
-        Asserts.assertEquals(valueTypeGetField.myPoint.x, 5, "invalid myPoint x value");
-        Asserts.assertEquals(valueTypeGetField.myPoint.y, 6, "invalid myPoint y value");
+    static void checkPoint(Point p , int x, int y) {
+	Asserts.assertEquals(p.x, x, "invalid x value");
+	Asserts.assertEquals(p.y, y, "invalid y value");
     }
+    
 }