English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Detailed Explanation of Java Garbage Collection Mechanism (compiled by Power Node Java Academy)

1. The significance of garbage collection

  In C++In, the memory occupied by objects is always occupied before the program ends, and it cannot be allocated to other objects before it is explicitly released; in Java, when there are no object references pointing to the memory originally allocated to an object, the memory becomes garbage. A system-level thread in JVM will automatically release this memory block. Garbage collection means that objects that are no longer needed by the program are "useless information", which will be discarded. When an object is no longer referenced, the memory recycles the space it occupies, so that the space can be used by new objects later. In fact, in addition to releasing unused objects, garbage collection can also clear memory fragments. Due to the creation of objects and the release of discarded objects by garbage collectors, memory fragments may occur. Fragments are idle memory holes between memory blocks allocated to objects. Fragmentation reclaims the occupied heap memory to one end of the heap, and JVM allocates the reclaimed memory to new objects.

  Garbage collection can automatically release memory space, lighten the burden of programming, and give the Java virtual machine some advantages. First, it can improve programming efficiency. Without garbage collection mechanisms, it may take a lot of time to solve a difficult storage problem. When programming in Java, the garbage collection mechanism can greatly shorten the time. Secondly, it protects the integrity of the program, and garbage collection is an important part of the Java language security strategy.

  One potential drawback of garbage collection is that its overhead affects program performance. The Java virtual machine must track useful objects in the running program and eventually release unused objects. This process requires processor time. Secondly, the incompleteness of garbage collection algorithms, some garbage collection algorithms adopted earlier cannot guarantee100% of the discarded memory is collected. Of course, with the continuous improvement of garbage collection algorithms and the continuous increase in the efficiency of hardware and software, these problems can be easily solved.

2. Analysis of garbage collection algorithms

  The Java language specification does not explicitly state which garbage collection algorithm JVM uses, but any garbage collection algorithm generally needs to do2Basic things:

(1) Discover unused information objects;

