Table Of Contents

Other iGC Subjects

Object reclamations

OnReclaim routine

Before the system taking any action to unused objects, it calls a user-defined pre-class OnReclaim routine for each these objects. After all OnReclaim routines are done, the system reconsiders the status and determine objects really need to be reclaimed.

For C++ class, use HNXGC_ONRECLAIM to define an OnReclaim routine.

For Objective-C class, use [HNXGCOnReclaim] to define an OnReclaim routine

In the OnReclaim routine, you can control reclamation ordering and/or resurrection as described below.

Reclamation ordering

“C” API for reclamation control

In OnReclaim routine of Objective-C, you can use C function _hnxgc_onreclaim_declare to declare objects that may be used in the [dealloc] methods of self or other related objects.

@implementation Foo
- (void)HNXGCOnReclaim: (void*)caller {
    _hnxgc_onreclaim_declare(caller, 1, &_bar);  // _bar is kept alive until end of Foo
}
@end

strong property in MRRGC, ARCGC

In ARCGC code, you can use @property __strong to declare a strong reference that will keep target object alive even in [dealloc] method.

In MRR code, __strong is not available, you can use @property(retain) instead to achieve the same effects as @property __strong.

C++ helper macros

In C++, you can use HNXGC_DEPEND_DECLARE(...), and HNXGC_DEPEND_MULTIPLE_BEGIN to do the same job.

declare dependence of others

You can call _hnxgc_onreclaim_change_depender to change depender, which defaults to this. For example, by calling this function in object A’s OnReclaim routine, you can declare destructor of object ‘B’ depends on object ‘C’.

Resurrection before things changes

Once destructions begin, the application status may change irreversibly, e.g. network connections are closed, flags are marked finished, etc. We don’t think it was a good idea to do resurrection after these irreversible changes, it is too late to return to original status.

In HnxGC and iGC, you should do resurrection before the beginning of any destructions of related objects. That is, you should resurrect objects in OnReclaim routine, but avoid in C++ destructors or Objective-C [dealloc] methods.

To resurrect an object, assign the object to an effective reference, the system will detect that and put all directly or indirectly referenced objects back to live again.

Delay [dealloc] run

There is an old practice of writing a special [dealloc] method to have actual work done at main thread. See below code excerpts from Apple’s open source.

bool WebCoreObjCScheduleDeallocateOnMainThread(Class cls, id object)
{
    if (isMainThread()) return false;

    ClassAndIdPair* pair = new ClassAndIdPair(cls, object);
    callOnMainThread(deallocCallback, pair);
    return true;
}

...

@implementation WebCoreSharedBufferData
- (void)dealloc
{
    if (WebCoreObjCScheduleDeallocateOnMainThread(
        [WebCoreSharedBufferData class], self)) return;

    ...

    [super dealloc];
}
@end

iGC supports this practice to works well as before. But, we recommend use resurrection mechanism do do that. see Resurrection before things changes.

Nested and non-nested destructions

When all references to an object drops, the object is destructed immediately. Many reference counting implementations, including Apple’s Objective-C(MRR and ARC) and C++ shared_ptr<>, destruct consequent zero-RC objects in a nested manner. For example, suppose object A hold the last reference to object B, when A is reclaimed, the destructor of A dropes the reference to B, and B is destructed nested in the destructor of A.

This may cause stack overflow if involving a large number of nested destructor calls. For exmaple, a single-linked list of 50,000 nodes will crash in reference counting (but it is fine in manual delete or pop each elements before destructions).

In iGC, consequent zero-RC objects are destructed in a non-nested manner. References do not drop until the destruction finishes. Thus, destructors are executed one by one but not nested.

If you want to go back to nested destructions, assign nil to instance variables in the [dealloc] method.

- (void)dealloc {
    ...
    ivar1 = nil;    // lead to nested destruction
    ...
    [super dealloc];
}

Similar practices apply to HnxGC C++ code.

Direct Kill

You can instruct the system to immediately reclaim a “live” object, which is referenced by other code or live objects.

make sure don’t call [dealloc] more than once

There are several ways to do that. You can send [dealloc] message to the object but you have to make sure that [dealloc] won’t be called more than once, e.g. you should avoid this situation: during or after the first [dealloc] call, the object’s RC drops to zero and the system invokes [dealloc] again.

call iGC [destruct] is safer than [dealloc]

iGC provides [destruct] method for safely reclaiming an object immediately. It can be called multiple times and the system guarantees the [dealloc] method will be invoked only once in the object’s lifetime.

// MRRGC
Foo * foo = [[Foo alloc] init];

[foo destruct];     // [dealloc] will be invoked
[foo destruct];     // safe to invoke [destruct] multiple times
[foo destruct];
[foo destruct];

[foo release];

C API and C++ helper function

You can call low level C function _hnxgc_destruct_object or C++ gcdelete operator to achieve the same result as sending [destruct] message.

Compiler and Linker flags

Compiler flags

Here is a list of iGC compiler flags:

  • -fobjc-gc

    Enable GC for Apple’s MRR and ARC, i.e. switch them to MRRGC and ARCGC_C respectively.

  • -fobjc-arc-gc

    This will turn on ARCGC, and disable Apple’s ARC -fobj-arc.

  • -fobjc-arc-gc-compatible

    This must be used with -fobjc-arc-gc to switch ARCGC to ARCGC_C mode.

Linker flags

Here is a list of linker flags and explanations:

  • -u __iGC__

    Required to link against iGC library and replacements (iGC_Sim3x and iGC_RR).

  • -liGC

    Link in iGC library.

  • -lstdc++

    If none of the source code of a Xcode project is C++ or Objective-C++, you need this flag to link in standard C++ library.

  • -fobjc-arc-gc, or -fobjc-arc

    If none of the source code of a Xcode project is under ARC, but some libraries are built under ARC, i.e. using helper functions like: objc_retain, objc_autoreleaseReturnValue, etc., then you need to add either -fobjc-arc or -fobjc-arc-gc to link against libarclite library.

  • -liGC_Sim3x

    Replace -liGC with -liGC_Sim3x for building an app for iOS Simulator 3.0 ~ 3.2.2.

    You don’t need this for app for iOS 3.x devices or later, or for iOS Simulator 4.0 or later.

  • -liGC_RR

    Replace -liGC with -liGC_RR to roll back GC to pure reference counting. The libiGC_RR.a does not contain any GC code.

  • -lSystemLite

    You only need this flag for Block support on Simulator 3.0 ~ 3.1.3.

    You don’t need this for Block support on iOS device, including 3.0 ~ 3.1.3, providing a 4.0+ SDK is used for building your project.

Performance

With automatic management of object lifetimes, HnxGC opens a wider door to optimizations. Many optimizations become viable to apply as the system has more controls on object lifetimes.

RC optimizations

reduce deallocation cost

For example, when an object loses all references to it, it was destructed (method [dealloc] was called) immediately, but its memory was not freed immediately. Instead, the memory is put back into a pool, accumulated and released(reuse) at a right time, to improve performance.

reduce RC cost between heap objects

For objects that can tolerate delay of destruction, you call _hnxgc_set_nonrc to set them as non-RC objects, which no longer not incur RC cost between heap objects.

You can set some objects of a class to be non-rc, while others of the same class remain normal.

Objects created from Linear Pool are automatically set to non-rc, as they are not reclaimed immediately.

transfer ownership from callee to caller

When function X calls function Y, which returns a retained object, the ownership is transferred from Y to X without incurring RC operation. Other appropriate operations also do this kind of ownership transferring to reduce RC costs.

GC optimizations

RC contributes to GC

HnxGC does not simply combine RC and GC together, instead RC and GC are tightly coupled together as a whole. A significant portion of RC operations are reused by GC, thus the overall cost is much less than the sum of RC and GC.

Lower initiation cost

HnxGC does not suspend app threads or scan app stacks, this design eliminates the expensive part of process in garbage collection. Therefore, the initiation cost of HnxGC is very small and almost negligible. For example, for a heap of hundreds to thousands of objects, the cost of a thorough GC is comparable to the cost of 1 ~ 3 malloc calls.

Below is a chart of GC cost related to the size of heap (all objects are cycles).

_images/Throughputs.png

We can see, when heap is small, HnxGC took a few microseconds to complete a GC, while others took at least 100 ~ 1000 microseconds.

GC runs Less frequently

Because RC already detects and reclaims acyclic garbage, the requirement of performing GC is reduced significantly compared to a pure GC implementation. We don’t need to invoke GC as frequently as it was in a traditional GC environment.

Linear allocation from linear pool

Most of time, you can create a managed object from a linear pool by bumping a pointer, just a few machine instructions, much faster than a standard malloc call.

Responsiveness

Responsiveness is very important for real-time or soft real time applications. People often concern about the interrupts caused by a garbage collector, making a bad user experiences or missing some important signals, like reading data of external asynchronous source.

HnxGC guarantees that the collector runs as a normal thread, never rudely interrupt your work like others.

For example, suppose a high priority application thread dedicated to handling external asynchronous signals. It can be waked up immediately at any time (because it never be in the state of being suspended as in other GCs), and during the signal processing, it will not be interrupts if it does not invoke something like gcnew or cause RC drops to zero (by default, zero-RC will lead to destructor being executed immediately in the current thread, but you can change that using reclamation control, etc).

Fully concurrent GC

The HnxGC collector is fully concurrent. It runs independently from application threads(the mutators). When mutators modify the graph of reference relationship, such as copying (assigning) a reference to a new pointer, write barriers are performed to ensure a consistency view from the concurrent collector.

Most of time, write barriers are skipped as GC runs less frequently than before (thanks the RC part of HnxGC).

When write barriers are required, i.e. there is a concurrent garbage collection performing, they occur not only at references between objects, but also in RC operations, like assigning a reference to a variable in stack.

Never suspend threads

In the code of HnxGC, there is no calls to suspend thread or the like. HnxGC algorithm does not depend on that. This is a main difference from others, including those on-the-fly GCs.

App threads run at full speed

HnxGC collector does not require a higher priority than application threads. On the contrary, HnxGC collector usually runs at a lower priority level than a normal thread. Only when free memory space drops to a critical (abnormal) level, HnxGC will raise its priority to get more CPU resources for garbage collection, intentionally slowing down the executions of mutators.

A testing result on Win32

A testing was conducted on Win32 platforms (other GCs are not available for iOS platform). An event was raised for every 5ms, and time between them was measured (under 1ms resolution). The performance of HnxGC is very satisfied compared to other GCs, see below.

_images/Pauseless.png

Limitations

iOS versions earlier than 3.0 are not supported

iGC has been tested on iOS 4.1 armv6 device and iPhoneOS Simulator 3.0 via Xcode-4.2 Snow Leopard [1]. iPhone OS version earlier than 3.0 is not supported by iGC.

[1]By some hacking on Xcode-4.2, you can build and run on iPhone Simulator 3.x (3.0 ~ 3.1.3, 3.2). see here

MRR ivar cycles are not collected

Look at the following Objective-C declaration in MRR.

@interface Foo : NSObject
{
    id aObj;       // is this a strong or a weak reference?
}
@end

There is not enough information to distinguish strong reference ivars from weak, or vice versa. Therefore, iGC does not collect cycles involving MRR ivars.

You can convert MRR code to MRRGC to get ivar cycles collected.

ARC ivar cycles are not collected by default

By default, iGC does not collect cycles of instance variables in ARC code.

The reasons is, early versions of Clang compilers [*] have a bug, which emits incorrect information of ARC ivars. This makes GC on ARC ivars unsafe.

This bug does not exist in ARCGC and ARCGC_C. It is safe to do GC on ivar of ARCGC and ARCGC_C.

[*]This bug exists in Xcode 4.2 (clang-211.10.1) ~ 4.3 (clang-318.0.45), and was fixed in Xcode 4.4 (clang-421.0.57) and later, see clang revision 150639 & rdar 10832643 for details.

Note

If you are sure, your app is free of this bug, including all components from 3rd party and Apple’s Framework (it is difficult to ensure that at current time), then you may enable garbage collection working on ARC ivars.

Cycles of Core Foundation are not collected

The C/C++ based Core Foundation objects currently are GC-disabled. iGC does not collect cycles of Core Foundation objects, except for some special classes.

Common errors

compile error on gcptr<>: “expected expression”

Make sure your source file is C++ or Objective-C++.

C++ smart pointers, like gcptr<>, is not available for C or Objective-C code.

dyld: library not loaded: /usr/lib/libc++abi.dylib

Environment: Xcode 4.2, Simulator 4.3, LLVM GCC 4.2

Solution

Add -Wl,-no_implicit_dylibs to "Other Linker Flags(OTHER_LDFLAGS)" build setting.

ld: missing __Unwind_Resume, ___udivdi3, ... symbols

Environment: Xcode-4.5.2 Lion + Simulator 3.1.3 SDK

Symptom: missing __Unwind_Resume, __udivdi3, ... symbols

Solution:

add -lgcc_s.1 to “Other Linker Flags”

Explanation:

All these missing symbols are resolved by libgcc_s.1.dylib in iPhoneSimulator3.1.3.sdk.

Note, the libSystem.dylib in 3.1.3.sdk does not provide these symbols.

Questions:

  1. Why does Xcode-4.2 not need this flag?

    Because when an app links against <sdk>/usr/lib/libSystem.dylib, and the dylib for simulator usually reference host counterpart /usr/lib/libSystem.dylib, which may resolve these symbols. On MacOSX Snow Leopard, the host file /usr/lib/libSystem.dylib provides these symbols. So

    app --> <3.1.3.sdk> libSystem.dylib --> <host> /usr/lib/libSystem.dylib (resolved!)
  2. Why it does not work on MacOSX Lion, what is the difference?

    The /usr/lib/libSystem.dylib on MacOSX Lion no longer provides these symbols, which are moved to some small dylib files, such as /usr/lib/system/libunwind.dylib and /usr/lib/system/libcompiler_rt.dylib, etc.

    If an app only links against libSystem.dylib in 3.1.3.sdk, and in turn indirectly links against /usr/lib/libSystem.dylib without linking to those small dylib files, it does not resolve these missing symbols.

  3. Why does Xcode-4.5.2 with latest SDK not need this flag?

    The app is linked against libSystem.dylib in the latest SDK(e.g. 6.0.sdk), which does not directly provide the symbols but references many other small libraries that provides these symbols. By using otool -L, we can see these relationships:

    app
     `-> 6.0.sdk/usr/lib/libSystem.dylib
          `-> 6.0.sdk/usr/lib/system/libSystem.host.dylib
               `-> /usr/lib/system/libunwind.dylib (__Unwind_Resume, ...)
                   /usr/lib/system/libcompiler_rt.dylib (___udivdi3, ...)
  4. How about if I explicitly link against 6.0 sdk files libSystem.dylib or system/libSystem.host.dylib?

    if explicitly link against libSystem.dylib, then build fails with:

    missing file /usr/lib/system/libcache_sim.dylib for architecture i386.

    if link explicitly against system/libSystem.host.dylib, then build passed but with warning of:

    linking against 6.0.sdk/../system/libSystem.host.dylib built for MacOSX.

Others:

  1. The source code of clang shows that Xcode-4.2 (clang-211.10.1) is the same as Xcode-4.5.2 (clang-425.0.24), the -lgcc_s.1 is emitted only for iOS devices before 5.0, not for simulators.
  2. Pass -t option to ld will show a list of full paths of files that the app linked against.

undefined symbols: _OBJC_CLASS_$_NSObject, _OBJC_CLASS_$_NSArray, ...

This failure comes from one or more Objective-C ABI version 2.0 binaries(e.g., object files, libraries, frameworks) were linked against 3.x simulator sdk, which is Objective-C ABI version 1.0 based.

Solution:

Remove these binaries, or rebuild them to Objectie-C ABI version 1.0.

Note

For instance, libarclite_iphonesimulator.a are based on Objective-C ABI version 2.0, it cannot be linked against 3.x simulator sdk. To remove arclite library, add CLANG_LINK_OBJC_RUNTIME = NO to project settings.