The Garbage Collector


Considering all the accolades the Microsoft marketing gurus have received, you might think that they perhaps blinked while naming this particular CLR feature. Who would have thought that a name as unglamorous as the "garbage collector" would stick? Well, considering what the GC actually does, I cannot think of a more appropriate name myself . Believe me, I tried ”for example, "memory space reclaimer" and "managed heap recycler." These entries just do not seem to have the same pizzazz as the "garbage collector." So, sticking with GC, let's examine the "garbage" that gets collected.

Memory Management

On the mainframe, when your COBOL batch Job ended, you knew that you were freeing resources. Additionally, when you did mainframe COBOL CICS development, you knew that the completion of your CICS task freed resources. Now, if I were to ask you to list the "resources" that were freed, you would likely begin with external input and output devices (including disk storage). Perhaps you might continue by mentioning the operating system's Job processing initiator as a freed resource.

Naturally, you would not stop there. You would include the resources associated with the variables defined in your program's Working-Storage Section and Linkage Section as freed resources. Lastly, you would mention the virtual copy of your COBOL program and JCL Job as freed resources. Now for a question: Have you ever given thought to the disposition of these "resources" after they were freed? As I recall, we referred to this as "concerns for the electronic heavens."

These freed resources represented garbage: resources that had been used and discarded. As unsettling as it is to admit, we mainframe programmers just did not care about (or need to care about) the disposition of our garbage as long as the resources were in fact freed. Little did we know, our resident system programmer kept a watchful eye on the available resources systemwide using system-level software to identify the freed resources (our garbage), reclaim unused memory, and compact freed space to minimize fragmentation.

Creating .NET Garbage

On the .NET platform, you will create lots of garbage. [2] That is to say, your well-written applications will contain many managed objects that eventually are freed. Once freed, the objects become garbage and candidates for collection.

In Chapter 7, you learned about the .NET Framework. As you recall, the Framework includes delegates, interfaces, classes, enumerations, and structures (which I referred to using the acronym DICES). With the exception of enumerations and structures, these .NET Framework objects are destined to become collected garbage. So, why are enumerations and structures treated differently?

Well, in Chapter 7, you learned that enumerations could only contain numeric data types (structures), right? You also learned that all enumerations are derived from the System.Enum class. So, where do you think the System.Enum class is derived from? That is right. System.Enum is derived from the System.ValueType class. As you know, all structures are derived from the same class: System.ValueType. There you have it: Enumerations and structures are excluded from garbage collection because they are both derived from the System.ValueType class and are considered ValueType objects. [3]

Note  

ValueType objects can participate in garbage collection if they are boxed . Boxing is further discussed in the section "The Boxing of ValueType Objects" later in this chapter.

Basically, that leaves you with delegates, interfaces, and classes (and any boxed ValueTypes) to be garbage collected. As the GC makes its rounds looking for garbage (objects that are no longer referenced and available for collection), one of several things can occur:

  • Dead objects can be collected (i.e., they are effectively removed from the managed heap).

  • Dead objects that happen to have a Finalizer method can be flagged and moved to a different managed heap [4] (I discuss this further in the section "Garbage Collection Schedule" later in this chapter).

  • Live objects can be promoted from one heap generation to the next heap generation (i.e., from frequently scanned generation 0 to less frequently scanned generation 1, and so forth).

Figure 8-1 illustrates boxed structures and other .NET Framework objects on the managed heap waiting to be collected by the GC.

click to expand
Figure 8-1: The .NET Framework objects on the managed heap

As you will notice in Figure 8-1, your installed system will typically support multiple heap generations. As garbage collections occur, any objects that survive will be promoted to the next heap generation. These objects are said to have aged. The following set of code samples is provided to demonstrate the CLR promoting an object from one heap generation to the next. The CLR does this for performance reasons.

Note  

The System namespace and GC class are introduced in each code sample to support this demonstration. Comments are included in each code sample to help explain the GC methods being used.

COBOL .NET Example

As you will notice in Listing 8-1, I have created a console application and named the project HeapGenerationExampleCobol . For your convenience, the sample code from the .cob module is provided in Listing 8-1.

