Resposta ao Desafio da Semana #12 [Memory Leak/Crash/Hang – Liberando COM e String null & vazia em C# e VB.NET]


Por: Roberto Alexis Farah


 


Olá pessoal!


Eis a resposta do desafio #12: http://blogs.technet.com/latam/archive/2006/11/03/desafio-da-semana-12.aspx


 


 


PROBLEMA 1


 


Observe os diferentes cenários com um string válida, vazia e nula.


 


Execução com uma string válida.


 


string str = “Abcd”;


                            


DoSomething(str);


    


 


Eis a thread no lado gerenciado (.NET), especificamente no CLR (Common Language Runtime):


 


ESP       EIP    


0012f438 00cc011e Demo.Program.DoSomething(System.String)


    PARAMETERS:


        str = 0x01271be0                      ß Objeto string.


    LOCALS:


        0x0012f43c = 0x00000000


        0x0012f438 = 0x00000000


        0x0012f450 = 0x00000000


        0x0012f44c = 0x00000000


        0x0012f448 = 0x00000000


        0x0012f444 = 0x00000000


 


ESP/REG  Object   Name


0012f47c 00cc0096 Demo.Program.Main(System.String[])


    PARAMETERS:


        args = 0x01271bd0


    LOCALS:


        <CLR reg> = 0x01271be0


 


ESP/REG  Object   Name


0012f69c 79e88f63 [GCFrame: 0012f69c]


 


 


Eis o detalhe do objeto acima:


 


Name: System.String


MethodTable: 790fa3e0


EEClass: 790fa340


Size: 26(0x1a) bytes


GC Generation: 0


 (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)


String: Abcd


Fields:


      MT    Field   Offset                 Type VT     Attr    Value Name


790fed1c  4000096        4         System.Int32  0 instance        5 m_arrayLength


790fed1c  4000097        8         System.Int32  0 instance        4 m_stringLength           ß É maior que 0.


790fbefc  4000098        c          System.Char  0 instance       41 m_firstChar


790fa3e0  4000099       10        System.String  0   shared   static Empty


    >> Domain:Value  0014c1b8:790d6584 <<


79124670  400009a       14        System.Char[]  0   shared   static WhitespaceChars


    >> Domain:Value  0014c1b8:01271754 <<


 


 


 


 


Excução com uma string vazia:


 


DoSomething(“Def”);


                            


str = “”;


 


 


OS Thread Id: 0x72b8 (0)


ESP       EIP     


ESP/REG  Object   Name


eax      01271c14 System.String


ecx      01271c14 System.String


esi      01271c14 System.String


0012f478 00cc00d8 Demo.Program.DoSomething(System.String)


    PARAMETERS:


        str = 0x01271c14                             ß String vazia mas objeto string é válido. Aponta para 790fa3e0 que é o MethodTable.


    LOCALS:


        <no data>


        <no data>


        <no data>


        <no data>


        <no data>


        <no data>


 


ESP/REG  Object   Name


eax      01271c14 System.String


ecx      01271c14 System.String


esi      01271c14 System.String


0012f47c 00cc00b4 Demo.Program.Main(System.String[])


    PARAMETERS:


        args = 0x01271bd0


    LOCALS:


        <CLR reg> = 0x01271c14


 


ESP/REG  Object   Name


eax      01271c14 System.String


ecx      01271c14 System.String


esi      01271c14 System.String


0012f47c 01271bd0 System.Object[]    (System.String[])


0012f534 01271bd0 System.Object[]    (System.String[])


0012f69c 79e88f63 [GCFrame: 0012f69c]


 


 


Eis a string vazia:


 


Name: System.String


MethodTable: 790fa3e0


EEClass: 790fa340


Size: 18(0x12) bytes


GC Generation: 0


 (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)


String:


Fields:


      MT    Field   Offset                 Type VT     Attr    Value Name


790fed1c  4000096        4         System.Int32  0 instance        1 m_arrayLength


790fed1c  4000097        8         System.Int32  0 instance        0 m_stringLength   ß Tamanho 0.


790fbefc  4000098        c          System.Char  0 instance        0 m_firstChar


790fa3e0  4000099       10        System.String  0   shared   static Empty


    >> Domain:Value  0014c1b8:790d6584 <<


79124670  400009a       14        System.Char[]  0   shared   static WhitespaceChars


    >> Domain:Value  0014c1b8:01271754 <<


 


 


 


