/** * Invalidate the whole view. If the view is visible, * {@link #onDraw(android.graphics.Canvas)} will be called at some point in * the future. * <p> * This must be called from a UI thread. To call from a non-UI thread, call * {@link #postInvalidate()}. */ publicvoidinvalidate(){ invalidate(true); }
/** * This is where the invalidate() work actually happens. A full invalidate() * causes the drawing cache to be invalidated, but this function can be * called with invalidateCache set to false to skip that invalidation step * for cases that do not need it (for example, a component that remains at * the same dimensions with the same content). * * @param invalidateCache Whether the drawing cache for this view should be * invalidated as well. This is usually true for a full * invalidate, but may be set to false if the View's contents or * dimensions have not changed. */ voidinvalidate(boolean invalidateCache){ invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true); }
if (invalidateCache) { mPrivateFlags |= PFLAG_INVALIDATED; mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID; }
// Propagate the damage rectangle to the parent view. final AttachInfo ai = mAttachInfo; final ViewParent p = mParent; if (p != null && ai != null && l < r && t < b) { final Rect damage = ai.mTmpInvalRect; damage.set(l, t, r, b); //往父view传递,调用父view的invalidateChild方法 p.invalidateChild(this, damage); }
/** * Don't call or override this method. It is used for the implementation of * the view hierarchy. */ publicfinalvoidinvalidateChild(View child, final Rect dirty){ ViewParent parent = this;
/** * Don't call or override this method. It is used for the implementation of * the view hierarchy. * * This implementation returns null if this ViewGroup does not have a parent, * if this ViewGroup is already fully invalidated or if the dirty rectangle * does not intersect with this ViewGroup's bounds. */ public ViewParent invalidateChildInParent(finalint[] location, final Rect dirty){ if ((mPrivateFlags & PFLAG_DRAWN) == PFLAG_DRAWN || (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) { if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) != FLAG_OPTIMIZE_INVALIDATE) { dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX, location[CHILD_TOP_INDEX] - mScrollY); if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0) { dirty.union(0, 0, mRight - mLeft, mBottom - mTop); }
@Override public ViewParent invalidateChildInParent(int[] location, Rect dirty){ checkThread(); ... if (dirty == null) { invalidate(); returnnull; } elseif (dirty.isEmpty() && !mIsAnimating) { returnnull; }
...
invalidateRectOnScreen(dirty);
returnnull; }
最关键的一步在invalidateRectOnScreen中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
privatevoidinvalidateRectOnScreen(Rect dirty){ final Rect localDirty = mDirty; ...
// Add the new dirty rect to the current one localDirty.union(dirty.left, dirty.top, dirty.right, dirty.bottom); // Intersect with the bounds of the window to skip // updates that lie outside of the visible region ... if (!mWillDrawSoon && (intersected || mIsAnimating)) { scheduleTraversals(); } }
/** * <p>Cause an invalidate to happen on a subsequent cycle through the event loop. * Use this to invalidate the View from a non-UI thread.</p> * * <p>This method can be invoked from outside of the UI thread * only when this View is attached to a window.</p> * * @see #invalidate() * @see #postInvalidateDelayed(long) */ publicvoidpostInvalidate(){ postInvalidateDelayed(0); }
/** * <p>Cause an invalidate to happen on a subsequent cycle through the event * loop. Waits for the specified amount of time.</p> * * <p>This method can be invoked from outside of the UI thread * only when this View is attached to a window.</p> * * @param delayMilliseconds the duration in milliseconds to delay the * invalidation by * * @see #invalidate() * @see #postInvalidate() */ publicvoidpostInvalidateDelayed(long delayMilliseconds){ // We try only with the AttachInfo because there's no point in invalidating // if we are not attached to our window final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this, delayMilliseconds); } }
/** * Call this when something has changed which has invalidated the * layout of this view. This will schedule a layout pass of the view * tree. This should not be called while the view hierarchy is currently in a layout * pass ({@link #isInLayout()}. If layout is happening, the request may be honored at the * end of the current layout pass (and then layout will run again) or after the current * frame is drawn and the next layout occurs. * * <p>Subclasses which override this method should call the superclass method to * handle possible request-during-layout errors correctly.</p> */ @CallSuper publicvoidrequestLayout(){ if (mMeasureCache != null) mMeasureCache.clear();
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) { // Only trigger request-during-layout logic if this is the view requesting it, // not the views in its parent hierarchy ViewRootImpl viewRoot = getViewRootImpl(); if (viewRoot != null && viewRoot.isInLayout()) { if (!viewRoot.requestLayoutDuringLayout(this)) { return; } } mAttachInfo.mViewRequestingLayout = this; }
voidcheckThread(){ if (mThread != Thread.currentThread()) { thrownew CalledFromWrongThreadException( "Only the original thread that created a view hierarchy can touch its views."); } }
/** * The data set used to store unused views that should be reused during the next layout * to avoid creating new ones */ final RecycleBin mRecycler = new RecycleBin();
/** * The RecycleBin facilitates reuse of views across layouts. The RecycleBin has two levels of * storage: ActiveViews and ScrapViews. ActiveViews are those views which were onscreen at the * start of a layout. By construction, they are displaying current information. At the end of * layout, all views in ActiveViews are demoted to ScrapViews. ScrapViews are old views that * could potentially be used by the adapter to avoid allocating views unnecessarily. */
final RecycleBin recycleBin = mRecycler; if (dataChanged) { for (int i = 0; i < childCount; i++) { recycleBin.addScrapView(getChildAt(i), firstPosition+i); } } else { recycleBin.fillActiveViews(childCount, firstPosition); }
// Clear out old views detachAllViewsFromParent(); recycleBin.removeSkippedScrap(); …… // Flush any cached views that did not get reused above recycleBin.scrapActiveViews();
// Detect the case where a cursor that was previously invalidated has // been repopulated with new data. if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null && mOldItemCount == 0 && mItemCount > 0) { AdapterView.this.onRestoreInstanceState(mInstanceState); mInstanceState = null; } else { rememberSyncState(); } checkFocus(); requestLayout(); }
if (AdapterView.this.getAdapter().hasStableIds()) { // Remember the current state for the case where our hosting activity is being // stopped and later restarted mInstanceState = AdapterView.this.onSaveInstanceState(); }
// Data is invalid so we should reset our state mOldItemCount = mItemCount; mItemCount = 0; mSelectedPosition = INVALID_POSITION; mSelectedRowId = INVALID_ROW_ID; mNextSelectedPosition = INVALID_POSITION; mNextSelectedRowId = INVALID_ROW_ID; mNeedSync = false;
private View makeAndAddView(int position, int y, boolean flow, int childrenLeft, boolean selected){ View child;
if (!mDataChanged) { // Try to use an existing view for this position child = mRecycler.getActiveView(position); if (child != null) { // Found it -- we're using an existing child // This just needs to be positioned setupChild(child, position, y, flow, childrenLeft, selected, true);
return child; } }
// Make a new view for this position, or convert an unused view if possible child = obtainView(position, mIsScrap); // This needs to be positioned and measured setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]); return child; }
final View transientView = mRecycler.getTransientStateView(position); if (transientView != null) { final LayoutParams params = (LayoutParams) transientView.getLayoutParams();
// If the view type hasn't changed, attempt to re-bind the data. if (params.viewType == mAdapter.getItemViewType(position)) { final View updatedView = mAdapter.getView(position, transientView, this);
View getActiveView(int position){ int index = position - mFirstActivePosition; final View[] activeViews = mActiveViews; if (index >=0 && index < activeViews.length) { final View match = activeViews[index]; activeViews[index] = null; return match; } returnnull; }
// first clears the measured dimension flag mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;
resolveRtlPropertiesIfNeeded();
int cacheIndex = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ? -1 : mMeasureCache.indexOfKey(key); if (cacheIndex < 0 || sIgnoreMeasureCache) { // measure ourselves, this should set the measured dimension flag back onMeasure(widthMeasureSpec, heightMeasureSpec); mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } else { long value = mMeasureCache.valueAt(cacheIndex); // Casting a long to int drops the high 32 bits, no mask needed setMeasuredDimensionRaw((int) (value >> 32), (int) value); mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } ...... }
/** * <p> * Measure the view and its content to determine the measured width and the * measured height. This method is invoked by {@link #measure(int, int)} and * should be overridden by subclasses to provide accurate and efficient * measurement of their contents. * </p> * * <p> * <strong>CONTRACT:</strong> When overriding this method, you * <em>must</em> call {@link #setMeasuredDimension(int, int)} to store the * measured width and height of this view. Failure to do so will trigger an * <code>IllegalStateException</code>, thrown by * {@link #measure(int, int)}. Calling the superclass' * {@link #onMeasure(int, int)} is a valid use. * </p> * * <p> * The base class implementation of measure defaults to the background size, * unless a larger size is allowed by the MeasureSpec. Subclasses should * override {@link #onMeasure(int, int)} to provide better measurements of * their content. * </p> * * <p> * If this method is overridden, it is the subclass's responsibility to make * sure the measured height and width are at least the view's minimum height * and width ({@link #getSuggestedMinimumHeight()} and * {@link #getSuggestedMinimumWidth()}). * </p> * * @param widthMeasureSpec horizontal space requirements as imposed by the parent. * The requirements are encoded with * {@link android.view.View.MeasureSpec}. * @param heightMeasureSpec vertical space requirements as imposed by the parent. * The requirements are encoded with * {@link android.view.View.MeasureSpec}. * * @see #getMeasuredWidth() * @see #getMeasuredHeight() * @see #setMeasuredDimension(int, int) * @see #getSuggestedMinimumHeight() * @see #getSuggestedMinimumWidth() * @see android.view.View.MeasureSpec#getMode(int) * @see android.view.View.MeasureSpec#getSize(int) */ protectedvoidonMeasure(int widthMeasureSpec, int heightMeasureSpec){ setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); }
publicstaticintgetDefaultSize(int size, int measureSpec){ int result = size; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) { case MeasureSpec.UNSPECIFIED: result = size; break; case MeasureSpec.AT_MOST: case MeasureSpec.EXACTLY: result = specSize; break; } return result; }
protectedvoidmeasureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed){ final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
publicstaticintgetChildMeasureSpec(int spec, int padding, int childDimension){ //获取父视图传来的mode和size int specMode = MeasureSpec.getMode(spec); int specSize = MeasureSpec.getSize(spec);
//计算size减去padding的大小,如果小于0就返回0 int size = Math.max(0, specSize - padding);
//最后返回的结果值。 int resultSize = 0; int resultMode = 0;
switch (specMode) { // 父视图指定了一个准确的大小 case MeasureSpec.EXACTLY: if (childDimension >= 0) { //说明开发者在xml文件或者java中设置了一个具体的大于等于0的大小值,所以将这个值设为结果,并且mode为EXACTLY. resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } elseif (childDimension == LayoutParams.MATCH_PARENT) { // 子视图就设为当前值 resultSize = size; resultMode = MeasureSpec.EXACTLY; } elseif (childDimension == LayoutParams.WRAP_CONTENT) { // 子视图自己决定大小,但是最大是当前这个值。所以mode设为AT_MOST, 大小设为size resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; //以下两种case的逻辑思路和上述的默认case差不多,这里就不再赘述了,大家看代码应该很容易理解。 case MeasureSpec.AT_MOST: if (childDimension >= 0) { // Child wants a specific size... so be it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } elseif (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size, but our size is not fixed. // Constrain child to not be bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } elseif (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; case MeasureSpec.UNSPECIFIED: if (childDimension >= 0) { // Child wants a specific size... let him have it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } elseif (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size... find out how big it should be resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; resultMode = MeasureSpec.UNSPECIFIED; } elseif (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size.... find out how // big it should be resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; resultMode = MeasureSpec.UNSPECIFIED; } break;
protectedbooleansetFrame(int left, int top, int right, int bottom){ boolean changed = false; ...... if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) { changed = true;
// 保存drawn位 int drawn = mPrivateFlags & PFLAG_DRAWN;
int oldWidth = mRight - mLeft; int oldHeight = mBottom - mTop; int newWidth = right - left; int newHeight = bottom - top; boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);
if (sizeChanged) { sizeChange(newWidth, newHeight, oldWidth, oldHeight); }
if ((mViewFlags & VISIBILITY_MASK) == VISIBLE || mGhostView != null) { // If we are visible, force the DRAWN bit to on so that // this invalidate will go through (at least to our parent). // This is because someone may have invalidated this view // before this call to setFrame came in, thereby clearing // the DRAWN bit. mPrivateFlags |= PFLAG_DRAWN; invalidate(sizeChanged); // 需要显示列表的父view可能因为子view的边界改变而需要重新创建 invalidateParentCaches(); }
@Override publicfinalvoidlayout(int l, int t, int r, int b){ if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) { if (mTransition != null) { mTransition.layoutChange(this); } super.layout(l, t, r, b); } else { // record the fact that we noop'd it; request layout when transition finishes mLayoutCalledWhileSuppressed = true; } }
privatevoiddraw(boolean fullRedrawNeeded){ Surface surface = mSurface; ...... final Rect dirty = mDirty; ...... if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) { return; } ...... }
我们看到,在draw中关键一步是调用了ViewRootImpl的drawSoftware方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
privatebooleandrawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff, boolean scalingRequired, Rect dirty){ final Canvas canvas; try { finalint left = dirty.left; finalint top = dirty.top; finalint right = dirty.right; finalint bottom = dirty.bottom;
/** * Draw one child of this View Group. This method is responsible for getting * the canvas in the right state. This includes clipping, translating so * that the child's scrolled origin is at 0, 0, and applying any animation * transformations. * * @param canvas The canvas on which to draw the child * @param child Who to draw * @param drawingTime The time at which draw is occurring * @return True if an invalidate() was issued */ protectedbooleandrawChild(Canvas canvas, View child, long drawingTime){ return child.draw(canvas, this, drawingTime); }
/** * This method is called by ViewGroup.drawChild() to have each child view draw itself. * * This is where the View specializes rendering behavior based on layer type, * and hardware acceleration. */ booleandraw(Canvas canvas, ViewGroup parent, long drawingTime){ finalboolean hardwareAcceleratedCanvas = canvas.isHardwareAccelerated(); ...... if (!drawingWithDrawingCache) { if (drawingWithRenderNode) { mPrivateFlags &= ~PFLAG_DIRTY_MASK; ((DisplayListCanvas) canvas).drawRenderNode(renderNode); } else { // Fast path for layouts with no backgrounds if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) { mPrivateFlags &= ~PFLAG_DIRTY_MASK; dispatchDraw(canvas); } else { draw(canvas); } } } ...... }
//设置当前同步状态 protectedfinalvoidsetState(int newState){ state = newState; }
//使用CAS设置当前状态,保证原子性 protectedfinalbooleancompareAndSetState(int expect, int update){ // See below for intrinsics setup to support this return unsafe.compareAndSwapInt(this, stateOffset, expect, update); }
protectedfinalbooleancompareAndSetState(int expect, int update){ // See below for intrinsics setup to support this return unsafe.compareAndSwapInt(this, stateOffset, expect, update); }
privatevoidunparkSuccessor(Node node){ int ws = node.waitStatus; if (ws < 0) compareAndSetWaitStatus(node, ws, 0);
Node s = node.next; if (s == null || s.waitStatus > 0) { s = null; for (Node t = tail; t != null && t != node; t = t.prev) if (t.waitStatus <= 0) s = t; } if (s != null) LockSupport.unpark(s.thread); }
/** * Atomically sets the value to the given updated value * if the current value {@code ==} the expected value. * @param expect the expected value * @param update the new value * @return {@code true} if successful. False return indicates that * the actual value was not equal to the expected value. */ publicfinalbooleancompareAndSet(V expect, V update){ return unsafe.compareAndSwapObject(this, valueOffset, expect, update); }
其又调用了native方法:
1
publicfinalnativebooleancompareAndSwapObject(Object object, long valueOffset, Object expect, Object update);
这里就和AtomicInteger等原子类一样了。同样的方法还有如下:
1 2 3
publicfinal V getAndSet(V newValue){ return (V)unsafe.getAndSetObject(this, valueOffset, newValue); }
publicfinal V getAndUpdate(UnaryOperator<V> updateFunction){ V prev, next; do { prev = get(); next = updateFunction.apply(prev); } while (!compareAndSet(prev, next)); return prev; }
publicfinal V updateAndGet(UnaryOperator<V> updateFunction){ V prev, next; do { prev = get(); next = updateFunction.apply(prev); } while (!compareAndSet(prev, next)); return next; }
privatestaticfinalint base = unsafe.arrayBaseOffset(int[].class); privatestaticfinalint shift;
static { int scale = unsafe.arrayIndexScale(int[].class); if ((scale & (scale - 1)) != 0) thrownew Error("data type scale not a power of two"); shift = 31 - Integer.numberOfLeadingZeros(scale); }
int base = unsafe.arrayBaseOffset(int[].class); int scale = unsafe.arrayIndexScale(int[].class); int elementIdx = 3; int[] array = { 0, 1, 2 ,3, 4 }; long offsetForIdx = base + (elementIdx * scale);
publicintarrayBaseOffset(Class clazz){ Class<?> component = clazz.getComponentType(); if (component == null) { thrownew IllegalArgumentException("Valid for array classes only: " + clazz); } // TODO: make the following not specific to the object model. int offset = 12; if (component == long.class || component == double.class) { offset += 4; // 4 bytes of padding. } return offset; }
publicintarrayIndexScale(Class clazz){ Class<?> component = clazz.getComponentType(); if (component == null) { thrownew IllegalArgumentException("Valid for array classes only: " + clazz); } // TODO: make the following not specific to the object model. if (!component.isPrimitive()) { return4; } elseif (component == long.class || component == double.class) { return8; } elseif (component == int.class || component == float.class) { return4; } elseif (component == char.class || component == short.class) { return2; } else { // component == byte.class || component == boolean.class. return1; } }
publicfinalintgetAndUpdate(int i, IntUnaryOperator updateFunction){ long offset = checkedByteOffset(i); int prev, next; do { prev = getRaw(offset); next = updateFunction.applyAsInt(prev); } while (!compareAndSetRaw(offset, prev, next)); return prev; }
publicfinalintupdateAndGet(int i, IntUnaryOperator updateFunction){ long offset = checkedByteOffset(i); int prev, next; do { prev = getRaw(offset); next = updateFunction.applyAsInt(prev); } while (!compareAndSetRaw(offset, prev, next)); return next; }
用法和AtomicInteger差不多,多传入一个index.
1 2 3
int[] intArray = newint[]{1, 2, 3, 4, 5}; AtomicIntegerArray aia = new AtomicIntegerArray(intArray); aia.updateAndGet(1, n -> (n % 2 == 0 ? n - 2 : n - 1));
/*这段代码位于Unsafe*/ publicfinalintgetAndAddInt(Object object, long valueOffset, int delta){ int ans; do { ans = this.getIntVolatile(object, valueOffset); } while(!this.compareAndSwapInt(object, valueOffset, ans, ans + delta));
publicfinalintgetAndUpdate(IntUnaryOperator updateFunction){ int prev, next; do { prev = get(); next = updateFunction.applyAsInt(prev); } while (!compareAndSet(prev, next)); return prev; }
publicfinalintupdateAndGet(IntUnaryOperator updateFunction){ int prev, next; do { prev = get(); next = updateFunction.applyAsInt(prev); } while (!compareAndSet(prev, next)); return next; }
简单用法如下:
1 2
AtomicInteger ai = new AtomicInteger(10); ai.updateAndGet(n -> (n %2 == 0 ? n-2:n -1));
在android源码中,ART的部分的GC在使用mark-sweep算法进行自动垃圾收集时,根据轻重程度不同,分为三类,sticky,partial,full。可以看到,ART里的GC的改进,首先就是收集器的多样化。 而根据GC时是否暂停所有的线程分类并行和非并行两类。所以在ART中一共定义了6个mark-sweep收集器。参看art/runtime/gc/heap.cc可见。根据不同情况,ART会选择不同的GC collector进行GC工作。其实最复杂的就是Concurrent Mark Sweep 收集器。如果理解了最复杂的Concurrent Mark Sweep算法,其他5种GC收集器的工作原理就也理解了。同样的,垃圾回收工作从整体上可以划分两个大的阶段:Mark 和 Sweep。
//Clear all of the spaces' mark bitmaps. for (constauto& space : GetHeap()->GetContinuousSpaces()) { if (space->GetGcRetentionPolicy() != space::kGcRetentionPolicyNeverCollect) { space->GetMarkBitmap()->Clear(); } } mark_stack_->Reset();
其实sticky mark sweep的主要步骤也是和mark sweep的过程大致一样,主要完成三次并发的mark阶段,然后进行一个stop the world的非并发进行一次对堆对象的遍历。
1 2 3 4 5 6 7 8 9 10 11 12 13
void StickyMarkSweep::BindBitmaps() { PartialMarkSweep::BindBitmaps(); WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); // For sticky GC, we want to bind the bitmaps of all spaces as the allocation stack lets us // know what was allocated since the last GC. A side-effect of binding the allocation space mark // and live bitmap is that marking the objects will place them in the live bitmap. for (const auto& space : GetHeap()->GetContinuousSpaces()) { if (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect) { BindLiveToMarkBitmap(space); } } GetHeap()->GetLargeObjectsSpace()->CopyLiveToMarked(); }
但是可以通过实现方法发现,有一个getGcRetenionPolicy,获取的是一个枚举。
只有符合总是收集的条件的,就把live bitmap和mark bitmap绑定起来。其余的过程和full是一样的。Sticky Mark sweep只扫描自从上次GC后被修改过的堆空间,并且也只回收自从上次GC后分配的空间。Sticky是只回收kGcRetentionPolicyAlwaysCollect的space。不回收其他两个,因此sticky的回收的力度是最小的。作为最全面的full mark sweep, 上面的三个策略都是会回收的。
2.3 partial mark sweep收集器
这是mark sweep收集器里使用的最少的GC收集策略。按照官方文档,一般是使用sticky mark sweep较多。这里有一个概念就是吞吐率,即一次GC释放的字节数和GC持续的时间(秒)的比值。由于一般是sticky mark sweep进行GC,所以当上次GC的吞吐率小于同时的partial mark sweep的吞吐率时,就会把下次GC收集器从sticky变成partial。但是在partial执行一次GC后,就仍然会恢复到stick mark sweep收集器。 阅读源码发现,partial重写了父类的成员函数。 其实分析这些可以发现,从full mark sweep到partial mark sweep到stick mark sweep,GC的力度是越来越小的,因为可以回收的越来越少。之所以说回收力度大,就是指可以回收的space多,比如上图的partial, 是不回收kGcRetentionPolicyFullCollect,但是是会回收kGcRetentionPolicyAlwaysCollect的space的。 因此partial mark sweep每次执行一次GC后,就会自动切换到sticky策略,这样才能使系统更流畅得进行GC,并减少了GC带来的消耗。。
首先通过对dalvik的GC的过程的分析,我们可以发现dalvik的在GC时出现的几个主要问题,首先即在GC时会有三次暂停其他进程运行,三次停顿导致的总的时间太长会导致丢帧卡顿现象严重。其次,就是在堆空间中给较大的对象分配空间后会导致碎片化比较严重,并且可能会导致GC调用次数变多增加开销。 我们可以发现,针对dalvik的以上两个问题,ART都有做了对应的优化来解决这些问题。针对第一个问题,ART在标记阶段做了非常大的优化,消除了第一次遍历堆地址空间的停顿,和第二次标记根集对象的停顿,并缩短了第三次处理card table的停顿,因此大大的缩短了应用程序在执行时的卡顿时间。针对第二个问题,提出了LOS的管理方法。 除此以外,还提供了丰富的GC收集器,例如继承自mark sweep的sticky mark sweep和partial mark sweep,二者的回收力度都要比full mark sweep小,因此性能上也得到了一些提升。一般情况下的收集器的主力就是sticky mark sweep, 这是对应用程序的性能影响最小的一种方式,因此大多数情况的GC表现,都要比dalvik的GC表现更好。 并且,通过实验数据的显示我们也可以看到,ART的GC的性能确实有了显著的提升,应用程序的流畅性得到了较好的保证。 以上都只是一个比较初步的分析比较,进一步的原理研究还需要详细学习源码才能融会贯通。