Changing GC mark phase to be non-recursive. It is noticeably slower on some test cases, but doesn't cause stack overflow.

This commit is contained in:
Ruben Ayrapetyan
2015-02-19 14:03:59 +03:00
parent a751ab4f9d
commit bb18970151
4 changed files with 153 additions and 81 deletions
+108 -74
View File
@@ -35,9 +35,34 @@
#include "jrt-bit-fields.h"
/**
* Global lists of objects sorted by generation identifier.
* An object's GC color
*
* Tri-color marking:
* WHITE_GRAY, unvisited -> WHITE // not referenced by a live object or the reference not found yet
* WHITE_GRAY, visited -> GRAY // referenced by some live object
* BLACK -> BLACK // all referenced objects are gray or black
*/
static ecma_object_t *ecma_gc_objects_lists;
typedef enum
{
ECMA_GC_COLOR_WHITE_GRAY, /**< white or gray */
ECMA_GC_COLOR_BLACK, /**< black */
ECMA_GC_COLOR__COUNT /**< number of colors */
} ecma_gc_color_t;
/**
* List of marked (visited during current GC session) and umarked objects
*/
static ecma_object_t *ecma_gc_objects_lists [ECMA_GC_COLOR__COUNT];
/**
* Current state of an object's visited flag that indicates whether the object is in visited state:
* visited_field | visited_flip_flag | real_value
* false | false | false
* false | true | true
* true | false | true
* true | true | false
*/
static bool ecma_gc_visited_flip_flag = false;
static void ecma_gc_mark (ecma_object_t *object_p);
static void ecma_gc_sweep (ecma_object_t *object_p);
@@ -114,9 +139,11 @@ ecma_gc_is_object_visited (ecma_object_t *object_p) /**< object */
{
JERRY_ASSERT (object_p != NULL);
return jrt_extract_bit_field (object_p->container,
ECMA_OBJECT_GC_VISITED_POS,
ECMA_OBJECT_GC_VISITED_WIDTH);
bool flag_value = jrt_extract_bit_field (object_p->container,
ECMA_OBJECT_GC_VISITED_POS,
ECMA_OBJECT_GC_VISITED_WIDTH);
return (flag_value != ecma_gc_visited_flip_flag);
} /* ecma_gc_is_object_visited */
/**
@@ -128,6 +155,11 @@ ecma_gc_set_object_visited (ecma_object_t *object_p, /**< object */
{
JERRY_ASSERT (object_p != NULL);
if (ecma_gc_visited_flip_flag)
{
is_visited = !is_visited;
}
object_p->container = jrt_set_bit_field_value (object_p->container,
is_visited,
ECMA_OBJECT_GC_VISITED_POS,
@@ -142,11 +174,11 @@ ecma_init_gc_info (ecma_object_t *object_p) /**< object */
{
ecma_gc_set_object_refs (object_p, 1);
ecma_gc_set_object_next (object_p, ecma_gc_objects_lists);
ecma_gc_objects_lists = object_p;
ecma_gc_set_object_next (object_p, ecma_gc_objects_lists [ECMA_GC_COLOR_WHITE_GRAY]);
ecma_gc_objects_lists [ECMA_GC_COLOR_WHITE_GRAY] = object_p;
/* Should be set to false at the beginning of garbage collection */
ecma_gc_set_object_visited (object_p, true);
ecma_gc_set_object_visited (object_p, false);
} /* ecma_init_gc_info */
/**
@@ -174,18 +206,18 @@ ecma_deref_object (ecma_object_t *object_p) /**< object */
void
ecma_gc_init (void)
{
ecma_gc_objects_lists = NULL;
ecma_gc_objects_lists [ECMA_GC_COLOR_WHITE_GRAY] = NULL;
ecma_gc_objects_lists [ECMA_GC_COLOR_BLACK] = NULL;
} /* ecma_gc_init */
/**
* Mark objects as visited starting from specified object as root
*/
void
ecma_gc_mark (ecma_object_t *object_p) /**< start object */
ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */
{
JERRY_ASSERT(object_p != NULL);
ecma_gc_set_object_visited (object_p, true);
JERRY_ASSERT (ecma_gc_is_object_visited (object_p));
bool traverse_properties = true;
@@ -194,19 +226,13 @@ ecma_gc_mark (ecma_object_t *object_p) /**< start object */
ecma_object_t *lex_env_p = ecma_get_lex_env_outer_reference (object_p);
if (lex_env_p != NULL)
{
if (!ecma_gc_is_object_visited (lex_env_p))
{
ecma_gc_mark (lex_env_p);
}
ecma_gc_set_object_visited (lex_env_p, true);
}
if (ecma_get_lex_env_type (object_p) == ECMA_LEXICAL_ENVIRONMENT_OBJECTBOUND)
{
ecma_object_t *binding_object_p = ecma_get_lex_env_binding_object (object_p);
if (!ecma_gc_is_object_visited (binding_object_p))
{
ecma_gc_mark (binding_object_p);
}
ecma_gc_set_object_visited (binding_object_p, true);
traverse_properties = false;
}
@@ -216,10 +242,7 @@ ecma_gc_mark (ecma_object_t *object_p) /**< start object */
ecma_object_t *proto_p = ecma_get_object_prototype (object_p);
if (proto_p != NULL)
{
if (!ecma_gc_is_object_visited (proto_p))
{
ecma_gc_mark (proto_p);
}
ecma_gc_set_object_visited (proto_p, true);
}
}
@@ -242,10 +265,7 @@ ecma_gc_mark (ecma_object_t *object_p) /**< start object */
{
ecma_object_t *value_obj_p = ecma_get_object_from_value (value);
if (!ecma_gc_is_object_visited (value_obj_p))
{
ecma_gc_mark (value_obj_p);
}
ecma_gc_set_object_visited (value_obj_p, true);
}
break;
@@ -258,18 +278,12 @@ ecma_gc_mark (ecma_object_t *object_p) /**< start object */
if (getter_obj_p != NULL)
{
if (!ecma_gc_is_object_visited (getter_obj_p))
{
ecma_gc_mark (getter_obj_p);
}
ecma_gc_set_object_visited (getter_obj_p, true);
}
if (setter_obj_p != NULL)
{
if (!ecma_gc_is_object_visited (setter_obj_p))
{
ecma_gc_mark (setter_obj_p);
}
ecma_gc_set_object_visited (setter_obj_p, true);
}
break;
@@ -317,10 +331,7 @@ ecma_gc_mark (ecma_object_t *object_p) /**< start object */
{
ecma_object_t *obj_p = ECMA_GET_NON_NULL_POINTER(ecma_object_t, property_value);
if (!ecma_gc_is_object_visited (obj_p))
{
ecma_gc_mark (obj_p);
}
ecma_gc_set_object_visited (obj_p, true);
break;
}
@@ -366,24 +377,18 @@ ecma_gc_sweep (ecma_object_t *object_p) /**< object to free */
void
ecma_gc_run (void)
{
/* clearing visited flags for all objects of generations to be processed */
for (ecma_object_t *obj_iter_p = ecma_gc_objects_lists;
obj_iter_p != NULL;
obj_iter_p = ecma_gc_get_object_next (obj_iter_p))
{
ecma_gc_set_object_visited (obj_iter_p, false);
}
JERRY_ASSERT (ecma_gc_objects_lists [ECMA_GC_COLOR_BLACK] == NULL);
/* if some object is referenced from stack or globals (i.e. it is root),
* start recursive marking traverse from the object */
for (ecma_object_t *obj_iter_p = ecma_gc_objects_lists;
/* if some object is referenced from stack or globals (i.e. it is root), mark it */
for (ecma_object_t *obj_iter_p = ecma_gc_objects_lists [ECMA_GC_COLOR_WHITE_GRAY];
obj_iter_p != NULL;
obj_iter_p = ecma_gc_get_object_next (obj_iter_p))
{
if (ecma_gc_get_object_refs (obj_iter_p) > 0
&& !ecma_gc_is_object_visited (obj_iter_p))
JERRY_ASSERT (!ecma_gc_is_object_visited (obj_iter_p));
if (ecma_gc_get_object_refs (obj_iter_p) > 0)
{
ecma_gc_mark (obj_iter_p);
ecma_gc_set_object_visited (obj_iter_p, true);
}
}
@@ -401,38 +406,67 @@ ecma_gc_run (void)
{
ecma_object_t *obj_p = ecma_get_object_from_value (reg_value);
if (!ecma_gc_is_object_visited (obj_p))
{
ecma_gc_mark (obj_p);
}
ecma_gc_set_object_visited (obj_p, true);
}
}
}
for (ecma_object_t *obj_iter_p = ecma_gc_objects_lists, *obj_prev_p = NULL, *obj_next_p;
bool marked_anything_during_current_iteration = false;
do
{
marked_anything_during_current_iteration = false;
for (ecma_object_t *obj_iter_p = ecma_gc_objects_lists [ECMA_GC_COLOR_WHITE_GRAY], *obj_prev_p = NULL, *obj_next_p;
obj_iter_p != NULL;
obj_iter_p = obj_next_p)
{
obj_next_p = ecma_gc_get_object_next (obj_iter_p);
if (ecma_gc_is_object_visited (obj_iter_p))
{
/* Moving the object to list of marked objects */
ecma_gc_set_object_next (obj_iter_p, ecma_gc_objects_lists [ECMA_GC_COLOR_BLACK]);
ecma_gc_objects_lists [ECMA_GC_COLOR_BLACK] = obj_iter_p;
if (likely (obj_prev_p != NULL))
{
JERRY_ASSERT (ecma_gc_get_object_next (obj_prev_p) == obj_iter_p);
ecma_gc_set_object_next (obj_prev_p, obj_next_p);
}
else
{
ecma_gc_objects_lists [ECMA_GC_COLOR_WHITE_GRAY] = obj_next_p;
}
ecma_gc_mark (obj_iter_p);
marked_anything_during_current_iteration = true;
}
else
{
obj_prev_p = obj_iter_p;
}
}
} while (marked_anything_during_current_iteration);
/* Sweeping objects that are currently unmarked */
for (ecma_object_t *obj_iter_p = ecma_gc_objects_lists [ECMA_GC_COLOR_WHITE_GRAY], *obj_next_p;
obj_iter_p != NULL;
obj_iter_p = obj_next_p)
{
obj_next_p = ecma_gc_get_object_next (obj_iter_p);
if (!ecma_gc_is_object_visited (obj_iter_p))
{
ecma_gc_sweep (obj_iter_p);
JERRY_ASSERT (!ecma_gc_is_object_visited (obj_iter_p));
if (likely (obj_prev_p != NULL))
{
ecma_gc_set_object_next (obj_prev_p, obj_next_p);
}
else
{
ecma_gc_objects_lists = obj_next_p;
}
}
else
{
obj_prev_p = obj_iter_p;
}
ecma_gc_sweep (obj_iter_p);
}
/* Unmarking all objects */
ecma_gc_objects_lists [ECMA_GC_COLOR_WHITE_GRAY] = ecma_gc_objects_lists [ECMA_GC_COLOR_BLACK];
ecma_gc_objects_lists [ECMA_GC_COLOR_BLACK] = NULL;
ecma_gc_visited_flip_flag = !ecma_gc_visited_flip_flag;
} /* ecma_gc_run */
/**
-7
View File
@@ -414,13 +414,6 @@ typedef struct
ECMA_OBJECT_GC_NEXT_CP_WIDTH)
#define ECMA_OBJECT_GC_VISITED_WIDTH (1)
/**
* Flag indicating that the object may reference objects of younger generations in its properties.
*/
#define ECMA_OBJECT_GC_MAY_REF_YOUNGER_OBJECTS_POS (ECMA_OBJECT_GC_VISITED_POS + \
ECMA_OBJECT_GC_VISITED_WIDTH)
#define ECMA_OBJECT_GC_MAY_REF_YOUNGER_OBJECTS_WIDTH (1)
/* Objects' only part */
+36
View File
@@ -0,0 +1,36 @@
// Copyright 2015 Samsung Electronics Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
function f (o, i) {
if (--i > 0) {
f ({a:o, b:o}, i);
}
}
for (var i = 0; i < 100; i++)
{
({} + f ({}, 12));
}
for(var i = 0; i < 100; i++)
{
var obj = {}, obj_l;
obj_l = obj;
for (var k = 0; k < 1500; k++)
{
obj_l.prop = {};
obj_l = obj_l.prop;
}
}
+9
View File
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
var obj = {}, obj_l;
obj_l = obj;
for (var k = 0; k < 1500; k++)
{
obj_l.prop = {};
obj_l = obj_l.prop;
}
function f (o, i) {
if (--i > 0) {
f ({a:o, b:o}, i);