Listing 8-1: COBOL .NET Example of How the CLR Promotes Aged Objects
start example
 000010 IDENTIFICATION DIVISION. 000020* This is an example of how the CLR Promotes Objects 000030* from one HEAP Generation to the next - as an object ages 000040 PROGRAM-ID. MAIN. 000050 ENVIRONMENT DIVISION. 000060 CONFIGURATION SECTION. 000070 REPOSITORY. 000080* .NET Framework Classes 000090    CLASS SYS-OBJECT AS "System.Object" 000100    CLASS SYS-INTEGER AS "System.Int32" 000110    CLASS GC AS "System.GC". 000120* 000130 DATA DIVISION. 000140 WORKING-STORAGE SECTION. 000150    77 obj OBJECT REFERENCE SYS-OBJECT. 000160    77 GC_OBJ OBJECT REFERENCE GC. 000170    77 My_Int OBJECT REFERENCE SYS-INTEGER. 000180    77 My_String PIC X(20). 000190  000200    01 NULL-X PIC X(1). 000210 LINKAGE SECTION. 000220 000230 PROCEDURE DIVISION. 000240  000250    DISPLAY "Begin Heap Generation COBOL.NET Example" 000260       DISPLAY " " 000270 000280* Instantiate Object from .NET Framework Classes 000290    INVOKE SYS-OBJECT "NEW" RETURNING obj 000300  000310* Execute GC "GetGeneration" Method Using Inline Invoke syntax 000320    SET My_Int to GC::"GetGeneration" (obj) 000330* Execute ToString Method Using Inline Invoke syntax 000340    SET My_String to My_Int::"ToString" 000350    Display "HEAP Generation of obj BEFORE FIRST collection: "  000360             My_String 000370  000380* Manually Induce Garbage Collection on all Generations  000390    INVOKE GC "Collect". 000400  000410* Execute GC "GetGeneration" Method Using Inline Invoke  000420    SET My_Int to GC::"GetGeneration" (obj) 000430* Execute ToString Method Using Inline Invoke syntax 000440    SET My_String to My_Int::"ToString" 000450    Display "HEAP Generation of obj AFTER FIRST collection: "  000460             My_String 000470  000480* Manually Induce Garbage Collection on all Generations  000490    INVOKE GC "Collect". 000500  000510* Execute GC "GetGeneration" Method Using Inline Invoke  000520    SET My_Int to GC::"GetGeneration" (obj) 000530* Execute ToString Method Using Inline Invoke syntax 000540    SET My_String to My_Int::"ToString" 000550    Display "HEAP Generation of obj AFTER SECOND collection: "  000560             My_String 000570  000580* Remove Object reference 000590* This will make it eligible for Garbage Collection 000600    SET obj to NULL 000610  000620* Manually Induce Garbage Collection on all Generations  000630    INVOKE GC "Collect" 000640  000650* Optionally, I could have induced a collection 000660* specifically on the generation # containing my obj. 000670* Using the syntax "GC.Collect USING BY VALUE var1"  000680* with var1 having the value of 1, to target generation 1 000690  000700    DISPLAY "Enter X and Press Enter to Exit.". 000710    ACCEPT NULL-X.  000720 000730 END PROGRAM MAIN. 
end example
 

After you execute the HeapGenerationExampleCobol project, the output shown in Figure 8-2 will appear.

click to expand
Figure 8-2: The console display of the HeapGenerationExampleCobol project

Feel free to view the associated IL (using ILDASM) for this assembly. The more that you do this, the better you will understand the effect your coding statements have on application processing. ( Please see the related common intermediate language [CIL] reference in the "Books" subsection of the "To Learn More" section.)

Let's now take a look at this heap generation example implemented using VB .NET.

VB .NET Example

As you will notice in Listing 8-2, the VB .NET code sample also is created as a console application. The .vb code for HeapGenerationExampleVB is provided in Listing 8-2.

Listing 8-2: VB .NET Example of How the CLR Promotes Aged Objects
start example
 Module Module1            'This is an example of how the CLR Promotes Objects           'from one HEAP Generation to the next - as an object ages        Sub Main()                  Console.WriteLine _              ("Begin Heap Generation Visual Basic.NET Example")              Console.WriteLine(String.Empty)                  'Instantiate Object from .NET Framework Classes              Dim obj As New Object()                  'Execute GC "GetGeneration" Method and ToString Method              Console.WriteLine _              ("HEAP Generation of obj BEFORE FIRST collection: " _              & GC.GetGeneration(obj).ToString)                  'Manually Induce Garbage Collection on all Generations               GC.Collect()                  'Execute GC "GetGeneration" Method and ToString Method              Console.WriteLine _              ("HEAP Generation of obj AFTER FIRST collection: " _              & GC.GetGeneration(obj).ToString)                  'Manually Induce Garbage Collection on all Generations               GC.Collect()                  Console.WriteLine _              ("HEAP Generation of obj AFTER SECOND collection: " _              & GC.GetGeneration(obj).ToString)                  'Remove Object reference              'This will make it eligible for Garbage Collection              obj = Nothing                  'Manually Induce Garbage Collection on all Generations               GC.Collect()                  'Optionally, I could have induced a collection              'specifically on the generation # containing my obj.              'Using the syntax GC.Collect(1) to target generation 1                  Console.WriteLine("Press Enter to Exit")              Console.ReadLine()            End Sub     End Module 
end example
 

For your convenience, I have executed the HeapGenerationExampleVB project. As you can see in Figure 8-3, the display shows that the CLR has promoted the object from heap generation 0 to heap generation 1 and then to heap generation 2.

click to expand
Figure 8-3: The console display of the HeapGenerationExampleVB project

The Boxing of ValueType Objects

