Five recommendations for managing Java garbage collection

Source: Internet
Author: User

"Editor's note" The writer is Niv Steingarten, co-founder of Takipi, who is passionate about writing elegant and concise code.

By introducing and combing the garbage collector, the author puts forward five suggestions on the management of garbage collection. Reduce collector overhead. Help you to further improve project performance. This article is a domestic ITOM management platform OneAPM Project division compiled and organized.

What is the most useful advice for keeping GC low cost?

There have been news that Java 9 is about to be announced, but now it has been postponed. One of the more notable concerns is that the G1 ("Garbage-first") garbage collector will be the default collector for the hotspot JVM. From the serial collector to the CMS collector, the JVM has undergone multiple generations of GC implementations and updates throughout its lifecycle, and the next. The G1 collector will compose a new chapter.

As the garbage collector continues to evolve, every generation will improve and improve. The parallel collector after the serial collector leverages the powerful computing power of multicore machines to achieve multi-threaded garbage collection. And after the CMS (Concurrent mark-sweep) collector, the collection is divided into multiple stages to run. Agree to do a lot of collection at the same time that the application thread runs. Significantly reduces the frequency of "stop-the-world" global pauses. Now, G1 has added a lot of heap and pre-measured even pauses to the JVM, effectively improving performance.

Although the GC is constantly intact, its Achilles heel is the same: redundant and unpredictable object allocations.

But in this article, there are some highly effective, long-term, useful recommendations that can help you reduce GC overhead, regardless of which garbage collector you choose.

Recommendation 1: Pre-measured collection capabilities

The full set of Java standards and most of their own defined extension implementations (such as Trove and Google's guava). will use the underlying array (whether based on raw or object-based). Once the length of the data has been allocated, the array is immutable, so in many cases. Adding items to a collection may result in the old underlying array being deleted, and then a larger array will need to be allocated again instead.

Most collection implementations try to optimize the redistribution process and reduce its overhead when the collection is not set to the expected size. However, the best results are set to the expected size when the collection is constructed.

Let's take a look at the following simple example:

publicstaticreverse(List<?

extends T> list) { new ArrayList(); for (int10; i--) { result.add(list.get(i)); } return result;}

The above method assigns a new array. A list of items is then populated, but only in reverse order.

However, it is difficult to optimize the step of adding a project to a new list.

After each join, the list also needs to ensure that its underlying array has enough empty slots to fit the new item.

Assuming it can be mounted, it will store the new item directly in the next empty slot. But assuming there is not enough space, it allocates an underlying array again, copies the contents of the old array into the new array, and then joins the new project. This process causes the allocation of multiple arrays to occupy memory until the GC is finally reclaimed.

So we were able to tell the array how many items to hold at the time of construction. The refactored code is as follows:

publicstaticreverse(List<?

extends T> list) { new ArrayList(list.size()); for (int10; i--) { result.add(list.get(i)); } return result;}

In this way, the ArrayList constructor can be guaranteed to accommodate the next list.size () item when it is initially configured. This means that it does not need to allocate memory again in the iteration.

The Guava collection class is more advanced, agreeing that we initialize the set with an exact number or estimate.

List result = Lists.newArrayListWithCapacity(list.size());List result = Lists.newArrayListWithExpectedSize(list.size());

The first line of code is that we know how many items need to be stored, and the second row allocates some extra padding to accommodate the estimated error.

Recommendation 2: Working with Flow directly

When processing data streams, such as reading data from a file or downloading data from the Web. For example, we can usually find something from the data stream:

byte[] fileData = readFileToByteArray(new File("myfile.txt"));

The resulting byte array can be parsed into an XML document, JSON object, or protocol buffered message to name some of the frequently used options.

When working with files of large or unknown size. The idea does not apply because a outofmemoryerrors error occurs when the JVM cannot allocate a buffer of file size.

However, even if the data size seems manageable, when it comes to garbage collection. The above pattern still creates a lot of overhead because it allocates a fairly large blob on the heap to accommodate file data.

A better approach is to use the appropriate inputstream (in this case, FileInputStream) and send it directly to the parser instead of reading the entire file into a byte array in advance.

All major libraries expose the API directly to the parsing stream. Like what:

new FileInputStream(fileName);MyProtoBufMessage msg = MyProtoBufMessage.parseFrom(fis);

Recommendation 3: Using immutable objects

Immutability has many advantages. But one advantage is rarely taken seriously. That's the effect of immutability on garbage collection.

Immutable objects refer to objects once created. Its field (not the original field in this case) will not be altered.

Like what:

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; }}

The result of instantiating the above class is immutable--all fields cannot be changed once they are marked.

Immutability means that all objects referenced by immutable containers have been created before the container is constructed. In the GC it appears that the container will be consistent with its newest generation. This means that when the garbage collection cycle is run against the new generation (young generations), the GC is able to skip the immutable objects in the old age (older generations) because it knows that immutable objects cannot reference the new generation of whatever content.

Fewer object scans means less memory pages to scan. The less memory page scan means that the shorter the GC cycle, the same time it is indicative of a shorter GC pause and better overall throughput.

Recommendation 4: Use string connections with caution

The string may be the most common non-raw data structure in any JVM-based application. However, their implied weight and ease of use make them the culprit for the larger application memory.

It is clear that the problem is not in the text strings that are inline and detained, but because the strings are allocated and constructed at run time. Let's take a look at a simple demo example of building a dynamic string:

publicstatictoString(T[] array) {    "[";    for (int0; i < array.length; i++) {        result += (array[i] == array ?

"this" : array[i]); if1) { ", "; } } "]"; return result;}

It is a good idea to get an array and return its string representation. But this is where the problem is with the allocation of objects.

It's not easy to see all the syntactic sugars behind it, but the real behind-the-scenes scenario should be 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;}

The string is immutable. So when the connection is not changed, the new string is assigned in turn.

In addition, the compiler uses the standard StringBuilder classes to run these links.

This leads to double trouble at each iteration of the loop. We get (1) Implicit allocation of temporary strings, (2) implicitly allocating temporary StringBuilder objects to help us build the end result.

The best way to avoid this problem is to understand the use of StringBuilder and attach it directly to it. Instead of using a slightly naïve concatenation operator ("+").

So it should be like this:

publicstatictoString(T[] array) {    new StringBuilder("[");    for (int0; i < array.length; i++) {        "this" : array[i]);        if1) {            sb.append(", ");        }    }    sb.append("]");    return sb.toString();}

At this time We only assigned StringBuilder at the beginning of the method.

From this point of view, all strings and list items are added to the unique StringBuilder. Finally just call the ToString method once to convert to a string, and then return the result.

Recommendation 5: Use a dedicated original collection

Java's standard library is convenient and versatile, and supports using collections to bind semi-static types. For example, suppose you want to use a set of strings ( Set<String> ), or a pair of strings to map to a list of strings ( Map<Pair, List<String>> ). It is convenient to use the standard library directly.

In fact, the problem arises because we want to place the value of the double type in the list collection or map map of type int. Because generics cannot call the original collection, they can be replaced with a wrapper type, so it is better to discard List<int> and use it List<Integer> .

In fact, this is a waste, and the integer itself is a complete object, composed of 12-byte object headers and an internal 4-byte integer number field. Add up each integer object to account for 16 bytes, which is 4 times times the length of the base class int of the same size! However, the bigger problem is that all these integers are actually object instances in the garbage collection process.

In order to solve the problem, we use the excellent Trove collection library in Takipi. Trove discards some (but not all) generics that support the original collection of professional, efficient memory. Like what. A map without waste

new TIntDoubleHashMap();map.put(57.0);map.put(-19.999);...

The trove bottom implements the use of the original array, so there is no boxing (int-like integer) or unboxing (integer-int) When manipulating the collection, so objects are not stored in the base class.

Conclusion

As the garbage collector progresses, and the real-time optimization and JIT compilers become smarter, we, as developers, are less concerned with the GC-friendliness of the code. Nevertheless, no matter how advanced G1 is, there are still many problems that need to be explored and practiced to improve the JVM, endeavors still need to be further.

(Compiled from: https://www.javacodegeeks.com/2015/12/5-tips-reducing-java-garbage-collection-overhead.html)

utm_source=community&utm_medium=article&utm_term=%e9%ab%98%e6%80%a7%e8%83%bd%e7%9a%84%e6%99%ba%e8%83% Bd%e6%97%a5%e5%bf%97&utm_content=wk321-327&utm_campaign=aijavaarti&from=jscljdkkkr?

utm_source=community&utm_medium=article&utm_term=%e7%ae%a1%e7%90%86java%e5%9e%83%e5%9c%be%e5%9b%9e%e6% 94%b6%e7%9a%84%e4%ba%94%e4%b8%aa%e5%bb%ba%e8%ae%ae&utm_content=wk321-327&utm_campaign=aijavaarti& FROM=JSCLJDKKCX ">ONEAPM provides you with end-to-end

utm_source=community&utm_medium=article&utm_term=%e7%ae%a1%e7%90%86java%e5%9e%83%e5%9c%be%e5%9b%9e%e6% 94%b6%e7%9a%84%e4%ba%94%e4%b8%aa%e5%bb%ba%e8%ae%ae&utm_content=wk321-327&utm_campaign=aijavaarti& FROM=JSCLJDKKCX ">java Application performance solution. We support all common Java frameworks and application servers to help you discover system bottlenecks at high speed and pinpoint the root cause of the anomalies. Minute-level deployment. With instant experience, Java monitoring has never been easier.

To read a lot of other technical articles, visit the OneAPM Official technology blog.

This article was transferred from OneAPM official blog

To know a lot of other things about Java performance optimization, please scan the code below for the public number:

Five recommendations for managing Java garbage collection

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.