/platform/external/webkit/WebCore/bindings/v8/V8GCController.cpp

https://github.com/aharish/totoro-gb-opensource-update2 · C++ · 454 lines · 284 code · 60 blank · 110 comment · 47 complexity · 85e6e2d7549369e5ae8122452a1f044d MD5 · raw file

  1. /*
  2. * Copyright (C) 2009 Google Inc. All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions are
  6. * met:
  7. *
  8. * * Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * * Redistributions in binary form must reproduce the above
  11. * copyright notice, this list of conditions and the following disclaimer
  12. * in the documentation and/or other materials provided with the
  13. * distribution.
  14. * * Neither the name of Google Inc. nor the names of its
  15. * contributors may be used to endorse or promote products derived from
  16. * this software without specific prior written permission.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  19. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  20. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  21. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  22. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  23. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  24. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  25. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  26. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  27. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  28. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29. */
  30. #include "config.h"
  31. #include "V8GCController.h"
  32. #include "DOMDataStore.h"
  33. #include "DOMObjectsInclude.h"
  34. #include "V8DOMMap.h"
  35. #include "V8Index.h"
  36. #include "V8Proxy.h"
  37. #include <algorithm>
  38. #include <utility>
  39. #include <v8.h>
  40. #include <v8-debug.h>
  41. #include <wtf/HashMap.h>
  42. #include <wtf/StdLibExtras.h>
  43. #include <wtf/UnusedParam.h>
  44. namespace WebCore {
  45. #ifndef NDEBUG
  46. // Keeps track of global handles created (not JS wrappers
  47. // of DOM objects). Often these global handles are source
  48. // of leaks.
  49. //
  50. // If you want to let a C++ object hold a persistent handle
  51. // to a JS object, you should register the handle here to
  52. // keep track of leaks.
  53. //
  54. // When creating a persistent handle, call:
  55. //
  56. // #ifndef NDEBUG
  57. // V8GCController::registerGlobalHandle(type, host, handle);
  58. // #endif
  59. //
  60. // When releasing the handle, call:
  61. //
  62. // #ifndef NDEBUG
  63. // V8GCController::unregisterGlobalHandle(type, host, handle);
  64. // #endif
  65. //
  66. typedef HashMap<v8::Value*, GlobalHandleInfo*> GlobalHandleMap;
  67. static GlobalHandleMap& globalHandleMap()
  68. {
  69. DEFINE_STATIC_LOCAL(GlobalHandleMap, staticGlobalHandleMap, ());
  70. return staticGlobalHandleMap;
  71. }
  72. // The function is the place to set the break point to inspect
  73. // live global handles. Leaks are often come from leaked global handles.
  74. static void enumerateGlobalHandles()
  75. {
  76. for (GlobalHandleMap::iterator it = globalHandleMap().begin(), end = globalHandleMap().end(); it != end; ++it) {
  77. GlobalHandleInfo* info = it->second;
  78. UNUSED_PARAM(info);
  79. v8::Value* handle = it->first;
  80. UNUSED_PARAM(handle);
  81. }
  82. }
  83. void V8GCController::registerGlobalHandle(GlobalHandleType type, void* host, v8::Persistent<v8::Value> handle)
  84. {
  85. ASSERT(!globalHandleMap().contains(*handle));
  86. globalHandleMap().set(*handle, new GlobalHandleInfo(host, type));
  87. }
  88. void V8GCController::unregisterGlobalHandle(void* host, v8::Persistent<v8::Value> handle)
  89. {
  90. ASSERT(globalHandleMap().contains(*handle));
  91. GlobalHandleInfo* info = globalHandleMap().take(*handle);
  92. ASSERT(info->m_host == host);
  93. delete info;
  94. }
  95. #endif // ifndef NDEBUG
  96. typedef HashMap<Node*, v8::Object*> DOMNodeMap;
  97. typedef HashMap<void*, v8::Object*> DOMObjectMap;
  98. #ifndef NDEBUG
  99. static void enumerateDOMObjectMap(DOMObjectMap& wrapperMap)
  100. {
  101. for (DOMObjectMap::iterator it = wrapperMap.begin(), end = wrapperMap.end(); it != end; ++it) {
  102. v8::Persistent<v8::Object> wrapper(it->second);
  103. V8ClassIndex::V8WrapperType type = V8DOMWrapper::domWrapperType(wrapper);
  104. void* object = it->first;
  105. UNUSED_PARAM(type);
  106. UNUSED_PARAM(object);
  107. }
  108. }
  109. class DOMObjectVisitor : public DOMWrapperMap<void>::Visitor {
  110. public:
  111. void visitDOMWrapper(void* object, v8::Persistent<v8::Object> wrapper)
  112. {
  113. V8ClassIndex::V8WrapperType type = V8DOMWrapper::domWrapperType(wrapper);
  114. UNUSED_PARAM(type);
  115. UNUSED_PARAM(object);
  116. }
  117. };
  118. class EnsureWeakDOMNodeVisitor : public DOMWrapperMap<Node>::Visitor {
  119. public:
  120. void visitDOMWrapper(Node* object, v8::Persistent<v8::Object> wrapper)
  121. {
  122. UNUSED_PARAM(object);
  123. ASSERT(wrapper.IsWeak());
  124. }
  125. };
  126. #endif // NDEBUG
  127. // A map from a DOM node to its JS wrapper, the wrapper
  128. // is kept as a strong reference to survive GCs.
  129. static DOMObjectMap& gcProtectedMap()
  130. {
  131. DEFINE_STATIC_LOCAL(DOMObjectMap, staticGcProtectedMap, ());
  132. return staticGcProtectedMap;
  133. }
  134. void V8GCController::gcProtect(void* domObject)
  135. {
  136. if (!domObject)
  137. return;
  138. if (gcProtectedMap().contains(domObject))
  139. return;
  140. if (!getDOMObjectMap().contains(domObject))
  141. return;
  142. // Create a new (strong) persistent handle for the object.
  143. v8::Persistent<v8::Object> wrapper = getDOMObjectMap().get(domObject);
  144. if (wrapper.IsEmpty())
  145. return;
  146. gcProtectedMap().set(domObject, *v8::Persistent<v8::Object>::New(wrapper));
  147. }
  148. void V8GCController::gcUnprotect(void* domObject)
  149. {
  150. if (!domObject)
  151. return;
  152. if (!gcProtectedMap().contains(domObject))
  153. return;
  154. // Dispose the strong reference.
  155. v8::Persistent<v8::Object> wrapper(gcProtectedMap().take(domObject));
  156. wrapper.Dispose();
  157. }
  158. class GCPrologueVisitor : public DOMWrapperMap<void>::Visitor {
  159. public:
  160. void visitDOMWrapper(void* object, v8::Persistent<v8::Object> wrapper)
  161. {
  162. ASSERT(wrapper.IsWeak());
  163. V8ClassIndex::V8WrapperType type = V8DOMWrapper::domWrapperType(wrapper);
  164. switch (type) {
  165. #define MAKE_CASE(TYPE, NAME) \
  166. case V8ClassIndex::TYPE: { \
  167. NAME* impl = static_cast<NAME*>(object); \
  168. if (impl->hasPendingActivity()) \
  169. wrapper.ClearWeak(); \
  170. break; \
  171. }
  172. ACTIVE_DOM_OBJECT_TYPES(MAKE_CASE)
  173. default:
  174. ASSERT_NOT_REACHED();
  175. #undef MAKE_CASE
  176. }
  177. // Additional handling of message port ensuring that entangled ports also
  178. // have their wrappers entangled. This should ideally be handled when the
  179. // ports are actually entangled in MessagePort::entangle, but to avoid
  180. // forking MessagePort.* this is postponed to GC time. Having this postponed
  181. // has the drawback that the wrappers are "entangled/unentangled" for each
  182. // GC even though their entaglement most likely is still the same.
  183. if (type == V8ClassIndex::MESSAGEPORT) {
  184. // Mark each port as in-use if it's entangled. For simplicity's sake, we assume all ports are remotely entangled,
  185. // since the Chromium port implementation can't tell the difference.
  186. MessagePort* port1 = static_cast<MessagePort*>(object);
  187. if (port1->isEntangled())
  188. wrapper.ClearWeak();
  189. }
  190. }
  191. };
  192. class GrouperItem {
  193. public:
  194. GrouperItem(uintptr_t groupId, Node* node, v8::Persistent<v8::Object> wrapper)
  195. : m_groupId(groupId)
  196. , m_node(node)
  197. , m_wrapper(wrapper)
  198. {
  199. }
  200. uintptr_t groupId() const { return m_groupId; }
  201. Node* node() const { return m_node; }
  202. v8::Persistent<v8::Object> wrapper() const { return m_wrapper; }
  203. private:
  204. uintptr_t m_groupId;
  205. Node* m_node;
  206. v8::Persistent<v8::Object> m_wrapper;
  207. };
  208. bool operator<(const GrouperItem& a, const GrouperItem& b)
  209. {
  210. return a.groupId() < b.groupId();
  211. }
  212. typedef Vector<GrouperItem> GrouperList;
  213. class ObjectGrouperVisitor : public DOMWrapperMap<Node>::Visitor {
  214. public:
  215. ObjectGrouperVisitor()
  216. {
  217. // FIXME: grouper_.reserveCapacity(node_map.size()); ?
  218. }
  219. void visitDOMWrapper(Node* node, v8::Persistent<v8::Object> wrapper)
  220. {
  221. // If the node is in document, put it in the ownerDocument's object group.
  222. //
  223. // If an image element was created by JavaScript "new Image",
  224. // it is not in a document. However, if the load event has not
  225. // been fired (still onloading), it is treated as in the document.
  226. //
  227. // Otherwise, the node is put in an object group identified by the root
  228. // element of the tree to which it belongs.
  229. uintptr_t groupId;
  230. if (node->inDocument() || (node->hasTagName(HTMLNames::imgTag) && !static_cast<HTMLImageElement*>(node)->haveFiredLoadEvent()))
  231. groupId = reinterpret_cast<uintptr_t>(node->document());
  232. else {
  233. Node* root = node;
  234. if (node->isAttributeNode()) {
  235. root = static_cast<Attr*>(node)->ownerElement();
  236. // If the attribute has no element, no need to put it in the group,
  237. // because it'll always be a group of 1.
  238. if (!root)
  239. return;
  240. } else {
  241. while (root->parent())
  242. root = root->parent();
  243. // If the node is alone in its DOM tree (doesn't have a parent or any
  244. // children) then the group will be filtered out later anyway.
  245. if (root == node && !node->hasChildNodes() && !node->hasAttributes())
  246. return;
  247. }
  248. groupId = reinterpret_cast<uintptr_t>(root);
  249. }
  250. m_grouper.append(GrouperItem(groupId, node, wrapper));
  251. }
  252. void applyGrouping()
  253. {
  254. // Group by sorting by the group id.
  255. std::sort(m_grouper.begin(), m_grouper.end());
  256. // FIXME Should probably work in iterators here, but indexes were easier for my simple mind.
  257. for (size_t i = 0; i < m_grouper.size(); ) {
  258. // Seek to the next key (or the end of the list).
  259. size_t nextKeyIndex = m_grouper.size();
  260. for (size_t j = i; j < m_grouper.size(); ++j) {
  261. if (m_grouper[i].groupId() != m_grouper[j].groupId()) {
  262. nextKeyIndex = j;
  263. break;
  264. }
  265. }
  266. ASSERT(nextKeyIndex > i);
  267. // We only care about a group if it has more than one object. If it only
  268. // has one object, it has nothing else that needs to be kept alive.
  269. if (nextKeyIndex - i <= 1) {
  270. i = nextKeyIndex;
  271. continue;
  272. }
  273. Vector<v8::Persistent<v8::Value> > group;
  274. group.reserveCapacity(nextKeyIndex - i);
  275. for (; i < nextKeyIndex; ++i) {
  276. v8::Persistent<v8::Value> wrapper = m_grouper[i].wrapper();
  277. if (!wrapper.IsEmpty())
  278. group.append(wrapper);
  279. /* FIXME: Re-enabled this code to avoid GCing these wrappers!
  280. Currently this depends on looking up the wrapper
  281. during a GC, but we don't know which isolated world
  282. we're in, so it's unclear which map to look in...
  283. // If the node is styled and there is a wrapper for the inline
  284. // style declaration, we need to keep that style declaration
  285. // wrapper alive as well, so we add it to the object group.
  286. if (node->isStyledElement()) {
  287. StyledElement* element = reinterpret_cast<StyledElement*>(node);
  288. CSSStyleDeclaration* style = element->inlineStyleDecl();
  289. if (style != NULL) {
  290. wrapper = getDOMObjectMap().get(style);
  291. if (!wrapper.IsEmpty())
  292. group.append(wrapper);
  293. }
  294. }
  295. */
  296. }
  297. if (group.size() > 1)
  298. v8::V8::AddObjectGroup(&group[0], group.size());
  299. ASSERT(i == nextKeyIndex);
  300. }
  301. }
  302. private:
  303. GrouperList m_grouper;
  304. };
  305. // Create object groups for DOM tree nodes.
  306. void V8GCController::gcPrologue()
  307. {
  308. v8::HandleScope scope;
  309. #ifndef NDEBUG
  310. DOMObjectVisitor domObjectVisitor;
  311. visitDOMObjectsInCurrentThread(&domObjectVisitor);
  312. #endif
  313. // Run through all objects with possible pending activity making their
  314. // wrappers non weak if there is pending activity.
  315. GCPrologueVisitor prologueVisitor;
  316. visitActiveDOMObjectsInCurrentThread(&prologueVisitor);
  317. // Create object groups.
  318. ObjectGrouperVisitor objectGrouperVisitor;
  319. visitDOMNodesInCurrentThread(&objectGrouperVisitor);
  320. objectGrouperVisitor.applyGrouping();
  321. }
  322. class GCEpilogueVisitor : public DOMWrapperMap<void>::Visitor {
  323. public:
  324. void visitDOMWrapper(void* object, v8::Persistent<v8::Object> wrapper)
  325. {
  326. V8ClassIndex::V8WrapperType type = V8DOMWrapper::domWrapperType(wrapper);
  327. switch (type) {
  328. #define MAKE_CASE(TYPE, NAME) \
  329. case V8ClassIndex::TYPE: { \
  330. NAME* impl = static_cast<NAME*>(object); \
  331. if (impl->hasPendingActivity()) { \
  332. ASSERT(!wrapper.IsWeak()); \
  333. wrapper.MakeWeak(impl, &DOMDataStore::weakActiveDOMObjectCallback); \
  334. } \
  335. break; \
  336. }
  337. ACTIVE_DOM_OBJECT_TYPES(MAKE_CASE)
  338. default:
  339. ASSERT_NOT_REACHED();
  340. #undef MAKE_CASE
  341. }
  342. if (type == V8ClassIndex::MESSAGEPORT) {
  343. MessagePort* port1 = static_cast<MessagePort*>(object);
  344. // We marked this port as reachable in GCPrologueVisitor. Undo this now since the
  345. // port could be not reachable in the future if it gets disentangled (and also
  346. // GCPrologueVisitor expects to see all handles marked as weak).
  347. if (!wrapper.IsWeak() && !wrapper.IsNearDeath())
  348. wrapper.MakeWeak(port1, &DOMDataStore::weakActiveDOMObjectCallback);
  349. }
  350. }
  351. };
  352. int V8GCController::workingSetEstimateMB = 0;
  353. namespace {
  354. int getMemoryUsageInMB()
  355. {
  356. #if PLATFORM(CHROMIUM)
  357. return ChromiumBridge::memoryUsageMB();
  358. #else
  359. return 0;
  360. #endif
  361. }
  362. } // anonymous namespace
  363. void V8GCController::gcEpilogue()
  364. {
  365. v8::HandleScope scope;
  366. // Run through all objects with pending activity making their wrappers weak
  367. // again.
  368. GCEpilogueVisitor epilogueVisitor;
  369. visitActiveDOMObjectsInCurrentThread(&epilogueVisitor);
  370. workingSetEstimateMB = getMemoryUsageInMB();
  371. #ifndef NDEBUG
  372. // Check all survivals are weak.
  373. DOMObjectVisitor domObjectVisitor;
  374. visitDOMObjectsInCurrentThread(&domObjectVisitor);
  375. EnsureWeakDOMNodeVisitor weakDOMNodeVisitor;
  376. visitDOMNodesInCurrentThread(&weakDOMNodeVisitor);
  377. enumerateDOMObjectMap(gcProtectedMap());
  378. enumerateGlobalHandles();
  379. #endif
  380. }
  381. void V8GCController::checkMemoryUsage()
  382. {
  383. #if PLATFORM(CHROMIUM)
  384. // These values are appropriate for Chromium only.
  385. const int lowUsageMB = 256; // If memory usage is below this threshold, do not bother forcing GC.
  386. const int highUsageMB = 1024; // If memory usage is above this threshold, force GC more aggresively.
  387. const int highUsageDeltaMB = 128; // Delta of memory usage growth (vs. last workingSetEstimateMB) to force GC when memory usage is high.
  388. int memoryUsageMB = getMemoryUsageInMB();
  389. if ((memoryUsageMB > lowUsageMB && memoryUsageMB > 2 * workingSetEstimateMB) || (memoryUsageMB > highUsageMB && memoryUsageMB > workingSetEstimateMB + highUsageDeltaMB))
  390. v8::V8::LowMemoryNotification();
  391. #endif
  392. }
  393. } // namespace WebCore