Agora a execução com um objeto string que é null:


 


str = null;


 


DoSomething(str);


 


 


Vamos aos detalhes:


 


OS Thread Id: 0x72b8 (0)


ESP       EIP    


ESP/REG  Object   Name


0012f478 00cc00d8 Demo.Program.DoSomething(System.String)


    PARAMETERS:


        str = 0x00000000             ß É como se fosse um ponteiro em C++ apontando para NULL.


    LOCALS:


        <no data>


        <no data>


        <no data>


        <no data>


        <no data>


        <no data>


 


ESP/REG  Object   Name


0012f47c 00cc00bf Demo.Program.Main(System.String[])


    PARAMETERS:


        args = 0x01271bd0


    LOCALS:


        <CLR reg> = 0x00000000


 


ESP/REG  Object   Name


0012f47c 01271bd0 System.Object[]    (System.String[])


0012f534 01271bd0 System.Object[]    (System.String[])


0012f69c 79e88f63 [GCFrame: 0012f69c]


                            


 


Em .NET todo objeto deriva de System.Object e um objeto como uma string pode ser vazia mas não nula, ou pode ser nula. São conceitos diferentes.


Observe os objetos gerenciados na pilha:


 


OS Thread Id: 0x72b8 (0)


ESP/REG  Object   Name


0012f47c 01271bd0 System.Object[]    (System.String[])     ß Eis nosso objeto: 01271bd0


0012f534 01271bd0 System.Object[]    (System.String[])


0012f6e0 01271bd0 System.Object[]    (System.String[])


0012f708 01271bd0 System.Object[]    (System.String[])


 


 


Nosso objeto:


 


Name: System.Object[]                   


MethodTable: 79124228


EEClass: 7912479c


Size: 16(0x10) bytes


GC Generation: 0


Array: Rank 1, Number of elements 0, Type CLASS


Element Type: System.String                                    ß Tipo string… mas é apenas um objeto nulo. Não foi inicializado com uma string ou string vazia.


Fields:


None


 


 


Agora a execução vai para:


 


// Checa se string é vazia.


if(0 == str.Length)


 


 


E temos no lado de código nativo:


 


eax=00000000 ebx=0012f4ac ecx=00000000 edx=00000000 esi=00000000 edi=00000000


eip=00cc0123 esp=0012f438 ebp=0012f474 iopl=0         nv up ei pl zr na pe nc


cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246


Demo!Demo.Program.DoSomething(System.String)+0x4b:


00cc0123 8b7808          mov     edi,dword ptr [eax+8] ds:0023:00000008=????????     ß Interrogação é memória não inicializada, afinal, o endereço 0x8 é inválido.


 


0x8 nada mais é que eax que é 0x0 + offset que é 0x8. Porque o offset? Porque o offset é onde se está armazenado o tamanho da string em um objeto do tipo System.String.


 


 


Eis a pilha no lado nativo:


 


ChildEBP RetAddr 


0012f474 00cc00bf Demo!Demo.Program.DoSomething(System.String)+0x4b


0012f490 79e88f63 Demo!Demo.Program.Main(System.String[])+0x4f


0012f490 79e88ee4 mscorwks!CallDescrWorker+0x33


0012f510 79e88e31 mscorwks!CallDescrWorkerWithHandler+0xa3


0012f650 79e88d19 mscorwks!MethodDesc::CallDescr+0x19c


0012f668 79e88cf6 mscorwks!MethodDesc::CallTargetWorker+0x20


0012f67c 79f084b0 mscorwks!MethodDescCallSite::Call_RetArgSlot+0x18


0012f7e0 79f082a9 mscorwks!ClassLoader::RunMain+0x220


0012fa48 79f0817e mscorwks!Assembly::ExecuteMainMethod+0xa6


0012ff18 79f07dc7 mscorwks!SystemDomain::ExecuteMainMethod+0x398


0012ff68 79f05f61 mscorwks!ExecuteEXE+0x59


0012ffb0 79011b5f mscorwks!_CorExeMain+0x11b


0012ffc0 7c816fd7 mscoree!_CorExeMain+0x2c


0012fff0 00000000 KERNEL32!BaseProcessStart+0x23


 


E a exceção:


 


ExceptionAddress: 00cc0123 (Demo!Demo.Program.DoSomething(System.String)+0x0000004b)


   ExceptionCode: c0000005 (Access violation)


  ExceptionFlags: 00000000


