[二分答案] Copying Books

本文提供了一道POJ题目的详细解答,通过二分查找确定最优解,再使用贪心算法从后向前分配资源,确保前序任务获得尽可能多的资源。代码采用Pascal实现,展示了完整的算法逻辑。

上次做到那个收入计划的时候就提到了这道题目,在POJ上是让输出方案,并且保证前面的人赋值的书稿尽量多。所以不需要SJ。

具体实现就是先二分出正确的答案,然后根据答案从后往前进行贪心划分。

[pascal 代码]

VAR
        A,WAY:ARRAY[0..100000]OF LONGINT;
        N,M,L,R,MID,I,J,TOT,T,IT:LONGINT;
FUNCTION CHECK(P:LONGINT):BOOLEAN;
 VAR
        PRE,I:LONGINT;
 BEGIN
        TOT:=M;
        PRE:=0;
        I:=1;
        WHILE I<=N DO
                BEGIN
                        IF PRE+A[I]<=P THEN
                                BEGIN
                                        PRE:=PRE+A[I];
                                        INC(I);
                                END
                        ELSE
                                BEGIN
                                        PRE:=0;
                                        DEC(TOT);
                                        IF TOT=0 THEN EXIT(FALSE);
                                END;
                END;
        EXIT(TRUE);
 END;
PROCEDURE GETWAYS(P:LONGINT);
 VAR
        PRE,I,TOT:LONGINT;
 BEGIN
        TOT:=0;
        PRE:=0;
        I:=N;
        WHILE I>=1 DO
                BEGIN
                        IF (PRE+A[I]<=P)AND(M-TOT<=I) THEN
                                BEGIN
                                        PRE:=PRE+A[I];
                                        DEC(I);
                                END
                        ELSE
                                BEGIN
                                        PRE:=0;
                                        INC(TOT);
                                        WAY[TOT]:=I;
                                        IF TOT=0 THEN EXIT;
                                END;
                END;
 END;
BEGIN
        READLN(T);
        FOR IT:=1 TO T DO
        BEGIN
        READLN(N,M);
        R:=0;L:=0;
        FOR I:=1 TO N DO
                BEGIN
                        READ(A[I]);
                        R:=R+A[I];
                END;
        WHILE L<R DO
                BEGIN
                        MID:=(L+R)>>1;
                        IF CHECK(MID) THEN R:=MID ELSE L:=MID+1;
                END;
        //WRITELN(L);
        GETWAYS(L);
        L:=1;
        FOR I:=M-1 DOWNTO 1 DO
                BEGIN
                        R:=WAY[I];
                        FOR J:=L TO R DO WRITE(A[J],' ');WRITE('/ ');
                        L:=R+1;
                END;
        FOR I:=L TO N DO WRITE(A[I],' ');WRITELN;
        END;
END.

转载于:https://www.cnblogs.com/FreeDestiny/archive/2011/10/29/2228336.html

