Title: [228366] trunk
Revision
228366
Author
fpi...@apple.com
Date
2018-02-10 15:49:54 -0800 (Sat, 10 Feb 2018)

Log Message

Don't waste memory for error.stack
https://bugs.webkit.org/show_bug.cgi?id=182656

Reviewed by Saam Barati.
        
JSTests:

Tests the policy.

* stress/gc-error-stack.js: Added. Shows that the GC forgets frames now.
* stress/no-gc-error-stack.js: Added. Shows that the GC won't forget things if you ask for the stack.

Source/_javascript_Core:

This makes the StackFrames in ErrorInstance and Exception weak. We simply forget their
contents if we GC.
        
This isn't going to happen under normal operation since your callees and code blocks will
still be alive when you ask for .stack.
        
Bug 182650 tracks improving this so that it's not lossy. For now, I think it's worth it,
since it is likely to recover 3-5 MB on membuster.

* heap/Heap.cpp:
(JSC::Heap::finalizeUnconditionalFinalizers):
* runtime/ErrorInstance.cpp:
(JSC::ErrorInstance::visitChildren):
(JSC::ErrorInstance::finalizeUnconditionally):
* runtime/ErrorInstance.h:
(JSC::ErrorInstance::subspaceFor):
* runtime/Exception.cpp:
(JSC::Exception::visitChildren):
(JSC::Exception::finalizeUnconditionally):
* runtime/Exception.h:
(JSC::Exception::valueOffset): Deleted.
(JSC::Exception::value const): Deleted.
(JSC::Exception::stack const): Deleted.
(JSC::Exception::didNotifyInspectorOfThrow const): Deleted.
(JSC::Exception::setDidNotifyInspectorOfThrow): Deleted.
* runtime/StackFrame.cpp:
(JSC::StackFrame::isFinalizationCandidate):
(JSC::StackFrame::finalizeUnconditionally):
(JSC::StackFrame::visitChildren): Deleted.
* runtime/StackFrame.h:
* runtime/VM.cpp:
(JSC::VM::VM):
* runtime/VM.h:

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (228365 => 228366)


--- trunk/JSTests/ChangeLog	2018-02-10 15:44:55 UTC (rev 228365)
+++ trunk/JSTests/ChangeLog	2018-02-10 23:49:54 UTC (rev 228366)
@@ -1,3 +1,15 @@
+2018-02-09  Filip Pizlo  <fpi...@apple.com>
+
+        Don't waste memory for error.stack
+        https://bugs.webkit.org/show_bug.cgi?id=182656
+
+        Reviewed by Saam Barati.
+        
+        Tests the policy.
+
+        * stress/gc-error-stack.js: Added. Shows that the GC forgets frames now.
+        * stress/no-gc-error-stack.js: Added. Shows that the GC won't forget things if you ask for the stack.
+
 2018-02-08  Yusuke Suzuki  <utatane....@gmail.com>
 
         [JSC] Update Test262 to Feb 9 version

Added: trunk/JSTests/stress/gc-error-stack.js (0 => 228366)


--- trunk/JSTests/stress/gc-error-stack.js	                        (rev 0)
+++ trunk/JSTests/stress/gc-error-stack.js	2018-02-10 23:49:54 UTC (rev 228366)
@@ -0,0 +1,18 @@
+"use strict";
+
+var errors = [];
+
+for (let i = 0; i < 1000; ++i)
+    eval("(function foo" + i + "() { errors.push(new Error()); })();");
+
+gc();
+
+let didGCAtLeastOne = false;
+for (let error of errors) {
+    if (!error.stack.startsWith("foo"))
+        didGCAtLeastOne = true;
+}
+
+if (!didGCAtLeastOne)
+    throw new Error("Bad result: didGCAtLeastOne = " + didGCAtLeastOne);
+

Added: trunk/JSTests/stress/no-gc-error-stack.js (0 => 228366)


