目录

GC

Garbage Collection (GC) is a process that identifies and reclaims memory no longer in use by a program. It aims to free up unused memory space. Modern programming languages implement two primary types of GC algorithms: reference counting and tracing GC.

GC Algorithm Overview

[h2]Types of GC

Reference Counting

When object A is referenced by object B, A's reference count increases by 1. Conversely, when the reference is removed, A's reference count decreases by 1. When A's reference count reaches 0, object A is reclaimed.

class Parent {
  constructor() {
    this.child = null;
  }
  child: Child | null = null;
}

class Child {
  constructor() {
    this.parent = null;
  }
  parent: Parent | null = null;
}

function main() {
  let parent: Parent = new Parent();
  let child: Child = new Child();
  parent.child = child;
  child.parent = parent;
}

In the code above, the parent object holds a reference to the child object, incrementing the reference count of parent. Simultaneously, the child object is also held by the parent object, incrementing the reference count of child. This forms a circular reference. As a result, the parent and child objects cannot be released until the main function ends, causing a memory leak.

Tracking GC

Root objects include stack objects and global objects that are guaranteed to be alive at a given moment during program execution. All objects (reachable objects) that can be accessed through reference chains starting from root objects are alive. By traversing these references, all live objects can be identified. As illustrated in the diagram, starting from the root objects, objects that are reachable are marked in blue, indicating they are live; the remaining unreachable objects are marked in yellow, indicating they are garbage.

Both reference counting and object tracing algorithms have their pros and cons. Given the memory leak issues associated with reference counting, ArkTS Runtime opts for a GC design based on object tracing (tracing GC).

[h2]Types of Tracing GC

Tracing GC algorithms identify garbage by traversing objects. Based on the collection method, tracing GC can be categorized into three basic types: mark-sweep, mark-copy, and mark-compact. In the diagrams below, blue indicates live objects, whereas yellow indicates garbage.

Mark-Sweep Collection

After the object graph traversal is complete, delete the content of unreachable objects and place them in the free queue for the next object allocation.

This collection method does not move objects and is highly efficient. However, the memory addresses of collected objects are not contiguous, which causes memory fragmentation and reduces allocation efficiency. In extreme cases, even if there is a large amount of free memory, a large object may still fail to be placed.

Mark-Copy Collection

When traversing the object graph, copy the reachable objects to a new memory space. After the traversal is complete, reclaim the old memory space.

This method can solve the memory fragmentation issue. It completes the entire GC process in a single traversal and is highly efficient. However, in extreme cases, half of the memory space must be reserved to ensure that all reachable objects can be copied, which results in low space utilization.

Mark-Compact Collection

After the traversal, live objects (blue) are copied to the beginning of the current space (or a designated area), and the copied objects are reclaimed and placed in the free list.

[h2]HPP GC

High Performance Partial Garbage Collection (HPP GC) is designed for high performance in partial GC, leveraging generational models, hybrid algorithms, and optimized GC processes. HPP GC uses different collection methods for different object areas.

Generational Model

ArkTS Runtime uses a traditional generational model, categorizing objects based on their lifetimes. Most newly allocated objects are reclaimed after one GC cycle, while most objects that survive multiple GC cycles continue to exist. ArkTS Runtime divides objects into young and old generations and allocates them to separate spaces.

The ArkTS runtime directly allocates newly allocated objects to the From space of the young generation (YoungSpace, also known as SemiSpace). Objects that remain alive after one GC are moved to the To space. Objects that remain alive after another GC are moved to the old generation (OldSpace).

Hybrid Algorithm

HPP GC is a hybrid algorithm that combines partial copy, partial compact, and partial sweep. It adopts different collection methods based on the characteristics of young generation and old generation objects.

Given the short lifetimes, small size, and frequent collection of young generation objects, ArkTS Runtime uses the mark-copy algorithm to efficiently reclaim memory for these objects.

