I have a couple of simple applications written in java, one of them written to act as a widget. What surprised me how much RAM even small applications use.
I wrote the following to see if it is a bug in my programs, or a general Java issue:
public class ram {
public static void main(String[] args){
while(true)System.out.print("Hello World");//while loop to give me time to check RAM usage
}
}
Then compiled and ran it with java ram and it gave me the following RAM usage:
The process java (with pid 4489) is using approximately 43.3 MB of memory.
34460 KB [heap]
7088 KB /usr/lib/jvm/java-7-openjdk/jre/lib/amd64/server/libjvm.so
1712 KB /usr/lib/jvm/java-7-openjdk/jre/lib/rt.jar
136 KB [stack:4495]
120 KB /usr/lib/jvm/java-7-openjdk/jre/lib/amd64/libjava.so
Isn't this too high? Especially a heap of 34MB. My system is ArchLinux x86_64 and openjdk-7.
Is there any way to minimise the amount of RAM used by the JVM?
Edit: I tried using the -Xmx flag and this is what I got (1281k was the smallest it would let me start with):
java -Xmx1281k ram
The process java (with pid 4987) is using approximately 27.6 MB of memory.
18388 KB [heap]
For comparison, Python2 uses 4.4MB, Mono uses 4.3MB.
I see similar questions to this asked frequently and I have yet to see a satisfactory answer.
The answers I see most frequently are angry "why do you care?" answers. Some people put a lot of thought and effort into making the question and whoever asks it seem stupid.
Some people will answer this question with a long diatribe about the different kinds of memory Java allocates, why it does so, and what command line parameters you should look into. (rarely does anyone get very specific)
The fact is that for small utilities Java is a memory hog on most operating systems. The smaller the utility, the more obvious it really is. Java developers have long been conditioned to deal with or ignore this fact.
The reasons for the seemingly abnormal memory usage are plentiful. Each thread gets a certain amount of memory for it's stack. There are several threads that will get started regardless of how simple a program is for things like garbage cleanup, RMI, etc. On Windows/64-bit that's 1MB per thread. A bunch of classes get loaded by default and all of your classes. I'm sure a lot of other things are happening behind the scenes.
Java has made tradeoff choices that other languages haven't. The load time is slower than most other languages. The initial memory requirement is higher. Strings as they are most commonly used eat up a lot more memory than most people realize. There are countless others. The benefit for many situations really does pay off. Something like Hello World shows off the cost of those choices more than anything else. Benefits like easy multi-threading and near-native performance really don't do you any good with Hello World.
Unfortunately, there's not a lot that you can do to really minimize the memory used by a simple application. There are the aforementioned command line switches and trial and error could shrink your reported usage down to the ~10-15mb level I'm sure, but those choices and time you spend figuring them out aren't going to be applicable to a wide variety of future applications. You'll have to go through the same process again for the next application. Throw in a GUI interface and some logging and another common library or two and your base number will shoot up to 60mb pretty darn quick.
You aren't doing anything wrong. It's just the way things work in Java. You'll get used to it. You'll choose another language on occasion because of it, and that's OK too.
There are several reasons for this:
The java runtime is a rather complex program in itself. It needs to take the byte code of your Java program (the output from javac), translate it into machine code for the system it is running on, there's an optimizer for that, there are interfaces for debugging, etc.
Although your program is rather small, Java will still load many classes from its standard library. You can observe this by starting it using 'java -verbose:class ram'
Java allocates a great bunch of memory for your program in advance - it cannot know how much memory it will actually need. This is, among others, governed by the -Xmx option. There are several types of such memory. To find out more about them, you can use the JConsole tool, which is included in the JDK's bin folder. You can read more about it at java.sun.com. A summary of the memory areas used by Java is also given at this stackoverflow question
Related
I am building a very complex software that will be used for production and will run on a server as a service.
I need to make this jar have set max RAM usage when running with some calculations made by my program, i have seen that there are ways for setting the memory before running the built program, but i would like to set how much memory the jar is going to use when i am running it, is this possible?
There are two issues here. As mentioned above, you can only request up to a specific amount of memory. Efficient garbage collection can help you reclaim memory that is no longer needed.
The second, and probably real, issue here is metering how much memory is actually used by the application. There are many frameworks (e.g., JMeter) for measuring how much memory is used - and this can be done with respect to the amount of data used. When doing NP-complete (or even just more than O(n) problems) this can be very useful from the users perspective ("This works well with up to 2 ||| objects")
I have to run a couple of java services on my machine to obtain a certain dev environment (and get my not-java-related work done)
java -Xmx400m -jar foo-app/target/foo-app-SNAPSHOT.jar
java -Xmx250m -jar bar-app/target/bar-app-SNAPSHOT.jar
...
To not run out of memory, I need to limit the memory usage. The default (512m afaik) ist too high for my machine so I lowered them somewhat (on a wild as guessing basis). Except for one, where I learned the hard way (crashed, even freezes, and thankfully some .pid error files left behind in the project folder...), that I better settle a little higher:
java -Xmx800m -jar doo-app/target/doo-app-SNAPSHOT.jar
Question: is there a way, to track memory usage of a certain app over time?
By some java command line parameter or even with ps -ae, htop or similar? (thus not fiddling in the source itself, remap garbage collectors, etc, etc)
I see plenty of numbers, but figuring out which belong to which java project running, and what could roughly indicate me a proper peak memory consumption (in a -Xmx___m sense)... I have no idea.
I work under Ubuntu-MATE 16.04, x64.
The best way to analyze memory consumption is a profiler. In your jdk there comes the jvisualvm profiler, which is absolutely sufficient for this task. A (lengthy) tutorial can be found here: https://engineering.talkdesk.com/ninjas-guide-to-getting-started-with-visualvm-f8bff061f7e7
Other approaches are basically shotgun-style -reduce the xmx and then generate load in the system and see if it runs oom. If you do NOT have a straight controll flow you have no way to predict the used memory.
I have a java program that is a typical machine learning algorithm, updating the values for some parameters by some equations:
for (int iter=0; iter<1000; iter++) {
// 1. Create many temporary variables and do some computations
// 2. Update the value for the parameters
}
The computations of updating parameters are rather complex, and I have to create many temporary objects, but they are not referenced out of the loop. The code in the loop is CPU-intensive, and does not access disk. This program loads a relatively large training dataset, therefore, I granted 10G memory (-Xmx10G) to JVM, which is much larger than it requires (peak at ~6G by "top" command or window's task manager).
I tested it on several linux machines (centos 6, 24G memory) and a window machine (win7, 12G), both with SUN Hotspot JDK/JRE 1.8 installed. I did not specify other JVM parameters except -Xmx. Both machines are dedicated to my program.
On windows, my program runs well: each iteration uses very similar running time. However, the running time on all of the centos machines is weird.
It initially runs properly, but slows down dramatically (~10 times slower) at 7th/8th iteration, and then keeps slow down ~10% in each iteration ever after.
I suspect it might be caused by Java's garbage collector. Therefore, I use jconsole to monitor my program. Minor GC happens very frequently on both machines , that is because the program creates many temporary variable in the loop. Furthermore, I used "jstat -gcutil $pid$ 1s" command and captured the statistics:
Centos: https://www.dropbox.com/s/ioz7ai6i1h57eoo/jstat.png?dl=0
Window: https://www.dropbox.com/s/3uxb7ltbx9kpm9l/jstat-winpng.png?dl=0
[Edited] However, the statistics on two kinds of machines differ a lot:
"S1” on windows jumps fast between 0 to 50, while stays at "0.00" on centos.
"E" on windows changes very rapidly from 0 to 100. As I print the stat for every second, the screenshot does not show its increment to 100. On centos, however, "E" increases rather slowly towards 100, and then reduces to 0, and increases again.
It seems the weird behaviour of my program is due to Java GC? I am new to Java performance monitor and do not have a good idea to optimize GC parameter setting. Do you have any suggestions? Thank you very much!
I'm sorry to post this as an answer but I don't have enough score to comment.
If you think it's a GC related issue I'd change it for the Garbage 1 Collector –XX:+UseG1GC
I found this brief explanation about it:
http://blog.takipi.com/garbage-collectors-serial-vs-parallel-vs-cms-vs-the-g1-and-whats-new-in-java-8/
Can you run your software under profiling? Try to use the jprofiler, VisualVM or even the netbeans profiler. It may help you a lot.
I noticed that you have your own encapsulation of a vector and matrix. Maybe your are spending a lot more memory than necessary with that too. But I don't think that is the problem.
Sorry again about not contributing as a comment. (It would be more appropriate)
I would consider declaring the vars outside the loop so mem allocation is done once and eliminate GC completely.
Giving Java (or any garbage collecting language) too much memory has an adverse effect on performance. The live (referenced) objects become increasing sparse in memory resulting in more frequent fetches from main memory. Note that in the examples you've shown us the faster windows is doing more quick and full GC than Linux - but GC cycles (especially full gcs) are usually bad for performance.
If running the training set does not take an exceptionally long time, then try benchmarking at different memory allocations.
A more radical solution, but one which should have a big impact is to eliminate (or reduce as much as possible) object creation within the loop by recycling objects in pools.
First, it is a common best practice to declare Variables outside of loops to avoid garbace collection.
as 'Wagner Tsuchiya' said, try running a profiler if you have doubts about the GC.
If you want some tips on GC tuning, i found nice blogpost.
You could try calling System.gc() every couple iterations to see if performance goes up or down. This may help you narrow it down to some of the previous answers diagnostics.
If the GC time is hundreds of milliseconds as shown in your screenshot then GC is likely not the issue here. I suggest you look into lock contention and possibly IO using a profiler (Netbeans is great). I know you stated your program did very little IO but with profiling (much like debugging) you have to remove all your assumptions and go step by step.
In my experience JAVA needs enough memory and 2+ CPU. Otherwise CPU usage will be very extensive when GC starts running.
I wrote a wrapper application in c# NET that runs when the .jar file is running, closes when the .jar file closes, etc. This was basically to allow for our web panel to be able to query the executable to find out if it was actually running or not.
I have seen some other panels specifically intended for this software that have an option to reduce the memory usage of it when no one is connected. The java application (Minecraft) basically scales the RAM usage based on the size of the player world rather than how many players are connected. When no one is connected, it should be perfectly fine to reduce the usage.
So is there any way to reduce the RAM usage programatically from C# NET for a Java application?
AFAIK, there is no way to tell a JVM to give regular heap memory back to the operating system ... apart from telling it exit completely.
No.
Why not? Because you can't control the Java-Program in that way for two reasons:
You can't control what the JRE does with it's memory and how the GC is working.
If minecraft.jar requests 512MiB of RAM, he gets 512MiB of RAM. You can't just go all Hey, there's no one connected so I disallow you to allocate memory on an application. I mean, you could...but I don't think you want that (that would trigger exceptions and odd side-effects).
Edit: The only rather easy way to achieve this behavior would be to change the program. Since Minecraft is not free/open-source software, the only thing you could do is file a bug/feature request. Maybe even with extended information and a layout concept on how to achieve better memory usage.
I mean, I'm pretty sure that this could also be achieved with heavy usage of reflection via a Java program...but things go pretty fast downhill from there.
We ship Java applications that are run on Linux, AIX and HP-Ux (PA-RISC). We seem to struggle to get acceptable levels of performance on HP-Ux from applications that work just fine in the other two environments. This is true of both execution time and memory consumption.
Although I'm yet to find a definitive article on "why", I believe that measuring memory consumption using "top" is a crude approach due to things like the shared code giving misleading results. However, it's about all we have to go on with a customer site where memory consumption on HP-Ux has become an issue. It only became an issue this time when we moved from Java 1.4 to Java 1.5 (on HP-Ux 11.23 PA-RISC). By "an issue", I mean that the machine ceased to create new processes because we had exhausted all 16GB of physical memory.
By measuring "before" and "after" total "free memory" we are trying to gauge how much has been consumed by a Java application. I wrote a quick app that stores 10,000 random 64 bit strings in an ArrayList and tried this approach to measuring consumption on Linux and HP-Ux under Java 1.4 and Java 1.5.
The results:
HP Java 1.4 ~60MB
HP Java 1.5 ~150MB
Linux Java 1.4 ~24MB
Linux Java 1.5 ~16MB
Can anyone explain why these results might arise? Is this some idiosyncrasy of the way "top" measures free memory? Does Java 1.5 on HP really consume 2.5 times more memory than Java 1.4?
Thanks.
The JVMs might just have different default parameters. The heap will grow to the size that you have configured to let it. The default on the Sun VM is a certain percentage of the RAM in the machine - that's to say that Java will, by default, use more memory if you use a machine with more memory on it.
I'd be really surprised if the HP-UX VM hadn't had lots of tuning for this sort of thing by HP. I'd suggest you fiddle with the parameters on both - figure out what the smallest max heap size you can use without hurting performance or throughput.
I don't have a HP box right now to test my hypothesis. However, if I were you, I would use a profiler like JConsole(comes with JDK) OR yourkit to measure what is happening.
However, it appears that you started measuring after you saw something amiss; So, I'm NOT discounting that it's happening -- just pointing you at something I'd have done in the same situation.
First, it's not clear what did you measure by "10,000 random 64 bit strings" test. You supposed to start the application, measure it's bootstrap memory footprint, and then run your test. It could easily be that Java 1.5 acquires more heap right after start (due to heap manager settings, for instance).
Second, we do run Java apps under 1.4, 1.5 and 1.6 under HP-UX, and they don't demonstrate any special memory requirements. We have Itanium hardware, though.
Third, why do you use top? Why not just print Runtime.getRuntime().totalMemory()?
Fourth, by adding values to ArrayList you create memory fragmentation. ArrayList has to double it's internal storage now and then. Depending on GC settings and ArrayList.ensureCapacity() implementation the amount of non-collected memory may differ dramatically between 1.4 and 1.5.
Essentially, instead of figuring out the cause of problem you have run a random test that gives you no useful information. You should run a profiler on the application to figure out where the memory leaks.
You might also want to look at the problem you are trying to solve... I don't imagine there are many problems that eat 16GB of memory that aren't due for a good round of optimization.
Are you launching multiple VMs? Are you reading large datasets into memory, and not discarding them quickly enough? etc etc etc.