堆空间是JAVA程序的重压组成部分,几乎所有应用相关的内存空间都和堆有关。

-Xms和-Xmx

一般来说虚拟机会尽可能维持在初堆空间范围内(-Xms指定)运行,但是如果初始堆空间耗尽,虚拟机将会对堆空间进行扩展,其扩展上限为最大空间用-Xmx指定

初始堆、最大堆和系统可用内存

代码测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class HeapAlloc {
public static void main(String[] args) {
System.out.println("MaxMemory="+Runtime.getRuntime().maxMemory()+"bytes");
System.out.println("Free memory="+Runtime.getRuntime().freeMemory()+"bytes");
System.out.println("Total memory="+Runtime.getRuntime().totalMemory()+"bytes");

System.out.println("我是分割线----------------------------------------");

byte [] b = new byte[1*1024*1024]; //b数组分配了1M的空间
System.out.println("MaxMemory="+Runtime.getRuntime().maxMemory()+"bytes");
System.out.println("Free memory="+Runtime.getRuntime().freeMemory()+"bytes");
System.out.println("Total memory="+Runtime.getRuntime().totalMemory()+"bytes");
}
}

输出

1
2
3
4
5
6
7
MaxMemory=1884815360bytes
Free memory=126929920bytes
Total memory=128974848bytes
我是分割线----------------------------------------
MaxMemory=1884815360bytes
Free memory=125881328bytes
Total memory=128974848bytes

可以看出第二次空余内存减少了1M

通过设置-Xmx20m -Xms5m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseSerialGC参数再次运行

可得输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
-XX:InitialHeapSize=5242880 -XX:MaxHeapSize=20971520 -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseSerialGC 
MaxMemory=20316160bytes
Free memory=5319728bytes
Total memory=6094848bytes
我是分割线----------------------------------------
[GC (Allocation Failure) [DefNew: 756K->192K(1856K), 0.0032907 secs] 756K->516K(5952K), 0.0034158 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
MaxMemory=20316160bytes
Free memory=4448984bytes
Total memory=6094848bytes
Heap
def new generation total 1856K, used 1333K [0x00000000fec00000, 0x00000000fee00000, 0x00000000ff2a0000)
eden space 1664K, 68% used [0x00000000fec00000, 0x00000000fed1d438, 0x00000000feda0000)
from space 192K, 100% used [0x00000000fedd0000, 0x00000000fee00000, 0x00000000fee00000)
to space 192K, 0% used [0x00000000feda0000, 0x00000000feda0000, 0x00000000fedd0000)
tenured generation total 4096K, used 324K [0x00000000ff2a0000, 0x00000000ff6a0000, 0x0000000100000000)
the space 4096K, 7% used [0x00000000ff2a0000, 0x00000000ff2f1220, 0x00000000ff2f1400, 0x00000000ff6a0000)
Metaspace used 2482K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 273K, capacity 386K, committed 512K, reserved 1048576K

初始堆空间 51024*1024=5242880

最大堆空间2010241024 = 20971520

打印最大可用内存为20316160(与垃圾回收有关,与计算机对from/to空间估算大小有关)

实际项目中可以将-Xms与最大堆-Xmx设置相等,可以减少垃圾回收次数,提高系统性能

新生代配置

-Xmn可以用于设置新生代大小,设置一个较大的新生代会减少老年代的大小,这个参数对系统性能以及GC行为有很大的影响。新生代的大小一般设置为整个堆空间的1/3到1/4

-XX:SurvivorRatio用来设置eden空间和from/to空间的比例

测试程序:

1
2
3
4
5
6
7
8
public class NewSizeDemo {
public static void main(String[] args) {
byte[] b = null;
for (int i = 0; i < 10; i++) {
b = new byte[1*1024*1024]; //连续向系统请求10m
}
}
}
  • 第一次使用参数-Xmx20m -Xms20m -Xmn1m -XX:SurvivorRatio=2 -XX:+PrintGCDetails

输出:

1
2
3
4
5
6
7
8
9
10
11
[GC (Allocation Failure) [PSYoungGen: 510K->496K(1024K)] 510K->512K(19968K), 0.0433597 secs] [Times: user=0.00 sys=0.00, real=0.04 secs] 
Heap
PSYoungGen total 1024K, used 708K [0x00000000ffe80000, 0x0000000100000000, 0x0000000100000000)
eden space 512K, 41% used [0x00000000ffe80000,0x00000000ffeb50a0,0x00000000fff00000)
from space 512K, 96% used [0x00000000fff00000,0x00000000fff7c020,0x00000000fff80000)
to space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
ParOldGen total 18944K, used 10256K [0x00000000fec00000, 0x00000000ffe80000, 0x00000000ffe80000)
object space 18944K, 54% used [0x00000000fec00000,0x00000000ff6040a0,0x00000000ffe80000)
Metaspace used 2476K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 273K, capacity 386K, committed 512K, reserved 1048576K
Java HotSpot(TM) 64-Bit Server VM warning: NewSize (1536k) is greater than the MaxNewSize (1024k). A new max generation size of 1536k will be used.

eden/from(to)=2,eden=512k,from=to=256k

看最后的警告信息。新生代内存过小,系统采用了新的1536k作为新生代,均分给eden,from,to

由于eden不过1m,所以触发了一次新生代GC,对eden区进行了部分回收,由于新生代无法为数组预留足够空间,故数组分配在了老年区。

  • -Xmx20m -Xms20m -Xmn7m -XX:SurvivorRatio=2 -XX:+PrintGCDetails

输出:

1
2
3
4
5
6
7
8
9
10
11
12
[GC (Allocation Failure) [PSYoungGen: 3883K->1512K(5632K)] 3883K->1584K(18944K), 0.0039618 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 4746K->1528K(5632K)] 4818K->1600K(18944K), 0.0039290 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 4670K->1528K(5632K)] 4742K->1600K(18944K), 0.0023263 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
PSYoungGen total 5632K, used 2634K [0x00000000ff900000, 0x0000000100000000, 0x0000000100000000)
eden space 4096K, 27% used [0x00000000ff900000,0x00000000ffa14930,0x00000000ffd00000)
from space 1536K, 99% used [0x00000000ffd00000,0x00000000ffe7e040,0x00000000ffe80000)
to space 1536K, 0% used [0x00000000ffe80000,0x00000000ffe80000,0x0000000100000000)
ParOldGen total 13312K, used 72K [0x00000000fec00000, 0x00000000ff900000, 0x00000000ff900000)
object space 13312K, 0% used [0x00000000fec00000,0x00000000fec12000,0x00000000ff900000)
Metaspace used 2476K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 273K, capacity 386K, committed 512K, reserved 1048576K