Based on the characteristics of old generation objects, a heuristic collection set (CSet) algorithm is used. This algorithm collects the sizes of live objects in each region during the marking phase. During the collection phase, ArkTS Runtime uses mark-compact for regions with fewer live objects and lower collection costs, but mark-sweep for the remaining regions.

The collection policies are as follows:

This heuristic approach combines the benefits of mark-compact and mark-sweep algorithms, addressing memory fragmentation while maintaining performance.

Process Optimization

HPP GC introduces extensive concurrency and parallelism optimizations to minimize the impact on application performance. The GC process includes concurrent and parallel marking, sweeping, evacuation, updating, and clearing tasks.

GC Process

[h2]Types of HPP GC

Young GC

Old GC

Full GC

After that, Smart GC or Idle GC selects one of the three GC types described above.

[h2]Trigger Strategies

Space Threshold Triggering

GC Triggered When the Native Bound Size Reaches the Threshold

Background Switch Triggering

Triggering GC When Idle

[h2]Execution Strategies

Concurrent Marking

Threshold Adjustment Before and After YoungSpace GC

Threshold Adjustment After the First Old GC

Adjustment of OldSpace and GlobalSpace thresholds and the growth factor for the second and subsequent Old GCs

CSet Selection Strategies for Partial Old GC

Heap

The Heap contains two types: LocalHeap and SharedHeap. LocalHeap is the virtual machine heap exclusively owned by each ArkTS thread in an application process, while SharedHeap is the virtual machine heap shared by all ArkTS threads in an application process.

[h2]LocalHeap Structure

Each space is managed by one or more Regions. A Region is the unit requested by a space from the memory allocator.

[h2]Shared Heap Structure

SharedHeap is used to store objects shared between threads, improving efficiency and saving memory. The shared heap does not belong to any single thread; it stores objects that have sharing value, improving the survival rate of objects.

SharedHeap removes the SemiSpace type because shared objects usually have a long lifecycle and do not need the copy algorithm of the young generation.

Each space is managed by one or more Regions for partitioned management. A Region is the unit requested by a space from the memory allocator.

Features

[h2]Smart GC

Smart GC is an intelligent GC suppression mechanism that reduces the GC trigger frequency by temporarily raising the GC trigger threshold in cold start scenarios and performance-sensitive scenarios, thereby avoiding frame drops caused by GC and improving user experience.

Supported Scenarios

Precautions

Log Keywords

Interaction Process

Log Interpretation

[h2]Enabling Full Logs

By default, detailed GC logs are printed only when GC duration exceeds 40 ms. To enable logs for all GC executions, run commands on the device.

Example

# Enable full GC logs with parameter 0x905d. Disable full GC logs and revert to the default value (0x105c).
hdc shell param set persist.ark.properties 0x905d
# Reboot to apply changes.
hdc shell reboot

[h2]Typical Logs

The following logs represent a complete GC execution, with variations based on the type of GC. You can search for the keyword [gc] in the exported log file to view GC logs. You can also check for the keyword ArkCompiler to view more comprehensive VM logs.

