/// <summary> /// Fetches the information we know about a particular object given its address in memory. Note that our /// understanding of what generation an object is in may not be completely in sync with the GCs. /// </summary> public GCHeapSimulatorObject GetObjectInfo(Address objectAddress, out int gen) { GCHeapSimulatorObject ret = null; // Start in Gen 2 since you most objects will be there. for (int i = 2; 0 <= i; --i) { if (m_ObjsInGen[i].TryGetValue(objectAddress, out ret)) { gen = i; return(ret); } } gen = 0; return(ret); }
/// <summary> /// Takes the set of objects (a plug) in the region from 'fromPtr' to fromEnd, and relocates the objects to new address 'toPtr'. /// It also places the objects into whatever generation it is current in to m_condemedGenerationNum+1. /// Note that while our understanding of where what generation an object is might differ from the GC's idea, (we ALWAYS promote /// to a m_condemedGenerationNum+1 where the GC might not), this does not affect the movement/liveness calculation. /// </summary> private void CopyPlugToNextGen(Address fromPtr, Address fromEnd, Address toPtr) { Address origFromPtr = fromPtr; // Only used for asserts. Debug.Assert(m_condemedGenerationNum <= 2); var toGen = m_ObjsInGen[m_condemedGenerationNum + 1]; // We always put the survived objects in the generation after the one condemned. // For every object in the plug int fromGenNum = m_condemedGenerationNum; // This is the starting point where we search for an objects generation. Debug.WriteLine(string.Format("Plug {0:x} - {1:x} going to {2:x}", fromPtr, fromEnd, toPtr)); int pointerSizeMask = m_pointerSize - 1; while (fromPtr < fromEnd) { // Find the generation in the condemned region where the object lives, start the search where we // looked last time because it tends to find it quicker int genStartSearch = fromGenNum; // TODO: in the sampling case, when you miss we only increment by the pointer size, which is pretty inefficient. // if we has a sorted table, we could do much better. uint sizeToNextObj = (uint)m_pointerSize; bool foundGen = false; // once we have found the generation for a plug, we don't have to search. for (; ;) { var fromGen = m_ObjsInGen[fromGenNum]; GCHeapSimulatorObject objInfo = null; if (fromGen.TryGetValue(fromPtr, out objInfo)) { foundGen = true; sizeToNextObj = (uint)((objInfo.Size + pointerSizeMask) & ~pointerSizeMask); if (sizeToNextObj < (uint)m_pointerSize) { sizeToNextObj = (uint)m_pointerSize; } if (fromGenNum > m_condemedGenerationNum) { Debug.WriteLine(string.Format("Found in old generation, already alive.")); } // Note even if we find this in in an non-condemned generation we do the logic to move it because we basically were incorrect // to have put it in the older generation (the GC obviously had demoted the plug). fromGen.Remove(fromPtr); // Remove it from the current generation toGen[toPtr] = objInfo; // Copy survived data into next generation Debug.WriteLine(string.Format("Object Survived {0:x} -> {1:x}", fromPtr, toPtr)); break; } // Once we have found a plug's generation everything in the plug will have the same gen, so we don't // need to search more than once before we fail in that case. if (foundGen) { break; } --fromGenNum; // decrement with wrap around. if (fromGenNum < 0) { fromGenNum = 2; // We search all generations not just condemned because things may be demoted and we want to walk the plug efficiently. } if (genStartSearch != fromGenNum) { // Debug.Assert(false, "Error, could not find first object in plug, skipping and continuing."); break; } } if (sizeToNextObj == m_pointerSize) { Debug.WriteLine(string.Format("Synching {0:x}", fromPtr)); } fromPtr += sizeToNextObj; toPtr += sizeToNextObj; } }