Why is it bad practice to call System.gc()?

2018-12-31 02:47发布

After answering a question about how to force-free objects in Java (the guy was clearing a 1.5GB HashMap) with System.gc(), I was told it's bad practice to call System.gc() manually, but the comments were not entirely convincing. In addition, no one seemed to dare to upvote, nor downvote my answer.

I was told there that it's bad practice, but then I was also told that garbage collector runs don't systematically stop the world anymore, and that it could also effectively be used by the JVM only as a hint, so I'm kind of at loss.

I do understand that the JVM usually knows better than you when it needs to reclaim memory. I also understand that worrying about a few kilobytes of data is silly. I also understand that even megabytes of data isn't what it was a few years back. But still, 1.5 gigabytes? And you know there's like 1.5 GB of data hanging around in memory; it's not like it's a shot in the dark. Is System.gc() systematically bad, or is there some point at which it becomes okay?

So the question is actually double:

  • Why is or isn't it bad practice to call System.gc()? Is it really merely a hint to the JVM under certain implementations, or is it always a full collection cycle? Are there really garbage collector implementations that can do their work without stopping the world? Please shed some light over the various assertions people have made in the comments to my answer.
  • Where's the threshold? Is it never a good idea to call System.gc(), or are there times when it's acceptable? If so, what are those times?

13条回答
谁念西风独自凉
2楼-- · 2018-12-31 02:56

Lots of people seem to be telling you not to do this. I disagree. If, after a large loading process like loading a level, you believe that:

  1. You have a lot of objects that are unreachable and may not have been gc'ed. and
  2. You think the user could put up with a small slowdown at this point

there is no harm in calling System.gc(). I look at it like the c/c++ inline keyword. It's just a hint to the gc that you, the developer, have decided that time/performance is not as important as it usually is and that some of it could be used reclaiming memory.

Advice to not rely on it doing anything is correct. Don't rely on it working, but giving the hint that now is an acceptable time to collect is perfectly fine. I'd rather waste time at a point in the code where it doesn't matter (loading screen) than when the user is actively interacting with the program (like during a level of a game.)

There is one time when i will force collection: when attempting to find out is a particular object leaks (either native code or large, complex callback interaction. Oh and any UI component that so much as glances at Matlab.) This should never be used in production code.

查看更多
不再属于我。
3楼-- · 2018-12-31 02:56

Sometimes (not often!) you do truly know more about past, current and future memory usage then the run time does. This does not happen very often, and I would claim never in a web application while normal pages are being served.

Many year ago I work on a report generator, that

  • Had a single thread
  • Read the “report request” from a queue
  • Loaded the data needed for the report from the database
  • Generated the report and emailed it out.
  • Repeated forever, sleeping when there were no outstanding requests.
  • It did not reuse any data between reports and did not do any cashing.

Firstly as it was not real time and the users expected to wait for a report, a delay while the GC run was not an issue, but we needed to produce reports at a rate that was faster than they were requested.

Looking at the above outline of the process, it is clear that.

  • We know there would be very few live objects just after a report had been emailed out, as the next request had not started being processed yet.
  • It is well known that the cost of running a garbage collection cycle is depending on the number of live objects, the amount of garbage has little effect on the cost of a GC run.
  • That when the queue is empty there is nothing better to do then run the GC.

Therefore clearly it was well worth while doing a GC run whenever the request queue was empty; there was no downside to this.

It may be worth doing a GC run after each report is emailed, as we know this is a good time for a GC run. However if the computer had enough ram, better results would be obtained by delaying the GC run.

This behaviour was configured on a per installation bases, for some customers enabling a forced GC after each report greatly speeded up the protection of reports. (I expect this was due to low memory on their server and it running lots of other processes, so hence a well time forced GC reduced paging.)

We never detected an installation that did not benefit was a forced GC run every time the work queue was empty.

But, let be clear, the above is not a common case.