// Pre-GC object size (region size) -> Post-GC object size (region size), total duration (+concurrentMark duration), GC trigger reason.
C03F00/ArkCompiler: [gc]  [ CompressGC ] 26.1164 (35) -> 7.10049 (10.5) MB, 160.626(+0)ms, Switch to background
// Various states during GC execution and application name.
C03F00/ArkCompiler: [gc] IsInBackground: 1; SensitiveStatus: 0; OnStartupEvent: 0; BundleName: com.example.demo;
// Duration statistics for each GC phase.
C03F00/ArkCompiler: [gc] /***************** GC Duration statistic: ****************/
C03F00/ArkCompiler: [gc] TotalGC:                 160.626 ms
C03F00/ArkCompiler: Initialize:              0.179   ms
C03F00/ArkCompiler: Mark:                    159.204 ms
C03F00/ArkCompiler: MarkRoots:               6.925   ms
C03F00/ArkCompiler: ProcessMarkStack:        158.99  ms
C03F00/ArkCompiler: Sweep:                   0.957   ms
C03F00/ArkCompiler: Finish:                  0.277   ms
// Memory usage statistics after GC.
C03F00/ArkCompiler: [gc] /****************** GC Memory statistic: *****************/
C03F00/ArkCompiler: [gc] AllSpaces        used:  7270.9KB     committed:   10752KB
C03F00/ArkCompiler: ActiveSemiSpace  used:       0KB     committed:     256KB
C03F00/ArkCompiler: OldSpace         used:  4966.9KB     committed:    5888KB
C03F00/ArkCompiler: HugeObjectSpace  used:    2304KB     committed:    2304KB
C03F00/ArkCompiler: NonMovableSpace  used:       0KB     committed:    2304KB
C03F00/ArkCompiler: MachineCodeSpace used:       0KB     committed:       0KB
C03F00/ArkCompiler: HugeMachineCodeSpace used:       0KB     committed:       0KB
C03F00/ArkCompiler: SnapshotSpace    used:       0KB     committed:       0KB
C03F00/ArkCompiler: AppSpawnSpace    used: 4736.34KB     committed:    4864KB
C03F00/ArkCompiler: [gc] Anno memory usage size:  45      MB
C03F00/ArkCompiler: Native memory usage size:2.99652 MB
C03F00/ArkCompiler: NativeBindingSize:       0.577148KB
C03F00/ArkCompiler: ArrayBufferNativeSize:   0.0117188KB
C03F00/ArkCompiler: RegExpByteCodeNativeSize:0.280273KB
C03F00/ArkCompiler: ChunkNativeSize:         19096   KB
C03F00/ArkCompiler: [gc] Heap alive rate:         0.202871
// Summary statistics for this type of GC in the VM.
C03F00/ArkCompiler: [gc] /***************** GC summary statistic: *****************/
C03F00/ArkCompiler: [gc] CompressGC occurs count  6
C03F00/ArkCompiler: CompressGC max pause:    2672.33 ms
C03F00/ArkCompiler: CompressGC min pause:    160.626 ms
C03F00/ArkCompiler: CompressGC average pause:1076.06 ms
C03F00/ArkCompiler: Heap average alive rate: 0.635325

GC Developer Debugging Interfaces

The following interfaces are for debugging purposes only and are not official SDK interfaces. They should not be used in production applications.

[h2]ArkTools.hintGC()

Usage example:

// Declare the interface first.
declare class ArkTools {
  static hintGC(): void;
}

@Entry
@Component
struct Index {
  @State message: string = 'Hello World';

  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
        Button('Trigger HintGC').onClick((event: ClickEvent) => {
          ArkTools.hintGC(); // Call the method directly.
          this.message = 'Success';
        })
      }
      .width('100%')
    }
    .height('100%')
  }
}

GC FAQs

[h2]GC Stability Issues

GC stability issues are caused by two types of exceptions: object exceptions caused by invalid multithreading operations and pointer exceptions caused by memory access errors. These issues typically manifest as address access exceptions in the GC task stack.

To identify GC tasks, look for thread names and methods within the stack. The OS_GC_Thread thread primarily handles GC tasks and PGO-related tasks (collection tasks); keywords like GCTask in the stack can be used to identify GC tasks. When GC tasks report crashes with address exceptions, you should first investigate invalid multithreading and memory access issues.

The following examples list only some scenarios. The actual reported address exceptions can vary widely and are not detailed here.

Common stack information about object exceptions:

0xffff000000000048 is an object exception offset error.

