Live data from Hacker News

5 Coding Hacks to Reduce GC Overhead

takipiblog.com

11–20 of 25 posts

Re: 5 Coding Hacks to Reduce GC Overhead

#11
These kinds of optimization/"hacks" articles need to include benchmarks. How does using one StringBuilder compare to using the implicit three or five? How many strings does one need to append before you should've used a StringBuilder? Etc.

I imagine that there's going to be novice programmers whose takeaway from the article is "Hashmaps with ints as keys are slow" or "String concatenation in Java is slow".

Re: 5 Coding Hacks to Reduce GC Overhead

#12
Another: buy more RAM if needed, and set -Xmx###M and -Xms###M to sufficiently high numbers so that the JVM never runs out of memory and forces a collection. You can also do System.gc() to "suggest" a collection be done soon, such as while you're busy doing something slow that's I/O bound like reading a file or getting network data. Though I wouldn't be surprised if most of the time modern JVM GCs already know when you're doing such things, making such a hint redundant.

Re: 5 Coding Hacks to Reduce GC Overhead

#13
Why should the manually managing your stringbuilders be preferable to having the compiler do it for you? The example given is:

  String result = foo() + arg;
  result += boo();
  System.out.println(“result = “ + result);
creates 3 StringBuilders. I would have thought that it wouldn't be particularly hard to track that the input to subsequent "+" was the output of a previous stringbuilder, allowing easy re-use?

Re: 5 Coding Hacks to Reduce GC Overhead

#14

I'm not sure 1. is relevant anymore. Almost all JVMs default to generational GCs, which would make all those allocations basically free.

Most of the 'hacks' were not relevant anymore 10 years ago. The blog is probably written to pitch the product.

Re: 5 Coding Hacks to Reduce GC Overhead

#15
post #12

Another: buy more RAM if needed, and set -Xmx###M and -Xms###M to sufficiently high numbers so that the JVM never runs out of memory and forces a collection. You can also do System.gc() to "suggest" a collection be done soon, such as while you're busy doing something slow that's I/O bound like reading a file or getting network data. Though I wouldn't be surprised if most of the time modern JVM GCs already know when y…

Buying more RAM only gets you so far. G1 handles large heaps fairly well, but you can still run into issues with long pauses if you are not careful and those pauses increase with the size of the heap. Most real time services don't have the luxury of manually triggering GCs and must do whatever they can to limit the stress they put on the collector.

Re: 5 Coding Hacks to Reduce GC Overhead

#16

I'm not sure 1. is relevant anymore. Almost all JVMs default to generational GCs, which would make all those allocations basically free.

I'm not sure how that's "basically free". If the nursery fills up, even if the objects are dead, it will still require a root set scan and copy/compact of the live objects. That's not basically free, that can be quite expensive.

Re: 5 Coding Hacks to Reduce GC Overhead

#17

Earlier quoted context omitted.

The general advice is to produce less garbage. Strategies for doing so are specific to the language and APIs involved.

It's not just that you need to produce less garbage - you want to produce less live objects in general, as a large part of the cost during GC is determining which objects are live.

That depends - if your code is mostly single threaded, and you're using the concmarksweep collector on a multicore CPU, determinign which objects are live is basically 'free'. On machines with a high number of cores, have all cores on 100% is very rare.

Re: 5 Coding Hacks to Reduce GC Overhead

#18
"use streams instead of buffers" is completely wrong. It is much faster to read off a file or the network into a buffer rather than byte-by-byte so you can assume that anything which takes a stream, buffers it internally.

What you should do: allocate one read buffer with a capacity > expected length, overwrite-read into it, copy the data into a fresh capacity == actual length byte array, parse the copy, empty and reuse the read buffer.

What you definitely shouldn't do, despite it being tempting: allocate a read buffer each time and pass the whole thing around.

Re: 5 Coding Hacks to Reduce GC Overhead

#19
This article is way inaccurate. It recommends approaches that were maybe important in 1997. It also breaks the "profile before optimising" rule.

Currently the Java GC works splendidly with lots of short-lived, small immutable classes. Using this approach can be far better for performance than worrying about telling the ArrayList constructor how many elements the list will probably have.

Re: 5 Coding Hacks to Reduce GC Overhead

#20

"use streams instead of buffers" is completely wrong. It is much faster to read off a file or the network into a buffer rather than byte-by-byte so you can assume that anything which takes a stream, buffers it internally. What you should do: allocate one read buffer with a capacity > expected length, overwrite-read into it, copy the data into a fresh capacity == actual length byte array, parse the copy, empty and reu…

An approach I've taken in .NET is to write a class that implements IDisposable and wraps a byte buffer of a standardized size. Behind the scenes it used an array of stacks for recycling the byte arrays. Normal use case would be something like:

    using (ByteBuffer buffer = pool.AllocateBuffer(128 * 1024))
    {
         // use buffer.array
    }
The pool used weak references to the buffers so they could eventually be GC'd. Dead weak references were cleaned out when found during allocation. Typically the byte arrays needed were big enough to need to go into the large object heap, the cutoff for which was 80k IIRC. The LOH was not collected until gen2 collection.
Post reply on HN