NumberParameters: 2


   Parameter[0]: 00000000


   Parameter[1]: 00000008


Attempt to read from address 00000008


 


 


O depurador pega a exceção. Nesse ponto continuei a execução para forçar uma exceção fatal. (2nd chance exception):


 


(72bc.72b8): CLR notification exception – code e0444143 (first chance)


(72bc.72b8): Access violation – code c0000005 (!!! second chance !!!)


eax=00000000 ebx=0012f4ac ecx=00000000 edx=00000000 esi=00000000 edi=00000000


eip=00cc0123 esp=0012f438 ebp=0012f474 iopl=0         nv up ei pl zr na pe nc


cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246


Demo!Demo.Program.DoSomething(System.String)+0x4b:


00cc0123 8b7808          mov     edi,dword ptr [eax+8] ds:0023:00000008=????????


 


E no lado .NET temos:


 


System.NullReferenceException was unhandled


  Message=”Object reference not set to an instance of an object.


  Source=”Demo”


  StackTrace:


       at Demo.Program.DoSomething(String str) in C:\Development\My Tools\BLOG Articles\Article #17\Demo\Demo\Program.cs:line 31


       at Demo.Program.Main(String[] args) in C:\Development\My Tools\BLOG Articles\Article #17\Demo\Demo\Program.cs:line 24


       at System.AppDomain.nExecuteAssembly(Assembly assembly, String[] args)


       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)


       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()


       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)


       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)


       at System.Threading.ThreadHelper.ThreadStart()


 


 


Portanto, o primeiro problema é que o código não lida com situações onde a string é nula. O comentário do código explica que a rotina deve retornar false se a string do parâmetro for vazia ou nula mas a implementação apenas testa se a string é vazia!


 


 


PROBLEMA 2


 


Sem dúvida o maior causador de instâncias nunca decrementadas no COM+ e consequentes hangs.


 


Eis a execução antes de instanciar o componente COM do Word.


 


 


Index SyncBlock MonitorHeld Recursion Owning Thread Info  SyncBlock Owner


—————————–


Total           0


CCW             0


RCW             0                  ß Runtime Callable Wrapper, responsável por gerenciar o contador de referência de objetos COM.


ComClassFactory 0


Free            0


 


 


Após a instanciação do componente:


 


Index SyncBlock MonitorHeld Recursion Owning Thread Info  SyncBlock Owner


—————————–


Total           1


CCW             0


RCW             1                  ß Ok, internamente houve um AddRef() do COM.


ComClassFactory 0


Free            0


 


 


Na linha do msWord.Quit():


 


OS Thread Id: 0x72b8 (0)


ESP/REG  Object   Name


eax      0127244c System.Boolean


esi      0127244c System.Boolean


0012f43c 0127243c Microsoft.Office.Interop.Word.ApplicationClass


0012f440 01272420 System.String    ABCD


0012f450 0127244c System.Boolean


0012f460 01271be0 System.String    Abcd


0012f46c 01271be0 System.String    Abcd


0012f47c 01271bd0 System.Object[]    (System.String[])


0012f534 01271bd0 System.Object[]    (System.String[])


0012f6e0 01271bd0 System.Object[]    (System.String[])


0012f708 01271bd0 System.Object[]    (System.String[])


 


 


Após finally:


 


Index SyncBlock MonitorHeld Recursion Owning Thread Info  SyncBlock Owner


    1 001af254            0         0 00000000     none    0127243c Microsoft.Office.Interop.Word.ApplicationClass


—————————–


Total           1


CCW             0


RCW             1              ß Ei, Relase() do COM não foi chamado! O objeto COM não sabe que pode sair da memória pois ainda há referências para suas interfaces!


ComClassFactory 0


Free            0


 


 


RuntimeCallableWrappers (RCW) a serem liberados:


     RCW  CONTEXT   THREAD Apartment


       0 80000001        0       MTA


MTA Interfaces to be released: 1


STA Interfaces to be released: 0               


 


 


Após várias execuções do mesmo método temos um aumento constante das referências para o objeto COM MTA do Word.


Veja a segunda execução logo após a segunda instanciação do componente do Word:


 


Index SyncBlock MonitorHeld Recursion Owning Thread Info  SyncBlock Owner


    1 001af254            0         0 00000000     none    0127243c Microsoft.Office.Interop.Word.ApplicationClass


    2 001af284            0         0 00000000     none    01272470 Microsoft.Office.Interop.Word.ApplicationClass