As previously mentioned in the section "Creating .NET Garbage," structures and enumerations (in other words, ValueType objects) can participate in garbage collection through the CLR feature called boxing . Using the boxing feature, you can reference a structure or an enumeration (value type) as a reference type object. Usually, this conversion is done implicitly as the result of a coding assignment. Likewise, there is an unboxing feature, which is the boxing operation occurring in reverse. The COBOL .NET and VB .NET code snippets in this section illustrate the boxing feature in practice.

Listing 8-3 presents the COBOL .NET code snippet (using a Console Application template).

Listing 8-3: COBOL .NET Code Snippet from the BoxingExampleCOBOL Project
start example
 000010 IDENTIFICATION DIVISION. 000020* This is an example of IMPLICITLY BOXING a Structure 000030 PROGRAM-ID. MAIN. 000040 ENVIRONMENT DIVISION. 000050 CONFIGURATION SECTION. 000060 REPOSITORY. 000070* .NET Framework Classes 000080              CLASS SYS-OBJECT AS "System.Object". 000090* 000100 DATA DIVISION. 000110 WORKING-STORAGE SECTION. 000120  000130* Declare Data Items with COBOL.NET Data Types 000140* this Data Type maps to a Structure/Value Type. 000150* Initialize with the value of 9999 (Hex x'270F). 000160* The myFirstInt variable is allocated on the Stack 000170* does not get Garbage Collected 000180 000190  77 myFirstInt PIC S9(9) USAGE IS COMP-5 Value 9999. 000200* Declare Data Items using .NET Data Types 000210* that References an Object 000220* The myobject variable is allocated memory on the HEAP  000230* and will be Garbage Collected  000240 000250  77 myobject               OBJECT REFERENCE SYS-OBJECT. 000260  000270  01 NULL-X PIC X(1). 000280 LINKAGE SECTION. 000290* 000300 PROCEDURE DIVISION. 000310* Reference the Value of the Value Type 000320    SET myobject to myFirstInt. 000330  000340    DISPLAY "Use ILDASM to view the BOXing of the Structure" 000350    DISPLAY "Enter X and Press Enter to Exit.". 000360    ACCEPT NULL-X.  000370 000380 END PROGRAM MAIN. 
end example
 

Listing 8-4 presents the VB .NET code snippet (which also uses a Console Application template).

Listing 8-4: VB .NET Code Snippet from the BoxingExampleVB Project
start example
 Module Module1            Sub Main()                'This is an example of IMPLICITLY BOXING a Structure                    'Declare a Structure/Value Type.                 'Initialize with the value of 9999 (Hex x'270F)                'The myFirstInt variable is allocated on the Stack                'and does not get Garbage Collected                Dim myFirstInt As Integer = 9999                    'Declare a Reference Type Object                'Reference the Value of the Value Type                'The myobject variable is allocated memory on the HEAP                'and will be Garbage Collected                Dim myobject As Object = myFirstInt                Console.WriteLine _                ("Use ILDASM to view the BOXing of the Structure")                Console.WriteLine _                ("Press Enter to Exit")                Console.ReadLine()        End Sub End Module 
end example
 

Now, let's take a peek at the ILDASM display and look at the MSIL for evidence that this boxing feature is actually executed. You will notice that the box command appears in the MSIL for each language. Notice also that the hex value of x'270F' is used to initialize the value type variable.

Listing 8-5 presents the MSIL snippet for the COBOL .NET BoxingExampleCOBOL project.

Listing 8-5: MSIL Snippet for COBOL .NET Showing the Box Command Being Performed
start example
 . . . <copied from the _InitialProgram Method Output>     IL_0016: ldc.i4   0x270f     IL_001b: stsfld   int32 MAIN::MYFIRSTINT_001CE9F8     IL_0020: ldnull     IL_0021: stsfld    object MAIN::MYOBJECT_001CEAB8 . . . <copied from the Procedure Method Output>     IL_006a: stelem.ref     IL_006b: br.s     IL_006d     IL_006d: ldsfld   int32 MAIN::MYFIRSTINT_001CE9F8     IL_0072: box      int32     IL_0077: stsfld   object MAIN::MYOBJECT_001CEAB8     IL_007c: ldloc    __DisplayInfo     IL_0080: ldc.i4.0     . . . 
end example
 

Listing 8-6 shows the MSIL snippet for the VB .NET BoxingExampleVB project.

Listing 8-6: MSIL Snippet for VB .NET Showing the Box Command Being Performed
start example
 <copied from the Main Method Output> .method public static void Main() cil managed . . .  .locals init ([0] int32 myFirstInt,                [1] object myobject)  IL_0000: nop  IL_0001: ldc.i4     0x270f  IL_0006: stloc.0  IL_0007: ldloc.0  IL_0008: box        [mscorlib]System.Int32  IL_000d: stloc.1 . . .  IL_002a: nop  IL_002b: ret } // end of method Module1::Main 
end example
 

I included this small amount of COBOL .NET and VB .NET code to demonstrate the activities that go on behind the scenes. Some of these activities result in different amounts of garbage being created by your application. Some of these things will matter to you.