eden区有足够的空间,因此所有的数组都首先分配在eden区,但是eden区并不足以存放10m,幸运的是,内次为数组分配空间时,之前分配的空间也是去了引用,成为垃圾回收的对象,因此触发了三次垃圾回收。

最终结果是:所有的数组分配都在eden区进行,在GC过程中,部分失去引用的空间晋升到老年代

  • -Xmx20m -Xms20m -Xmn15m -XX:SurvivorRatio=8 -XX:+PrintGCDetails

输出:

1
2
3
4
5
6
7
8
9
Heap
PSYoungGen total 13824K, used 11526K [0x00000000ff100000, 0x0000000100000000, 0x0000000100000000)
eden space 12288K, 93% used [0x00000000ff100000,0x00000000ffc41828,0x00000000ffd00000)
from space 1536K, 0% used [0x00000000ffe80000,0x00000000ffe80000,0x0000000100000000)
to space 1536K, 0% used [0x00000000ffd00000,0x00000000ffd00000,0x00000000ffe80000)
ParOldGen total 5120K, used 0K [0x00000000fec00000, 0x00000000ff100000, 0x00000000ff100000)
object space 5120K, 0% used [0x00000000fec00000,0x00000000fec00000,0x00000000ff100000)
Metaspace used 2473K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 272K, capacity 386K, committed 512K, reserved 1048576K

eden区大小为15*8/10=12M完全满足了10M空间的需求,因此整个过程中没有触发垃圾回收机制,from/to和老年代的使用率均为0

-XX:NewSize指定新生代初始大小,-XX:MaxNewSize指定新生代最大空间 -Xmn为两者的额缩写形式

也可以使用-XX:NewRatio指定老年代/新生代比例

总结

堆空间分布会对系统执行产生一定影响,实际工程中,应根据系统的特点做合理的设置,尽可能将对象预留在新生代,减少老年代GC的此次数。