<think> 我们正在讨论的是Android系统中的垃圾回收(GC)机制,特别是关于"NativeAlloc concurrent copying GC"的信息。用户之前提供了一些引用,其中提到了GC的不同原因和类别,包括GC_CONCURRENT、GC_FOR_MALLOC等,以及NativeAlloc(在API 10以下用于回收Dalvik堆以外的内存,如Bitmap或NIO buffer)。但是,在较新的Android版本中,内存分配都在Dalvik堆(或ART堆)中,所以NativeAlloc在较新版本中可能指的是Native内存分配触发的GC。 根据引用[4],GC Reason中的"NativeAlloc"表示当native内存有压力的时候触发的GC。而GC Name中的"concurrent copying"是一种GC算法的名称,它属于并发复制垃圾回收器。 在ART(Android Runtime)中,GC算法有多种,包括: - Concurrent Mark-Sweep (CMS) - Concurrent Copying (CC) 因此,"NativeAlloc concurrent copying GC"可以理解为:由于Native内存分配压力而触发的并发复制垃圾回收。 下面我们将详细介绍并发复制垃圾回收(Concurrent Copying GC)以及它在NativeAlloc场景下的应用。 ### 一、并发复制垃圾回收(Concurrent Copying GC)概述 并发复制是一种垃圾回收算法,它在回收内存时采用复制的方式,并且大部分工作与应用程序线程并发执行,以减少暂停时间(STW, Stop-The-World)。该算法主要应用于ART中的移动设备,特别是Android 8.0(Oreo)及更高版本。 #### 1.1 算法原理 并发复制垃圾回收器将堆分为两个空间(或更多): - **From空间**:当前正在使用的内存区域。 - **To空间**:用于复制的目标空间。 当GC开始时,它首先会暂停所有应用线程(STW阶段)以标记根对象(Root Set)。然后,恢复应用线程(并发阶段),同时垃圾回收器将存活对象从From空间复制到To空间。复制完成后,再次暂停应用线程(STW阶段)以更新引用,最后交换From和To空间。 #### 1.2 并发复制GC的优势 - **减少暂停时间**:由于复制阶段是并发执行的,所以应用暂停时间主要发生在标记根对象和最终更新引用阶段,时间较短。 - **内存整理**:复制过程中存活对象被紧凑地排列在To空间,消除了内存碎片。 - **并行处理**:在多核设备上,复制工作可以并行进行,提高效率。 #### 1.3 与Native内存分配的关系 在Android中,Native内存通常指通过`malloc`或`new`在堆上分配的内存(例如,通过JNI分配的内存)。当Native内存分配过多导致内存紧张时,系统可能会触发ART的GC来回收Java堆,因为Native内存和Java堆内存共享整个设备的物理内存资源。因此,当Native内存压力大时,ART的GC(包括并发复制GC)会被触发以释放Java堆中不再使用的对象,从而间接缓解内存压力。 ### 二、NativeAlloc concurrent copying GC的工作流程 当系统检测到Native内存压力(如频繁分配大块Native内存或Native内存使用接近阈值)时,ART虚拟机可能会触发一次并发复制GC。具体步骤如下: 1. **触发条件**:Native内存分配器(如jemalloc或scudo)报告内存压力,通过回调通知ART运行时。 2. **启动GC**:ART运行时确定进行垃圾回收,并选择并发复制GC(如果当前GC策略设置为并发复制)。 3. **初始暂停(STW)**:暂停所有应用线程,标记根对象(如静态变量、活动线程栈上的局部变量等)。 4. **并发复制**: - 恢复应用线程,同时GC线程开始遍历对象图,将存活对象从From空间复制到To空间。 - 在复制过程中,应用线程如果修改了对象引用,则通过写屏障(Write Barrier)记录这些更改,确保不会遗漏存活对象。 5. **最终暂停(STW)**:再次暂停所有应用线程,处理在并发复制阶段记录的引用更改,并更新所有指向旧对象地址的引用到新地址。 6. **空间交换**:交换From和To空间,此时To空间成为新的活动空间,From空间(包含垃圾)被清空或作为下一次GC的To空间。 7. **回收Native内存**:虽然GC主要回收Java堆,但Java堆的释放可能会减少进程的RSS(Resident Set Size),从而间接缓解Native内存压力。 ### 三、性能特点 - **暂停时间**:并发复制GC的暂停时间相对较短,适合对延迟敏感的应用(如游戏、视频播放)。 - **内存开销**:由于需要保留一半堆空间作为To空间,所以内存利用率较低(50%)。但在Android中,通常采用分代收集,只有部分堆(如年轻代)使用复制算法,因此实际开销较小。 - **吞吐量**:并发复制GC的吞吐量通常比并发标记清除(CMS)低,因为复制对象需要额外时间。 ### 四、在Android中的实际应用 在ART运行时中,并发复制GC是默认的GC之一,尤其用于年轻代的回收(Young Generation Collection)。对于老年代(Old Generation),ART通常使用并发标记清除(CMS)或标记压缩(Mark-Compact)算法。 当触发原因为`NativeAlloc`时,表明这次GC是由于Native内存压力引起的。开发者可以通过分析GC日志来识别此类事件: ```log I/art: NativeAlloc concurrent copying GC freed ... ``` ### 五、优化建议 1. **减少Native内存分配**:避免在JNI中频繁分配大块内存,使用对象池或缓存。 2. **及时释放Native资源**:确保在Java对象被回收时释放对应的Native内存(通过`finalize`或`Cleaner`)。 3. **调整GC策略**:对于特定应用,可以通过系统属性调整GC策略(如`dalvik.vm.gctype`),但需谨慎。 4. **监控内存使用**:使用Android Profiler监控Native和Java堆内存,定位内存泄漏。 ### 六、与其他GC算法的对比 | GC算法 | 暂停时间 | 内存开销 | 碎片处理 | 适用场景 | |--------|----------|-----------|----------|----------| | 并发复制 (Concurrent Copying) | 短(两次短暂STW) | 高(需要双倍空间) | 无碎片 | 年轻代、对延迟敏感场景 | | 并发标记清除 (Concurrent Mark-Sweep) | 中等(并发标记,最终STW) | 低 | 有碎片 | 老年代 | | 标记压缩 (Mark-Compact) | 长(全STW) | 低 | 无碎片 | 老年代、内存碎片严重时 | §§相关问题§§ 1. 如何监控Android应用中的Native内存分配? 2. 并发复制GC与并发标记清除GC的具体区别是什么? 3. 在ART中,如何手动触发垃圾回收?是否推荐这样做? 4. 如何分析Android GC日志以优化应用性能? [^1]: Android GC日志格式解析 [^2]: ART垃圾回收机制 [^3]: Android Native内存管理 [^4]: 并发垃圾回收算法研究 </think>### NativeAlloc Concurrent Copying GC 详解 `NativeAlloc concurrent copying GC` 是 Android ART 运行时中的一种高级垃圾回收机制,主要用于处理 **Native 内存压力**场景下的高效内存回收。以下是核心技术解析: --- ### 一、核心机制 #### 1. 设计目标 - **解决 Native 内存压力**:当 JNI 分配的 Native 内存(如 Bitmap/NIO Buffer)接近阈值时触发[^4] - **并发执行**:GC 线程与应用线程并行工作,最大程度减少 STW(Stop-The-World)时间[^3] - **空间整理**:采用复制算法消除内存碎片,提升内存利用率 #### 2. 工作流程 ```mermaid sequenceDiagram App->>+ART: 分配Native内存 ART->>GC: 检测内存压力(NativeAlloc) activate GC GC-->>App: 启动并发标记(不暂停线程) App->>GC: 继续执行(写屏障记录修改) GC->>GC: 复制存活对象 GC-->>-ART: 完成回收 ART->>App: 释放可用内存 ``` #### 3. 算法特性对比 | 特性 | Mark-Sweep | Concurrent Copying | |------|------------|-------------------| | 暂停时间 | 长(全堆扫描) | 短(两次微暂停) | | 内存碎片 | 严重 | 无(空间连续) | | 内存开销 | 低 | 高(需双倍空间) | | 适用场景 | 老年代 | 新生代/Native压力 | --- ### 二、触发条件与日志分析 #### 1. 典型触发场景 - JNI 分配大块 Native 内存(如 `ByteBuffer.allocateDirect()`) - Bitmap 解码超过堆限制 - MediaCodec 视频缓冲区分配 #### 2. GC 日志解析 ```log I/art: NativeAlloc concurrent copying GC freed 65595(3MB) AllocSpace objects, ← Java堆回收 9(4MB) LOS objects, ← 大对象空间回收 34% free, ← 堆空闲率 38MB/58MB, ← 已用/总内存 paused 1.195ms total 87.219ms ← 暂停时间/总耗时 ``` 关键指标: - **paused**:应用线程总暂停时间(通常 < 5ms) - **LOS objects**:Large Object Space 处理(如图片/数组) - **free%**:回收后堆空闲率(>30% 为健康状态) --- ### 三、内存模型与复制过程 #### 1. 堆空间划分 $$ \begin{array}{|c|c|} \hline \text{From Space} & \text{To Space} \\ \text{(存活对象来源)} & \text{(复制目标)} \\ \hline \end{array} $$ #### 2. 复制算法实现 ```java // 伪代码:对象复制核心逻辑 void copyObject(Object src) { Object dest = allocateInToSpace(src.size()); // 新空间分配 copyMemory(src, dest); // 对象复制 src.setForwardingPointer(dest); // 设置转发指针 for (Field field : src.references()) { // 递归处理引用 if (!isCopied(field)) copyObject(field); } } ``` #### 3. 写屏障技术 ```cpp // 写屏障示例(ARM汇编) STR R1, [R0] // 常规对象写入 DMB ISH // 内存屏障 LDR R2, [R0, #offset] // 检查转发指针 CBNZ R2, update_ref // 若对象已移动则更新引用 ``` --- ### 四、性能优化实践 #### 1. 减少 Native 内存压力 ```kotlin // 优化前:直接分配大缓冲区 val buffer = ByteBuffer.allocateDirect(1024 * 1024) // 优化后:使用对象池复用 val buffer = BufferPool.obtain(1024 * 1024) ... BufferPool.recycle(buffer) ``` #### 2. 避免 GC 触发临界点 - 监控 Native 内存:`Debug.getNativeHeapAllocatedSize()` - 阈值控制:保持分配量 < `Runtime.getRuntime().maxMemory() * 0.6` #### 3. 配置 GC 参数(需 root) ```sh # 调整并发GC线程数 adb shell setprop dalvik.vm.gc-thread-count 4 ``` --- ### 五、与其他 GC 的对比 | 特性 | NativeAlloc Concurrent Copying | Concurrent Mark-Sweep | |------|-------------------------------|----------------------| | 触发条件 | Native 内存压力 | Java 堆压力 | | 暂停时间 | 极短 (1-5ms) | 中等 (10-50ms) | | 内存增量 | 需要双倍空间 | 无额外需求 | | 碎片处理 | 完全消除 | 可能产生碎片 | | 最佳场景 | 频繁操作 Native 资源的应用 | 常规 Java 对象密集型应用 | > **注意**:从 Android 8.0 开始,Concurrent Copying 成为默认 GC 策略,替代了之前的 CMS 回收器[^4]。 --- ### 六、问题排查工具 1. **GC 日志分析** ```sh adb logcat -v threadtime | grep "GC_" ``` 2. **内存快照** ```java // 生成HPROF文件 Debug.dumpHprofData("/sdcard/heapdump.hprof"); ``` 3. **Systrace 跟踪** ```sh python systrace.py gfx view wm am pm ss dalvik app ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值