—————————–


Total           2


CCW             0


RCW             2


ComClassFactory 0


Free            0


 


 


Portanto, no segundo problema o wrapper que o .NET usa para se comunicar com o COM não decrementa as referências logo o objeto continua na memória!


 


 


SOLUÇÃO – PROBLEMA 1


 


Para o Problema 1 a solução é bastante simples…


 


Em .NET Framework 1.1 e 2.0:


 


static bool DoSomething(string str)


{


          // Checa se string é nula ou vazia.


          if((null == str) || (0 == str.Length))


          {


                   return false;


          }






 


Em .NET Framework 2.0 especificamente:


 


static bool DoSomething(string str)


{


          // Checa se string é nula ou vazia.


          if(String.IsNullOrEmpty(str))


          {


                   return false;


          }






 


 


Para evitar esse tipo de problema, mantenha em mente que uma string Null é diferente de uma string vazia.


 


 


<IMPORTANTE>


 


String vazia:


 


Uma string vazia é uma instância de System.String que contém zero caracteres.


 


 


String nula (null string):


 


Uma string nula não referencia uma instância de um objeto System.String e tentativas de se executar métodos de uma string nula resultam em uma exceção do tipo NullReferenceException.


 


</IMPORTANTE>


 


 


Eis alguns artigos de referência:


 


http://msdn2.microsoft.com/en-us/library/ms228362.aspx


http://msdn2.microsoft.com/en-us/library/sxw2ez55.aspx


 


 


SOLUÇÃO – PROBLEMA 2


 


Para o Problema 2 a solução também é bastante simples.


 


O que? Você pensou em set msWord = Nothing no VB.NET ou msWord = null no C#?


Não, infelizmente não… isso funciona no mundo nativo, em Visual Basic 6 e ASP por exemplo. Em .NET as coisas são um pouco diferentes…


 


A solução se baseia em utilizarmos uma chamada de método que força o wrapper usado pelo .NET a liberar a referência para o objeto COM.


 


Eis o código corrigido e o resultado da depuração do mesmo:


 


 


namespace Demo


{


          class Program


          {


                   static void Main(string[] args)


                   {


                             string str = “Abcd”;


                            


                             DoSomething(str);


                            


 


                             DoSomething(“Def”);


                            


                             str = “”;


                            


                             DoSomething(str);


 


                             str = null;


 


                             DoSomething(str);    // Agora não falha mais!


                            


                   }


                  


                   static bool DoSomething(string str)


                   {


                             // Checa se string é nula ou vazia.


                             if((null == str) || 0 == str.Length)


                             {


                                      return false;


                             }


                            


                             str = str.ToUpper();


                                     


                             Microsoft.Office.Interop.Word.Application msWord = null;


                                                                            


                             try


                             {


                                      msWord = new Microsoft.Office.Interop.Word.Application();


 


                                      object saveChanges = false;


                                      object missing = null;


 


                                      msWord.Quit(ref saveChanges, ref missing, ref missing);                                   


                             }


                             catch(OutOfMemoryException ex)


                             {


                                      Console.WriteLine(“Não há memória disponível…”);


                             }


                             finally


                             {


// Libera wrapper da memória que, por sua vez, libera as referências para o


// objeto COM.


                                      // Evita NullReferenceException caso tentarmos liberar o objeto e


                                      // sem que o mesmo tenha sido inicializado!


                                      if(msWord != null)


                                      {


                                                // Para .NET Framework 2.0.


                                                System.Runtime.InteropServices.Marshal.FinalReleaseComObject(msWord);                                                         }


                                     


                                  msWord = null;  


                             }


                            


                             return true;


                   }


          }


}


 


 


Note que para compatibilidade com .NET Framework 1.1 e 2.0, dentro do finally e dentro do if() acima você deve usar:


 


while(System.Runtime.InteropServices.Marshal.ReleaseComObject(msWord) > 0);


 


 


 


<IMPORTANTE>


 


System.Runtime.InteropServices.Marshal.ReleaseComObject() decrementa em um o contador de referência interna portanto se faz necessário chamá-lo em um loop para garantir que o wrapper liberou todas as referências para o objeto COM.


Na aplicação usada no desafio não é necessário se chamar dentro de um loop, entretanto, recomendo que o loop sempre seja utilizado pois numa aplicação real caso haja mudanças que impliquem em incremento de contador de referências interna do COM não é necessário se preocupar em mudar o código que libera o objeto COM, se o loop acima for utilizado. Portanto, eliminando um potencial bug na aplicação! J


 


</IMPORTANTE>


 


 


Eis a depuração logo antes de instanciarmos o componente Word, usando a solução para o problema 2:


 


 


Thread 0:


 


ChildEBP RetAddr 


0012f474 00cc0096 Demo!Demo.Program.DoSomething(System.String)+0x8e


0012f490 79e88f63 Demo!Demo.Program.Main(System.String[])+0x26


0012f490 79e88ee4 mscorwks!CallDescrWorker+0x33


0012f510 79e88e31 mscorwks!CallDescrWorkerWithHandler+0xa3


0012f650 79e88d19 mscorwks!MethodDesc::CallDescr+0x19c


0012f668 79e88cf6 mscorwks!MethodDesc::CallTargetWorker+0x20


0012f67c 79f084b0 mscorwks!MethodDescCallSite::Call_RetArgSlot+0x18


0012f7e0 79f082a9 mscorwks!ClassLoader::RunMain+0x220


0012fa48 79f0817e mscorwks!Assembly::ExecuteMainMethod+0xa6


0012ff18 79f07dc7 mscorwks!SystemDomain::ExecuteMainMethod+0x398


0012ff68 79f05f61 mscorwks!ExecuteEXE+0x59


0012ffb0 79011b5f mscorwks!_CorExeMain+0x11b


0012ffc0 7c816fd7 mscoree!_CorExeMain+0x2c


0012fff0 00000000 KERNEL32!BaseProcessStart+0x23


 


 


Referências para RCW:


 


Index SyncBlock MonitorHeld Recursion Owning Thread Info  SyncBlock Owner


—————————–


Total           0


CCW             0


RCW             0


ComClassFactory 0


Free            0


 


 


Agora logo após a instanciação:


 


Index SyncBlock MonitorHeld Recursion Owning Thread Info  SyncBlock Owner


    1 03ba004c            0         0 00000000     none    0127243c Microsoft.Office.Interop.Word.ApplicationClass


—————————–


Total           1


CCW             0


RCW             1


ComClassFactory 0


Free            0


 


 


RuntimeCallableWrappers (RCW) a serem liberados:


     RCW  CONTEXT   THREAD Apartment


       0 80000001        0       MTA


MTA Interfaces to be released: 1


STA Interfaces to be released: 0


 


 


Agora acabo de executar a chamada para ReleaseComObject():


 


Index SyncBlock MonitorHeld Recursion Owning Thread Info  SyncBlock Owner


    1 03ba004c            0         0 00000000     none    0127243c Microsoft.Office.Interop.Word.ApplicationClass


—————————–


Total           1


CCW             0


RCW             0         ß Liberado! Com isso o objeto COM será liberado também!


ComClassFactory 0


Free            0


 


 


RuntimeCallableWrappers (RCW) a serem liberados:


     RCW  CONTEXT   THREAD Apartment


       0 80000001        0       MTA


MTA Interfaces to be released: 1


STA Interfaces to be released: 0   ß Se fosse um componente VB 6 sendo chamado então seria STA.


 


 


Eis a pilha do CLR:


 


OS Thread Id: 0xa7e4 (0)


ESP       EIP    


ESP/REG  Object   Name


esi      0127244c System.Boolean


0012f434 00cc01ee Demo.Program.DoSomething(System.String)


    PARAMETERS:


        str = 0x01272420


    LOCALS:


        0x0012f43c = 0x0127243c      ß Equivale ao objeto msWord do código fonte.


        0x0012f450 = 0x0127244c         


        0x0012f44c = 0x00000000


        0x0012f438 = 0x00000000


        0x0012f448 = 0x00000000


        0x0012f444 = 0x00000001


 


ESP/REG  Object   Name


esi      0127244c System.Boolean


0012f43c 0127243c Microsoft.Office.Interop.Word.ApplicationClass


0012f440 01272420 System.String    ABCD


0012f450 0127244c System.Boolean


0012f460 01271be0 System.String    Abcd


0012f46c 01271be0 System.String    Abcd


0012f47c 00cc0096 Demo.Program.Main(System.String[])


    PARAMETERS:


        args = 0x01271bd0


    LOCALS:


        <CLR reg> = 0x01271be0


 


ESP/REG  Object   Name


esi      0127244c System.Boolean


0012f47c 01271bd0 System.Object[]    (System.String[])


0012f534 01271bd0 System.Object[]    (System.String[])


0012f69c 79e88f63 [GCFrame: 0012f69c]


 


 


Eis o objeto RCW:


 


Name: Microsoft.Office.Interop.Word.ApplicationClass


MethodTable: 03435afc


EEClass: 0336ea90


Size: 16(0x10) bytes


GC Generation: 0


 (C:\WINDOWS\assembly\GAC\Microsoft.Office.Interop.Word\11.0.0.0__71e9bce111e9429c\Microsoft.Office.Interop.Word.dll)


Fields:


      MT    Field   Offset                 Type VT     Attr    Value Name


790f9c18  4000184        4        System.Object  0 instance 00000000 __identity


790fea70  4000277        8 …ections.Hashtable  0 instance 00000000 m_ObjectToDataMap   ß Wrapper liberado! Agora é com o GC.


 


 


 


Como estou usando uma versão Debug, o código não está otimizado portanto o IL e o código disassemblado são mais fáceis de visualizar. No caso, eis parte do método DoSomething():


 


00cc01e8 e833167a78    call    mscorlib_ni!System.Runtime.InteropServices.Marshal.ReleaseComObject(System.Object) (79461820)


00cc01ed 90              nop


00cc01ee 33d2           xor     edx,edx                          ß Conteúdo de msWord em null.


00cc01f0 8955c8        mov   dword ptr [ebp-38h],edx     ß Atribui null a msWord.


00cc01f3 90              nop


00cc01f4 58              pop     eax


 


 


Agora a execução de msWord = null.


Note que é uma boa prática se atribuir null (ou nothing em Visual Basic .NET) para um objeto, após usá-lo, e sempre fazer comparações com null antes de usá-lo. Isso ajuda a identificar bugs mais facilmente.


 


edx:


 


00140608  7c97c500


 


ebp-0x38  (msWord):


 


0012f43c  0127243c


 


Após msWord = null:


 


edx:


 


00000000  ????????


 


ebp-0x38 (msWord):


 


0012f43c  00000000   ( msWord = null; )


 


Portanto, nesse ponto seguramente nosso objeto msWord é null e o RCW foi liberado da memória, liberando o objeto COM!


 


Note que é uma boa prática de programação configurar objetos para null após liberá-los/usá-los e sempre testar se um objeto não é null antes de usar.


Por que isso? Porque se você compara o objeto contra null e tenta utilizá-lo, após ter liberado os recursos associados ao mesmo (no nosso caso o wrapper para o objeto COM), a comparação vai suceder e o código vai lançar uma exceção!


 


Por exemplo:


 


if(msWord != null)


{


          // msWord não mais aponta para uma instância do wrapper após a linha abaixo ser executada…


          System.Runtime.InteropServices.Marshal.FinalReleaseComObject(msWord); 


}


 





if(msWord != null)  // Ao mesmo tempo o if() retornará true porque não explicitamente configuramos msWord = null;


{


          // Usa método de msWord. Exceção!!! msWord não aponta para uma instância válida do seu tipo.


}


 


 


Outro importante ponto é o código dentro de finally. Não podemos assumir no código dentro de finally que o objeto que apontava para null agora aponta para uma instância do tipo, afinal, no bloco try uma exceção OutOfMemoryException poderia ter sido lançada na inicialização do objeto! Se assumirmos que o objeto sempre é inicializado teremos uma exceção NullReferenceException dentro do bloco finally quando o objeto for null!


 


Eis os artigos de referências:


 


PRB: COM+ Instance Count Does Not Decrease When Called from .NET Application


http://support.microsoft.com/kb/305823/en-us


 


Marshal.FinalReleaseComObject Method 


http://msdn2.microsoft.com/en-us/library/system.runtime.interopservices.marshal.finalreleasecomobject(VS.80).aspx


 


Marshal.ReleaseComObject


http://msdn2.microsoft.com/en-us/library/system.runtime.interopservices.marshal.releasecomobject(VS.80).aspx


 


E artigo explicando sobre RCW:


 


Runtime Callable Wrapper


http://msdn2.microsoft.com/en-us/library/8bwh56xe.aspx


 


Hum… agora você está entendendo porque aquele componente no COM+ sendo chamado pela sua página ASPX sempre aumenta o contador de referências, algumas vezes causando hang no servidor. Ótimo!


 


 


Até a próxima!


 

Comments (0)