Reason:Signal:SIGSEGV(SEGV_MAPERR)@0xffff000000000048
Fault thread info:
Tid:6490, Name:OS_GC_Thread
#00 pc 0000000000507310 /system/lib64/platformsdk/libark_jsruntime.so(panda::ecmascript::JSHClass::SizeFromJSHClass(panda::ecmascript::TaggedObject*)+0)(a3d1ba664de66d31faed07d711ee1299)
#01 pc 0000000000521f94 /system/lib64/platformsdk/libark_jsruntime.so(panda::ecmascript::CompressGCMarker::EvacuateObject(unsigned int, panda::ecmascript::TaggedObject*, panda::ecmascript::MarkWord const&, panda::ecmascript::ObjectSlot)+80)(a3d1ba664de66d31faed07d711ee1299)
#02 pc 0000000000521ee4 /system/lib64/platformsdk/libark_jsruntime.so(panda::ecmascript::CompressGCMarker::MarkObject(unsigned int, panda::ecmascript::TaggedObject*, panda::ecmascript::ObjectSlot)+372)(a3d1ba664de66d31faed07d711ee1299)
#03 pc 0000000000523e40 /system/lib64/platformsdk/libark_jsruntime.so(a3d1ba664de66d31faed07d711ee1299)
#04 pc 0000000000516d74 /system/lib64/platformsdk/libark_jsruntime.so(a3d1ba664de66d31faed07d711ee1299)
#05 pc 00000000005206d4 /system/lib64/platformsdk/libark_jsruntime.so(panda::ecmascript::CompressGCMarker::ProcessMarkStack(unsigned int)+160)(a3d1ba664de66d31faed07d711ee1299)
#06 pc 000000000050460c /system/lib64/platformsdk/libark_jsruntime.so(panda::ecmascript::Heap::ParallelGCTask::Run(unsigned int)+228)(a3d1ba664de66d31faed07d711ee1299)
#07 pc 000000000064f648 /system/lib64/platformsdk/libark_jsruntime.so(panda::ecmascript::Runner::Run(unsigned int)+188)(a3d1ba664de66d31faed07d711ee1299)
#08 pc 000000000064f718 /system/lib64/platformsdk/libark_jsruntime.so(a3d1ba664de66d31faed07d711ee1299)
#09 pc 00000000001ba6b8 /system/lib/ld-musl-aarch64.so.1(start+236)(8102fa8a64ba5e1e9f2257469d3fb251)

Common stack information about pointer exceptions:

0x000056c2fffc0008 indicates that the pointer is abnormal and the pointer mapping is incorrect.

Reason:Signal:SIGSEGV(SEGV_MAPERR)@0x000056c2fffc0008
Fault thread info:
Tid:2936, Name:OS_GC_Thread
#00 pc 00000000004d2ec0 /system/lib64/platformsdk/libark_jsruntime.so(733f61d2f51e825872484cc344970fe5)
#01 pc 00000000004c6cac /system/lib64/platformsdk/libark_jsruntime.so(733f61d2f51e825872484cc344970fe5)
#02 pc 00000000004cd180 /system/lib64/platformsdk/libark_jsruntime.so(panda::ecmascript::NonMovableMarker::ProcessMarkStack(unsigned int)+256)(733f61d2f51e825872484cc344970fe5)
#03 pc 000000000049d108 /system/lib64/platformsdk/libark_jsruntime.so(panda::ecmascript::ConcurrentMarker::ProcessConcurrentMarkTask(unsigned int)+52)(733f61d2f51e825872484cc344970fe5)
#04 pc 00000000004b6620 /system/lib64/platformsdk/libark_jsruntime.so(panda::ecmascript::Heap::ParallelGCTask::Run(unsigned int)+236)(733f61d2f51e825872484cc344970fe5)
#05 pc 00000000005d6e60 /system/lib64/platformsdk/libark_jsruntime.so(panda::ecmascript::Runner::Run(unsigned int)+168)(733f61d2f51e825872484cc344970fe5)
#06 pc 00000000005d6f30 /system/lib64/platformsdk/libark_jsruntime.so(733f61d2f51e825872484cc344970fe5)
#07 pc 00000000001bdb84 /system/lib/ld-musl-aarch64.so.1(start+236)(e65f5c83306cf9c7dd4643794946ab9f)