You may come across information in the future (and even in the next chapter of this book) that will point out advantages of value type objects over reference type objects. But as you can see here, depending on how you use them, you could end up actually creating reference type objects that will get garbage collected.

In other words, there are times when you should use value type objects and other times when reference type objects are more suitable. Each object type has special program coding considerations. Each object type has its pros and cons. However, once a value type object is boxed, the playing field is somewhat leveled.

Cross-Reference  

In Chapter 9, you will explore value type objects and reference type objects in detail.

Tip  

Just for clarification , the distinction made in Chapter 6 between COBOL .NET data types and .NET data types shouldn't be confused with the distinction made here between value types and reference types. For example, within the category of .NET data types, you have both value types and reference types. This was further explained in Chapter 7 in the section "Structures." Did I confuse you? Don't worry, in Chapter 9 I thoroughly discuss the topic of objects. Then, having come full circle, I'm sure your understanding will be complete.

Garbage Collection Schedule

In the section "Creating .NET Garbage" earlier in this chapter, I mentioned that the .NET GC "makes its rounds." Then, I turned around and used a GC.Collect method in the heap generation code samples to manually induce a garbage collection. Of course, this is an obvious contradiction.

At this point, you are ready to have the following questions answered :

  • What is the garbage collection schedule?

  • Can you control the garbage collection schedule?

  • Should you control the garbage collection schedule?

The real answer to the first question is "Who knows ?" Now, this does not sound like a polite way to answer a question, right? Given the technical nature of the topic, some will say that this answer is just barely acceptable. We reformed mainframe programmer types have been trained for years to never guess, to never assume anything. So, in our honor , here is a more appropriate answer. (Be sure to take a deep breath before you continue!)

The GC has an optimizing engine that determines how often to perform a collection. The GC is described as following a heuristic approach. Depending on the actual characteristic of managed heap allocation attempts and available space at the time, the GC follows an established algorithm. [5] For example, if an application attempts to allocate space, the first target managed heap generation is generation 0. The GC checks to see if there is enough space to complete the allocation (this also involves processing of objects and generation promotion of objects). If the needed space is not available on generation 0, the GC then follows the same algorithm on generation 1 and then again on generation 0 (remember that eligible objects are being removed, disposed, finalized, and/or promoted). The GC compacts the free space found on the managed heap. The GC, if needed, then goes through the same process with generation 2, then generation 1, and then generation 0. Again, it is processing objects, promoting objects, reclaiming and compacting space, and so forth.

All right, now exhale. That was certainly a mouthful. Can you do me a favor? The next time that someone asks you about the GC's schedule, try giving them this same answer. As you watch the person's jaw drop, calmly sum things up by saying something like "It's really intuitive and simple." Hey, why not have a little fun every now and then?

Note  

Just a reminder: The GC is a feature of the CLR.

Let us now tackle the remaining two questions, which both speak to the topic of controlling the GC schedule.

Yes, for better or for worse , you can control the GC schedule. Why "for better or for worse"? Well, first understand that generally speaking, you should not even think about controlling, influencing, manipulating, or otherwise intervening with the GC's schedule.

My liberal use of the GC.Collect method in this chapter in various code samples is for demonstration purposes only. Some of the same coding statements available (e.g., GC.Collect) for increasing the frequency of collection could in some cases improve one application's performance but seriously degrade a different application's performance. This also holds true for the use of the Dispose and Finalize methods. You should use the Dispose and Finalize methods only after you have performed detailed analysis regarding the intended benefit and any potential negative impact of using them. Having said that, here are a few other legitimate scenarios for explicitly influencing the GC schedule:

  • Your managed code makes use of unmanaged resources (e.g., file, window, or network connections).

  • Your managed code makes use of unmanaged objects (e.g., legacy applications, COM objects, and so forth).

  • Your managed code makes use of only managed objects and managed resources, yet your application fits a rare example of one that has the need to adopt a "deterministic" cleanup approach.

I strongly recommend that if your application falls into one of these three scenarios you refer to the "To Learn More" section at the end of this chapter. You will find a healthy number of useful references to support your continued learning.

Caution  

A few of you might feel a little uncomfortable with the idea of my mentioning a piece of technology only to turn around and discourage its use. First, I wish to only discourage its misuse . Second, we highly skilled reformed mainframe programmers have done this sort of thing before. Haven't you cautioned others on the use of the FREEMAIN and GETMAIN commands when doing mainframe COBOL CICS development? As you know, the misuse of these "memory management" commands can have grave consequences. Suffice it to say that a warning is appropriate in this context as well.

Are you ready for one more stab at the GC topic? Good! I think you will like the next discussion.

Monitoring the GC

Perhaps you are starting to get a feel for this CLR feature, this powerful engine called the GC. Maybe, just maybe, you have a feel for the scheduling concerns that surround the GC. Through discussion and sample code you have even explored the heap generation promotion behavior of the CLR and GC. But still you are curious ; you are thirsty for more. OK, here you go.

