ECMAScript Utils module - Memory documentation

Copyright © 1995-2005 Opera Software AS. All rights reserved. This file is part of the Opera web browser. It may not be distributed under any circumstances.
$Id$

Used OOM policies / Who is handling OOM?

All functions in the various classes in the module that comprise its public API that can fail due to OOM signal such failures by returning OpStatus::ERR_NO_MEMORY. In such case, the caller is responsible for handling the out of memory condition. When an out of memory condition occurs in a call directly from the message loop (this includes, for instance, all out of memory conditions signalled by the ECMAScript execution engine,) OOM is signalled through a call to Window::RaiseCondition or MemoryManager::RaiseCondition (the latter if the ECMAScript environment is not one connected to a document.) When an asynchronous operation started through the ES_AsyncInterface interface fails due to OOM, this is (in addition to what is described above) signalled by calling the registered callback with the status ES_ASYNC_NO_MEMORY.

In addition to propagating OOM signals to outside the module, out of memory conditions that occur while executing threads are also handled by killing all threads in the affected scheduler (thus returning it to its initial state.)

Description of flow

The code flow in the module is divided into two main cases:

Called from external code
Calls from external code (code in other modules) primarily initiate new operations by allocating the necessary objects, compiling ECMAScript programs and posting messages to the message loop. Typical important entry points are
  1. ES_ThreadSchedulerImpl::AddRunnable
  2. ES_ThreadSchedulerImpl::AddWaiting
  3. ES_AsyncInterface::Eval
  4. ES_LoadManager::RegisterScript
  5. ES_LoadManager::SetScript
  6. ES_LoadManager::Write
Functions 3 through 6 typically allocate significant amounts of memory and are thus likely to be handling OOM.
Called from message loop
Calls from the message loop do most of the real work, by evaluating threads and possibly calling the ECMAScript execution engine to execute code. The typical call chain is
  1. <Message loop>
  2. ES_ThreadSchedulerImpl::HandleCallback
  3. ES_ThreadSchedulerImpl::RunNow
  4. ES_Thread::Evaluate
  5. <ECMAScript execution engine>
Out of memory conditions that occur during ECMAScript execution or in DOM code will be propagated out from the ECMAScript execution engine at 5, handled internally by 3 (through a call to ES_ThreadSchedulerImpl::HandleError) and then propagated out of the module by 2.

Heap memory usage

Most memory allocated is used for control structures such as thread schedulers (ES_ThreadSchedulerImpl) and threads (ES_Thread and subclasses) and for script generated document data (text written by a script through a call to either of the functions "document.write" and "document.writeln".) While both thread schedulers and threads are fairly large objects, there are normally few alive at any given time (at most one scheduler per document, and zero or more threads per thread scheduler.) Threads are deallocated as soon as they finish executing. The amount of script generated document data depends entirely on scripts on the page. There is no upper limit. Script generated document data is allocated in blocks of fixed size (see "Memory tuning") and freed (blockwise) as soon as it has been consumed by the document (HTML or XML) parser.

Some threads own potentially large heap allocated structures from the ECMAScript module, represented by ES_Context and ES_Program objects. Such objects are freed by the threads when the threads are deallocated. Most memory referenced by such objects are probably owned and freed by the ECMAScript garbage collector at an unknown time after the objects have been freed.

Stack memory usage

The module uses insignificant amounts of stack memory. There are no complex recursive functions or stack allocated arrays larger than a few bytes.

Caching and freeing memory

The module has no caching, and there is no non-destructive way to free memory. It is possible to kill all currently alive threads and thus free them and memory referenced from them by calling the function ES_ThreadScheduler::RemoveThreads.

Freeing memory on exit

Freeing memory on exit is dependant on that someone else frees all ES_ThreadScheduler and ES_Environment objects. This is typically the responsibility of the DOM module (in the case of thread schedulers) and whatever code that created the ES_Environment object (currently only Voice XML code does that.)

Temporary buffers

The module uses the shared buffer returned by the function MemoryManager::GetTempBuf. In one case it is simply to avoid having to allocate a separate string (pure optimization that is easy to remove) and in all other cases the shared buffer is used to return a generated string from a function without making the caller (which is in the ECMAScript module) responsible for deallocating the returned string. Changing this requires either using a non-shared but still globally allocated buffer, or to rewrite the function and the code that calls it so that the caller deallocates the string.

Memory tuning

The class load manager (ES_LoadManager) uses a list of blocks to store script generated data. The size of the blocks in that list depends on the tweak TWEAK_ESUTILS_BLOCK_DATA_SIZE. No other memory tuning is possible.

Tests

There are no tests specificly for testing memory usage.

Coverage

Running the "Scheduler regression tests" in the jstest2 CVS module and the selftests in the ECMAScript Utils module ("ESUtils.*") should provide good coverage for the code that is likely to be handling out of memory conditions.

Design choices

There are no design choices in the module's design relating directly to balancing memory usage, footprint, performance and/or design. Or put differently: essentially no compromises between the different priorities have been made.

Suggestions of improvements

There are currently no planned improvements with regards to memory handling.