查看更多
怪性笑人.
4楼-- · 2018-12-31 03:02

Yes, calling System.gc() doesn't guarantee that it will run, it's a request to the JVM that may be ignored. From the docs:

Calling the gc method suggests that the Java Virtual Machine expend effort toward recycling unused objects

It's almost always a bad idea to call it because the automatic memory management usually knows better than you when to gc. It will do so when its internal pool of free memory is low, or if the OS requests some memory be handed back.

It might be acceptable to call System.gc() if you know that it helps. By that I mean you've thoroughly tested and measured the behaviour of both scenarios on the deployment platform, and you can show it helps. Be aware though that the gc isn't easily predictable - it may help on one run and hurt on another.

查看更多
零度萤火
5楼-- · 2018-12-31 03:02

In my experience, using System.gc() is effectively a platform-specific form of optimization (where "platform" is the combination of hardware architecture, OS, JVM version and possible more runtime parameters such as RAM available), because its behaviour, while roughly predictable on a specific platform, can (and will) vary considerably between platforms.

Yes, there are situations where System.gc() will improve (perceived) performance. On example is if delays are tolerable in some parts of your app, but not in others (the game example cited above, where you want GC to happen at the start of a level, not during the level).

However, whether it will help or hurt (or do nothing) is highly dependent on the platform (as defined above).

So I think it is valid as a last-resort platform-specific optimization (i.e. if other performance optimizations are not enough). But you should never call it just because you believe it might help(without specific benchmarks), because chances are it will not.

查看更多
无与为乐者.
6楼-- · 2018-12-31 03:04

my 2 cents: I load some AnimationDrawables in an activity and play them. I load, play, then set the imageview background to null, one at the time. If I get out the activity and then come back again quickly, after 3 or 4 times the memory engaged grows too much until I get an out of memory exception.

By calling garbage collector explicitly after setting imageview background to null, I see on Eclipse logcat that memory is kept free enough - and in my case gc is actually run - and I don't get the app stopped working anymore.

It's obvious that system may decide to postpone the execution of gc but if you know more or less how a gc works, you can trust in a case like mine it will be called as soon as possible, for the reason system notices memory used growing and app it's about to ask for more to the system. I think it works like c++ std library containers: you get some starting memory and each time it's not enough, it doubles.

Saying that if you need to call it it's due to broken or bad code is an unreasonable dogmatic way of answering to me: expecially if you can program in a language with total manual memory management like C++ and you have to face the limit of resources on mobile device with a language like java instead, with no chance to free memory manually, you quickly can think of many situations in which it's necessary to call gc explicitly, expecially where you have a tracing gc and not a reference counting one, thou the code is clean and well done.

查看更多
泛滥B
7楼-- · 2018-12-31 03:06

GC efficiency relies on a number of heuristics. For instance, a common heuristic is that write accesses to objects usually occur on objects which were created not long ago. Another is that many objects are very short-lived (some objects will be used for a long time, but many will be discarded a few microseconds after their creation).

Calling System.gc() is like kicking the GC. It means: "all those carefully tuned parameters, those smart organizations, all the effort you just put into allocating and managing the objects such that things go smoothly, well, just drop the whole lot, and start from scratch". It may improve performance, but most of the time it just degrades performance.

To use System.gc() reliably(*) you need to know how the GC operates in all its fine details. Such details tend to change quite a bit if you use a JVM from another vendor, or the next version from the same vendor, or the same JVM but with slightly different command-line options. So it is rarely a good idea, unless you want to address a specific issue in which you control all those parameters. Hence the notion of "bad practice": that's not forbidden, the method exists, but it rarely pays off.

(*) I am talking about efficiency here. System.gc() will never break a correct Java program. It will neither conjure extra memory that the JVM could not have obtained otherwise: before throwing an OutOfMemoryError, the JVM does the job of System.gc(), even if as a last resort.

查看更多
登录 后发表回答