Included with the Windows and .NET platform are many tools that monitor performance. Given the impact that memory management (and mismanagement) can have on performance, familiarity with these monitoring tools is relevant. I will use this section to give you yet another view of the actual "behavior" of the GC.

Among the many Windows-based performance-monitoring tools are a few that are bundled with various Windows and .NET software editions:

  • Windows Task Manager (discussed in Chapter 3)

  • Application Center Test (ACT) (discussed in Chapter 13)

  • Performance Monitor (Perfmon)

As noted, the Windows Task Manager and Application Center Test are discussed elsewhere in this book ”in Chapter 3 and Chapter 13, respectively.

Tip  

To access the Windows Task Manager utility, press Ctrl-Alt-Delete. Depending on your version of the Windows operating system, you may have to click Task Manager after you press the keyboard combination. Optionally, you can access the Windows Task Manager utility by right- clicking an empty space on the taskbar and then clicking Task Manager.

At this time, I will introduce you to the Windows product called Performance Monitor (Perfmon). Functionally, you might (loosely) compare this product with some of the various OMEGAMON-type mainframe-monitoring products. As you will soon see, with Perfmon you can obtain a rather granular view of your application's performance (including the general performance of your system-level processes).

You can access the Perfmon tool in one of several ways:

  • From your desktop, click the Start button and select Programs Administrative Tools Performance.

  • From an open Windows Task Manager window on the toolbar, select File New Task (Run). Then enter Perfmon in the Open text box and click OK.

  • From your desktop, click the Start button and select Run. Then enter Perfmon in the Open text box and click OK.

  • Programmatically, you can access the Perfmon objects (or performance counter objects) via an instance of the .NET namespace System.Diagnostics.PerformanceCounter .

For this discussion, you will access Perfmon using the last two approaches. First, you will take a look at the "programmatic" approach with a set of sample applications. Following that discussion, I introduce the actual Perfmon tool.

Note  

You will notice a large block of COBOL .NET DISPLAY statements and VB .NET Console.WriteLine statements in the following set of sample applications (PerfmonCobol and PerfmonVB). As you execute the sample applications for the first discussion in the section "Creating a Performance Counter Component," simply disregard these code comments/instructions. I provided these code comments/ instructions as preparation for the discussion in the section "Using the Windows Performance Monitor Tool" later in this chapter. In this later section, you will again execute this same set of sample applications. At that time, you should pay attention to the code comments/ instructions.

Creating a Performance Counter Component

In the following set of code samples, you will separately create a COBOL .NET project and a VB .NET project. I created each using the Console Application template type. I chose to name the applications PerfmonCobol and PerfmonVB, respectively. In each project, you will focus on the PerformanceCounter class from the System.Diagnostics .NET namespace. The following five properties of the PerformanceCounter class are used:

  • Input: CategoryName = ".NET CLR Memory"

  • Input: CounterName = "# Bytes in all Heaps"

  • Input: MachineName = "."

  • Input: InstanceName = "PerfmonVB"

  • Output: RawValue

I chose this particular performance object (the value specified in the CategoryName property) and this particular performance counter (the value specified in the CounterName property) for demonstration purposes only. In the section "Performance Counter Objects" later in this chapter, I list a few of the other performance objects that are directly related to the .NET CLR.

COBOL .NET Example

The COBOL .NET code in Listing 8-7 will programmatically create a performance counter component. Please examine the code and the included comments before you run the application. This application demonstrates the activity of the GC. By showing how the total allocated heap bytes fluctuate (without manually inducing a garbage collection), you can see that the GC is alive and actively clearing space from the managed heaps.

