Content deleted Content added
m Reverted edit by 35.33.193.94 (talk) to last version by Frap |
|||
(37 intermediate revisions by 22 users not shown) | |||
Line 1:
{{Short description|Techniques used by computers to manage components with limited availability}}
In [[computer programming]], '''resource management''' refers to techniques for managing [[System resource|resources]] (components with limited availability).
[[Computer program]]s may manage their own resources
Host-based management is known as ''resource tracking,'' and consists of cleaning up resource leaks: terminating access to resources that have been acquired but not released after use. This is known as ''reclaiming'' resources, and is analogous to [[Garbage collection (computer science)|garbage collection]] for memory. On many systems, the operating system reclaims resources after the process makes the [[exit (system call)|exit]] [[system call]].
Memory can be treated as a resource, but [[memory management]] is usually considered separately, primarily because memory allocation and deallocation is significantly more frequent than acquisition and release of other resources, such as file handles. Memory managed by an ''external'' system has similarities to both (internal) memory management (since it is memory) and resource management (since it is managed by an external system). Examples include memory managed via native code and used from Java (via [[Java Native Interface]]); and objects in the [[Document Object Model]] (DOM), used from [[JavaScript]]. In both these cases, the [[Memory management|memory manager]] ([[Garbage collection (computer science)|garbage collector]]) of the [[runtime environment]] (virtual machine) is unable to manage the external memory (there is no shared memory management), and thus the external memory is treated as a resource, and managed analogously. However, cycles between systems (JavaScript referring to the DOM, referring back to JavaScript) can make management difficult or impossible.▼
==Controlling access==
A key distinction in resource management within a program is between ''stack management'' and ''heap management'' – whether a resource can be handled like a stack variable (lifetime is restricted to a single [[stack frame]], being acquired on entry to or within a particular scope, and released when execution exits that scope), or whether a resource must be handled like a heap variable, such as a resource acquired within a function and then returned from it, which must then be released outside of the acquiring function. Stack management is a common use case, and is significantly easier to handle than heap management.▼
The omission of releasing a resource when a program has finished using it is known as a [[resource leak]], and is an issue in sequential computing. Multiple processes wish to access a limited resource can be an issue in [[concurrent computing]], and is known as [[resource contention]].
Formally, resource management (preventing resource leaks) consists of ensuring that a resource is released if and only if it is successfully acquired. This general problem can be abstracted as "''before,'' ''body,'' and ''after''" code, which normally are executed in this order, with the condition that the ''after'' code is called if and only if the ''before'' code successfully completes, regardless of whether the ''body'' code executes successfully or not. This is also known as a ''code sandwich,'' and occurs in various other contexts,{{sfn|Elder|Jackson|Liblit|2008|p=3}} such as a temporary change of program state, or [[Tracing (software)|tracing]] entry and exit into a [[subroutine]]. However, resource management is the most commonly cited application.▼
Resource management seeks to control access in order to prevent both of these situations.
===Resource leak===
{{main|resource leak}}
▲Formally, resource management (preventing resource leaks) consists of ensuring that a resource is released if and only if it is successfully acquired. This general problem can be abstracted as "''before,'' ''body,'' and ''after''" code, which normally are executed in this order, with the condition that the ''after'' code is called if and only if the ''before'' code successfully completes, regardless of whether the ''body'' code executes successfully or not. This is also known as ''execute around''{{sfn|Beck|1997|pp=37–39}} or a ''code sandwich,'' and occurs in various other contexts,{{sfn|Elder|Jackson|Liblit|2008|p=3}} such as a temporary change of program state, or [[Tracing (software)|tracing]] entry and exit into a [[subroutine]]. However, resource management is the most commonly cited application. In [[aspect-oriented programming]], such execute around logic is a form of ''[[Advice (programming)|advice]]''.
In the terminology of [[control flow analysis]], resource release must [[postdominate]] successful resource acquisition;{{sfn|Elder|Jackson|Liblit|2008|p=2}} failure to ensure this is a bug, and a code path that violates this condition causes a resource leak. Resource leaks are often minor problems, generally not crashing the program, but instead causing some slowdown to the program or the overall system.{{sfn|Elder|Jackson|Liblit|2008|p=3}} However, they may cause crashes – either the program itself or other programs – due to ''resource exhaustion:'' if the system runs out of resources, acquisition requests fail. This can present a [[security bug]] if an attack can cause resource exhaustion. Resource leaks may happen under regular program flow – such as simply forgetting to release a resource – or only in exceptional circumstances, such as when a resource is not released if there is an exception in another part of the program. Resource leaks are very frequently caused by [[Structured programming#Early exit|early exit]] from a subroutine, either by a <code>return</code> statement, or an exception raised either by the subroutine itself, or a deeper subroutine that it calls. While resource release due to return statements can be handled by carefully releasing within the subroutine before the return, exceptions cannot be handled without some additional language facility that guarantees that release code is executed.
More subtly, successful resource acquisition must [[Dominator (graph theory)|dominate]] resource release, as otherwise the code will try to release a resource it has not acquired. The consequences of such an incorrect release range from being silently ignored to crashing the program or unpredictable behavior. These bugs generally manifest rarely, as they require resource allocation to first fail, which is generally an exceptional case. Further, the consequences may not be serious, as the program may already be crashing due to failure to acquire an essential resource. However, these can prevent recovery from the failure, or turn an orderly shutdown into a disorderly shutdown. This condition is generally ensured by first checking that the resource was successfully acquired before releasing it, either by having a boolean variable to record "successfully acquired" – which lacks atomicity if the resource is acquired but the flag variable fails to be updated, or conversely – or by the handle to the resource being a [[nullable type]], where "null" indicates "not successfully acquired", which ensures atomicity.
===Resource contention===
{{main|resource contention}}
In computer science, [https://www.techtarget.com/whatis/definition/resource-contention resource contention] refers to a conflict that arises when multiple entities attempt to access a shared resource, like random access memory, disk storage, cache memory, internal buses, or external network devices.
==Memory management==
{{main|Memory management}}
▲Memory can be treated as a resource, but [[memory management]] is usually considered separately, primarily because memory allocation and deallocation is significantly more frequent than acquisition and release of other resources, such as file handles. Memory managed by an ''external'' system has similarities to both (internal) memory management (since it is memory) and resource management (since it is managed by an external system). Examples include memory managed via native code and used from Java (via [[Java Native Interface]]); and objects in the [[Document Object Model]] (DOM), used from [[JavaScript]]. In both these cases, the [[Memory management|memory manager]] ([[Garbage collection (computer science)|garbage collector]]) of the [[runtime environment]] (virtual machine) is unable to manage the external memory (there is no shared memory management), and thus the external memory is treated as a resource, and managed analogously. However, cycles between systems (JavaScript referring to the DOM, referring back to JavaScript) can make management difficult or impossible.
==Lexical management and explicit management==
▲A key distinction in resource management within a program is between ''
==Basic techniques==
The basic approach to resource management is to acquire a resource, do something with it, then release it, yielding code of the form (illustrated with opening a file in [[Python (programming language)|Python]]):
<
▲ ...
</syntaxhighlight>
▲ f.close()
This is correct if the intervening <code>...</code> code does not contain an early exit (<code>return</code>), the language does not have exceptions, and <code>open</code> is guaranteed to succeed. However, it causes a resource leak if there is a return or exception, and causes an incorrect release of unacquired resource if <code>open</code> can fail.
Line 27 ⟶ 47:
The resource leak can be resolved in languages that support a <code>finally</code> construction (like Python) by placing the body in a <code>try</code> clause, and the release in a <code>finally</code> clause:
<
try:
finally:
...▼
</syntaxhighlight>
This ensures correct release even if there is a return within the body or an exception thrown. Further, note that the acquisition occurs ''before'' the <code>try</code> clause, ensuring that the <code>finally</code> clause is only executed if the <code>open</code> code succeeds (without throwing an exception), assuming that "no exception" means "success" (as is the case for <code>open</code> in Python). If resource acquisition can fail without throwing an exception, such as by returning a form of <code>null</code>, it must also be checked before release, such as:
<
try:
finally:
</syntaxhighlight>
While this ensures correct resource management, it fails to provide adjacency or encapsulation. In many languages there are mechanisms that provide encapsulation, such as the <code>with</code> statement in Python:
<
</syntaxhighlight>
The above techniques – unwind protection (<code>finally</code>) and some form of encapsulation – are the most common approach to resource management, found in various forms in [[C Sharp (programming language)|C#]], [[Common Lisp]], [[Java (programming language)|Java]], [[Python (programming language)|Python]], [[Ruby
== Approaches ==
=== Unwind protection ===
The most common approach to resource management across languages is to use unwind protection, which is called when execution exits a scope – by execution running off the end of the block, returning from within the block, or an exception being
An alternative, more imperative approach, is to write asynchronous code in [[direct style]]: acquire a resource, and then in the next line have a ''deferred'' release, which is called when the scope is exited – synchronous acquisition followed by asynchronous release. This originated in C++ as the ScopeGuard class, by [[Andrei Alexandrescu]] and Petru Marginean in 2000,
Line 78 ⟶ 95:
==== RAII ====
{{main
A natural approach is to make holding a resource be a [[class invariant]]: resources are acquired during object creation (specifically initialization), and released during object destruction (specifically finalization). This is known as [[Resource Acquisition Is Initialization]] (RAII), and ties resource management to [[object lifetime]], ensuring that live objects have all necessary resources. Other approaches do not make holding the resource a class invariant, and thus objects may not have necessary resources (because they've not been acquired yet, have already been released, or are being managed externally), resulting in errors such as trying to read from a closed file. This approach ties resource management to memory management (specifically object management), so if there are no memory leaks (no object leaks), there are no [[resource leak]]s. RAII works naturally for heap-managed resources, not only stack-managed resources, and is composable: resources held by objects in arbitrarily complicated relationships (a complicated [[object graph]]) are released transparently simply by object destruction (so long as this is done properly!).
Line 95 ⟶ 112:
Both are commonly found. For example, in the [[Java Class Library]], <code>[https://docs.oracle.com/javase/8/docs/api/java/io/Reader.html#close-- Reader#close()]</code> closes the underlying stream, and these can be chained. For example, a <code>[https://docs.oracle.com/javase/8/docs/api/java/io/BufferedReader.html BufferedReader]</code> may contain a <code>[https://docs.oracle.com/javase/8/docs/api/java/io/InputStreamReader.html InputStreamReader]</code>, which in turn contains a <code>[https://docs.oracle.com/javase/8/docs/api/java/io/FileInputStream.html FileInputStream]</code>, and calling <code>close</code> on the <code>BufferedReader</code> in turn closes the <code>InputStreamReader</code>, which in turn closes the <code>FileInputStream</code>, which in turn releases the system file resource. Indeed, the object that directly uses the resource can even be anonymous, thanks to encapsulation:
<
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(fileName)))) {
// Use reader.
}
// reader is closed when the try-with-resources block is exited, which closes each of the contained objects in sequence.
</syntaxhighlight>
However, it is also possible to manage only the object that directly uses the resource, and not use resource management on wrapper objects:
<
try (FileInputStream stream = new FileInputStream(fileName)))) {
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
Line 110 ⟶ 127:
// stream is closed when the try-with-resources block is exited.
// reader is no longer usable after stream is closed, but so long as it does not escape the block, this is not a problem.
</syntaxhighlight>
By contrast, in Python, a [https://docs.python.org/
<
with open(filename) as f:
r = csv.reader(f)
Line 120 ⟶ 137:
# f is closed when the with-statement is exited, and can no longer be used.
# Nothing is done to r, but the underlying f is closed, so r cannot be used either.
</syntaxhighlight>
In [[.NET Framework|.NET]], convention is to only have direct user of resources be responsible: "You should implement IDisposable only if your type uses unmanaged resources directly."<ref name="idisposable">{{cite web |url=https://msdn.microsoft.com/en-us/library/system.idisposable(v=vs.110).aspx |title=IDisposable Interface |accessdate=2016-04-03}}</ref>
In case of a more complicated [[object graph]], such as multiple objects sharing a resource, or cycles between objects that hold resources, proper resource management can be quite complicated, and exactly the same issues arise as in object finalization (via destructors or finalizers); for example, the [[lapsed listener problem]] can occur and cause resource leaks if using the
Line 141 ⟶ 158:
==References==
{{reflist}}
{{refbegin}}
* {{cite
| first = Kent
| last = Beck
| authorlink = Kent Beck
| year = 1997
| title = Smalltalk Best Practice Patterns
| publisher = Prentice Hall
| isbn = 978-0134769042
}}
* {{cite tech report
|url = http://research.cs.wisc.edu/techreports/2008/TR1647.pdf
|first1 = Matt |last1 = Elder
Line 160 ⟶ 187:
* [http://c2.com/cgi/wiki?DeterministicResourceManagement Deterministic Resource Management], ''[[WikiWikiWeb]]''
[[Category:Articles with example Python (programming language) code]]
[[Category:Programming constructs]]
|