Introduction
What is managed code and why is it important to use 100% managed
code in .NET applications?
Managed code is compiled for the .NET run-time environment. It
runs in the Common Language Runtime (CLR), which is the heart of
the .NET Framework. The CLR provides services such as security,
memory management, and cross-language integration. (3) Managed
applications written to take advantage of the features of the CLR
perform more efficiently
and safely, and take better advantage of developers’
existing expertise in languages that support the .NET
Framework.
Unmanaged code includes all code written before the .NET
Framework was introduced—this includes code written to use
COM, native Win32, and Visual Basic 6. Because it does not run
inside the .NET environment, unmanaged
code cannot make use of any .NET managed facilities. (1)
Advantages of Using Managed Code
Managed code runs entirely “inside the sandbox,”
meaning that it makes no
calls outside of the .NET Framework. That’s why managed
code gets the
maximum benefit from the features of the .NET Framework, and
why
applications built with managed code perform more safely and
efficiently.
Performance
The CLR was designed from the start to provide good performance.
By using 100% managed code, you can take advantage of the numerous
built-in services of the CLR to enhance the
performance of your managed
application. Because of the runtime services and checks that the
CLR performs, applications do not need to include separate versions
of these services. (9) And by using 100% managed code, you
eliminate the
performance costs associated with calling unmanaged code.
Just-In-Time compiler
The CLR never executes Common Intermediate Language (CIL)
directly.
Instead, the Just-In-Time (JIT) compiler translates CIL into
optimized x86
native instructions. (9) That’s why using managed code
lets your software run
in different environments safely and efficiently. In addition,
using machine
language lets you take full advantage of the features of the
processor the
application is running on. For example, when the JIT encounters
an Intel
processor, the code produced takes advantage of
hyper-threading
technology. (5)
Another advantage of the JIT is improved performance. The JIT
learns when
the code
does multiple iterations. The runtime is designed to be able to
retune the JIT compiled code as your program runs. (2)
NGEN utility
NGEN.exe is a .NET utility that pre-compiles the application at
install time.
Pre-compiling improves start-up performance for managed code,
especially
when the application uses Windows Forms. Methods are JITed when
they
are first used, incurring a larger startup penalty if the
application calls many
methods during start-up. Because Windows Forms uses many
shared
libraries in the operating system, pre-compiling Windows Forms
applications
usually improves performance. (12)
Pre-compiling also makes sure that the application is optimized
for the
machine on which it is being installed.
Maintaining a 100% managed code environment
Only when your .NET application uses components that are built
using 100%
managed code do you receive the full benefits of the .NET
environment.
For example, when accessing data through ADO.NET, using wire
protocol
.NET data providers lets you preserve your managed code
environment
because they do not make calls to native Win32 APIs and Client
pieces.
The performance advantages of the managed code environment are
lost
when you (or the components you are using) call unmanaged code.
The CLR
makes additional checks on calls to the unmanaged or native
code, which
impacts performance.
Unmanaged code includes the database client pieces that some
.NET data
providers require. Examples of .NET data providers that use both
managed
and unmanaged code are IBM’s DB2 data provider and the
Oracle Data
Provider for .NET (ODP.NET). Both of these data providers must
use client
libraries to access the database. The data providers shipped
Microsoft for
SQL Server and Oracle—as well as the Microsoft OLE DB data
providers,
and ODBC.NET—make calls to native Win32 database client
pieces or other
unmanaged code.
Automatic memory management
Automatic memory management is one of the most significant
features of
managed code. The CLR garbage collector automatically frees
allocated
objects when there are no longer any outstanding references to
them. The
developer does not need to explicitly free memory assigned to an
object,
which goes a long way toward reducing the amount of time spent
debugging
memory leaks. (10) There can be no memory leaks in 100% managed
code.
Automatic lifetime control of objects
Another significant advantage of using managed code is that the
CLR
provides automatic lifetime management of components and
modules.
Lifetime control includes:
• Garbage collection, which frees and compacts
memory.
• Scalability features, such as thread pooling
and the ability to use a nonpersistent
connection with a dataset.
• Support for side-by-side versions.
Garbage collection
When an object is created with the new operator,
the runtime allocates
memory from the managed heap. Periodically, the CLR garbage
collector
checks the heap and automatically disposes of any objects that
are no longer
being used by the application, reclaiming their memory.
The garbage collector also compacts the released memory,
reducing
fragmentation. (4) This function is particularly important when
the application
runs on large memory servers. Changing the application to use
smaller
objects can help to improve the effectiveness of the garbage
collector.
Similarly, because each DLL is assigned a 64-bit chunk of
memory,
combining small DLLs avoids inefficient use of memory.
Because the garbage collector automatically closes unused
objects, memory
leaks are not possible in an application that uses 100% managed
code.
Scalability features
Thread pooling lets you make much more efficient use of multiple
threads
and is an important scalability feature of using managed code.
The .NET
Framework comes with built-in support for creating threads and
using the
system-provided thread pool. In particular, the ThreadPool class
under the
System.Threading namespace provides static methods for
submitting
requests to the thread pool. In managed code, if one of the
threads becomes
idle, the thread pool injects another worker thread into the
multithread
apartment to keep all the processors busy.
The standard ThreadPool methods capture the caller’s stack
and merge it
into the stack of the thread-pool thread when the thread-pool
thread starts to
execute a task. If you are using unmanaged code, the entire
stack will be
checked, which incurs a performance cost. In some cases, you can
eliminate
the stack checking with the Unsafe methods
ThreadPool.UnsafeQueueUserWorkItem and
ThreadPool.UnsafeRegisterWaitForSingleObject, which provide
better
performance. However, using the Unsafe method calls does not
provide
complete safety. (8)
Further adding to scalability is the ability to use a
non-persistent connection
with a dataset, which is a cache of the records retrieved from
the database.
The dataset keeps track of the state of the data and stores the
data as pure
XML. Database connections are opened and closed only as needed
to
retrieve data into the dataset, or to return updated data.
(7)
Versioning
Versioning essentially eliminates “DLL hell.” When
you define an assembly as
strongly named, the .NET executable will be executed with the
same DLL
with which it was built. This means that you can have
side-by-side versions of
a DLL, allowing you to manage shared components. Versioning
ensures that
each time an application starts up, it checks its shared files.
If a file has
changed and the changes are incompatible, the application can
ask the
runtime for a compatible version.
However, when an application calls unmanaged DLLs, you can end
up back
in “DLL hell.” For example, Oracle’s ODP.NET
data provider calls the
unmanaged Oracle Client pieces, which are specific to a
particular version of
Oracle. You could install two versions of this unmanaged
data provider, for
example, one for Oracle9i and one for the upcoming Oracle10G,
but you
would have a conflict, because each data provider will require a
particular
version of the clients. Since the clients are native Win32 DLLs,
you cannot
easily have side-by-side versions running on the same machine.
Only with
native wire protocol data providers built from 100% managed code
can you
install side-by-side versions with no configuration required by
the end-user.
Checks by the .NET runtime
The .NET runtime automatically performs numerous checks to
ensure that
code is written correctly. Because these checks prevent a large
number of
bugs from ever happening, developer productivity is improved and
the
application quality is better. In addition, these checks thwart
system attacks
such as the exploitation of buffer overruns.
The CLR checks for type safety to ensure that applications
always access
allocated objects in appropriate ways. In other words, if a
method input
parameter is declared as accepting a 4-byte value, the common
language
runtime will detect and trap attempts to access the parameter as
an 8-byte
value. Type safety also means that execution flow will only
transfer to known
method entry points. There is no way to construct an arbitrary
reference to a
memory location and cause code at that location to begin
execution.
In addition, array indexes are checked to be sure they are in
the range of the
array. For example, if an object occupies 10 bytes in memory,
the application
can’t change the object so that it will allow more than 10
bytes to be read.
(11)
Cross-language integration
You can write .NET applications in many different languages,
such as C,
C++, Visual Basic, COBOL, Fortran, Perl, Pascal, Jscript, Lisp,
Python,
Smalltalk, and others. Programmers can use the languages that
they are
most proficient with to develop portions of an application.
All CLR-compliant languages compile to Common Intermediate
Language
(CIL). CIL is the key to making the .NET application
platform-neutral and
hardware independent.
In addition, programmers can choose specific languages for
specific tasks
within the same application. Some languages are stronger than
others for
particular tasks, and programmers can choose the language best
suited for
the task. The originating language doesn’t matter, because
all .NETcompliant
compilers produce CIL.
Platform-neutrality
A managed .NET application can execute on any Windows platform
that
supports the .NET common language runtime. Currently, these
platforms are
Windows 98, Windows 2000, Windows Me, Windows NT, Windows XP,
and
Windows Server 2003 (32-bit). Support for the .NET Framework
and
Common Language Runtime on Windows Server 2003 (64-bit) is
planned for
an upcoming release.
In addition, with the Microsoft Mobile Internet Toolkit,
developers can create a
.NET compliant, mobile Web application that can be adapted to
the display of
multiple wireless devices. (6)
Security
Managed code does not have direct access to memory, machine
registers, or
pointers. The .NET Framework security enforces security
restrictions on
managed code that protects the code and data from being misused
or
damaged by other code. An administrator can define a security
policy to grant
or revoke permissions on an enterprise, a machine, an assembly,
or a user
level. For these reasons, applications that use managed code are
much
safer.
Code access security
Code access security lets the administrator specify which
operations a piece
of code can perform, stopping inappropriate behavior before it
can start. You
can configure a complex set of rules to:
Specify whether a code group can both read and write files
Demand that the code’s callers have specific
permissions
Allow only callers from a particular organization or site to
call the code
Grant permissions to each assembly that is loaded
Compare the granted permissions
of every caller on the call stack at
runtime to the permissions that callers must have and which
resources the
code can access. (6)
The access privileges an administrator assigns depend in part on
where the
application is running. For example, by default, an application
that runs from
the local computer has a higher level of trust and more
privileges, such as
accessing the file system, than an application that is running
from the
Internet.
Calling unmanaged code bypasses the .NET CLR security. An
application
that calls unmanaged code doesn’t necessarily have a
security problem—it
simply has an open door to the possibility of problems due to
the functionality
of the unmanaged code that has direct access to memory or
machine
registers, or uses pointers. Once the unmanaged code is being
executed, the
CLR can no longer check it.
Avoiding buffer overruns
One common type of attack attempts to make API methods operate
out of
specification, causing a buffer overrun. This attack typically
passes
unexpected parameters, such as an out-of-range index or offset
value.
Managed code avoids the buffer overruns that trigger so many
security
snafus.
Buffer overruns usually occur in programs written in languages
such as C or
C++, which do not check array bounds and type safety. If an
application does
not check the validity of the destination buffer size and other
parameters, the
copied data might overrun the buffer, overwriting the data in
adjacent
addresses.
Buffer overruns are theoretically impossible in managed
code.
Summary
Using 100% managed code gives you solid performance, improved
security,
and fewer bugs. The CLR provides memory management and lifetime
control
of objects, including scalability features and versioning. When
you call
unmanaged code, you lose many of the valuable benefits of the
.NET
environment.
References
1.
Gentile, Sam. “Intro to Managed C++, Part 2: Mixing Managed
and
Unmanaged Code.” The O’Reilly Network.
http://www.ondotnet.com/lpt/a/3226
<08/20/2003>
2. Gray, Jan. “Writing Faster
Managed Code: Know What Things Cost.”
MSDN Library. http://msdn.microsoft.com/library/?url=/library/enus/
dndotnet/html/fastmanagedcode.asp
<08/20/2003>
3. Gregory, Kate. “Managed,
Unmanaged, Native: What Kind of Code Is
This?” http://www.developer.com/net/cplus/print.php/2197621
<08/20/2003>
4.
Mariani, Rico. “Garbage Collector Basics and Performance
Hints.” MSDN
Library. April 2003.
http://msdn.microsoft.com/library/default.asp?url=/library/enus/
dndotnet/html/dotnetgcbasics.asp
<08/20/2003>
5. McNaughton, Allan. “Boosting
the Performance of Microsoft .NET.” MSDN
Library.
http://msdn.microsoft.com/library/default.asp?url=/library/enus/
dndotnet/html/optimaldotnet.asp
<08/20/2003>
6. Microsoft Corporation.
“Deployment Guide for the Microsoft Mobile Internet
Toolkit.”
http://msdn.microsoft.com/library/default.asp?url=/library/enus/
dnmitta/html/deploymobilwebapp.asp?frame=true
<08/20/2003>
7. Microsoft. “The Windows
Server 2003 Application Environment.” MSDN
Library.
http://msdn.microsoft.com/library/default.asp?url=/library/enus/
dnnetserv/html/windowsnetserver.asp
<08/20/2003>
8.
Microsoft. .NET Framework Developer’s Guide .
Microsoft .NET Framework
SDK1.0. 2001.
9. Noriskin, Gregor. “Writing
High-Performance Managed Applications: A
Primer.” MSDN Library.
http://msdn.microsoft.com/library/default.asp?url=/library/enus/
dndotnet/html/optimaldotnet.asp
<08/04/2003>
10. Platt, David S. Introducing Microsoft .NET .
Microsoft Press. Redmond, WA.
2001.
11. Richter,
Jeffrey. “Microsoft .NET Framework Delivers the Platform for
an
Integrated, Service-Oriented Web.” MSDN Magazine .
http://msdn.microsoft.com/msdnmag/issues/0900/Framework/default.aspx
<08/20/2003>
12. Schanzer, Emmanuel. “Performance Tips and Tricks in
.NET Applications.”
MSDN Library.
http://msdn.microsoft.com/library/default.asp?url=/library/enus/