The question is not about the maximum heap size on a 32-bit OS, given that 32-bit OSes have a maximum addressable memory size of 4GB, and that the JVM's max heap size depends on how much contiguous free memory can be reserved.
I'm more interested in knowing the maximum (both theoretical and practically achievable) heap size for a 32-bit JVM running in a 64-bit OS. Basically, I'm looking at answers similar to the figures in a related question on SO.
As to why a 32-bit JVM is used instead of a 64-bit one, the reason is not technical but rather administrative/bureaucratic - it is probably too late to install a 64-bit JVM in the production environment.
You can ask the Java Runtime:
public class MaxMemory {
public static void main(String[] args) {
Runtime rt = Runtime.getRuntime();
long totalMem = rt.totalMemory();
long maxMem = rt.maxMemory();
long freeMem = rt.freeMemory();
double megs = 1048576.0;
System.out.println ("Total Memory: " + totalMem + " (" + (totalMem/megs) + " MiB)");
System.out.println ("Max Memory: " + maxMem + " (" + (maxMem/megs) + " MiB)");
System.out.println ("Free Memory: " + freeMem + " (" + (freeMem/megs) + " MiB)");
}
}
This will report the "Max Memory" based upon default heap allocation. So you still would need to play with -Xmx (on HotSpot). I found that running on Windows 7 Enterprise 64-bit, my 32-bit HotSpot JVM can allocate up to 1577MiB:
[C:scratch]> java -Xmx1600M MaxMemory
Error occurred during initialization of VM
Could not reserve enough space for object heap
Could not create the Java virtual machine.
[C:scratch]> java -Xmx1590M MaxMemory
Total Memory: 2031616 (1.9375 MiB)
Max Memory: 1654456320 (1577.8125 MiB)
Free Memory: 1840872 (1.75559234619 MiB)
[C:scratch]>
Whereas with a 64-bit JVM on the same OS, of course it's much higher (about 3TiB)
[C:scratch]> java -Xmx3560G MaxMemory
Error occurred during initialization of VM
Could not reserve enough space for object heap
[C:scratch]> java -Xmx3550G MaxMemory
Total Memory: 94240768 (89.875 MiB)
Max Memory: 3388252028928 (3184151.84297 MiB)
Free Memory: 93747752 (89.4048233032 MiB)
[C:scratch]>
As others have already mentioned, it depends on the OS.
For 32-bit Windows: it'll be <2GB (Windows internals book says 2GB for user processes)
For 32-bit BSD / Linux: <3GB (from the Devil Book)
For 32-bit MacOS X: <4GB (from Mac OS X internals book)
Not sure about 32-bit Solaris, but the code above has been tested in this answer.
For a 64-bit host OS, if the JVM is 32-bit, it'll still depend, most likely like above as demonstrated.
-- UPDATE 20110905: I just wanted to point out some other observations / details:
The hardware that I ran this on was 64-bit with 6GB of actual RAM installed. The operating system was Windows 7 Enterprise, 64-bit
The actual amount of Runtime.MaxMemory that can be allocated also depends on the operating system's working set. I once ran this while I also had VirtualBox running and found I could not successfully start the HotSpot JVM with -Xmx1590M and had to go smaller. This also implies that you may get more than 1590M depending upon your working set size at the time (though I still maintain it'll be under 2GiB for 32-bit because of Windows' design)
32-bit JVMs which expect to have a single large chunk of memory and use raw pointers cannot use more than 4 Gb (since that is the 32 bit limit which also applies to pointers). This includes Sun and - I'm pretty sure - also IBM implementations. I do not know if e.g. JRockit or others have a large memory option with their 32-bit implementations.
If you expect to be hitting this limit you should strongly consider starting a parallel track validating a 64-bit JVM for your production environment so you have that ready for when the 32-bit environment breaks down. Otherwise you will have to do that work under pressure, which is never nice.
Edit 2014-05-15: Oracle FAQ:
The maximum theoretical heap limit for the 32-bit JVM is 4G. Due to various additional constraints such as available swap, kernel address space usage, memory fragmentation, and VM overhead, in practice the limit can be much lower. On most modern 32-bit Windows systems the maximum heap size will range from 1.4G to 1.6G. On 32-bit Solaris kernels the address space is limited to 2G. On 64-bit operating systems running the 32-bit VM, the max heap size can be higher, approaching 4G on many Solaris systems.
(http://www.oracle.com/technetwork/java/hotspotfaq-138619.html#gc_heap_32bit)
You don't specify which OS.
Under Windows (for my application - a long running risk management application) we observed that we could go no further than 1280MB on Windows 32bit. I doubt that running a 32bit JVM under 64bit would make any difference.
We ported the app to Linux and we are running a 32bit JVM on 64bit hardware and have had a 2.2GB VM running pretty easily.
The biggest problem you may have is GC depending on what you are using memory for.
From 4.1.2 Heap Sizing:
"For a 32-bit process model, the maximum virtual address size of the
process is typically 4 GB, though some operating systems limit this to
2 GB or 3 GB. The maximum heap size is typically -Xmx3800m (1600m) for
2 GB limits), though the actual limitation is application dependent.
For 64-bit process models, the maximum is essentially unlimited."
Found a pretty good answer here: Java maximum memory on Windows XP.
We recently had some experience with this. We have ported from Solaris (x86-64 Version 5.10) to Linux (RedHat x86-64) recently and have realized that we have less memory available for a 32 bit JVM process on Linux than Solaris.
For Solaris this almost comes around to 4GB (http://www.oracle.com/technetwork/java/hotspotfaq-138619.html#gc_heap_32bit).
We ran our app with -Xms2560m -Xmx2560m -XX:MaxPermSize=512m -XX:PermSize=512m with no issues on Solaris for past couple of years. Tried to move it to linux and we had issues with random out of memory errors on start up. We could only get it to consistently start up on -Xms2300 -Xmx2300. Then we were advised of this by support.
A 32 bit process on Linux has a
maximum addressable address space of
3gb (3072mb) whereas on Solaris it is
the full 4gb (4096mb).
The limitations of a 32-bit JVM on a 64-bit OS will be exactly the same as the limitations of a 32-bit JVM on a 32-bit OS. After all, the 32-bit JVM will be running In a 32-bit virtual machine (in the virtualization sense) so it won't know that it's running on a 64-bit OS/machine.
The one advantage to running a 32-bit JVM on a 64-bit OS versus a 32-bit OS is that you can have more physical memory, and therefore will encounter swapping/paging less frequently. This advantage is only really fully realized when you have multiple processes, however.
As to why a 32-bit JVM is used instead of a 64-bit one, the reason is not technical but rather administrative/bureaucratic ...
When I was working for BEA, we found that the average application actually ran slower in a 64-bit JVM, then it did when running in a 32-bit JVM. In some cases, the performance hit was as high as 25% slower. So, unless your application really needs all that extra memory, you were better off setting up more 32-bit servers.
As I recall, the three most common technical justifications for using a 64-bit that BEA professional services personnel ran into were:
The application was manipulating multiple massive images,
The application was doing massive number crunching,
The application had a memory leak, the customer was the prime on a
government contract, and they didn't want to take the time and the
expense of tracking down the memory leak. (Using a massive memory
heap would increase the MTBF and the prime would still get paid)
.
The JROCKIT JVM currently owned by Oracle supports non-contiguous heap usage, thus allowing the 32 bit JVM to access more then 3.8 GB of memory when the JVM is running on a 64 bit windows OS. (2.8 GB when running on a 32 bit OS).
http://blogs.oracle.com/jrockit/entry/how_to_get_almost_3_gb_heap_on_windows
The JVM can be freely downloaded (registration required) at
http://www.oracle.com/technetwork/middleware/jrockit/downloads/index.html
Here is some testing under Solaris and Linux 64-bit
Solaris 10 - SPARC - T5220 machine with 32 GB RAM (and about 9 GB free)
$ java -XX:PermSize=128M -XX:MaxPermSize=256M -Xms512m -Xmx3750m MaxMemory
Error occurred during initialization of VM
Could not reserve space for ObjectStartArray
$ java -XX:PermSize=128M -XX:MaxPermSize=256M -Xms512m -Xmx3700m MaxMemory
Total Memory: 518520832 (494.5 MiB)
Max Memory: 3451912192 (3292.0 MiB)
Free Memory: 515815488 (491.91998291015625 MiB)
Current PID is: 28274
Waiting for user to press Enter to finish ...
$ java -version
java version "1.6.0_30"
Java(TM) SE Runtime Environment (build 1.6.0_30-b12)
Java HotSpot(TM) Server VM (build 20.5-b03, mixed mode)
$ which java
/usr/bin/java
$ file /usr/bin/java
/usr/bin/java: ELF 32-bit MSB executable SPARC Version 1, dynamically linked, not stripped, no debugging information available
$ prstat -p 28274
PID USERNAME SIZE RSS STATE PRI NICE TIME CPU PROCESS/NLWP
28274 user1 670M 32M sleep 59 0 0:00:00 0.0% java/35
BTW: Apparently Java does not allocate much actual memory with the startup. It seemed to take only about 100 MB per instance started (I started 10)
Solaris 10 - x86 - VMWare VM with 8 GB RAM (about 3 GB free*)
The 3 GB free RAM is not really true. There is a large chunk of RAM that ZFS caches use, but I don't have root access to check how much exactly
$ java -XX:PermSize=128M -XX:MaxPermSize=256M -Xms512m -Xmx3650m MaxMemory
Error occurred during initialization of VM
Could not reserve enough space for object heap
Could not create the Java virtual machine.
$ java -XX:PermSize=128M -XX:MaxPermSize=256M -Xms512m -Xmx3600m MaxMemory
Total Memory: 516423680 (492.5 MiB)
Max Memory: 3355443200 (3200.0 MiB)
Free Memory: 513718336 (489.91998291015625 MiB)
Current PID is: 26841
Waiting for user to press Enter to finish ...
$ java -version
java version "1.6.0_41"
Java(TM) SE Runtime Environment (build 1.6.0_41-b02)
Java HotSpot(TM) Server VM (build 20.14-b01, mixed mode)
$ which java
/usr/bin/java
$ file /usr/bin/java
/usr/bin/java: ELF 32-bit LSB executable 80386 Version 1 [FPU], dynamically linked, not stripped, no debugging information available
$ prstat -p 26841
PID USERNAME SIZE RSS STATE PRI NICE TIME CPU PROCESS/NLWP
26841 user1 665M 22M sleep 59 0 0:00:00 0.0% java/12
RedHat 5.5 - x86 - VMWare VM with 4 GB RAM (about 3.8 GB used - 200 MB in buffers and 3.1 GB in caches, so about 3 GB free)
$ alias java='$HOME/jre/jre1.6.0_34/bin/java'
$ java -XX:PermSize=128M -XX:MaxPermSize=256M -Xms512m -Xmx3500m MaxMemory
Error occurred during initialization of VM
Could not reserve enough space for object heap
Could not create the Java virtual machine.
$ java -XX:PermSize=128M -XX:MaxPermSize=256M -Xms512m -Xmx3450m MaxMemory
Total Memory: 514523136 (490.6875 MiB)
Max Memory: 3215654912 (3066.6875 MiB)
Free Memory: 511838768 (488.1274871826172 MiB)
Current PID is: 21879
Waiting for user to press Enter to finish ...
$ java -version
java version "1.6.0_34"
Java(TM) SE Runtime Environment (build 1.6.0_34-b04)
Java HotSpot(TM) Server VM (build 20.9-b04, mixed mode)
$ file $HOME/jre/jre1.6.0_34/bin/java
/home/user1/jre/jre1.6.0_34/bin/java: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.2.5, dynamically linked (uses shared libs), for GNU/Linux 2.2.5, not stripped
$ cat /proc/21879/status | grep ^Vm
VmPeak: 3882796 kB
VmSize: 3882796 kB
VmLck: 0 kB
VmHWM: 12520 kB
VmRSS: 12520 kB
VmData: 3867424 kB
VmStk: 88 kB
VmExe: 40 kB
VmLib: 14804 kB
VmPTE: 96 kB
Same machine using JRE 7
$ alias java='$HOME/jre/jre1.7.0_21/bin/java'
$ java -XX:PermSize=128M -XX:MaxPermSize=256M -Xms512m -Xmx3500m MaxMemory
Error occurred during initialization of VM
Could not reserve enough space for object heap
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
$ java -XX:PermSize=128M -XX:MaxPermSize=256M -Xms512m -Xmx3450m MaxMemory
Total Memory: 514523136 (490.6875 MiB)
Max Memory: 3215654912 (3066.6875 MiB)
Free Memory: 511838672 (488.1273956298828 MiB)
Current PID is: 23026
Waiting for user to press Enter to finish ...
$ java -version
java version "1.7.0_21"
Java(TM) SE Runtime Environment (build 1.7.0_21-b11)
Java HotSpot(TM) Server VM (build 23.21-b01, mixed mode)
$ file $HOME/jre/jre1.7.0_21/bin/java
/home/user1/jre/jre1.7.0_21/bin/java: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.6.9, dynamically linked (uses shared libs), for GNU/Linux 2.6.9, not stripped
$ cat /proc/23026/status | grep ^Vm
VmPeak: 4040288 kB
VmSize: 4040288 kB
VmLck: 0 kB
VmHWM: 13468 kB
VmRSS: 13468 kB
VmData: 4024800 kB
VmStk: 88 kB
VmExe: 4 kB
VmLib: 10044 kB
VmPTE: 112 kB
Should be a lot better
For a 32-bit JVM running on a 64-bit host, I imagine what's left over for the heap will be whatever unfragmented virtual space is available after the JVM, it's own DLL's, and any OS 32-bit compatibility stuff has been loaded. As a wild guess I would think 3GB should be possible, but how much better that is depends on how well you are doing in 32-bit-host-land.
Also, even if you could make a giant 3GB heap, you might not want to, as this will cause GC pauses to become potentially troublesome. Some people just run more JVM's to use the extra memory rather than one giant one. I imagine they are tuning the JVM's right now to work better with giant heaps.
It's a little hard to know exactly how much better you can do. I guess your 32-bit situation can be easily determined by experiment. It's certainly hard to predict abstractly, as a lot of things factor into it, particularly because the virtual space available on 32-bit hosts is rather constrained.. The heap does need to exist in contiguous virtual memory, so fragmentation of the address space for dll's and internal use of the address space by the OS kernel will determine the range of possible allocations.
The OS will be using some of the address space for mapping HW devices and it's own dynamic allocations. While this memory is not mapped into the java process address space, the OS kernel can't access it and your address space at the same time, so it will limit the size of any program's virtual space.
Loading DLL's depends on the implementation and the release of the JVM. Loading the OS kernel depends on a huge number of things, the release, the HW, how many things it has mapped so far since the last reboot, who knows...
In summary
I bet you get 1-2 GB in 32-bit-land, and about 3 in 64-bit, so an overall improvement of about 2x.
On Solaris the limit has been about 3.5 GB since Solaris 2.5. (about 10 years ago)
I was having the same problems with the JVM that App Inventor for Android Blocks Editor uses. It sets the heap at 925m max. This is not enough but I couldn't set it more than about 1200m, depending on various random factors on my machine.
I downloaded Nightly, the beta 64-bit browser from Firefox, and also JAVA 7 64 bit version.
I haven't yet found my new heap limit, but I just opened a JVM with a heap size of 5900m. No problem!
I am running Win 7 64 bit Ultimate on a machine with 24gb RAM.
I have tried setting the heap size upto 2200M on 32bit Linux machine and JVM worked fine. The JVM didnt start when I set it to 2300M.
This is heavy tunning, but you can get a 3gb heap.
http://www.microsofttranslator.com/bv.aspx?from=&to=en&a=http://forall.ru-board.com/egor23/online/FAQ/Virtual_Memory/Limits_Virtual_Memory.html
one more point here for hotspot 32-bit JVM:-
the native heap capacity = 4 Gig – Java Heap - PermGen;
It can get especially tricky for 32-bit JVM since the Java Heap and native Heap are in a race. The
bigger your Java Heap, the smaller the native Heap. Attempting to setup a large Heap for a 32-bit VM
e.g .2.5 GB+ increases risk of native OutOfMemoryError depending of your application(s) footprint,
number of Threads etc.
Theoretical 4gb, but in practice (for IBM JVM):
Win 2k8 64, IBM Websphere Application Server 8.5.5 32bit
C:\IBM\WebSphere\AppServer\bin>managesdk.bat -listAvailable -verbose CWSDK1003I: Доступные SDK: CWSDK1005I: Имя SDK: 1.6_32 - com.ibm.websphere.sdk.version.1.6_32=1.6 - com.ibm.websphere.sdk.bits.1.6_32=32 - com.ibm.websphere.sdk.location.1.6_32=${WAS_INSTALL_ROOT}/java - com.ibm.websphere.sdk.platform.1.6_32=windows - com.ibm.websphere.sdk.architecture.1.6_32=x86_32 - com.ibm.websphere.sdk.nativeLibPath.1.6_32=${WAS_INSTALL_ROOT}/lib/native/win /x86_32/
CWSDK1001I: Задача managesdk выполнена успешно.
C:\IBM\WebSphere\AppServer\java\bin>java -Xmx2036 MaxMemory
JVMJ9GC017E -Xmx слишком мала, должна быть не меньше 1 M байт
JVMJ9VM015W Ошибка инициализации для библиотеки j9gc26(2): Не удалось инициализи
ровать
Could not create the Java virtual machine.
C:\IBM\WebSphere\AppServer\java\bin>java -Xmx2047M MaxMemory
Total Memory: 4194304 (4.0 MiB)
Max Memory: 2146435072 (2047.0 MiB)
Free Memory: 3064536 (2.9225692749023438 MiB)
C:\IBM\WebSphere\AppServer\java\bin>java -Xmx2048M MaxMemory
JVMJ9VM015W Ошибка инициализации для библиотеки j9gc26(2): Не удалось создать эк
земпляр кучи; запрошено 2G
Could not create the Java virtual machine.
RHEL 6.4 64, IBM Websphere Application Server 8.5.5 32bit
[bin]./java -Xmx3791M MaxMemory
Total Memory: 4194304 (4.0 MiB)
Max Memory: 3975151616 (3791.0 MiB)
Free Memory: 3232992 (3.083221435546875 MiB)
[root#nagios1p bin]# ./java -Xmx3793M MaxMemory
Total Memory: 4194304 (4.0 MiB)
Max Memory: 3977248768 (3793.0 MiB)
Free Memory: 3232992 (3.083221435546875 MiB)
[bin]# /opt/IBM/WebSphere/AppServer/bin/managesdk.sh -listAvailable -verbose
CWSDK1003I: Available SDKs :
CWSDK1005I: SDK name: 1.6_32
- com.ibm.websphere.sdk.version.1.6_32=1.6
- com.ibm.websphere.sdk.bits.1.6_32=32
- com.ibm.websphere.sdk.location.1.6_32=${WAS_INSTALL_ROOT}/java
- com.ibm.websphere.sdk.platform.1.6_32=linux
- com.ibm.websphere.sdk.architecture.1.6_32=x86_32
-com.ibm.websphere.sdk.nativeLibPath.1.6_32=${WAS_INSTALL_ROOT}/lib/native/linux/x86_32/
CWSDK1001I: Successfully performed the requested managesdk task.
The limitation also comes from the fact that for a 32 bit VM, the heap itself has to start at address zero if you want all those 4GB.
Think about it, if you want to reference something via:
0000....0001
i.e.: a reference that has this particular bits representation, it means you are trying to access the very first memory from the heap. For that to be possible, the heap has to start at address zero. But that never happens, it starts at some offset from zero:
| .... .... {heap_start .... heap_end} ... |
--> (this can't be referenced) <--
Because heap never starts from address zero in an OS, there are quite a few bits that are never used from a 32 bits reference, and as such the heap that can be referenced is lower.
We have performed a server migration from Solaris SunOS 5.10 to Redhat Linux VM recently. JVM was upgraded from 1.5.0_22 (32-bit) to 1.6.0_06 (64-bit)
However, since then, we encounter OutOfMemory error frequently. We have studied that a 64-bit JVM would require 30 - 50% more heap, so we increased our heap
size from 1200MB to 2048MB and have a try. However, we still observed some OOME occured after server run for a few dates.
Upon checking the GC log, we found that Full GC happened frequently after server has started for few dates, and for each Full GC,
it will only release a little memory and frequent Full GC slows down the application.
As you can see the excerpt of the GC log, almost no memory was released in PSOldGen
205023.895: [Full GC [PSYoungGen: 225919K->157256K(240960K)] [PSOldGen: 1841151K->1841151K(1841152K)] 2067071K->1998408K(2082112K) [PSPermGen: 108720K->108720K(109056K)], 6.2785770 secs] [Times: user=6.23 sys=0.01, real=6.28 secs]
Heap after GC invocations=1638 (full 251):
PSYoungGen total 240960K, used 157256K [0x00002aab2e800000, 0x00002aab3e200000, 0x00002aab3e200000)
eden space 225920K, 69% used [0x00002aab2e800000,0x00002aab38192208,0x00002aab3c4a0000)
from space 15040K, 0% used [0x00002aab3d350000,0x00002aab3d350000,0x00002aab3e200000)
to space 15040K, 0% used [0x00002aab3c4a0000,0x00002aab3c4a0000,0x00002aab3d350000)
PSOldGen total 1841152K, used 1841151K [0x00002aaabe200000, 0x00002aab2e800000, 0x00002aab2e800000)
object space 1841152K, 99% used [0x00002aaabe200000,0x00002aab2e7fffc8,0x00002aab2e800000)
PSPermGen total 109056K, used 108720K [0x00002aaaae200000, 0x00002aaab4c80000, 0x00002aaabe200000)
object space 109056K, 99% used [0x00002aaaae200000,0x00002aaab4c2c3f8,0x00002aaab4c80000)
}
Here is the heap usage pattern for a single OC4J instance within 24 hours, which is quite strange to me, it doesn't show a zig-zag path but instead, some random pattern.
May I know what can I do?
Server config:
Red Hat Enterprise Linux Server release 5.7 (Tikanga) 2.6.18 274.el5 (64-bit)
CPU : 8, 16GB RAM
JVM version : Java(TM) SE Runtime Environment (build 1.6.0_06-b02)
Application server : OC4J 10.1.3.5
JVM starup arguments:
//Old confing
-server -Xms1200M -Xmx1200M -XX:MaxPermSize=64M -XX:+PrintHeapAtGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xnoclassgc -verbose:gc -XX:NewSize=250M -XX:MaxNewSize=250M -XX:SurvivorRatio=15 -Xconcurrentio -Xss128k
//New config
-server -Xms2048M -Xmx2048M -XX:MaxPermSize=256M -XX:+PrintHeapAtGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xnoclassgc -verbose:gc -XX:NewSize=250M -XX:MaxNewSize=250M -XX:SurvivorRatio=15 -Xconcurrentio -Xss128k
Either this is a memory leak in the application or a bug in the Java memory management.
Since the 1.6.0_06 release there is a whole bunch of bug fixes regarding memory management and garbage collection for example these two:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6676016
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6824570
So try the following:
Upgrade to a newer Java 1.6.0 build. (If not possible you can change GC strategy settings from (for example) parallel to concurrent to see if that bypasses a potential Java bug.
Troubleshoot your application for a memory leak using a heapdump (jmap) to see what´s occupying the heap.