Java Program Is there a memory vulnerability? Of course. In contrast to popular ideas, memory management still needs to be considered in Java programming. In this article, you will learn about what causes memory vulnerabilities and when to pay attention to these vulnerabilities. You have the opportunity to solve the vulnerability in your own project.
How are memory vulnerabilities in Java programs displayed?
Most programmers know that Programming Language A major benefit is that they no longer have to worry about memory allocation and release issues. You only need to create objects. When applications no longer need these objects, Java deletes these objects through a mechanism called "Garbage Collection. This processing means that Java has solved the annoying problem of other programming languages-the terrible memory vulnerability. Is that true?
Before further discussion, let's first review how garbage collection works. The purpose of the garbage collector is to find objects that are no longer needed by the application and delete them when they are no longer accessed or referenced. The garbage collector traverses all referenced nodes from the root node (the classes that always exist throughout the lifecycle of the Java application) and clears them. While traversing these nodes, it tracks which objects are currently being referenced. As long as any class is no longer referenced, it meets the garbage collection conditions. After deleting these objects, you can return the memory resources they occupy to the Java Virtual Machine (JVM ).
So it does. Java Code Programmers are not required to manage and clear the memory. It automatically collects unwanted objects. However, it is important to note that an object is counted as useless only when it is no longer referenced. Figure 1 illustrates this concept.
Figure 1. useless but still referenced object
The preceding two classes have different lifecycles during Java application execution. Class A is first instantiated and will exist for a long period of time or throughout the lifetime of the program. At a time, Class B is created, and Class A adds a reference to the newly created class. Now we assume that Class B is a user interface widget that is displayed or even removed by the user. If Class A's reference to Class B is not cleared, Class B still exists and occupies memory even after the next garbage collection cycle is executed.
When should I pay attention to memory vulnerabilities?
If your program sends a java. Lang. outofmemoryerror after a period of execution, the memory vulnerability is a serious suspect. In addition to this obvious situation, when should we pay attention to memory vulnerabilities? Perfectionist programmers will certainly answer that they should find and correct all memory vulnerabilities. However, before reaching this conclusion, there are several aspects to consider, including program survival and vulnerability size.
It is entirely possible that the Garbage Collector may never run during the lifetime of the application. It is not guaranteed when and whether the JVM will call the Garbage Collector-even if the program explicitly calls system. GC. Generally, when the current available memory can meet the program's memory requirements, the JVM will not automatically run the garbage collector. When the available memory cannot meet the requirements, JVM will first try to release more available memory by calling garbage collection. If this attempt still does not allow enough resources to be released, the JVM will obtain more memory from the operating system until the maximum allowed limit is reached.
For example, consider a small Java application that displays some simple user interface elements used to modify the configuration and has a memory vulnerability. It is likely that the spam collector will not be called when the application is closed, because JVM may have enough memory to create all the objects required by the program, and there will be little available memory later. Therefore, in this case, even if some "dead" objects occupy memory during program execution, it is actually useless.
If the Java code being developed needs to run on the server 24 hours a day, the impact of memory vulnerabilities here is much greater than that of our configuration utility. In some codes that require long running, even the smallest vulnerability may cause the JVM to exhaust all available memory.
On the contrary, even if the program has a short lifetime, if there is any Java code that allocates a large number of temporary objects (or a number of objects that consume a large amount of memory, in addition, when these objects are no longer needed and their references are not canceled, the memory limit may still be reached.
The last case is that memory vulnerabilities do not matter. We should not think that Java memory vulnerabilities are as dangerous as vulnerabilities in other languages (such as C ++). In those languages, memory will be lost and will never be returned to the operating system. In Java applications, we attach unnecessary objects to the memory resources provided by the operating system for JVM. Therefore, theoretically, once the Java application and its JVM are closed, all the memory allocated will be returned to the operating system.
Determine whether the application has a memory Vulnerability
To check whether a Java application running on Windows NT has a memory vulnerability, you may try to observe the memory settings in task manager when the application is running. However, after observing several running Java applications, you will find that they occupy much more memory than local applications. Some Java projects I have done require 10 to 20 mb system memory to start. The Windows Explorer program provided by the operating system only requires about 5 MB of memory.
In terms of memory usage of Java applications, it should be noted that this typical program occupies more and more system memory when running in the ibm jdk 1.1.8 JVM. It seems that it does not return memory to the system until it allocates a lot of physical memory. Are these symptoms of memory vulnerabilities?
To understand the reason, we must be familiar with how JVM uses the system memory as its heap. When you run java.exe, you can use certain options to control the starting size and maximum size of the garbage collection heap (represented by-MS and-mx respectively ). The default start time of Sun JDK 1.1.8 is 1 MB, and the default maximum value is 16 Mb. The default maximum size of ibm jdk 1.1.8 is half of the total physical memory size of the system. These memory settings have a direct impact on the operations performed by the JVM when the memory is exhausted. The JVM may continue to increase the heap without waiting for the completion of a garbage collection cycle.
In this way, we need a better tool than the task monitoring utility to find and ultimately eliminate memory vulnerabilities. When you try to debug a memory vulnerability, the memory debugging program (see resource reference) may come in handy. These programs usually display the number of objects in the heap, the number of instances of each object, and the memory occupied by these objects. In addition, they may also provide useful views that display references and references for each object so that you can track the source of memory vulnerabilities.
The following describes how to use sitraka software's jprobedebugger to detect and remove memory vulnerabilities, so that you can understand the deployment methods of these tools and the process required to successfully remove the vulnerabilities.
Memory vulnerability example
This example focuses on a problem. Our Department was developing a commercial release software, which is a Java JDK 1.1.8 application, it took a tester several hours to study the program before it finally came to the fore. The basic code and package of this Java application are developed by several different development teams at different times. I guess the unexpected memory vulnerabilities in this application are caused by programmers who do not really understand the code developed by others.
The Java code we are discussing allows users to create applications for the palm personal digital assistant without having to write any local Palm OS code. By using the graphical user interface, you can create a form, add controls to the form, and connect the events of these controls to create a palm application. The tester found that, as forms and controls are constantly created and deleted, the Java application will eventually run out of memory. Developers did not detect this problem because their machines have more physical memory.
To study this problem, I used Jprobe to determine where something went wrong. Despite the use of powerful tools and memory snapshots provided by Jprobe, the study is still a tedious and repetitive process. First, identify the cause of memory vulnerabilities and then modify the code, finally, we have to test the results.
Jprobe provides several options to control the information actually recorded during debugging. After several tests, I decided that the most effective way to obtain the required information was to disable performance data collection and focus on the captured heap data. Jprobe provides a view called runtime heap summary, which shows the amount of heap memory occupied by Java applications running with time. It also provides a toolbar button that forces the JVM to perform garbage collection if necessary. This function is useful if you try to figure out whether the Java application will be collected as garbage when a given class instance is no longer needed. Figure 2 shows the change of the Heap Storage in use over time.
Figure 2. runtime heap summary in heap usage chart, the blue part indicates the allocated heap space. After the Java program is started and stabilized, I force the Garbage Collector to run. In the figure, the Blue Line on the left (this line indicates that a checkpoint is inserted) drops sharply. Then, I added four forms, deleted them, and called the Garbage Collector again. When the program returns the initial state of only one visible form, the blue area after the checkpoint is higher than the blue area before the checkpoint, which indicates that there may be a memory vulnerability. I checked the instance summary and confirmed that there was indeed a vulnerability because the instance summary indicated that the count of the formframe class (which is the main user interface class of the form) increased by 4 after the checkpoint.
Find the reason
To remove the issues reported by testers, the first step I took was to find several simple and reproducible test cases. In this example, I found that you only need to add a form, delete it, and then force garbage collection, as a result, many class instances associated with the deleted form remain active. This problem is obvious in the instance summary view of Jprobe. This view counts the number of instances of each Java class in the heap.
To identify references that make it impossible for the garbage collector to complete its work normally, I use Jprobe's reference graph (3) to determine which classes still reference formframe classes that are not currently deleted. This process is one of the most complex processes when debugging this problem, because I found that many different objects still reference this useless object. The trial and error process used to find out which quote actually caused this problem is quite time-consuming.
In this example, a root class (the class marked in red in the upper left corner) is the source of the problem. The class highlighted in blue on the right is located in the path tracked from the original formframe class.
Figure 3. Tracking memory vulnerabilities in reference charts
In this example, the culprit is to include a static hashtable font manager class. Through the reverse tracing reference list, I found that the root node is a static hashtable used to store the font of each form. Each form can be individually zoomed in or out, so this hashtable contains a vector containing all the fonts of a given form. When the size of the form changes, the font vector is extracted and the appropriate scaling factor is applied to the font size.
The problem with this font manager class is that although the program saves the font vector to this hashtable when creating a form, it does not provide the code to delete the vector when deleting the form. Therefore, this static hashtable (which exists throughout the lifetime of the Application) will never delete those keys that reference each form. As a result, the form and all its associated classes are idle in the memory.
Correction
A simple solution to this problem is to add a method to the font manager class so that you can call the remove () method of hashtable with the appropriate key as a parameter when deleting a form. The removekeyfromhashtables () method is as follows:
Public void removekeyfromhashtables (graphcanvas graph ){
If (graph! = NULL ){
Viewfonttable. Remove (graph); // deletes the key in hashtable.
// Prevent memory Vulnerabilities
}
}
Then, I added a call to this method in the formframe class. Formframe actually uses the swing internal framework to implement the form user interface, so I will add the call to the font manager to the method called when the internal framework is completely closed, as shown below:
/**
* Called when dispose formframe is removed. Clear references to prevent memory vulnerabilities.
*/
Public void internalframeclosed (internalframeevent e ){
Fontmanager. Get (). removekeyfromhashtables (canvas );
Canvas = NULL;
Setmediatopicon (null );
}
After these modifications, I use the debugger to verify that when the same test case is executed, the number of objects associated with the deleted form decreases.
Prevent memory Vulnerabilities
You can observe some common problems to prevent memory vulnerabilities. Collection classes (such as hashtable and vector) are often prone to memory vulnerabilities. This is especially true when this class is declared with the static keyword and exists throughout the entire life cycle of the application.
Another common problem is that you register a class as an event listener without revoking the registration when you no longer need this class. In addition, you often need to set the class member variables pointing to other classes to null when appropriate.
Summary
Finding out the cause of memory vulnerabilities may be a tedious process, not to mention the need for dedicated debugging tools. However, once you are familiar with these tools and the search mode when the trace object is referenced, you can find the memory vulnerability. In addition, you will find some valuable skills that will not only help save project costs, it also helps you understand which encoding methods should be avoided in future projects to prevent memory vulnerabilities.
Reference resources
Before you start searching for memory vulnerabilities, familiarize yourself with one of the following debuggers:
● Sitraka software's Jprobe Profiler withmemory Debugger
● Optimizeit Java performance profiler of intuitive system
● Paul Moeller's win32java heap inspector
● Jinsight on the IBM alphaWorks website
Jinsight: A Tool for visualizing the execution of Java programs (developerworks, November 1999) details how this utility helps you analyze performance and debug code.
Note: The project discussed in this article is completed with JDK 1.1.8, but JDK 1.2 introduces a new package, java. Lang. Ref, which can interact with the garbage collector. In addition, JDK 1.2 introduces a java. util. weakhashmap class, which can be used to replace the traditional Java. util. hashtable class. This class does not prevent the garbage collector from recycling key objects. Java hotspot client VM is introduced in Solaris, Linux, and Microsoft Windows versions of JDK 1.3. This VM has a new and improved garbage collector.
Author Profile
Jim Patrick is a consultant programmer at IBM Pervasive Computing division. He began programming in Java in 1996. Please passPatrickj@us.ibm.comContact Jim.