La razón por la que el size_in_bytescampo del sys.dm_exec_cached_plansDMV, al menos en términos de "Planes compilados", es mayor que el CachedPlanSizeatributo del QueryPlannodo en el plan XML es porque un Plan compilado no es lo mismo que un Plan de consulta. Un plan compilado se compone de múltiples objetos de memoria, cuyo tamaño combinado equivale al size_in_bytescampo. Por lo tanto, la descripción de " Número de bytes consumidos por el objeto de caché " que encontró en la documentación es precisa; es solo que es fácil malinterpretar lo que se entiende por "objeto de caché" dado el nombre del DMV y que el término "plan" tiene múltiples significados.
Un plan compilado es un contenedor que contiene varias piezas de información relacionadas con el lote de consultas (es decir, no solo una sola declaración), una (o más) de esas piezas son los planes de consulta. Los planes compilados tienen un objeto de memoria de nivel superior de MEMOBJ_COMPILE_ADHOC, que es la fila sys.dm_os_memory_objectsque está vinculada a través del memory_object_addresscampo en ambos DMV. Este objeto de memoria contiene la tabla de símbolos, colección de parámetros, enlaces a objetos relacionados, caché de acceso, caché de metadatos TDS y posiblemente algunos otros elementos. Los planes compilados se comparten entre sesiones / usuarios que están ejecutando el mismo lote con la misma configuración de sesión. Sin embargo, algunos objetos relacionados no se comparten entre Sesiones / Usuarios.
Los planes compilados también tienen uno o más objetos dependientes que se pueden encontrar pasando el plan_handle(in sys.dm_exec_cached_plans) al sys.dm_exec_cached_plan_dependent_objectsDMF. Hay dos tipos de objetos dependientes: Plan ejecutable (Objeto de memoria = MEMOBJ_EXECUTE ) y Cursor (Objeto de memoria = MEMOBJ_CURSOREXEC ). Habrá 0 o más objetos de cursor, uno por cada cursor. También habrá uno o más objetos del Plan ejecutable, uno por cada Usuario que ejecute ese mismo lote , por lo tanto, los Planes ejecutables no soncompartido entre los usuarios. Los planes ejecutables contienen parámetros de tiempo de ejecución e información de variables locales, estado de tiempo de ejecución como la instrucción actualmente en ejecución, identificadores de objeto para objetos creados en tiempo de ejecución (supongo que esto se refiere a variables de tabla, tablas temporales, procedimientos almacenados temporales, etc.) y posiblemente otros artículos.
Cada declaración dentro de un lote de múltiples declaraciones está contenida dentro de una Declaración Compilada (Objeto de Memoria = MEMOBJ_STATEMENT ). El tamaño de cada instrucción compilada (es decir, pages_in_bytesdividido por 1024) debe coincidir con los CachedPlanSize="xx"valores de los <QueryPlan>nodos en el plan XML. Las declaraciones compiladas a menudo tendrán uno (¿posiblemente más?) Planes de consulta de tiempo de ejecución asociados (Objeto de memoria = MEMOBJ_XSTMT ). Finalmente, para cada Plan de consulta de tiempo de ejecución que sea una consulta, debe haber un Contexto de ejecución de consulta asociado (Objeto de memoria = MEMOBJ_QUERYEXECCNTXTFORSE ).
Con respecto a las declaraciones compiladas, los lotes de una sola declaración no tienen una declaración compilada separada (es decir, MEMOBJ_STATEMENT ) u objetos separados del plan de consulta en tiempo de ejecución (es decir, MEMOBJ_XSTMT ). El valor de cada uno de esos objetos se almacenará en el objeto principal del plan compilado (es decir, MEMOBJ_COMPILE_ADHOC ), y en ese caso, el pages_in_bytesvalor de ese objeto principal dividido por 1024 debe coincidir con el CachedPlanSizetamaño en el <QueryPlan>nodo del plan XML. Esos valores no serán iguales, sin embargo, en lotes de múltiples declaraciones.
El size_in_bytesvalor se puede obtener sumando las entradas en el sys.dm_os_memory_objectsDMV (los elementos indicados anteriormente en negrita), todos relacionados por dm_os_memory_objects.page_allocator_addressese Plan Compilado. El truco para obtener el valor correcto es primero obtener el memory_object_addressde sys.dm_exec_cached_plansun Plan Compilado en particular, luego usarlo para obtener la fila MEMOBJ_COMPILE_ADHOC correspondiente en sys.dm_os_memory_objectsfunción de su memory_object_addresscampo. Luego, tome el page_allocator_addressvalor de sys.dm_os_memory_objectsesa fila y úselo para tomar todas las filas sys.dm_os_memory_objectsque tengan el mismo page_allocator_addressvalor. (Tenga en cuenta que esta técnica no funciona para los otros tipos de objetos en caché: árbol de análisis , proceso extendido , proceso compilado CLR y función compilada CLR.)
Usando el memory_object_addressvalor obtenido de sys.dm_exec_cached_plans, puede ver todos los componentes del Plan Compilado a través de la siguiente consulta:
DECLARE @CompiledPlanAddress VARBINARY(8) = 0x00000001DC4A4060;
SELECT obj.memory_object_address, obj.pages_in_bytes, obj.type
FROM   sys.dm_os_memory_objects obj
WHERE  obj.page_allocator_address = (
                               SELECT planobj.page_allocator_address
                               FROM   sys.dm_os_memory_objects planobj
                               WHERE  planobj.memory_object_address = @CompiledPlanAddress
                              )