Listing 8-7: COBOL .NET PerfmonCobol Example
start example
 000010 IDENTIFICATION DIVISION. 000020* This is an example of how the CLR 000030* actively performs memory management. 000040 PROGRAM-ID. MAIN. 000050 ENVIRONMENT DIVISION. 000060 CONFIGURATION SECTION. 000070 REPOSITORY. 000080* .NET Framework Classes 000090    CLASS SYS-INT64 AS "System.Int64" 000100    CLASS PERFCOUNTER AS "System.Diagnostics.PerformanceCounter" 000110    PROPERTY PROP-CategoryName AS "CategoryName" 000120    PROPERTY PROP-CounterName AS "CounterName" 000130    PROPERTY PROP-MachineName AS "MachineName" 000140    PROPERTY PROP-InstanceName AS "InstanceName" 000150    PROPERTY PROP-RawValue AS "RawValue". 000160* 000170 DATA DIVISION. 000180 WORKING-STORAGE SECTION. 000190   77 PERFCOUNTER_Obj OBJECT REFERENCE PERFCOUNTER. 000200   77 myLong OBJECT REFERENCE SYS-INT64. 000210   77 My_String PIC X(20). 000220   77 myString1 PIC X(40) VALUE "This is an example of creating Garbage". 000230   77 i PIC S9(9) COMP-5. 000240* Set this variable to the number of times to process the loop 000250   77 maxInt PIC S9(9) COMP-5 VALUE 999. 000260  000270   01 NULL-X PIC X(1). 000280 LINKAGE SECTION. 000290 000300 PROCEDURE DIVISION. 000310 000320* Set Properties of PerformanceCounter Class 000330     INVOKE PERFCOUNTER "NEW" RETURNING PERFCOUNTER_Obj 000340     SET PROP-CategoryName OF PERFCOUNTER_Obj TO ".NET CLR Memory" 000350     SET PROP-CounterName OF PERFCOUNTER_Obj TO "# Bytes in all Heaps" 000360     SET PROP-MachineName OF PERFCOUNTER_Obj TO "." 000370     SET PROP-InstanceName OF PERFCOUNTER_Obj TO "PerfmonCobol" 000380  000390     SET myLong to PROP-RawValue OF PERFCOUNTER_Obj 000400     INITIALIZE My_String  000410     SET My_String to myLong::"ToString" 000420  000430     DISPLAY "Begin Performance Monitor COBOL Example" 000440     DISPLAY " " 000450     DISPLAY "1St Performance Monitor Reading: " My_String 000460     DISPLAY " " 000470     DISPLAY "Warning: This loop will run for a long time." 000480     DISPLAY "!! Depending on the Value of the maxInt variable !! " 000490     DISPLAY "I suggest that you let it run for a while" 000500     DISPLAY "at the same time, you can View the Perfmon Tool info." 000510     DISPLAY "You can either let the loop run and end normally or" 000520     DISPLAY "you can manually terminate the sample application" 000530     DISPLAY "by CLOSING the opened console window." 000540     DISPLAY " " 000550     DISPLAY "Please Prepare your Perfmon window as follows:" 000560     DISPLAY "Performance Object = .NET CLR Memory" 000570     DISPLAY "Counter = # Bytes in all Heaps" 000580     DISPLAY "Machine Name = Local computer" 000590     DISPLAY "Instance Name = PerfmonVB and/or PerfmonCobol" 000600     DISPLAY " " 000610     DISPLAY "Enter X and Press Enter to Resume Sample Application." 000620     ACCEPT NULL-X. 000630  000640     PERFORM VARYING i  000650             FROM 0 BY 1 UNTIL i >= maxInt 000660                MOVE "This String had been modified." TO myString1 000670                SET myLong to PROP-RawValue OF PERFCOUNTER_Obj 000680                INITIALIZE My_String  000690                SET My_String to myLong::"ToString" 000700                Display "Allocated Heap: " My_String 000710   END-PERFORM 000720 000730   DISPLAY "The loop has completed. Enter X and Press Enter to Exit.". 000740   ACCEPT NULL-X.  000750 000760 END PROGRAM MAIN. 
end example
 

If possible, please run the application. After it displays hundreds of output lines, the application will complete. Take a moment and scroll through the console window display. It should be obvious at which point the GC performed its collection (at each point where the displayed value decreased).

In the sample code, please notice the approach to "creating" the Performance-Counter object. In object-oriented terms, this is referred to as object instantiation.

VB .NET Example

In Listing 8-8, the performance counter component has been programmatically created. Please examine the code and the included comments. When you run this application, a stream of lines will be written to the console window. Notice how the RawValue amount fluctuates constantly. Well, this value is showing the total amount of allocated spaces across all of the managed heaps in each generation. Why does it fluctuate so? Because the GC is busily clearing space as the application continues to add space. This demonstrates that garbage collection will occur, even without any manual intervention.

Listing 8-8: VB .NET PerfmonVB Example
start example
 'This is an example of how the CLR 'actively performs memory management.     Module Module1   Sub Main()      Dim i As Int32     '* Set Properties of PerformanceCounter Class        Dim myCounter As New System.Diagnostics.PerformanceCounter()        With myCounter            .CategoryName = ".NET CLR Memory"            .CounterName = "# Bytes in all Heaps"            .MachineName = "."            .InstanceName = "PerfmonVB"        End With 'Set this variable to the number of times to process the loop        Dim maxInt As Int32 = 999        Console.WriteLine("Begin Performance Monitor VB Example")        Console.WriteLine(String.Empty)        Console.WriteLine("1St Performance Monitor Reading: " & _                          myCounter.RawValue.ToString())        Console.WriteLine(" ")        Console.WriteLine("Warning: This loop will run for a long time.")        Console.WriteLine("!! Depending on the Value of the maxInt variable !! ")        Console.WriteLine("I suggest that you let it run for a while")        Console.WriteLine("at the same time, you can View the Perfmon Tool info.")        Console.WriteLine("You can either let the loop run and end normally or")        Console.WriteLine("you can manually terminate the sample application")        Console.WriteLine("by CLOSING the opened console window.")        Console.WriteLine(" ")        Console.WriteLine("Please Prepare your Perfmon window as follows:")        Console.WriteLine("Performance Object = .NET CLR Memory")        Console.WriteLine("Counter = # Bytes in all Heaps")        Console.WriteLine("Machine Name = Local computer")        Console.WriteLine("Instance Name = PerfmonVB and/or PerfmonCobol")        Console.WriteLine(" ")        Console.WriteLine("Then Press Enter to Resume Sample Application.")        Console.ReadLine()        Dim myString1 As String = "This is an example of creating Garbage"        For i = 0 To maxInt               myString1 = "This String had been modified."               Console.WriteLine("Allocated Heap: " & _               myCounter.RawValue.ToString())        Next        Console.WriteLine("The loop has completed. Press Enter to Exit.")        Console.ReadLine() End Sub End Module 