(2) Recycle the memory space occupied by unused objects, making it available for the program to use again.

  Most garbage collection algorithms use the concept of the root set; the root set refers to the collection of reference variables that can be accessed by the Java program currently being executed (including local variables, parameters, and class variables). The program can use reference variables to access the properties of objects and invoke methods of objects. The first step in garbage collection is to determine which objects are reachable and which are not from the root, with all objects reachable from the root set being active objects and cannot be collected as garbage, which also includes objects reachable indirectly from the root set. Objects that are not reachable from the root set through any path meet the conditions for garbage collection and should be collected. The following introduces several commonly used algorithms.

  2.1The reference counting method (Reference Counting Collector)

  Reference counting is the only garbage collection method that does not use a root set. This algorithm uses a reference counter to distinguish between live objects and objects that are no longer used. Generally, each object in the heap corresponds to a reference counter. When an object is created and assigned to a variable for the first time, the reference counter is set to1. Each time an object is assigned to any variable, the reference counter is incremented1When an object goes out of scope (the object is discarded and no longer used), the reference counter is decremented1. Once the reference counter is 0, the object meets the condition for garbage collection.

  Garbage collectors based on reference counting run faster and do not interrupt the program execution for a long time, making them suitable for programs that must run in real-time. However, the reference counter increases the overhead of program execution because each time an object is assigned to a new variable, the counter is incremented1and the counter is decremented each time an existing object goes out of scope1.

  2.2. Tracing algorithm (Tracing Collector)

  The tracing algorithm was proposed to solve the problem of reference counting, and it uses the concept of the root set. Garbage collectors based on the tracing algorithm start from the root set, identify which objects are reachable and which objects are not reachable, and mark the reachable objects in some way, such as setting one or more bits for each reachable object. During the scanning and identification process, garbage collection based on the tracing algorithm is also known as marking and sweeping (mark-and-sweep garbage collector.

  2.3. Compacting algorithm (Compacting Collector)

  To solve the problem of heap fragmentation, garbage collection based on tracing absorbs the idea of the Compacting algorithm, and during the clearing process, the algorithm moves all objects to one end of the heap, and the other end of the heap becomes an adjacent free memory area. The collector updates all references to the objects it moves, so that these references can identify the original objects at the new location. In the implementation of collectors based on the Compacting algorithm, it is generally necessary to increase handles and handle tables.

  2.4. Copying algorithm (Coping Collector)

  The proposal of this algorithm is to overcome the overhead of handles and solve the problem of heap fragmentation in garbage collection. It starts by dividing the heap into one object area and multiple free areas, and the program allocates space for objects from the object area. When the object is full, garbage collection based on the copying algorithm scans the active objects from the root set, and copies each active object to a free area (so that there are no free intervals between the memory occupied by active objects), so that the free area becomes the object area, and the original object area becomes the free area. The program allocates memory in the new object area.

  A typical garbage collection based on the copying algorithm is stop-and-The copy algorithm divides the heap into object areas and free area regions, and the program pauses during the switching process between object areas and free areas.

  2.5. Generation algorithm (Generational Collector)

  stop-and-One of the defects of the copy garbage collector is that the collector must copy all active objects, which increases the waiting time of the program. This is why the coping algorithm is inefficient. In program design, there is such a rule: most objects exist for a short period of time, while a few exist for a long time. Therefore, the generation algorithm divides the heap into two or more, with each sub-heap acting as a generation (generation) of objects. Since most objects exist for a short period of time, as the program discards unused objects, the garbage collector will collect these objects from the youngest sub-heap. After the garbage collector runs in a generational manner, the objects that survived the last run are moved to the next highest generation sub-heap. Since the sub-heap of the old generation is not frequently collected, time is saved.

  2.6. adaptive algorithm (Adaptive Collector)

  In certain cases, some garbage collection algorithms may be superior to others. Garbage collectors based on Adaptive algorithms monitor the current usage of the heap and select an appropriate garbage collection algorithm.

3. The System.gc() method

       Command line parameters to inspect the operation of the garbage collector

  Using System.gc() can request garbage collection from Java regardless of which garbage collection algorithm JVM is using. There is a parameter in the command line-verbosegc can view the heap memory usage of Java, its format is as follows:

  java -verbosegc classfile

  Let's take an example:

class TestGC 
{ 
 public static void main(String[] args) 
 { 
   new TestGC(); 
   System.gc(); 
   System.runFinalization(); 
   } 
} 

 In this example, a new object is created, and since it is not used, the object quickly becomes unreachable. After the program is compiled, the command is executed:

     java -verbosegc TestGC, the result is as follows:

  [Full GC 168K->97K(1984K), 0.0253873 secs]

  The machine environment is Windows 2000 + JDK1.3.1, the data before and after the arrow168K and97K respectively represent the memory capacity used by all surviving objects before and after garbage collection GC, indicating that there is168K-97K=71K's object capacity is reclaimed, the data in the brackets1984K represents the total capacity of the heap memory, and the time required to collect is 0.0253873seconds (this time varies each time it is executed).

      It should be noted that calling System.gc() is merely a request (recommendation). After JVM receives this message, it does not immediately perform garbage collection, but only weights several garbage collection algorithms to make garbage collection operations more likely to occur, or to occur earlier, or to reclaim more.

4. finalize() method

      Before the JVM garbage collector collects an object, it is generally required that the program call the appropriate method to release resources. However, in the absence of explicit resource release, JAVA provides a default mechanism to terminate the object and release resources, which is the finalize() method. Its prototype is:

  protected void finalize() throws Throwable

  After the finalize() method returns, the object disappears and garbage collection begins to execute. The throws Throwable in the prototype indicates that it can throw any type of exception.

  The reason for using finalize() is that there are special cases that cannot be handled by the garbage collector. Assume that your object (not using the new method) has obtained a 'special' memory area. Since the garbage collector only knows those memory spaces allocated explicitly by new, it does not know how to release this 'special' memory area. In this case, JAVA allows the definition of a finalize() method in the class.

      special areas such as:

1Due to the fact that similar methods to C language may be used when allocating memory instead of the usual new method in JAVA. This situation mainly occurs in native methods, such as native methods calling C/C++method series to allocate storage space, but unless the free() function is called, these memory spaces will not be released, which may cause memory leaks. However, since the free() method is in C/C++functions, so local methods can be used in finalize() to release these 'special' memory spaces.

2Or open file resources, which are not within the scope of garbage collector's collection.

      In other words, the main function of finalize() is to release some memory space allocated by other methods and to perform some cleanup work. Because there is no function like 'destructor' or a similar concept in JAVA, when you need to do some similar cleanup work, you must manually create a regular method to perform the cleanup, which is to override the finalize() method in the Object class. For example, suppose an object will draw itself to the screen during the creation process. If it is not explicitly erased from the screen, it may never be cleaned up. If an erasing function is added to finalize(), when the GC is working and finalize() is called, the image will be erased. If the GC does not occur, then the image will be kept forever.

     

  ++++++If an object is created as a local object, such as creating it on the stack (this is not possible in Java, as Java always creates objects on the heap), the cleanup or destruction work will be done at the end of the scope represented by the 'end curly brace' where the object was created. If the object is created using 'new' (similar to Java), then all objects will be destroyed (cleared). Or, in other words, all objects 'should' be destroyed. If the concept of 'destructor' is slightly conflicting in C++when the 'delete' command is called (Java does not have this command), the corresponding destructor will be called. If the programmer forgets, the destructor will never be called, and in the end, we will have a memory 'leak', as well as other parts of the object that will never be cleared.

  On the contrary, Java does not allow us to create local (local) objects--No matter what, you should always use 'new'. But in Java, there is no 'delete' command to free up objects because the garbage collector helps us automatically release storage space. So if we take a relatively simplified position, we can say that it is precisely because of the existence of the garbage collection mechanism that Java does not have a destructor. However, as we delve deeper into our studies, we will come to know that the existence of the garbage collector cannot completely eliminate the need for a destructor, or cannot eliminate the need for the mechanism represented by the destructor (the reason is explained in the next paragraph. In addition, the finalize() function is called when the garbage collector is preparing to release the storage space occupied by the object, and it should never be called directly, so it should be avoided as much as possible). If you want to perform some form of cleanup other than releasing storage space, you still need to call a method in Java. It is equivalent to C++The destructor is just not as convenient as the latter.

      In C++All objects in the middle must be destroyed using delete(), while Java objects are not always collected by the garbage collector. In another word, 1 An object may not be garbage collected,2 Garbage collection is not equal to 'deconstruction',3 Garbage collection is only related to memory. That is, is it necessary to release other objects contained in the object in the finalize() method if the object is no longer used? No. Because regardless of how an object is created, the garbage collector is responsible for releasing the memory occupied by those objects.

5. Conditions for triggering the full GC (Garbage Collector)

  The JVM performs a minor GC frequently, but because this type of GC takes a very short time, it has little impact on the system. What is more worth paying attention to is the trigger conditions for the full GC, as they have a significant impact on the system. In summary, there are two conditions that will trigger the full GC:

  1When the application is idle, that is, when there are no application threads running, GC will be called. Because GC is performed in the thread with the lowest priority, so when the application is busy, the GC thread will not be called, but there are exceptions to this.

  2When the Java heap memory is insufficient, GC will be called. When application threads are running and creating new objects during the execution process, if the memory space is insufficient at this time, the JVM will forcibly call the GC thread to recycle memory for new allocation. If the memory allocation requirements are still not met after one GC, the JVM will perform two more GCs as further attempts. If it still cannot meet the requirements, the JVM will report the error 'out of memory', and the Java application will stop.

  Since whether to perform a full GC is determined by the JVM based on the system environment, and the system environment is constantly changing, the operation of the full GC is uncertain, and it is impossible to predict when it will necessarily occur. However, it can be determined that for a long-running application, its full GC is repeated.

6Measures to reduce GC overhead

  According to the mechanism of the above GC, the operation of the program directly affects the change of the system environment, thereby affecting the trigger of GC. If the design and coding are not tailored to the characteristics of GC, a series of negative impacts such as memory residency will occur. To avoid these impacts, the basic principle is to minimize garbage and reduce the overhead in the GC process.

Specific measures include the following aspects:

  )(1Do not explicitly call System.gc()

  This function suggests that the JVM perform a full GC, although it is a suggestion rather than a requirement, it often triggers a full GC, thereby increasing the frequency of full GCs, which is to say, it increases the number of intermittent pauses.

  )(2Minimize the use of temporary objects

  Temporary objects become garbage after exiting a function call, so reducing the use of temporary variables is equivalent to reducing the generation of garbage, thereby extending the time until the second trigger condition mentioned above occurs, and reducing the opportunity for the main GC to occur.

  )(3)对象不用时最好显式置为Null

  一般而言,为Null的对象都会被作为垃圾处理,所以将不用的对象显式地设为Null,有利于GC收集器判定垃圾,从而提高了GC的效率。

  )(4)尽量使用StringBuffer,而不用String来累加字符串

  由于String是固定长的字符串对象,累加String对象时,并非在一个String对象中扩增,而是重新创建新的String对象,如Str5=Str1+Str2+Str3+Str4,这条语句执行过程中会产生多个垃圾对象,因为对次作"+在进行"操作时都必须创建新的String对象,但这些过渡对象对系统来说是没有实际意义的,只会增加更多的垃圾。避免这种情况可以改用StringBuffer来累加字符串,因为StringBuffer是可变长的,它在原有基础上进行扩增,不会产生中间对象。

  )(5)能用基本类型如Int,Long,就不用Integer,Long对象

  )基本类型变量占用的内存资源比相应对象占用的少得多,如果没有必要,最好使用基本变量。

  )(6)尽量少用静态对象变量

  静态变量属于全局变量,不会被GC回收,它们会一直占用内存。

  )(7)分散对象创建或删除的时间

  短时间内集中创建大量新对象,特别是大对象,会导致突然需要大量内存,JVM在面临这种情况时,只能进行主要GC,以回收内存或整合内存碎片,从而增加主要GC的频率。集中删除对象,道理也是一样的。它使得突然出现了大量的垃圾对象,空闲空间必然减少,从而大大增加了下一次创建新对象时强制主要GC的机会。 

      以下例子向大家展示了垃圾收集经历的过程,并对前面的陈述进行了总结。  

