Revision: 4582
Author: [email protected]
Date: Tue May 4 09:42:11 2010
Log: Clean JS function results cache on each major GC.
We don't want to retain cached objects for too long.
Review URL: http://codereview.chromium.org/1780001
http://code.google.com/p/v8/source/detail?r=4582
Modified:
/branches/bleeding_edge/src/bootstrapper.cc
/branches/bleeding_edge/src/heap.cc
/branches/bleeding_edge/src/heap.h
/branches/bleeding_edge/src/objects-debug.cc
/branches/bleeding_edge/src/objects-inl.h
/branches/bleeding_edge/src/objects.h
/branches/bleeding_edge/test/cctest/test-threads.cc
=======================================
--- /branches/bleeding_edge/src/bootstrapper.cc Thu Apr 29 03:39:36 2010
+++ /branches/bleeding_edge/src/bootstrapper.cc Tue May 4 09:42:11 2010
@@ -1335,14 +1335,12 @@
static FixedArray* CreateCache(int size, JSFunction* factory) {
// Caches are supposed to live for a long time, allocate in old space.
int array_size = JSFunctionResultCache::kEntriesIndex + 2 * size;
- Handle<FixedArray> cache =
- Factory::NewFixedArrayWithHoles(array_size, TENURED);
+ // Cannot use cast as object is not fully initialized yet.
+ JSFunctionResultCache* cache = reinterpret_cast<JSFunctionResultCache*>(
+ *Factory::NewFixedArrayWithHoles(array_size, TENURED));
cache->set(JSFunctionResultCache::kFactoryIndex, factory);
- cache->set(JSFunctionResultCache::kFingerIndex,
- Smi::FromInt(JSFunctionResultCache::kEntriesIndex));
- cache->set(JSFunctionResultCache::kCacheSizeIndex,
- Smi::FromInt(JSFunctionResultCache::kEntriesIndex));
- return *cache;
+ cache->MakeZeroSize();
+ return cache;
}
=======================================
--- /branches/bleeding_edge/src/heap.cc Thu Apr 29 07:01:37 2010
+++ /branches/bleeding_edge/src/heap.cc Tue May 4 09:42:11 2010
@@ -306,6 +306,7 @@
void Heap::GarbageCollectionPrologue() {
TranscendentalCache::Clear();
+ ClearJSFunctionResultCaches();
gc_count_++;
unflattened_strings_length_ = 0;
#ifdef DEBUG
@@ -539,6 +540,28 @@
// Memory is exhausted and we will die.
V8::FatalProcessOutOfMemory("Committing semi space failed.");
}
+
+
+class ClearThreadJSFunctionResultCachesVisitor: public ThreadVisitor {
+ virtual void VisitThread(ThreadLocalTop* top) {
+ Context* context = top->context_;
+ if (context == NULL) return;
+
+ FixedArray* caches =
+ context->global()->global_context()->jsfunction_result_caches();
+ int length = caches->length();
+ for (int i = 0; i < length; i++) {
+ JSFunctionResultCache::cast(caches->get(i))->Clear();
+ }
+ }
+};
+
+
+void Heap::ClearJSFunctionResultCaches() {
+ if (Bootstrapper::IsActive()) return;
+ ClearThreadJSFunctionResultCachesVisitor visitor;
+ ThreadManager::IterateThreads(&visitor);
+}
void Heap::PerformGarbageCollection(AllocationSpace space,
=======================================
--- /branches/bleeding_edge/src/heap.h Thu Apr 29 07:01:37 2010
+++ /branches/bleeding_edge/src/heap.h Tue May 4 09:42:11 2010
@@ -1178,6 +1178,8 @@
HeapObject* target,
int size);
+ static void ClearJSFunctionResultCaches();
+
#if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING)
// Record the copy of an object in the NewSpace's statistics.
static void RecordCopiedObject(HeapObject* obj);
=======================================
--- /branches/bleeding_edge/src/objects-debug.cc Mon Apr 19 05:39:07 2010
+++ /branches/bleeding_edge/src/objects-debug.cc Tue May 4 09:42:11 2010
@@ -1326,6 +1326,32 @@
}
return true;
}
+
+
+void JSFunctionResultCache::JSFunctionResultCacheVerify() {
+ JSFunction::cast(get(kFactoryIndex))->Verify();
+
+ int size = Smi::cast(get(kCacheSizeIndex))->value();
+ ASSERT(kEntriesIndex <= size);
+ ASSERT(size <= length());
+ ASSERT_EQ(0, size % kEntrySize);
+
+ int finger = Smi::cast(get(kFingerIndex))->value();
+ ASSERT(kEntriesIndex <= finger);
+ ASSERT(finger < size || finger == kEntriesIndex);
+ ASSERT_EQ(0, finger % kEntrySize);
+
+ if (FLAG_enable_slow_asserts) {
+ for (int i = kEntriesIndex; i < size; i++) {
+ ASSERT(!get(i)->IsTheHole());
+ get(i)->Verify();
+ }
+ for (int i = size; i < length(); i++) {
+ ASSERT(get(i)->IsTheHole());
+ get(i)->Verify();
+ }
+ }
+}
#endif // DEBUG
=======================================
--- /branches/bleeding_edge/src/objects-inl.h Tue May 4 07:49:50 2010
+++ /branches/bleeding_edge/src/objects-inl.h Tue May 4 09:42:11 2010
@@ -567,6 +567,22 @@
bool Object::IsSymbolTable() {
return IsHashTable() && this == Heap::raw_unchecked_symbol_table();
}
+
+
+bool Object::IsJSFunctionResultCache() {
+ if (!IsFixedArray()) return false;
+ FixedArray* self = FixedArray::cast(this);
+ int length = self->length();
+ if (length < JSFunctionResultCache::kEntriesIndex) return false;
+ if ((length - JSFunctionResultCache::kEntriesIndex)
+ % JSFunctionResultCache::kEntrySize != 0) {
+ return false;
+ }
+#ifdef DEBUG
+
reinterpret_cast<JSFunctionResultCache*>(this)->JSFunctionResultCacheVerify();
+#endif
+ return true;
+}
bool Object::IsCompilationCacheTable() {
@@ -1594,6 +1610,7 @@
CAST_ACCESSOR(FixedArray)
CAST_ACCESSOR(DescriptorArray)
CAST_ACCESSOR(SymbolTable)
+CAST_ACCESSOR(JSFunctionResultCache)
CAST_ACCESSOR(CompilationCacheTable)
CAST_ACCESSOR(CodeCacheHashTable)
CAST_ACCESSOR(MapCache)
@@ -1834,6 +1851,20 @@
ExternalTwoByteString::Resource* resource) {
*reinterpret_cast<Resource**>(FIELD_ADDR(this, kResourceOffset)) =
resource;
}
+
+
+void JSFunctionResultCache::MakeZeroSize() {
+ set(kFingerIndex, Smi::FromInt(kEntriesIndex));
+ set(kCacheSizeIndex, Smi::FromInt(kEntriesIndex));
+}
+
+
+void JSFunctionResultCache::Clear() {
+ int cache_size = Smi::cast(get(kCacheSizeIndex))->value();
+ Object** entries_start = RawField(this,
OffsetOfElementAt(kEntriesIndex));
+ MemsetPointer(entries_start, Heap::the_hole_value(), cache_size);
+ MakeZeroSize();
+}
byte ByteArray::get(int index) {
=======================================
--- /branches/bleeding_edge/src/objects.h Tue May 4 07:49:50 2010
+++ /branches/bleeding_edge/src/objects.h Tue May 4 09:42:11 2010
@@ -606,6 +606,7 @@
inline bool IsHashTable();
inline bool IsDictionary();
inline bool IsSymbolTable();
+ inline bool IsJSFunctionResultCache();
inline bool IsCompilationCacheTable();
inline bool IsCodeCacheHashTable();
inline bool IsMapCache();
@@ -2326,6 +2327,16 @@
static const int kEntriesIndex = kDummyIndex + 1;
static const int kEntrySize = 2; // key + value
+
+ inline void MakeZeroSize();
+ inline void Clear();
+
+ // Casting
+ static inline JSFunctionResultCache* cast(Object* obj);
+
+#ifdef DEBUG
+ void JSFunctionResultCacheVerify();
+#endif
};
=======================================
--- /branches/bleeding_edge/test/cctest/test-threads.cc Wed Aug 19 08:14:11
2009
+++ /branches/bleeding_edge/test/cctest/test-threads.cc Tue May 4 09:42:11
2010
@@ -50,3 +50,87 @@
script->Run();
}
+
+
+enum Turn {
+ FILL_CACHE,
+ CLEAN_CACHE,
+ SECOND_TIME_FILL_CACHE,
+ DONE
+};
+
+static Turn turn = FILL_CACHE;
+
+
+class ThreadA: public v8::internal::Thread {
+ public:
+ void Run() {
+ v8::Locker locker;
+ v8::HandleScope scope;
+ v8::Context::Scope context_scope(v8::Context::New());
+
+ CHECK_EQ(FILL_CACHE, turn);
+
+ // Fill String.search cache.
+ v8::Handle<v8::Script> script = v8::Script::Compile(
+ v8::String::New(
+ "for (var i = 0; i < 3; i++) {"
+ " var result = \"a\".search(\"a\");"
+ " if (result != 0) throw \"result: \" + result + \" @\" + i;"
+ "};"
+ "true"));
+ CHECK(script->Run()->IsTrue());
+
+ turn = CLEAN_CACHE;
+ do {
+ {
+ v8::Unlocker unlocker;
+ Thread::YieldCPU();
+ }
+ } while (turn != SECOND_TIME_FILL_CACHE);
+
+ // Rerun the script.
+ CHECK(script->Run()->IsTrue());
+
+ turn = DONE;
+ }
+};
+
+
+class ThreadB: public v8::internal::Thread {
+ public:
+ void Run() {
+ do {
+ {
+ v8::Locker locker;
+ if (turn == CLEAN_CACHE) {
+ v8::HandleScope scope;
+ v8::Context::Scope context_scope(v8::Context::New());
+
+ // Clear the caches by forcing major GC.
+ v8::internal::Heap::CollectAllGarbage(false);
+ turn = SECOND_TIME_FILL_CACHE;
+ break;
+ }
+ }
+
+ Thread::YieldCPU();
+ } while (true);
+ }
+};
+
+
+TEST(JSFunctionResultCachesInTwoThreads) {
+ v8::V8::Initialize();
+
+ ThreadA threadA;
+ ThreadB threadB;
+
+ threadA.Start();
+ threadB.Start();
+
+ threadA.Join();
+ threadB.Join();
+
+ CHECK_EQ(DONE, turn);
+}
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev