美文网首页Tech
内存优化-原理篇

内存优化-原理篇

作者: tom__zhu | 来源:发表于2019-07-21 22:56 被阅读0次

手机的物理内存是有限的,所以在对内存资源使用的优化不仅能使自家APP尽量少出现内存导致的崩溃,同时也能使手机中的各个APP和谐相处的共用手机资源,也就是会减少因为内存导致的后台被杀。因此,对公司APP内存的优化进行了一次大整理,同时也推动我们自研的性能监控系统。现在将调研阶段整理的一些知识分享出来。

准备知识

为了解释清楚接下来的问题,我们首先从iOS系统级实现机制来理解闪退是如何产生的。

FreeBSD is a free Unix-like operating system. It is characterized as “the unknown giant among free operating systems”,- reliable and robust.It is not a clone of UNIX, but works like UNIX, with UNIX-compliant internals and system APIs. FreeBSD is a complete operating system. The kernel,device drivers and all of the userland utilities, such as the shell, are held in the same source coderevision tracking tree, whereas with Linux distributions, the kernel, userland utilities and applications are developed separately, then packaged together in various ways by others.
*Mac OS X is based upon the Mach kernel. Certain parts from FreeBSD’s and NetBSD’s implementation of Unix were incorporated in Nextstep, the core of Mac OS X. * ref1

Mac OS X内核成为xnu。在最简单的意义上,可以把xnu视作具有一个基于Mach的核心,一种基于BSD的操作系统个性设置,以及一种用于驱动程序和其他内核扩展的面向对象的运行时环境。Mach组件基于Mach 3,而BSD组件则基于Free Bsd 5。通过层级可以了解到操作系统中的异常会从Mach或者BSD中抛出。事实上Mach暴露了一些异常接口给用户态以供开发者来捕获这些Mach异常(crash事件)。所有Mach异常都被ux_exception转换为相应的Unix信号量,并通过threadsignal将信号投递到出错的线程中。


捕获异常信息可以通过Mach异常或者Unix信号量。那么他们有什么区别呢。
Mach异常会先于Unix信号量,所以遇到Mach异常先于Unix使进程exit,那么信号量将永远无法进入到进程中去。转换Unix信号量只是为了兼容POSIX标准,可以类比为OSI七层模型。

什么是Jetsam

为了深入iOS内存相关的知识,就不会绕开Jetsam。关于Jetsam可能有些人比较陌生,可以从设置->隐私->分析与改进->分析数据中发现一些以JetsamEvent-xxxx-xx-xx-xxxxxx.ips命名的日志。通过日志右上角分享按钮将日志导入Mac中我们可以详细的看到里面有kernel、product、memoryStatus等信息。

电脑中虚拟内存是运行进程可以使用的RAM和磁盘空间的组合。当我们需要使用更多内存资源时间需要从硬盘中获取一部分空间来弥补。 交换空间是虚拟内存在硬盘上的部分,在RAM满时使用,交换空间可帮助计算机的操作系统假装其内存比实际更多。在Linux中叫交换空间,在windows中叫虚拟内存。

发生Jetsam的原因是应为iOS内存不存在类似Linux交换空间( Swap Space
)机制。出于iOS设备内存容量以及读写寿命的考量取而代之的是虚拟内存压缩(Virtual memory compression AKA RAM compression and memory compression),这种利用数据压缩来减少对存储分页大小以及数量请求的技术,将被压缩的请求分页存储在RAM中,因此对压缩数据的请求会触发页面错误和过程逆转ref2。这种解压缩需要较高的CPU耗费以及电量损耗。如果iOS内核不得不将优先级不高或者占用较多内存的APP杀掉。那么Jetsam就是操作系统在执行杀死APP时候的日志记录。从这个角度来说发生Jetsam是一种Crash,前文中我们知道信号量的采集是有限制的,而内存导致的APP闪退是无法被捕获的。

需要注意一点,macOS中是通过交换文件,在/var/vm中。
OOM的管理是通过Jetsam实现的,Jetsam是独立运行的,每一个进程都有一个high water mark(HWM),一旦超过这个值就会触发Jetsam机制杀掉当前进程(FOOM)。

总结一下,Jetsam机制是因为iOS中不存在交换空间机制。所以在iOS中,系统只能将一些优先级不高或内存过大的进程直接杀掉。

窥探源码

苹果在2017年开源了iOS系统上的XNU内核代码,Jetsam在内核中的源码中表现为kern_memorystatus.hbsd/kern/kern_memorystatus.c
通过窥探Jetsam的具体实现可以对iOS如何管理内存有清晰的思路。此处只贴出一些关键代码,感兴趣的同学可以去整体阅读以下源码。

/*
 * This function is called very early on in the Mach startup, from the
 * function start_kernel_threads() in osfmk/kern/startup.c.  
*/
void
bsd_init(void)
{

  。。。。。。

/*
 * Initialize the kernel memory allocator 
*/
/// --->  内核内存的构造方法
kmeminit();

  。。。。。。

#if CONFIG_FREEZE
#ifndef CONFIG_MEMORYSTATUS
    #error "CONFIG_FREEZE defined without matching CONFIG_MEMORYSTATUS"
#endif
    /* Initialise background freezing */
    bsd_init_kprintf("calling memorystatus_freeze_init\n");
    /// ---> iOS仅有的内存与进程休眠的deamon线程
    memorystatus_freeze_init();
#endif>

  。。。。。。

#if CONFIG_MEMORYSTATUS
    /* Initialize kernel memory status notifications */
    bsd_init_kprintf("calling memorystatus_init\n");
        /// ---> 敲黑板 iOS仅有的Jetsamdeamon线程
    memorystatus_init();
#endif /* CONFIG_MEMORYSTATUS */

  。。。。。。

}

以上代码的作用看注释能比较直观了解到iOS是通过守护线程监控系统内存。

  • 讲一下代码中两个宏的作用
    1.CONFIG_FREEZE
    除了判断CONFIG_FREEZE MACRO初始化memorystatus_freeze_init,还涉及到compressor等相关的操作。当开启时,内核中通过memorystatus_freeze_thread在未超出内存页范围的情况下调用memorystatus_demote_frozen_processes进程进行冻结(并非exit)。
  1. CONFIG_MEMORYSTATUS
    当开启时,针对低内存情形,将低优先级或占用大量内存的APP强杀。而这块逻辑的实现是在memorystatus_init方法中实现,接下来重点看一下其具体代码细节。
__private_extern__ void
memorystatus_init(void)
{

  。。。。。。

    /* Init buckets */

    for (i = 0; i < MEMSTAT_BUCKET_COUNT; i++) {
         /// ---> 构造MEMSTAT_BUCKET_COUNT个使用链表管理进程的结构体
        TAILQ_INIT(&memstat_bucket[i].list);
        /// ---> 优先级队列
        memstat_bucket[i].count = 0;
    }
    memorystatus_idle_demotion_call = thread_call_allocate((thread_call_func_t)memorystatus_perform_idle_demotion, NULL);

#if CONFIG_JETSAM
    nanoseconds_to_absolutetime((uint64_t)DEFERRED_IDLE_EXIT_TIME_SECS * NSEC_PER_SEC, &memorystatus_sysprocs_idle_delay_time);
    nanoseconds_to_absolutetime((uint64_t)DEFERRED_IDLE_EXIT_TIME_SECS * NSEC_PER_SEC, &memorystatus_apps_idle_delay_time);
    
    /* Apply overrides */
    PE_get_default("kern.jetsam_delta", &delta_percentage, sizeof(delta_percentage));
    if (delta_percentage == 0) {
        delta_percentage = 5;
    }
    assert(delta_percentage < 100);
    PE_get_default("kern.jetsam_critical_threshold", &critical_threshold_percentage, sizeof(critical_threshold_percentage));
    assert(critical_threshold_percentage < 100);
    PE_get_default("kern.jetsam_idle_offset", &idle_offset_percentage, sizeof(idle_offset_percentage));
    assert(idle_offset_percentage < 100);
    PE_get_default("kern.jetsam_pressure_threshold", &pressure_threshold_percentage, sizeof(pressure_threshold_percentage));
    assert(pressure_threshold_percentage < 100);
    PE_get_default("kern.jetsam_freeze_threshold", &freeze_threshold_percentage, sizeof(freeze_threshold_percentage));
    assert(freeze_threshold_percentage < 100);
    
    if (!PE_parse_boot_argn("jetsam_aging_policy", &jetsam_aging_policy,
            sizeof (jetsam_aging_policy))) {

        if (!PE_get_default("kern.jetsam_aging_policy", &jetsam_aging_policy,
                sizeof(jetsam_aging_policy))) {

            jetsam_aging_policy = kJetsamAgingPolicyLegacy;
        }
    }

    if (jetsam_aging_policy > kJetsamAgingPolicyMax) {
        jetsam_aging_policy = kJetsamAgingPolicyLegacy;
    }

    switch (jetsam_aging_policy) {

        case kJetsamAgingPolicyNone:
            system_procs_aging_band = JETSAM_PRIORITY_IDLE;
            applications_aging_band = JETSAM_PRIORITY_IDLE;
            break;

        case kJetsamAgingPolicyLegacy:
            /*
             * Legacy behavior where some daemons get a 10s protection once
             * AND only before the first clean->dirty->clean transition before
             * going into IDLE band.
             */
            system_procs_aging_band = JETSAM_PRIORITY_AGING_BAND1;
            applications_aging_band = JETSAM_PRIORITY_IDLE;
            break;

        case kJetsamAgingPolicySysProcsReclaimedFirst:
            system_procs_aging_band = JETSAM_PRIORITY_AGING_BAND1;
            applications_aging_band = JETSAM_PRIORITY_AGING_BAND2;
            break;

        case kJetsamAgingPolicyAppsReclaimedFirst:
            system_procs_aging_band = JETSAM_PRIORITY_AGING_BAND2;
            applications_aging_band = JETSAM_PRIORITY_AGING_BAND1;
            break;

        default:
            break;
    }

    /*
     * The aging bands cannot overlap with the JETSAM_PRIORITY_ELEVATED_INACTIVE
     * band and must be below it in priority. This is so that we don't have to make
     * our 'aging' code worry about a mix of processes, some of which need to age
     * and some others that need to stay elevated in the jetsam bands.
     */
    assert(JETSAM_PRIORITY_ELEVATED_INACTIVE > system_procs_aging_band);
    assert(JETSAM_PRIORITY_ELEVATED_INACTIVE > applications_aging_band);

    /* Take snapshots for idle-exit kills by default? First check the boot-arg... */
        /// ---> 对空闲进程exit快照的命令参数检测
    if (!PE_parse_boot_argn("jetsam_idle_snapshot", &memorystatus_idle_snapshot, sizeof (memorystatus_idle_snapshot))) {
            /* ...no boot-arg, so check the device tree */
            PE_get_default("kern.jetsam_idle_snapshot", &memorystatus_idle_snapshot, sizeof(memorystatus_idle_snapshot));
    }

    memorystatus_delta = delta_percentage * atop_64(max_mem) / 100;
    memorystatus_available_pages_critical_idle_offset = idle_offset_percentage * atop_64(max_mem) / 100;
    memorystatus_available_pages_critical_base = (critical_threshold_percentage / delta_percentage) * memorystatus_delta;
    memorystatus_policy_more_free_offset_pages = (policy_more_free_offset_percentage / delta_percentage) * memorystatus_delta;
    
    /* Jetsam Loop Detection */
    if (max_mem <= (512 * 1024 * 1024)) {
        /* 512 MB devices */
        memorystatus_jld_eval_period_msecs = 8000;  /* 8000 msecs == 8 second window */
    } else {
        /* 1GB and larger devices */
        memorystatus_jld_eval_period_msecs = 6000;  /* 6000 msecs == 6 second window */
    }

    memorystatus_jld_enabled = TRUE;

    /* No contention at this point */
    memorystatus_update_levels_locked(FALSE);

#endif /* CONFIG_JETSAM */

    memorystatus_jetsam_snapshot_max = maxproc;

    memorystatus_jetsam_snapshot_size = sizeof(memorystatus_jetsam_snapshot_t) +
        (sizeof(memorystatus_jetsam_snapshot_entry_t) * memorystatus_jetsam_snapshot_max);

    memorystatus_jetsam_snapshot = 
        (memorystatus_jetsam_snapshot_t*)kalloc(memorystatus_jetsam_snapshot_size);
    if (!memorystatus_jetsam_snapshot) {
        panic("Could not allocate memorystatus_jetsam_snapshot");
    }

    memorystatus_jetsam_snapshot_copy =
        (memorystatus_jetsam_snapshot_t*)kalloc(memorystatus_jetsam_snapshot_size);
    if (!memorystatus_jetsam_snapshot_copy) {
        panic("Could not allocate memorystatus_jetsam_snapshot_copy");
    }

    nanoseconds_to_absolutetime((uint64_t)JETSAM_SNAPSHOT_TIMEOUT_SECS * NSEC_PER_SEC, &memorystatus_jetsam_snapshot_timeout);

    memset(&memorystatus_at_boot_snapshot, 0, sizeof(memorystatus_jetsam_snapshot_t));

#if CONFIG_FREEZE
    memorystatus_freeze_threshold = (freeze_threshold_percentage / delta_percentage) * memorystatus_delta;
#endif
    
    /* Check the boot-arg to see if fast jetsam is allowed */
    if (!PE_parse_boot_argn("fast_jetsam_enabled", &fast_jetsam_enabled, sizeof (fast_jetsam_enabled))) {
        fast_jetsam_enabled = 0;
    }

    /* Check the boot-arg to configure the maximum number of jetsam threads */
    if (!PE_parse_boot_argn("max_jetsam_threads", &max_jetsam_threads, sizeof (max_jetsam_threads))) {
        max_jetsam_threads = JETSAM_THREADS_LIMIT;
    }

    /* Restrict the maximum number of jetsam threads to JETSAM_THREADS_LIMIT */
    if (max_jetsam_threads > JETSAM_THREADS_LIMIT) {
        max_jetsam_threads = JETSAM_THREADS_LIMIT;
        }

    /* For low CPU systems disable fast jetsam mechanism */
    if (vm_pageout_state.vm_restricted_to_single_processor == TRUE) {
        max_jetsam_threads = 1;
        fast_jetsam_enabled = 0;
    }

    /* Initialize the jetsam_threads state array */
        /// ---> 初始化关于Jetsam线程状态的数组
    jetsam_threads = kalloc(sizeof(struct jetsam_thread_state) * max_jetsam_threads);
}

Jetsam直译为废弃物,可以理解为杀掉消耗最多内存的进程并且抛弃这些进程占用内存页的过程。内核对所有的进程设有优先级,通过一个大小为#define MEMSTAT_BUCKET_COUNT (JETSAM_PRIORITY_MAX + 1)的数组维护这些进程,并用以在内存不足的时候按顺序杀掉进程。数组中内容是一个包含进程链表list的结构体,定义如下。

#define MEMSTAT_BUCKET_COUNT (JETSAM_PRIORITY_MAX + 1)

typedef struct memstat_bucket {
    TAILQ_HEAD(, proc) list;    /// 双向链表,用来存放同优先级的进程
    int count;                  /// 进程数
} memstat_bucket_t;

苹果官方文档中对Low Memory Reports是这样介绍的

When a low-memory condition is detected, the virtual memory system in iOS relies on the cooperation of applications to release memory. Low-memory notifications are sent to all running applications and processes as a request to free up memory, hoping to reduce the amount of memory in use. If memory pressure still exists, the system may terminate background processes to ease memory pressure. If enough memory can be freed up, your application will continue to run. If not, your application will be terminated by iOS because there isn't enough memory to satisfy the application's demands, and a low memory report will be generated and stored on the device.

总结一下就是内存资源不足的时候,首先通过发送Low-memory通知(-applicationDidReceiveMemoryWarning:application ; UIApplicationDidReceiveMemoryWarningNotification)。如果内存不足仍然存在,后台进程会被杀掉。假如此时仍然内存资源紧张,那么FOOM就会发生在目前运行的进程中。
关于后台为什么会先于前台进程被杀掉是因为每个进程处在的优先级队列不同,关于进程的优先级有如下定义:

#define JETSAM_PRIORITY_REVISION                  2

#define JETSAM_PRIORITY_IDLE_HEAD                -2
/* The value -1 is an alias to JETSAM_PRIORITY_DEFAULT */
#define JETSAM_PRIORITY_IDLE                      0
#define JETSAM_PRIORITY_IDLE_DEFERRED         1 /* Keeping this around till all xnu_quick_tests can be moved away from it.*/
#define JETSAM_PRIORITY_AGING_BAND1       JETSAM_PRIORITY_IDLE_DEFERRED
#define JETSAM_PRIORITY_BACKGROUND_OPPORTUNISTIC  2
#define JETSAM_PRIORITY_AGING_BAND2       JETSAM_PRIORITY_BACKGROUND_OPPORTUNISTIC
#define JETSAM_PRIORITY_BACKGROUND                3
#define JETSAM_PRIORITY_ELEVATED_INACTIVE     JETSAM_PRIORITY_BACKGROUND
#define JETSAM_PRIORITY_MAIL                      4
#define JETSAM_PRIORITY_PHONE                     5
#define JETSAM_PRIORITY_UI_SUPPORT                8
#define JETSAM_PRIORITY_FOREGROUND_SUPPORT        9
#define JETSAM_PRIORITY_FOREGROUND               10
#define JETSAM_PRIORITY_AUDIO_AND_ACCESSORY      12
#define JETSAM_PRIORITY_CONDUCTOR                13
#define JETSAM_PRIORITY_HOME                     16
#define JETSAM_PRIORITY_EXECUTIVE                17
#define JETSAM_PRIORITY_IMPORTANT                18
#define JETSAM_PRIORITY_CRITICAL                 19

#define JETSAM_PRIORITY_MAX                      21

/* TODO - tune. This should probably be lower priority */
#define JETSAM_PRIORITY_DEFAULT                  18
#define JETSAM_PRIORITY_TELEPHONY                19
  • JETSAM_PRIORITY_IDLE --> 空闲优先级 --> 0
  • JETSAM_PRIORITY_BACKGROUND --> 后台优先级 --> 3
  • JETSAM_PRIORITY_FOREGROUND --> 前台优先级 --> 10

优先级数值越大,优先级越高。所以被系统杀掉的顺序是

  1. JETSAM_PRIORITY_IDLE
  2. JETSAM_PRIORITY_BACKGROUND
  3. JETSAM_PRIORITY_FOREGROUND

我是一条分割线


触发OOM的关键函数

至此,Jetsam基本知识讲解完毕,那么XNU到底如何处理Jetsam事件呢?
在memorystatus_init初始化方法中,在BSD层中起了一个线程,它的优先级是内核中最高的95 /* MAXPRI_KERNEL */,这个线程会维护两个表,一个是上文中提到的优先级列表,还有一个是内存快照列表用以保存每个进程内存消耗情况。

__private_extern__ void
memorystatus_init(void)
{
    ... 
    /* Initialize all the jetsam threads */
        /// ---> 创建 Jetsam线程,个数由内核参数和设备性能决定一般为1,fast jetsam时稍有不同。
    for (i = 0; i < max_jetsam_threads; i++) {
                /// ---> memorystatus_thread线程被创建,并且线程优先级是内核能分配的最高级
        result = kernel_thread_start_priority(memorystatus_thread, NULL, 95 /* MAXPRI_KERNEL */, &jetsam_threads[i].thread);
        if (result == KERN_SUCCESS) {
            jetsam_threads[i].inited = FALSE;
            jetsam_threads[i].index = i;
            thread_deallocate(jetsam_threads[i].thread);
        } else {
            panic("Could not create memorystatus_thread %d", i);
        }
    }
}

static void
memorystatus_thread(void *param __unused, wait_result_t wr __unused)
{
  。。。。。。
  /// --> 创建memstatus线程
  。。。。。。
}

这个常驻线程接受内核给每个进程发送的内存压力通知,这个通知转换到用户层就是对应的-applicationDidReceiveMemoryWarning:application ; UIApplicationDidReceiveMemoryWarningNotification

那么有什么原因会导致内存压力的出现呢?我们先看一下memorystatus_action_needed函数

static boolean_t
memorystatus_action_needed(void)
{
#if CONFIG_EMBEDDED
    return (is_reason_thrashing(kill_under_pressure_cause) ||  /// --> 频繁的的页面换进换出
            is_reason_zone_map_exhaustion(kill_under_pressure_cause) ||  /// --> Mach Zone耗尽了。Mach内核虚拟内存管理相关。
           memorystatus_available_pages <= memorystatus_available_pages_pressure); /// --> 内存可用页表判断
#else /* CONFIG_EMBEDDED */
    return (is_reason_thrashing(kill_under_pressure_cause) ||  /// --> 频繁的的页面换进换出
            is_reason_zone_map_exhaustion(kill_under_pressure_cause));  /// --> Mach Zone耗尽了。Mach内核虚拟内存管理相关。
#endif /* CONFIG_EMBEDDED */
}

/* Does cause indicate vm or fc thrashing? */
/// --> 频繁的的页面换进换出
static boolean_t
is_reason_thrashing(unsigned cause)
{
    switch (cause) {
    case kMemorystatusKilledFCThrashing:
    case kMemorystatusKilledVMCompressorThrashing:
    case kMemorystatusKilledVMCompressorSpaceShortage:
        return TRUE;
    default:
        return FALSE;
    }
}

/* Is the zone map almost full? */
/// --> Mach Zone耗尽了。Mach内核虚拟内存管理相关。
static boolean_t
is_reason_zone_map_exhaustion(unsigned cause)
{
    if (cause == kMemorystatusKilledZoneMapExhaustion)
        return TRUE;
    return FALSE;
}

总结来说,导致内存压力出现的原因有

  1. 频繁的的页面换进换出。
  2. Mach Zone耗尽了。Mach内核虚拟内存管理相关。
  3. 内存可用页表与阈值的比较。
    在这几种情形下都会触发进程被杀掉。

杀掉进程函数分析

同步杀掉进程。相对比较好理解一些,通过pid杀掉对应的一个或一批进程,具体逻辑详见代码分析部分。在杀死进程的同时会标记上触发的原因。

static boolean_t 
memorystatus_kill_process_sync(pid_t victim_pid, uint32_t cause, os_reason_t jetsam_reason) {
    boolean_t res;

    uint32_t errors = 0;

    if (victim_pid == -1) {
        /* No pid, so kill first process */
        /// ---> pid == -1 则杀掉所有优先级较低的进程;或者同一优先级中内存占用较大的进程
        res = memorystatus_kill_top_process(TRUE, TRUE, cause, jetsam_reason, NULL, &errors);
    } else {
        /// ---> pid != -1 则杀掉对应的进程
        res = memorystatus_kill_specific_process(victim_pid, cause, jetsam_reason);
    }

    。。。。。。

    return res;
}

异步杀掉进程。通过memorystatus_thread_wake方法调用将Jetsam线程唤醒,而对应的线程是做内存状态管理事务,通过这种间接方式触发OOM。

static boolean_t 
memorystatus_kill_process_async(pid_t victim_pid, uint32_t cause) {
    /*
     * TODO: allow a general async path
     *
     * NOTE: If a new async kill cause is added, make sure to update memorystatus_thread() to
     * add the appropriate exit reason code mapping.
     */
    if ((victim_pid != -1) ||
            (cause != kMemorystatusKilledVMPageShortage &&
            cause != kMemorystatusKilledVMCompressorThrashing &&
            cause != kMemorystatusKilledVMCompressorSpaceShortage &&
            cause != kMemorystatusKilledFCThrashing &&
            cause != kMemorystatusKilledZoneMapExhaustion)) {
        return FALSE;
    }
    
    kill_under_pressure_cause = cause;
    memorystatus_thread_wake();
    return TRUE;
}

static void
memorystatus_thread_wake(void)
{      
    int thr_id = 0;
    int active_thr = atomic_load(&active_jetsam_threads);

    /* Wakeup all the jetsam threads */
    for (thr_id = 0; thr_id < active_thr; thr_id++) {
        thread_wakeup((event_t)&jetsam_threads[thr_id].memorystatus_wakeup); 
    }       
}  

接下来聊一聊接受压力通知的原因有哪些。

进程被杀掉是通过memorystatus_do_kill 调用 jetsam_do_kill 来执行,在jetsam_do_kill入参中会指定proc_t以及os_reason_t。也就是通知对应的PID以某种原因杀掉。

/*
 * Jetsam exit reason definitions - related to memorystatus
 *
 * When adding new exit reasons also update:
 *  JETSAM_REASON_MEMORYSTATUS_MAX
 *  kMemorystatusKilled... Cause enum
 *  memorystatus_kill_cause_name[]
 */
#define JETSAM_REASON_INVALID                               0
#define JETSAM_REASON_GENERIC                               1
#define JETSAM_REASON_MEMORY_HIGHWATER                      2
#define JETSAM_REASON_VNODE                                 3
#define JETSAM_REASON_MEMORY_VMPAGESHORTAGE                 4
#define JETSAM_REASON_MEMORY_PROCTHRASHING                  5
#define JETSAM_REASON_MEMORY_FCTHRASHING                    6
#define JETSAM_REASON_MEMORY_PERPROCESSLIMIT                7
#define JETSAM_REASON_MEMORY_DISK_SPACE_SHORTAGE            8
#define JETSAM_REASON_MEMORY_IDLE_EXIT                      9
#define JETSAM_REASON_ZONE_MAP_EXHAUSTION                   10
#define JETSAM_REASON_MEMORY_VMCOMPRESSOR_THRASHING         11
#define JETSAM_REASON_MEMORY_VMCOMPRESSOR_SPACE_SHORTAGE    12

#define JETSAM_REASON_MEMORYSTATUS_MAX  JETSAM_REASON_MEMORY_VMCOMPRESSOR_SPACE_SHORTAGE

零零碎碎说了遍Jetsam相关知识,我们能如何转换为生产力呢。其实在开发环节可以通过Instruments的Allocations来分析内存占用情况,但对上线后的项目缺鞭长莫及。对线上项目内存监控可以参考业界现有知名解决方案比如FB与微信开源的监控系统。再后续文章中再做介绍。

ref1 | https://dolgopa.wordpress.com/2010/04/14/jordan-k-hubbard-freebsd-relation-between-freebsd-and-mac-osx/?from=timeline&isappinstalled=0
ref2 | https://en.wikipedia.org/wiki/Virtual_memory_compression
https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/bsd/sys/kern_memorystatus.h

相关文章

网友评论

    本文标题:内存优化-原理篇

    本文链接:https://www.haomeiwen.com/subject/sckvlctx.html