ORDER BY obj.[type], obj.pages_in_bytes;
La consulta a continuación enumera todos los planes compilados sys.dm_exec_cached_plansjunto con el plan de consulta y las declaraciones para cada lote. La consulta directamente arriba se incorpora a la consulta a continuación a través de XML como el MemoryObjectscampo:
SELECT cplan.bucketid,
       cplan.pool_id,
       cplan.refcounts,
       cplan.usecounts,
       cplan.size_in_bytes,
       cplan.memory_object_address,
       cplan.cacheobjtype,
       cplan.objtype,
       cplan.plan_handle,
       '---' AS [---],
       qrypln.[query_plan],
       sqltxt.[text],
       '---' AS [---],
       planobj.pages_in_bytes,
       planobj.pages_in_bytes / 1024 AS [BaseSingleStatementPlanKB],
       '===' AS [===],
       cplan.size_in_bytes AS [TotalPlanBytes],
       bytes.AllocatedBytes,
       (SELECT CONVERT(VARCHAR(30), obj.memory_object_address, 1)
               AS [memory_object_address], obj.pages_in_bytes, obj.[type]
               --,obj.page_size_in_bytes
        FROM   sys.dm_os_memory_objects obj
        WHERE  obj.page_allocator_address = planobj.page_allocator_address
        FOR XML RAW(N'object'), ROOT(N'memory_objects'), TYPE) AS [MemoryObjects]
FROM   sys.dm_exec_cached_plans cplan
OUTER APPLY sys.dm_exec_sql_text(cplan.[plan_handle]) sqltxt
OUTER APPLY sys.dm_exec_query_plan(cplan.[plan_handle]) qrypln
INNER JOIN sys.dm_os_memory_objects planobj
        ON planobj.memory_object_address = cplan.memory_object_address
OUTER APPLY (SELECT SUM(domo.[pages_in_bytes]) AS [AllocatedBytes]
             FROM   sys.dm_os_memory_objects domo
             WHERE  domo.page_allocator_address = planobj.page_allocator_address) bytes
WHERE  cplan.parent_plan_handle IS NULL
AND    cplan.cacheobjtype IN (N'Compiled Plan', N'Compiled Plan Stub')
--AND cplan.plan_handle = 0x06000D0031CD572910529CE001000000xxxxxxxx
ORDER BY cplan.objtype, cplan.plan_handle;
Tenga en cuenta que:
- el TotalPlanBytescampo es solo una nueva declaración delsys.dm_exec_cached_plans.size_in_bytescampo,
- el AllocatedBytescampo es la SUMA de los objetos de memoria relacionados que normalmente coincidenTotalPlanBytes(es decirsize_in_bytes)
- el AllocatedBytescampo ocasionalmente será mayor queTotalPlanBytes(es decirsize_in_bytes) debido al aumento del consumo de memoria durante la ejecución. Esto parece suceder principalmente debido a la compilación (que debería ser evidente con elusecountscampo que se muestra1)
- el BaseSingleStatementPlanKBcampo debe coincidir con elCachedPlanSizeatributo delQueryPlannodo en el XML, pero solo cuando se utiliza un único lote de consulta.
- para lotes con múltiples consultas, debe haber filas marcadas como MEMOBJ_STATEMENTensys.dm_os_memory_objects, una para cada consulta. Elpages_in_bytescampo para estas filas debe coincidir con los<QueryPlan>nodos individuales del plan XML.
Recursos: