This article is reprinted in an article on the internet, after watching the feeling good, benefited a lot, so here to share to everyone.
Original:
What are the tips for keeping GC low cost?
The JAVA9,G1 ("garbage first") garbage collector, which is about to be released with repeated delays, will be the default garbage collector for the HotSpot virtual machine. From the serial garbage collector
To the CMS collector, the JVM has witnessed many GC implementations, and G1 will become its next-generation garbage collector.
With the development of the garbage collector, each generation of GC compared with the previous generation, has brought great progress and improvement. Compared to the serial GC, the parallel GC allows the garbage collector to
Multi-threaded way of working, taking full advantage of the computing power of multicore computers. The CMS ("Concurrent mark-sweep") collector, compared to the parallel GC, divides the recycling process into multiple
Stage, the collection work can be completed concurrently while the application thread is running, greatly improving the frequent execution of the "Stop-the-world" situation. G1 for JVMs with a large heap of memory
Show better performance and have a better predictable and unified pause process.
Tip #1: Predicting the capacity of a collection
All standard Java collections, including custom and extended implementations (such as Trove and Google's guava), use arrays (native data types or object-based types) at the bottom. Because
Once an array is assigned, its size is immutable, so when adding elements to the collection, most of the time it will result in the need to reapply for a new large-capacity array to replace the old array (referred to as the bottom of the collection
Implements the array to use). Even if the size of the set initialization is not provided, most implementations of the collection try to optimize the handling of the reassigned array and minimize its overhead. However, in the construction set
The best result is the size you provide when you close it. Let's analyze the following code as a simple example:
public static list Reverse (List < extends T > list) { list result = new ArrayList (); for (int i = List.size ()-1; i > = 0; i--) { Result.add (List.get (i)); } return result;}
This method is allocates a new array, then fills it up with the items from the another list, only in reverse order. This method assigns a new array and then uses the
Another list element fills the array, but the order of the elements changes.
This approach can be costly in performance, and its optimized point is to add elements to the new list in this line of code. As you add elements every time, the list needs to ensure that its underlying array
Have enough space to accommodate the new element. If there is an idle position, then simply store the new element in the next free slot. If not, a new underlying array is assigned,
Copy the old array contents into the new array, and then add the new elements. This causes the array to be allocated multiple times, and those remaining old arrays are eventually reclaimed by the GC.
We can avoid these extra allocations by letting the underlying array know how many elements it will store when constructing the collection.
public static list Reverse (List < extends T > list) { list result = new ArrayList (List.size ()); for (int i = List.size ()-1; i > = 0; i--) { Result.add (List.get (i)); } return result; }
The above code specifies enough space to store list.size () elements through the ArrayList constructor, which completes the execution of the assignment at initialization, which means that the list is in the process of iterating
There is no need to allocate memory again. The Guava collection class is further, allowing the initialization of the collection to explicitly specify the number of expected elements or to specify a forecast value.
List result = Lists.newarraylistwithcapacity (List.size ()); List result = Lists.newarraylistwithexpectedsize (List.size ());
In the above code, the former is used to know exactly how many elements the collection will store, and the latter's allocation takes into account the error estimate.
Tip #2: Working directly with Data flow
The following code is very common when working with data streams, such as reading data from a file or downloading data from a network:
byte[] FileData = Readfiletobytearray (New File ("MyFile.txt"));
The
resulting byte array may be parsed by an XML document, a JSON object, or a protocol buffer message, as well as some common optional options.
When dealing with large files or the size of a file cannot be predicted, the above approach is unwise, because when the JVM cannot allocate a buffer to process the real file, it causes
Outofmemeoryerrors. Even if the size of the data is manageable, when it comes to garbage collection, using the above pattern can still cause significant overhead because it allocates a very large area in the heap
To store file data. A better way to do this is to use the appropriate inputstream (for example, in this case, using FileInputStream) to pass directly to the parser, no longer once the entire file
Read into a byte array. All major open source libraries provide the appropriate API to directly accept an input stream for processing, such as:
FileInputStream fis = new FileInputStream (fileName); Myprotobufmessage msg = Myprotobufmessage.parsefrom (FIS);
Tip #3: Using Immutable objects
There are too many benefits to immutability. I don't even have to repeat anything. However, there is one advantage that can have an impact on garbage collection and should be followed.
The properties of an immutable object cannot be modified after the object is created (in this case, the properties of the reference data type are used), such as:
public class Objectpair { private final Object first; Private final Object second; Public Objectpair (object first, object second) { this.first = first; This.second = second; } Public Object GetFirst () { return first; } Public Object Getsecond () { return second; } }
instantiating the above class results in an immutable object-all its properties are final decorated and cannot be changed once the construction is complete.
immutability means that all objects referenced by an immutable container are created before the container is constructed. In the case of GC: This container is at least as young as the year it holds
The same as a light reference. This means that when garbage collection is performed in younger generations, the GC skips them because the immutable objects are in the old age, until they are determined that the immutable objects are not in the old age by any object
When they are referenced, they are only completed for recycling.
Fewer scan objects mean fewer scans of memory pages, less scanning of memory pages means shorter GC lifecycles, and shorter GC pauses and better overall throughput. ".
Tip #4: Watch out for string stitching
The string may be the most common non-native data structure in all JVM-based applications. However, due to its implicit overhead and ease of use, it is very easy to become the culprit for the memory-intensive crime.
The problem is obviously not in string literals, but in allocating memory initialization at run time. Let's take a quick look at examples of dynamic build strings:
public static string toString (t[] array) { string result = "["; for (int i = 0; i < Array.Length; i++) { result + = (Array[i] = = array? "This": Array[i]); if (I < array.length-1) { result + = ","; } } Result + = "]"; return result;}
This is a seemingly good way to receive a character array and then return a string. However, this is catastrophic for object memory allocation.
It's hard to see behind this grammatical sugar, but the reality behind the scenes is this:
public static string toString (t[] array) { string result = "["; for (int i = 0; i < Array.Length; i++) { StringBuilder sb1 = new StringBuilder (result); Sb1.append (Array[i] = = array? "This": Array[i]); result = Sb1.tostring (); if (I < array.length-1) { StringBuilder sb2 = new StringBuilder (result); Sb2.append (","); result = Sb2.tostring (); } } StringBuilder SB3 = new StringBuilder (result); Sb3.append ("]"); result = Sb3.tostring (); return result;}
Strings are immutable, which means that each time a splice occurs, they are not modified by themselves, but instead they are assigned a new string. In addition, the compiler uses the standard StringBuilder class to perform
These stitching operations. This can be problematic because each iteration implicitly allocates a temporary string and implicitly assigns a temporary StringBuilder object to help build the final result as well.
The best way is to avoid the above situation, using StringBuilder and direct append to replace the local concatenation operator ("+"). Here is an example:
public static String toString (t[] array) { StringBuilder sb = new StringBuilder ("["); for (int i = 0; i < Array.Length; i++) { sb.append (array[i] = = array? "This": Array[i]); if (I < array.length-1) { sb.append (","); } } Sb.append ("]"); return sb.tostring ();}
Here, we only assign the only one StringBuilder at the beginning of the method. At this point, all the strings and elements in the list are appended to a single StringBuilder. End Use
The ToString () method turns it back into a string at once.
Tip #5: Using a specific collection of native types
The Java standard Collection library is simple and supports generics, allowing semi-static binding of types when using collections. For example, if you want to create a Set or storage map<pair that only holds strings, list> such
Map, this is a great way to handle it. The real problem arises when we want to use a list to store the int type, or a map to store a double type as value. Because generics do not support native data classes
Type, so another option is to use the wrapper type for the replacement, where we use List.
This processing is very wasteful because an integer is a complete object, the head of an object takes up 12 bytes and its internal int attribute is maintained, each integer object occupies a total of
With 16 bytes. It consumes four times times more space than a list that stores the same number of int types! The more serious problem is that, in fact, because the Integer is a real object instance, it
The garbage collection phase needs to be considered for recycling by the garbage collector.
To deal with this problem, we use a very good Trove collection library in Takipi. Trove abandons the specific set of generics to support a specific collection of native types that use memory more efficiently. For example, I
Map<integer, double> with very expendable performance, there is another special option in Trove, in the form of Tintdoublemap.
Tintdoublemap map = new Tintdoublehashmap () Map.put (5, 7.0); Map.put (-1, 9.999);
The underlying implementation of Trove uses an array of primitive types, so no element boxing (Int->integer) or unboxing (integer->int) occurs when the collection is manipulated, because the underlying uses the original
stored in the native data type.
At last
As the garbage collector continues to improve, and the runtime's optimizations and JIT compilers become more and more intelligent. As a developer, we will find that we are increasingly thinking less about how to write GC-friendly code. However
, at this stage, we still have a lot to do to help the JVM improve performance, no matter how G1 is improved.
original link: takipi translation: importnew.com - Pan
Link: http://www.importnew.com/20656.html
5 tips to reduce Java garbage collection overhead