end example
 

Please run the VB .NET example application. If you scroll through the console window display, you will notice that the "TOTAL of BYTES in ALL HEAP" value increases and decreases. Each decrease signals that a garbage collection has occurred.

Please notice use of the With and End With language statements in the VB .NET sample code. The use of these statements is considered good coding practice when you need to access multiple properties on one object.

Performance Counter Objects

In the sample applications, you have used the System.Diagnostics.PerformanceCounter class. You set a few of its properties. One of those properties is CategoryName.

Now, what will you do when you want to use categories other than the one you used here (".NET CLR Memory")? Given that you have many performance categories to choose from (approximately 66), there has to be a way to find out what choices are available, right? Well, there is. You have a few options:

  • You can view the MSDN Web references included at the end of this chapter in the "Web Sites" subsection of the "To Learn More" section.

  • You can view the drop-down menus of the Perfmon GUI tool.

  • You can write a small program to instantiate the appropriate .NET namespace class: System.Diagnostics.PerformanceCounterCategory . Then, execute the appropriate method (i.e., GetCategories ) to receive an array that contains all of the available performance categories. The following sample code demonstrates this:

     'This is an example of how to retrieve 'the Performance Categories Module Module1        Sub Main()        Dim myCounterCategories As New _        System.Diagnostics.PerformanceCounterCategory()        Dim x As Array = myCounterCategories.GetCategories()        For Each myCounterCategories In x           Console.WriteLine(myCounterCategories.CategoryName)        Next        Console.WriteLine("Press Enter to Exit.")        Console.ReadLine()        End Sub End Module 
Note  

The preceding code sample is one of a few exceptions where I provide just the VB .NET code. Converting the sample to COBOL .NET would be a good exercise for you to do.

After running the small sample application GetPerfCategoriesVB, I did a little copying and pasting to include some of the following performance categories, which are directly related to .NET:

  • .NET CLR Data

  • .NET CLR Exceptions

  • .NET CLR Interop

  • .NET CLR JIT

  • .NET CLR Loading

  • .NET CLR LocksAndThreads

  • .NET CLR Memory

  • .NET CLR Networking

  • .NET CLR Remoting

  • .NET CLR Security

  • ASP.NET

  • ASP.NET Applications

  • ASP.NET Apps v1.0.3705.0

  • ASP.NET v1.0.3705.0

There will be occasions when you'll want to use some of the other performance categories (ones that don't appear in the preceding list). Feel free to run the GetPerfCategoriesVB application for a complete list.

Tip  

For your continued learning, I invite you to experiment with the many other performance objects and counters. I have provided several references related to performance objects and counters in the "To Learn More" section at the end of this chapter.

Using the Windows Performance Monitor Tool

Before I get into the fun stuff, please recall that the focus here is the performance concerns of .NET managed code, particularly as it relates to the CLR. Specifically, this section of the book focuses on the GC and its various aspects of memory management. Why am I making this point?

Well, Perfmon has a large set of objects available (a partial list appears in the section "Performance Counter Objects"). About 15 percent (10 of 66) of those objects are directly tied to the .NET CLR. Several more are directly related to other .NET managed code concerns (e.g., ASP.NET, Web services, WMI, and so on). Although most of the remaining Perfmon objects are indirectly related to .NET and the CLR, you can appreciate the fact that Perfmon provides both a granular view and a comprehensive view.

Using the GUI (front-end) that Windows provides to expose these objects, you can select one or more objects, which are referred to as performance objects. Once you select your performance object, you can select one counter, several counters, or all counters listed for that particular performance object. You will typically make two additional choices: choosing an Instance and choosing a Computer. The Instance option provides you the opportunity to see all executing processes or to narrow your view to just one executing process. The Computer Selection option provides you the opportunity to target just your local workstation or other remote servers.

Note  

To prepare for the steps that follow, I have modified the two Perfmon sample programs. I have modified the variable maxInt from 999 to 99999. This will cause the loop to run for "a long time." Please locate your two compiled sample code executables (PerfmonCobol.exe and PerfmonVB.exe). These files should be located in the bin subfolder within the folder location of each actual sample application. When you have located the .exe files, double-click them to launch the sample applications. Each sample application was designed to "pause" (remember the block of Display/Console.WriteLine statements?). Leave each sample application paused with the console window open.

