Managed vs Unmanaged Code – What are the Differences?
When designing a new application, or redesigning an existing one, the question of whether to use managed or unmanaged code may come up. Here’s a rundown of how managed and unmanaged code is executed, the differences between them, and the advantages and disadvantages of both.
What is the Difference Between Managed and Unmanaged Code?
When code runs in a framework that has control over the execution of that code, such as .Net’s Common Language Runtime (CLR) or Java’s Java Runtime Environment (JRE), it is considered to be managed code. If code is executed outside of these environments it is considered unmanaged. For example, managed code in a .Net framework application will only execute under the management of a CLR, which dictates how the code can be run. Unmanaged code, such as C++, does not have these limitations or frameworks.
To look into this further, application source code is typically compiled from a particular language (e.g. C, C++, Swift, etc.) into a specific hardware platform’s native code via a compiler. This code is then parsed and processed directly in a hardware environment, in which the binary code is processed by the Central Processing Unit (CPU), with the Operating System existing as the software environment link to the hardware. With specific software frameworks, including the .Net framework and the Java Runtime Environment, code is instead compiled into an intermediate language (MSIL for .NET or bytecode for Java).
The MSIL/bytecode represents an intermediate executable that runs in the virtual machine (the CLR for the .NET and the JVM for Java). These virtual machines parse and convert the intermediate executable into native architecture executables. This way the original compiled code (intermediate code) never touches the CPU directly, but is executed within a software environment. The environment is the application virtual machine. This allows for greater interoperability and scalability due to being able to parse and run code written in a variety of languages (for .NET) and run on different hardware environments.
The original high level source code (e.g., Java, C#, VB#, etc.) ends up being compiled into code that is executed and managed by the CLR. This makes it a flexible, yet powerful, system of software management as memory allocation, security, exception handling, garbage collection, etc. are all handled by the CLR. One of the significant benefits of running in this managed environment is that the runtime environment takes care of some issues that frequently trip up developers of unmanaged code. The runtime environment can help prevent buffer overflows, perform memory management, help perform exception management and more. Unmanaged code, on the other hand, must have all security processes, exception handling, memory allocation, etc. taken care of by the software engineer.
Differences in Memory Control
When using unmanaged code, memory allocation and deallocation needs to be handled by application developers. In C and C++ this can be done by using the malloc() and free() functions in combination with pointers. The malloc() function is used to dynamically allocate a block of memory that is then accessed by a pointer variable. Once that memory is no longer needed, free() needs to be called to ‘free’ the memory block so it can become available for allocation once more. Manual memory management is simple enough in theory, but can quickly become complicated, especially with large applications. If implemented poorly, it can lead to memory leaks, dangling pointers, etc., which can cause applications to crash.
Managed code systems take care of memory management by automatically allocating and deallocating memory (garbage collection) as needed. This takes quite a bit of control away from the developer by removing direct access to memory, but also prevents them from making mistakes while handling memory manually.
Advantages of Using an Unmanaged Language
Using an unmanaged code system for application development gives developers much more control over their code and the hardware it runs on. Software engineers can construct applications with lower level access and direct access to the hardware. Having fewer restrictions also allows experienced programmers to implement features that wouldn’t be directly allowed in a managed code system. Below are some advantages associated with using an unmanaged code system:
- Lower Level Access: Lower level programming languages allow engineers to use and directly control the resources associated with the lower level (the hardware architecture). Due to the ability to determine detailed memory management processes, C is often used to program Operating Systems, as it can touch the lower level of the PC architecture.
- Direct Access to the Hardware: C (a statically typed, unmanaged language) excels at lower level programming and is often used for hardware access and in embedded systems due to its ability to allocate memory, create buffers, allocate the heap and stack statically, and carefully manage the limited memory typically characteristic of embedded systems – all of which is crucial in embedded programming. Outside of embedded programming, unmanaged languages such as C allow for greater access to the hardware, which allows for more efficient applications that – according to benchmarks – typically outperform managed code languages/applications.
- Fewer Restrictions/Complete Control over the App: As noted before, unmanaged code allows programmers to bypass parameters and restrictions that managed code frameworks (such as the CLR) dictate, such as restrictions on pointer usage and direct/manual memory management.
Disadvantages of Using an Unmanaged Language
Being given full reins of an application’s code removes certain limitations, but also opens up the possibility for a myriad of issues, including run-time errors, security bugs (e.g. buffer overflows), and issues associated with memory management. Even error and exception handling must be dealt with fully by the developer in an efficient way.
It is possible to use unmanaged code in a managed system, which can allow for quicker development of applications without having to focus on certain factors such as garbage collection and security. However, it requires the use of certain precautions associated with calling the untrusted code, often using a complex permissions system and secure coding methods. That said, the increase in the number of possible variants and ways that a program can be developed creates an increase in the number of possible mechanisms that can go wrong.
Advantages of Using a Managed Language
Managed language systems enforce coding standards that can help improve application security. For example, memory buffer(s) are automatically checked by the runtime environment to ensure no buffer overflows occur. When using C# or Java, one must allow the use of ‘unsafe code’ (that may cause a buffer overflow vulnerability) for programs to compile. Because of this it is difficult to produce a buffer overflow using a managed language system.
Managed languages also implement garbage collection, which is the automatic management of allocated memory resources via the framework. This will typically entail the collector deallocating memory for objects no longer in use to free up memory resources. Since garbage collection is often regarded as difficult and time-consuming, being able to rely on the automatic collection of managed language systems can be advantageous to developers.
In addition to the above, managed languages provide both runtime type checking and reference checking, both of which are tremendous advantages:
- Runtime Type Checking (dynamic type checking): As opposed to static type checking – which is used at the time of compiling the source code – dynamic type checking occurs at run-time. While some managed languages such as C# are statically typed (checked at compile time), many managed language systems use dynamic type checking. This may cause a program to fail at run-time if type errors exist, which helps ensure that programs are developed properly. It is important to note that most languages use static and dynamic type checking for a thorough type checking system.
- Reference Checking: References are values used in programming languages to point to a given piece of data (e.g. a variable) in memory at a physical address, allowing a program to access it. Checking to determine whether references correctly point to valid objects or are not duplicated (in the case of a reference equality check) ensures the seamless execution of the given program and all of its functions.
Disadvantages of Using a Managed Language
The biggest disadvantage of using managed languages is not being able to allocate memory directly or access lower level functions of a PC’s architecture. Coding down to the metal (low level programming), which can be necessary to produce things like high quality game programs, is just not fully possible using managed languages. Everything from memory management and exception handling, to security, is largely out of the developers’ hands, leaving them without full control over the application being developed.
So, Managed or Unmanaged?
The decision of whether to use managed or unmanaged code will depend on the factors associated with the given project. For example, projects that have an emphasis on application security may be better suited to managed code systems that provide memory management and stricter rules on the use of ‘unsafe code.’ However, if speed is a more critical factor, unmanaged code might be more appropriate due to quicker execution times. Either one could be a great option depending on preferences the goals at hand.