--- trunk/JSTests/stress/no-gc-error-stack.js	                        (rev 0)
+++ trunk/JSTests/stress/no-gc-error-stack.js	2018-02-10 23:49:54 UTC (rev 228366)
@@ -0,0 +1,22 @@
+//@ runDefault
+
+"use strict";
+
+var errors = [];
+
+for (let i = 0; i < 1000; ++i)
+    eval("(function foo" + i + "() { errors.push(new Error()); })();");
+
+for (let error of errors)
+    error.stack;
+
+gc();
+
+for (let error of errors) {
+    if (!error.stack.startsWith("foo")) {
+        print("ERROR: Stack does not begin with foo: " + error.stack);
+        throw new Error("fail");
+    }
+}
+
+

Modified: trunk/Source/_javascript_Core/ChangeLog (228365 => 228366)


--- trunk/Source/_javascript_Core/ChangeLog	2018-02-10 15:44:55 UTC (rev 228365)
+++ trunk/Source/_javascript_Core/ChangeLog	2018-02-10 23:49:54 UTC (rev 228366)
@@ -1,3 +1,44 @@
+2018-02-09  Filip Pizlo  <fpi...@apple.com>
+
+        Don't waste memory for error.stack
+        https://bugs.webkit.org/show_bug.cgi?id=182656
+
+        Reviewed by Saam Barati.
+        
+        This makes the StackFrames in ErrorInstance and Exception weak. We simply forget their
+        contents if we GC.
+        
+        This isn't going to happen under normal operation since your callees and code blocks will
+        still be alive when you ask for .stack.
+        
+        Bug 182650 tracks improving this so that it's not lossy. For now, I think it's worth it,
+        since it is likely to recover 3-5 MB on membuster.
+
+        * heap/Heap.cpp:
+        (JSC::Heap::finalizeUnconditionalFinalizers):
+        * runtime/ErrorInstance.cpp:
+        (JSC::ErrorInstance::visitChildren):
+        (JSC::ErrorInstance::finalizeUnconditionally):
+        * runtime/ErrorInstance.h:
+        (JSC::ErrorInstance::subspaceFor):
+        * runtime/Exception.cpp:
+        (JSC::Exception::visitChildren):
+        (JSC::Exception::finalizeUnconditionally):
+        * runtime/Exception.h:
+        (JSC::Exception::valueOffset): Deleted.
+        (JSC::Exception::value const): Deleted.
+        (JSC::Exception::stack const): Deleted.
+        (JSC::Exception::didNotifyInspectorOfThrow const): Deleted.
+        (JSC::Exception::setDidNotifyInspectorOfThrow): Deleted.
+        * runtime/StackFrame.cpp:
+        (JSC::StackFrame::isFinalizationCandidate):
+        (JSC::StackFrame::finalizeUnconditionally):
+        (JSC::StackFrame::visitChildren): Deleted.
+        * runtime/StackFrame.h:
+        * runtime/VM.cpp:
+        (JSC::VM::VM):
+        * runtime/VM.h:
+
 2018-02-09  Carlos Alberto Lopez Perez  <clo...@igalia.com>
 
         Fix build on ARMv7 traditional JSCOnly bot after r228306

Modified: trunk/Source/_javascript_Core/heap/Heap.cpp (228365 => 228366)


