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:
@@ -35,9 +35,34 @@
|
|||||||
#include "jrt-bit-fields.h"
|
#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_mark (ecma_object_t *object_p);
|
||||||
static void ecma_gc_sweep (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);
|
JERRY_ASSERT (object_p != NULL);
|
||||||
|
|
||||||
return jrt_extract_bit_field (object_p->container,
|
bool flag_value = jrt_extract_bit_field (object_p->container,
|
||||||
ECMA_OBJECT_GC_VISITED_POS,
|
ECMA_OBJECT_GC_VISITED_POS,
|
||||||
ECMA_OBJECT_GC_VISITED_WIDTH);
|
ECMA_OBJECT_GC_VISITED_WIDTH);
|
||||||
|
|
||||||
|
return (flag_value != ecma_gc_visited_flip_flag);
|
||||||
} /* ecma_gc_is_object_visited */
|
} /* 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);
|
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,
|
object_p->container = jrt_set_bit_field_value (object_p->container,
|
||||||
is_visited,
|
is_visited,
|
||||||
ECMA_OBJECT_GC_VISITED_POS,
|
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_refs (object_p, 1);
|
||||||
|
|
||||||
ecma_gc_set_object_next (object_p, ecma_gc_objects_lists);
|
ecma_gc_set_object_next (object_p, ecma_gc_objects_lists [ECMA_GC_COLOR_WHITE_GRAY]);
|
||||||
ecma_gc_objects_lists = object_p;
|
ecma_gc_objects_lists [ECMA_GC_COLOR_WHITE_GRAY] = object_p;
|
||||||
|
|
||||||
/* Should be set to false at the beginning of garbage collection */
|
/* 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 */
|
} /* ecma_init_gc_info */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -174,18 +206,18 @@ ecma_deref_object (ecma_object_t *object_p) /**< object */
|
|||||||
void
|
void
|
||||||
ecma_gc_init (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 */
|
} /* ecma_gc_init */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark objects as visited starting from specified object as root
|
* Mark objects as visited starting from specified object as root
|
||||||
*/
|
*/
|
||||||
void
|
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);
|
JERRY_ASSERT(object_p != NULL);
|
||||||
|
JERRY_ASSERT (ecma_gc_is_object_visited (object_p));
|
||||||
ecma_gc_set_object_visited (object_p, true);
|
|
||||||
|
|
||||||
bool traverse_properties = true;
|
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);
|
ecma_object_t *lex_env_p = ecma_get_lex_env_outer_reference (object_p);
|
||||||
if (lex_env_p != NULL)
|
if (lex_env_p != NULL)
|
||||||
{
|
{
|
||||||
if (!ecma_gc_is_object_visited (lex_env_p))
|
ecma_gc_set_object_visited (lex_env_p, true);
|
||||||
{
|
|
||||||
ecma_gc_mark (lex_env_p);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ecma_get_lex_env_type (object_p) == ECMA_LEXICAL_ENVIRONMENT_OBJECTBOUND)
|
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);
|
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_set_object_visited (binding_object_p, true);
|
||||||
{
|
|
||||||
ecma_gc_mark (binding_object_p);
|
|
||||||
}
|
|
||||||
|
|
||||||
traverse_properties = false;
|
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);
|
ecma_object_t *proto_p = ecma_get_object_prototype (object_p);
|
||||||
if (proto_p != NULL)
|
if (proto_p != NULL)
|
||||||
{
|
{
|
||||||
if (!ecma_gc_is_object_visited (proto_p))
|
ecma_gc_set_object_visited (proto_p, true);
|
||||||
{
|
|
||||||
ecma_gc_mark (proto_p);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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);
|
ecma_object_t *value_obj_p = ecma_get_object_from_value (value);
|
||||||
|
|
||||||
if (!ecma_gc_is_object_visited (value_obj_p))
|
ecma_gc_set_object_visited (value_obj_p, true);
|
||||||
{
|
|
||||||
ecma_gc_mark (value_obj_p);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -258,18 +278,12 @@ ecma_gc_mark (ecma_object_t *object_p) /**< start object */
|
|||||||
|
|
||||||
if (getter_obj_p != NULL)
|
if (getter_obj_p != NULL)
|
||||||
{
|
{
|
||||||
if (!ecma_gc_is_object_visited (getter_obj_p))
|
ecma_gc_set_object_visited (getter_obj_p, true);
|
||||||
{
|
|
||||||
ecma_gc_mark (getter_obj_p);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setter_obj_p != NULL)
|
if (setter_obj_p != NULL)
|
||||||
{
|
{
|
||||||
if (!ecma_gc_is_object_visited (setter_obj_p))
|
ecma_gc_set_object_visited (setter_obj_p, true);
|
||||||
{
|
|
||||||
ecma_gc_mark (setter_obj_p);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
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);
|
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_set_object_visited (obj_p, true);
|
||||||
{
|
|
||||||
ecma_gc_mark (obj_p);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -366,24 +377,18 @@ ecma_gc_sweep (ecma_object_t *object_p) /**< object to free */
|
|||||||
void
|
void
|
||||||
ecma_gc_run (void)
|
ecma_gc_run (void)
|
||||||
{
|
{
|
||||||
/* clearing visited flags for all objects of generations to be processed */
|
JERRY_ASSERT (ecma_gc_objects_lists [ECMA_GC_COLOR_BLACK] == NULL);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if some object is referenced from stack or globals (i.e. it is root),
|
/* if some object is referenced from stack or globals (i.e. it is root), mark it */
|
||||||
* start recursive marking traverse from the object */
|
for (ecma_object_t *obj_iter_p = ecma_gc_objects_lists [ECMA_GC_COLOR_WHITE_GRAY];
|
||||||
for (ecma_object_t *obj_iter_p = ecma_gc_objects_lists;
|
|
||||||
obj_iter_p != NULL;
|
obj_iter_p != NULL;
|
||||||
obj_iter_p = ecma_gc_get_object_next (obj_iter_p))
|
obj_iter_p = ecma_gc_get_object_next (obj_iter_p))
|
||||||
{
|
{
|
||||||
if (ecma_gc_get_object_refs (obj_iter_p) > 0
|
JERRY_ASSERT (!ecma_gc_is_object_visited (obj_iter_p));
|
||||||
&& !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);
|
ecma_object_t *obj_p = ecma_get_object_from_value (reg_value);
|
||||||
|
|
||||||
if (!ecma_gc_is_object_visited (obj_p))
|
ecma_gc_set_object_visited (obj_p, true);
|
||||||
{
|
|
||||||
ecma_gc_mark (obj_p);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 != NULL;
|
||||||
obj_iter_p = obj_next_p)
|
obj_iter_p = obj_next_p)
|
||||||
{
|
{
|
||||||
obj_next_p = ecma_gc_get_object_next (obj_iter_p);
|
obj_next_p = ecma_gc_get_object_next (obj_iter_p);
|
||||||
|
|
||||||
if (!ecma_gc_is_object_visited (obj_iter_p))
|
JERRY_ASSERT (!ecma_gc_is_object_visited (obj_iter_p));
|
||||||
{
|
|
||||||
ecma_gc_sweep (obj_iter_p);
|
|
||||||
|
|
||||||
if (likely (obj_prev_p != NULL))
|
ecma_gc_sweep (obj_iter_p);
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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 */
|
} /* ecma_gc_run */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -414,13 +414,6 @@ typedef struct
|
|||||||
ECMA_OBJECT_GC_NEXT_CP_WIDTH)
|
ECMA_OBJECT_GC_NEXT_CP_WIDTH)
|
||||||
#define ECMA_OBJECT_GC_VISITED_WIDTH (1)
|
#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 */
|
/* Objects' only part */
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,15 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// 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) {
|
function f (o, i) {
|
||||||
if (--i > 0) {
|
if (--i > 0) {
|
||||||
f ({a:o, b:o}, i);
|
f ({a:o, b:o}, i);
|
||||||
|
|||||||
Reference in New Issue
Block a user