class Chair { 
  static boolean gcrun = false; 
  static boolean f = false; 
  static int created = 0; 
  static int finalized = 0; 
  int i; 
  Chair() { 
   i = ++created; 
   if(created == 47) 
     System.out.println("创建 ") 47"); 
  } 
  protected void finalize() { 
   if(!gcrun) { 
     gcrun = true; 
     System.out.println("开始进行最终化操作后 ") + created + "Chairs have been created" 
   } 
   if(i == 47) { 
     System.out.println("Finalizing Chair #"47, " +"Setting flag to stop Chair creation" 
     f = true; 
   } 
   finalized++; 
   if(finalized >= created) 
     System.out.println("All ") + finalized + " finalized" 
  } 
} 
public class Garbage { 
  public static void main(String[] args) { 
  if(args.length == 0) { 
    System.err.println("Usage:"); /n + "<a href="http://lib.csdn.net/base/java" rel="external nofollow" class='replace_word' title="[#1#]" target='_blank' style='color:#df3434; font-weight:bold;">Java </a>Garbage before/n or:/n + "java Garbage after" 
    return; 
  } 
  while(!Chair.f) { 
    new Chair(); 
    new String("To take up space"); 
  } 
  System.out.println("After all Chairs have been created:");/n + "total created = " + Chair.created + 
  "total finalized = " + Chair.finalized) 
  if(args[0].equals("before")) { 
    System.out.println("gc():"); 
    System.gc(); 
    System.out.println("runFinalization():"); 
    System.runFinalization(); 
  } 
  System.out.println("bye!"); 
  if(args[0].equals("after")) 
    System.runFinalizersOnExit(true); 
  } 
} 
  

This program creates many Chair objects and stops creating Chair at some point after the garbage collector starts running. Since the garbage collector may run at any time, we cannot accurately know when it starts. Therefore, the program uses a mark named gcrun to indicate whether the garbage collector has started. Using the second mark f, Chair can inform main() that it should stop object generation. These two marks are set within the finalize() method, which is called during garbage collection. The other two static variables--created and finalized--are used to track the number of objects created and the number of objects for which the garbage collector has completed finalization work. Finally, each Chair has its own (non-static) int i, so it can track and understand its specific number. The number is47After the Chair's finalization work is completed, the mark is set to true, and the final creation process of the Chair object is terminated.

7. Some supplementary points about garbage collection

  From the above explanations, it can be found that garbage collection has the following characteristics:

  (1The unpredictability of when garbage collection occurs: Due to the implementation of different garbage collection algorithms and the adoption of different collection mechanisms, it may occur at regular intervals, when there is system idle CPU resources, or when memory consumption reaches its limit, just like the original garbage collection. This is related to the choice of garbage collector and specific settings.

  (2The accuracy of garbage collection:

主要包括2 Aspects:

(a) Garbage collectors can accurately mark living objects;

(b) Garbage collectors can accurately locate the reference relationships between objects. The former is a prerequisite for completely recycling all discarded objects; otherwise, memory leaks may occur. The latter is a necessary condition for implementing algorithms such as compaction and copying. All unreachable objects can be reliably recycled, and all objects can be reallocated, allowing for object copying and consolidation of object memory, thereby effectively preventing memory fragmentation.

  (3There are many different garbage collectors, each with its own algorithm and performance characteristics. Some stop the application's execution when garbage collection begins, while others allow the application's threads to run. There are also those that allow multi-threaded garbage collection at the same time.

  (4The implementation of garbage collection is closely related to the specific JVM and the JVM's memory model. Different JVMs may adopt different garbage collection methods, while the JVM's memory model determines which types of garbage collection can be used by the JVM. Currently, the memory systems of the HotSpot series JVMs all adopt advanced object-oriented framework design, which allows this series of JVMs to use the most advanced garbage collection methods.

  (5)With the development of technology, modern garbage collection technology provides many optional garbage collectors, and different parameters can be set when configuring each collector, which makes it possible to obtain the optimal application performance according to different application environments.

  In view of the above characteristics, we should pay attention to when using them:

  (1)Do not try to assume the time of garbage collection. Everything is unknown. For example, a temporary object in a method becomes an unused object after the method call is completed, and its memory can be released at this time.

  (2)Java provides some classes related to garbage collection and provides a method to force garbage collection.--Call System.gc(), but this is also an uncertain method. Java does not guarantee that the garbage collection will start every time this method is called. It just sends an application to JVM, and whether the garbage collection will actually be executed is all unknown.

  (3)Choose a garbage collector suitable for yourself. Generally, if the system does not have special and strict performance requirements, you can use the default option of JVM. Otherwise, consider using targeted garbage collectors, such as incremental collectors are more suitable for systems with high real-time requirements. If the system has high configuration and more idle resources, you can consider using parallel marking/Clear the collector.

  (4)The key and difficult-to-grasp issue is memory leakage. Good programming habits and rigorous programming attitude are always the most important. Do not let a small mistake of yours lead to a big hole in memory.

  (5)Release the references of unused objects as soon as possible. Most programmers, when using temporary variables, set the reference variable to null automatically after exiting the activity domain (scope), hinting to the garbage collector to collect the object. It is also necessary to pay attention to whether the referenced object is being monitored. If so, the monitor should be removed before assigning the empty value.

The above-mentioned is the Java garbage collection mechanism introduced by the editor for everyone (collected by the Java Academy of DynaNode), hoping it will be helpful to everyone. If you have any questions, please leave a message, and the editor will reply to everyone in time. At the same time, I would also like to express my heartfelt thanks to everyone for their support of the Naoan tutorial website!

You May Also Like