--- trunk/Source/_javascript_Core/heap/Heap.cpp	2018-02-10 15:44:55 UTC (rev 228365)
+++ trunk/Source/_javascript_Core/heap/Heap.cpp	2018-02-10 23:49:54 UTC (rev 228366)
@@ -579,6 +579,8 @@
 
 void Heap::finalizeUnconditionalFinalizers()
 {
+    finalizeMarkedUnconditionalFinalizers<ErrorInstance>(vm()->errorInstancesWithFinalizers);
+    finalizeMarkedUnconditionalFinalizers<Exception>(vm()->exceptionsWithFinalizers);
     finalizeMarkedUnconditionalFinalizers<InferredType>(vm()->inferredTypesWithFinalizers);
     finalizeMarkedUnconditionalFinalizers<InferredValue>(vm()->inferredValuesWithFinalizers);
     vm()->forEachCodeBlockSpace(

Modified: trunk/Source/_javascript_Core/runtime/ErrorInstance.cpp (228365 => 228366)


--- trunk/Source/_javascript_Core/runtime/ErrorInstance.cpp	2018-02-10 15:44:55 UTC (rev 228365)
+++ trunk/Source/_javascript_Core/runtime/ErrorInstance.cpp	2018-02-10 23:49:54 UTC (rev 228366)
@@ -1,6 +1,6 @@
 /*
  *  Copyright (C) 1999-2000 Harri Porten (por...@kde.org)
- *  Copyright (C) 2003-2017 Apple Inc. All rights reserved.
+ *  Copyright (C) 2003-2018 Apple Inc. All rights reserved.
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Lesser General Public
@@ -23,6 +23,7 @@
 
 #include "CodeBlock.h"
 #include "InlineCallFrame.h"
+#include "IsoCellSetInlines.h"
 #include "JSScope.h"
 #include "JSCInlines.h"
 #include "ParseInt.h"
@@ -233,15 +234,35 @@
     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
     Base::visitChildren(thisObject, visitor);
 
+    bool isFinalizationCandidate = false;
     {
         auto locker = holdLock(thisObject->cellLock());
         if (thisObject->m_stackTrace) {
-            for (StackFrame& frame : *thisObject->m_stackTrace)
-                frame.visitChildren(visitor);
+            for (StackFrame& frame : *thisObject->m_stackTrace) {
+                if (frame.isFinalizationCandidate()) {
+                    isFinalizationCandidate = true;
+                    break;
+                }
+            }
         }
     }
+    if (isFinalizationCandidate)
+        visitor.vm().errorInstancesWithFinalizers.add(thisObject);
 }
 
+void ErrorInstance::finalizeUnconditionally(VM& vm)
+{
+    {
+        auto locker = holdLock(cellLock());
+        if (m_stackTrace) {
+            for (StackFrame& frame : *m_stackTrace)
+                frame.finalizeUnconditionally(vm);
+        }
+    }
+    
+    vm.errorInstancesWithFinalizers.remove(this);
+}
+
 bool ErrorInstance::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
 {
     VM& vm = exec->vm();

Modified: trunk/Source/_javascript_Core/runtime/ErrorInstance.h (228365 => 228366)


--- trunk/Source/_javascript_Core/runtime/ErrorInstance.h	2018-02-10 15:44:55 UTC (rev 228365)
+++ trunk/Source/_javascript_Core/runtime/ErrorInstance.h	2018-02-10 23:49:54 UTC (rev 228366)
@@ -1,6 +1,6 @@
 /*
  *  Copyright (C) 1999-2000 Harri Porten (por...@kde.org)
- *  Copyright (C) 2008-2017 Apple Inc. All rights reserved.
+ *  Copyright (C) 2008-2018 Apple Inc. All rights reserved.
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Lesser General Public
@@ -26,8 +26,17 @@
 
 namespace JSC {
 
+// FIXME: This should be final, but isn't because of bizarre (and mostly wrong) things done in
+// WebAssembly.
+// https://bugs.webkit.org/show_bug.cgi?id=182649
 class ErrorInstance : public JSDestructibleObject {
 public:
+    template<typename CellType>
+    static IsoSubspace* subspaceFor(VM& vm)
+    {
+        return &vm.errorInstanceSpace;
+    }
+
     typedef JSDestructibleObject Base;
     const static unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | OverridesGetPropertyNames;
 
@@ -68,6 +77,8 @@
     JS_EXPORT_PRIVATE String sanitizedToString(ExecState*);
     
     Vector<StackFrame>* stackTrace() { return m_stackTrace.get(); }
+    
+    void finalizeUnconditionally(VM&);
 
     bool materializeErrorInfoIfNeeded(VM&);
     bool materializeErrorInfoIfNeeded(VM&, PropertyName);

Modified: trunk/Source/_javascript_Core/runtime/Exception.cpp (228365 => 228366)


--- trunk/Source/_javascript_Core/runtime/Exception.cpp	2018-02-10 15:44:55 UTC (rev 228365)
+++ trunk/Source/_javascript_Core/runtime/Exception.cpp	2018-02-10 23:49:54 UTC (rev 228366)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -27,6 +27,7 @@
 #include "Exception.h"
 
 #include "Interpreter.h"
+#include "IsoCellSetInlines.h"
 #include "JSCInlines.h"
 
 namespace JSC {
@@ -57,11 +58,26 @@
     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
     Base::visitChildren(thisObject, visitor);
 
+    bool isFinalizationCandidate = false;
     visitor.append(thisObject->m_value);
-    for (StackFrame& frame : thisObject->m_stack)
-        frame.visitChildren(visitor);
+    for (StackFrame& frame : thisObject->m_stack) {
+        if (frame.isFinalizationCandidate()) {
+            isFinalizationCandidate = true;
+            break;
+        }
+    }
+    if (isFinalizationCandidate)
+        visitor.vm().exceptionsWithFinalizers.add(thisObject);
 }
 
+void Exception::finalizeUnconditionally(VM& vm)
+{
+    for (StackFrame& frame : m_stack)
+        frame.finalizeUnconditionally(vm);
+    
+    vm.exceptionsWithFinalizers.remove(this);
+}
+
 Exception::Exception(VM& vm)
     : Base(vm, vm.exceptionStructure.get())
 {

Modified: trunk/Source/_javascript_Core/runtime/Exception.h (228365 => 228366)


--- trunk/Source/_javascript_Core/runtime/Exception.h	2018-02-10 15:44:55 UTC (rev 228365)
+++ trunk/Source/_javascript_Core/runtime/Exception.h	2018-02-10 23:49:54 UTC (rev 228366)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -31,8 +31,14 @@
 
 namespace JSC {
     
-class Exception : public JSDestructibleObject {
+class Exception final : public JSDestructibleObject {
 public:
+    template<typename CellType>
+    static IsoSubspace* subspaceFor(VM& vm)
+    {
+        return &vm.exceptionSpace;
+    }
+
     typedef JSDestructibleObject Base;
     static const unsigned StructureFlags = StructureIsImmortal | Base::StructureFlags;
 
@@ -60,6 +66,8 @@
     void setDidNotifyInspectorOfThrow() { m_didNotifyInspectorOfThrow = true; }
 
     ~Exception();
+    
+    void finalizeUnconditionally(VM&);
 
 private:
     Exception(VM&);

Modified: trunk/Source/_javascript_Core/runtime/StackFrame.cpp (228365 => 228366)


--- trunk/Source/_javascript_Core/runtime/StackFrame.cpp	2018-02-10 15:44:55 UTC (rev 228365)
+++ trunk/Source/_javascript_Core/runtime/StackFrame.cpp	2018-02-10 23:49:54 UTC (rev 228366)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2016-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -142,13 +142,28 @@
     return traceBuild.toString().impl();
 }
 
-void StackFrame::visitChildren(SlotVisitor& visitor)
+bool StackFrame::isFinalizationCandidate()
 {
-    if (m_callee)
-        visitor.append(m_callee);
-    if (m_codeBlock)
-        visitor.append(m_codeBlock);
+    if (m_callee && !Heap::isMarked(m_callee.get()))
+        return true;
+    if (m_codeBlock && !Heap::isMarked(m_codeBlock.get()))
+        return true;
+    return false;
 }
 
+void StackFrame::finalizeUnconditionally(VM&)
+{
+    // FIXME: We should do something smarter. For example, if this happens, we could stringify the
+    // whole stack trace. The main shortcoming is that that requires doing operations that are not
+    // currently legal during finalization. We could make this work by giving JSC a proper "second
+    // chance" finalizer infrastructure. Or maybe there's an even easier way.
+    // https://bugs.webkit.org/show_bug.cgi?id=182650
+    
+    if (m_callee && !Heap::isMarked(m_callee.get()))
+        m_callee.clear();
+    if (m_codeBlock && !Heap::isMarked(m_codeBlock.get()))
+        m_codeBlock.clear();
+}
+
 } // namespace JSC
 

Modified: trunk/Source/_javascript_Core/runtime/StackFrame.h (228365 => 228366)


--- trunk/Source/_javascript_Core/runtime/StackFrame.h	2018-02-10 15:44:55 UTC (rev 228365)
+++ trunk/Source/_javascript_Core/runtime/StackFrame.h	2018-02-10 23:49:54 UTC (rev 228366)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2016-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -56,7 +56,8 @@
         return m_bytecodeOffset;
     }
     
-    void visitChildren(SlotVisitor&);
+    bool isFinalizationCandidate();
+    void finalizeUnconditionally(VM&);
 
 private:
     WriteBarrier<JSCell> m_callee { };

Modified: trunk/Source/_javascript_Core/runtime/VM.cpp (228365 => 228366)


--- trunk/Source/_javascript_Core/runtime/VM.cpp	2018-02-10 15:44:55 UTC (rev 228365)
+++ trunk/Source/_javascript_Core/runtime/VM.cpp	2018-02-10 23:49:54 UTC (rev 228366)
@@ -251,6 +251,8 @@
     , webAssemblyCodeBlockSpace("JSWebAssemblyCodeBlockSpace", heap, webAssemblyCodeBlockHeapCellType.get(), fastMallocAllocator.get())
 #endif
     , directEvalExecutableSpace ISO_SUBSPACE_INIT(heap, destructibleCellHeapCellType.get(), DirectEvalExecutable)
+    , errorInstanceSpace ISO_SUBSPACE_INIT(heap, destructibleObjectHeapCellType.get(), ErrorInstance)
+    , exceptionSpace ISO_SUBSPACE_INIT(heap, destructibleObjectHeapCellType.get(), Exception)
     , executableToCodeBlockEdgeSpace ISO_SUBSPACE_INIT(heap, cellHeapCellType.get(), ExecutableToCodeBlockEdge)
     , functionExecutableSpace ISO_SUBSPACE_INIT(heap, destructibleCellHeapCellType.get(), FunctionExecutable)
     , indirectEvalExecutableSpace ISO_SUBSPACE_INIT(heap, destructibleCellHeapCellType.get(), IndirectEvalExecutable)
@@ -264,6 +266,8 @@
     , structureSpace ISO_SUBSPACE_INIT(heap, destructibleCellHeapCellType.get(), Structure)
     , weakSetSpace ISO_SUBSPACE_INIT(heap, destructibleObjectHeapCellType.get(), JSWeakSet)
     , weakMapSpace ISO_SUBSPACE_INIT(heap, destructibleObjectHeapCellType.get(), JSWeakMap)
+    , errorInstancesWithFinalizers(errorInstanceSpace)
+    , exceptionsWithFinalizers(exceptionSpace)
     , executableToCodeBlockEdgesWithConstraints(executableToCodeBlockEdgeSpace)
     , executableToCodeBlockEdgesWithFinalizers(executableToCodeBlockEdgeSpace)
     , inferredTypesWithFinalizers(inferredTypeSpace)

Modified: trunk/Source/_javascript_Core/runtime/VM.h (228365 => 228366)


--- trunk/Source/_javascript_Core/runtime/VM.h	2018-02-10 15:44:55 UTC (rev 228365)
+++ trunk/Source/_javascript_Core/runtime/VM.h	2018-02-10 23:49:54 UTC (rev 228366)
@@ -339,6 +339,8 @@
 #endif
     
     IsoSubspace directEvalExecutableSpace;
+    IsoSubspace errorInstanceSpace;
+    IsoSubspace exceptionSpace;
     IsoSubspace executableToCodeBlockEdgeSpace;
     IsoSubspace functionExecutableSpace;
     IsoSubspace indirectEvalExecutableSpace;
@@ -353,6 +355,8 @@
     IsoSubspace weakSetSpace;
     IsoSubspace weakMapSpace;
     
+    IsoCellSet errorInstancesWithFinalizers;
+    IsoCellSet exceptionsWithFinalizers;
     IsoCellSet executableToCodeBlockEdgesWithConstraints;
     IsoCellSet executableToCodeBlockEdgesWithFinalizers;
     IsoCellSet inferredTypesWithFinalizers;
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to