Figure 8-4 shows each sample application launched and paused. Please do this before you proceed to the following Perfmon setup steps.

click to expand
Figure 8-4: The two sample applications, PerfmonCobol and PerfmonVB, have been launched and paused.

The following series of figures illustrates the basic steps in starting and setting up Perfmon. Figure 8-5 shows the initial launch of Perfmon.

click to expand
Figure 8-5: Launching Perfmon by clicking the Start button and selecting Run

Figure 8-6 shows how to access the Add counter button. Figure 8-7 shows how to select a .NET CLR-related performance object.

click to expand
Figure 8-6: Accessing the Add counter button to add performance counters
click to expand
Figure 8-7: Preparing to select a performance object

Figure 8-8 shows how to select a specific counter.

click to expand
Figure 8-8: Selecting a specific counter from the available choices

As shown in Figure 8-9, I have selected the "Use local computer counters" radio button. Additionally, in the "Select instances from list" box, I have selected both PerfmonCobol and PerfmonVB.

click to expand
Figure 8-9: Preparing to click Add in the Add Counters window to complete the setup
Note  

If you do not see the PerfmonCobol and PerfmonVB applications as available choices, launch each sample application by clicking its respective .exe file.

As shown in Figure 8-9, I have clicked the Explain button. You use this button to get a better understanding about each counter. To complete the Perfmon setup, make sure to click the Add button after you have made all of the selections on this screen. Afterward, simply click the Close button.

Note  

Optionally, you can format your Perfmon display (e.g., change the scale, colors, and so on) by pressing Ctrl-Q. If you select View from the toolbar, you can customize your Perfmon window ”for example, you can remove the console tree. Also, you can remove any instance displays (as I have done) that you may not have an immediate interest in. You do this by first selecting the instance in the box where the PerfmonCobol and PerfmonVB instances are displayed and pressing Delete.

After you start each Perfmon sample application (PerfmonVB and PerfmonCobol) and perform the appropriate setup on the Perfmon tool, you should see a display similar to the one shown in Figure 8-10. You will notice that I have moved the two console windows next to each other with the Performance window sitting on top of them both.

click to expand
Figure 8-10: The Performance window, with the two Perfmon sample applications processing. Their console windows are behind the Performance window.

If you have the time, let each application process to completion. It is rather interesting to watch the graphs change (well, more interesting than watching paint dry). I actually found it interesting that the behavior of the GC varied slightly between the two sample applications. One minute, their graphs (reflecting their total bytes allocated across each heap generation) are going up and then down (as the GC performs its collections and heap compactions), almost in unison . Then, occasionally, the VB .NET application will "break out" ahead and grab more storage (having less collected). But then, COBOL .NET (not to be left behind) "catches up" each time.

Note  

If you let these sample applications run for a while, you will eventually need to adjust the default scale for the graph (from 0-100 to 0-500, for example). You do this by pressing Ctrl-Q to access the System Monitor Properties page. Next, click the Graph tab. You will see a Vertical Scale section near the bottom portion of the dialog box. To adjust the vertical scale, change the Minimum and Maximum values as needed.

You will notice that over time, the allocated heap count continues to inch upward as the GC collects less. My theory about this is that perhaps there is a gradual increase in the amount of aged objects that have been promoted to managed heap generation 2. Obviously, you are welcome to modify the Perfmon sample applications as needed to prove or disprove this theory. And as for me? That is fun enough ”for now.

I believe that I've discussed the GC in sufficient depth. Although there's certainly more to discover, you're well equipped for that leg of the journey. The discovery process will be great.

start sidebar
Samples, Samples, and More Samples

You are sitting on top of a gold mine. Well, a gold mine's worth of free samples ” right on your own hard disk. A large number of Microsoft samples are bundled with the Visual Studio .NET (VS .NET) product. It is likely that you installed them when you installed your edition of VS .NET. Although you will not find any COBOL .NET samples there, you will find lots of good VB .NET and C# samples. Remember, you also have a good "stash" of free COBOL .NET samples sitting on your hard disk courtesy of Fujitsu. The locations of these samples, both Microsoft provided and Fujitsu provided, are as follows:

  • Microsoft: <Hard drive>:\Program Files\Microsoft Visual Studio .NET\FrameworkSDK\Samples

  • Fujitsu: <Hard drive>:\Program Files\Fujitsu NetCOBOL for .NET\Examples

end sidebar
 

[2] Pun intended.

[3] I further discuss ValueType objects in Chapter 9.

[4] This "other heap" is sometimes referred to as the finalization queue .

[5] Some believe that a particular phase of the moon or the velocity of the northern wind has some influence on the GC schedule as well. Well, if a groundhog's vision can influence seasonal weather patterns, anything is possible.




COBOL and Visual Basic on .NET
COBOL and Visual Basic on .NET: A Guide for the Reformed Mainframe Programmer
ISBN: 1590590481
EAN: 2147483647
Year: 2003
Pages: 204

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net