1. Java虚拟机内存结构

划分新生代和老年代,这样只在新生代分配内存,从而简化了新对象的分配。另外新生代和老年代使用不同的GC算法,可以更有效的清除不再需要的对象。
从上图可以看出,JVM内存由young+old+permanent组成,JVM又进一步将Young分成了eden,from survivor和to survivor三个区域。新对象会首先分配在 Eden 中(如果新对象过大,会直接分配在老年代中)。在GC中,Eden 中的对象会被移动到survivor中,直至对象熬过一定的GC的次数,会被移动到老年代。老年代一般是一些系统级(线程库,classloader等)的对象,官方推荐新生代占堆大小的3/8,而survivor区各占新生代的1/10。

2. GC原理


很多对象的生存时间都很短,而新生对象很少引用生存时间长的对象。所以,GC会频繁访问新生代对象,执行Minor GC。在新生代中,GC可以快速标记回收”死对象”,而不需要扫描整个Heap中的存活一段时间的”老对象”(即执行major/FULL GC)。
新生代的GC使用复制算法。在GC前To survivor区保持清空,对象保存在Eden和From survivor区中,GC运行时,Eden中的幸存对象被复制到 To survivor区。针对 From survivor取中的幸存对象,会考虑对象年龄,如果年龄没达到阀值(tenuring threshold),对象会被复制到To survivor区。如果达到阀值对象被复制到老年代。复制阶段完成后,Eden 和From survivor区中只保存死对象,可以被视为全部清空。如果在复制过程中To survivor区被填满了,剩余的对象会被复制到老年代中。最后 From和To会对换。

上图演示GC过程,黄色表示死对象,绿色表示剩余空间,红色表示幸存对象
如果新生代过小,会导致新生对象很快就晋升到老年代中,在老年代中对象很难被回收。如果新生代过大,会发生过多的复制过程。所以需要通过不断的测试调优,找到一个合适的JVM参数。

3. JVM内存参数

堆内存大小设置

-Xms :初始堆大小。只要启动,就占用的堆大小
-Xmx :最大堆大小。java.lang.OutOfMemoryError: Java heap这个错误可以通过配置-Xms和-Xmx参数来设置
-Xss:栈大小分配。栈是每个线程私有的区域,通常只有几百K大小,决定了函数调用的深度,而局部变量、参数都分配到栈上。当出现大量局部变量,递归时,会发生栈空间OOM(java.lang.StackOverflowError)之类的错误。
-XX:NewSize=n :设置新生代大小的绝对值
-XX:NewRatio=n: 设置年轻代和年老代的比值。比如设置为3,则新生代:老年代=1:3,新生代占1/4的总heap大小。
-XX:SurvivorRatio=n :年轻代中Eden区与两个Survivor区的比值。注意Survivor区有from和to两个。比如设置为8时,那么eden:from:to=8:1:1
-XX:MaxPermSize=n :设置持久代大小 ;java.lang.OutOfMemoryError: PermGen space这个OOM错误需要合理调大PermSize和MaxPermSize大小。
-XX:HeapDumpOnOutOfMemoryError :发生OOM时转储堆到文件,这是一个非常好的诊断方法。
-XX:HeapDumpPath :导出堆的转储文件路径
-XX:OnOutOfMemoryError:OOM时,执行一个脚本,比如发送邮件报警,重启程序。后面跟着一个脚本的路径。

测试1

第一次分配5M,没有超过xms。第二次再次分配5M,total mem会增加。第三次再申请40M,超过xmx限制所以报了OOM错误。

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class JVMTest {
    public static void main(String args[]) {
 
        //=====================Begin=========================
        System.out.print("Xmx=");
        System.out.println(Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M");
 
        System.out.print("free mem=");
        System.out.println(Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M");
 
        System.out.print("total mem=");
        System.out.println(Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M");
 
        //=====================First Allocated=========================
        System.out.println("5MB array allocated");
        byte[] b1 = new byte[5 * 1024 * 1024];
 
        System.out.print("Xmx=");
        System.out.println(Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M");
 
        System.out.print("free mem=");
        System.out.println(Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M");
 
        System.out.print("total mem=");
        System.out.println(Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M");
 
        //=====================Second Allocated=========================
        System.out.println("10MB array allocated");
        byte[] b2 = new byte[10 * 1024 * 1024];
 
        System.out.print("Xmx=");
        System.out.println(Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M");
 
        System.out.print("free mem=");
        System.out.println(Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M");
 
        System.out.print("total mem=");
        System.out.println(Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M");
 
        //=====================OOM=========================
        System.out.println("OOM!!!");
        System.gc();
        byte[] b3 = new byte[40 * 1024 * 1024];
    }
}

以50m XMX和10m XMS的运行测试:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
D:\>java -Xmx50m -Xms10m JVMTest
 
Xmx=44.5M
free mem=9.804550170898438M
total mem=10.5M
5MB array allocated
Xmx=44.5M
free mem=4.804534912109375M
total mem=10.5M
10MB array allocated
Xmx=44.5M
free mem=5.3045196533203125M
total mem=21.0M
OOM!!!
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at JVMTest.main(JVMTest.java:43)

测试2

 
1
2
3
4
5
6
7
public class JVMXmn1 {
    public static void main(String args[]) {
        byte[] b=null;
        for(int i=0;i<10;i++)
            b=new byte[1*1024*1024];
    }
}

下面按1m的新生代设置,这时对象大于新生代大小,会直接创建在老年代。新生代没有使用。没有触发gc

 
1
2
3
4
5
6
7
8
9
10
D:\>java -Xmx20m -Xms20m -Xmn1m -XX:+PrintGCDetails JVMXmn1
Heap
PSYoungGen      total 512K, used 0K [0x00000000fff00000, 0x0000000100000000, 0x0000000100000000)
  eden space 0K, -2147483648% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff00000)
  from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
  to   space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
ParOldGen       total 19456K, used 10836K [0x00000000fec00000, 0x00000000fff00000, 0x00000000fff00000)
  object space 19456K, 55% used [0x00000000fec00000,0x00000000ff695348,0x00000000fff00000)
PSPermGen       total 21504K, used 2442K [0x00000000f9a00000, 0x00000000faf00000, 0x00000000fec00000)
  object space 21504K, 11% used [0x00000000f9a00000,0x00000000f9c628d8,0x00000000faf00000)

下面按15m的新生代设置,全部分配在eden区,老年代没有使用,没有触发gc。

 
1
2
3
4
5
6
7
8
9
10
D:\>java -Xmx20m -Xms20m -Xmn15m -XX:+PrintGCDetails JVMXmn1
Heap
PSYoungGen      total 13824K, used 11525K [0x00000000ff100000, 0x0000000100000000, 0x0000000100000000)
  eden space 12288K, 93% used [0x00000000ff100000,0x00000000ffc417c8,0x00000000ffd00000)
  from space 1536K, 0% used [0x00000000ffe80000,0x00000000ffe80000,0x0000000100000000)
  to   space 1536K, 0% used [0x00000000ffd00000,0x00000000ffd00000,0x00000000ffe80000)
ParOldGen       total 5120K, used 0K [0x00000000fea00000, 0x00000000fef00000, 0x00000000ff100000)
  object space 5120K, 0% used [0x00000000fea00000,0x00000000fea00000,0x00000000fef00000)
PSPermGen       total 21504K, used 2442K [0x00000000f9800000, 0x00000000fad00000, 0x00000000fea00000)
  object space 21504K, 11% used [0x00000000f9800000,0x00000000f9a628d8,0x00000000fad00000)

下面按8m的新生代设置,触发了一次gc,由于from和to的大小小于1M的对象大小,eden区会直接进入老年代。

 
1
2
3
4
5
6
7
8
9
10
11
D:\>java -Xmx20m -Xms20m -Xmn8m -XX:+PrintGCDetails JVMXmn1
[GC [PSYoungGen: 5954K->536K(7168K)] 5954K->1560K(19456K), 0.0021655 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
PSYoungGen      total 7168K, used 5971K [0x00000000ff800000, 0x0000000100000000, 0x0000000100000000)
  eden space 6144K, 88% used [0x00000000ff800000,0x00000000ffd4ecb8,0x00000000ffe00000)
  from space 1024K, 52% used [0x00000000ffe00000,0x00000000ffe86010,0x00000000fff00000)
  to   space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
ParOldGen       total 12288K, used 1024K [0x00000000fec00000, 0x00000000ff800000, 0x00000000ff800000)
  object space 12288K, 8% used [0x00000000fec00000,0x00000000fed00010,0x00000000ff800000)
PSPermGen       total 21504K, used 2445K [0x00000000f9a00000, 0x00000000faf00000, 0x00000000fec00000)
  object space 21504K, 11% used [0x00000000f9a00000,0x00000000f9c63400,0x00000000faf00000)

下面按7m的新生代设置,from和to的大小可以为1/xmn,大于触发了3次新生代gc,一共回收了7M左右的空间,最后剩余3M在系统当中,没有使用老年代。

 
1
2
3
4
5
6
7
8
9
10
11
12
13
D:\>java -Xmx20m -Xms20m -Xmn7m -XX:SurvivorRatio=2 -XX:+PrintGCDetails JVMXmn1
[GC [PSYoungGen: 3785K->1512K(5632K)] 3785K->1568K(18944K), 0.0024118 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC [PSYoungGen: 4755K->1528K(5632K)] 4811K->1584K(18944K), 0.0013799 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC [PSYoungGen: 4631K->1496K(5632K)] 4687K->1552K(18944K), 0.0010990 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
PSYoungGen      total 5632K, used 2561K [0x00000000ff900000, 0x0000000100000000, 0x0000000100000000)
  eden space 4096K, 26% used [0x00000000ff900000,0x00000000ffa0a448,0x00000000ffd00000)
  from space 1536K, 97% used [0x00000000ffd00000,0x00000000ffe76020,0x00000000ffe80000)
  to   space 1536K, 0% used [0x00000000ffe80000,0x00000000ffe80000,0x0000000100000000)
ParOldGen       total 13312K, used 56K [0x00000000fec00000, 0x00000000ff900000, 0x00000000ff900000)
  object space 13312K, 0% used [0x00000000fec00000,0x00000000fec0e000,0x00000000ff900000)
PSPermGen       total 21504K, used 2445K [0x00000000f9a00000, 0x00000000faf00000, 0x00000000fec00000)
  object space 21504K, 11% used [0x00000000f9a00000,0x00000000f9c63400,0x00000000faf00000)

新生代占一半大小(10m),幸存区为3:1:1(6m:2m,2m),触发了1次gc,回收了7m左右空间,没有使用老年代。对于这种临时对象,减少老年代的使用是gc优化的关键。

 
1
2
3
4
5
6
7
8
9
10
11
D:\>java -Xmx20m -Xms20m -XX:NewRatio=1 -XX:SurvivorRatio=3 -XX:+PrintGCDetails JVMXmn1
[GC [PSYoungGen: 5954K->1624K(8192K)] 5954K->1624K(18432K), 0.0023152 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
PSYoungGen      total 8192K, used 7059K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 6144K, 88% used [0x00000000ff600000,0x00000000ffb4ecb8,0x00000000ffc00000)
  from space 2048K, 79% used [0x00000000ffc00000,0x00000000ffd96020,0x00000000ffe00000)
  to   space 2048K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x0000000100000000)
ParOldGen       total 10240K, used 0K [0x00000000fec00000, 0x00000000ff600000,0x00000000ff600000)
  object space 10240K, 0% used [0x00000000fec00000,0x00000000fec00000,0x00000000ff600000)
PSPermGen       total 21504K, used 2445K [0x00000000f9a00000, 0x00000000faf00000, 0x00000000fec00000)
  object space 21504K, 11% used [0x00000000f9a00000,0x00000000f9c63400,0x00000000faf00000)

^^

JVM堆内存设置和测试的更多相关文章

  1. 【转】JVM 堆内存设置原理

    堆内存设置 原理 JVM堆内存分为2块:Permanent Space 和 Heap Space. Permanent 即 持久代(Permanent Generation),主要存放的是Java类定 ...

  2. JVM堆内存设置

    今天碰到了一个题目,讲的是关于堆内存的问题,题目如下   下面哪种情况会导致持久区jvm堆内存溢出? A.循环上万次的字符串处理 B.在一段代码内申请上百M甚至上G的内存 C.使用CGLib技术直接操 ...

  3. [转]JVM 堆内存设置原理

    堆内存设置 原理 JVM堆内存分为2块:Permanent Space 和 Heap Space. Permanent 即 持久代(Permanent Generation),主要存放的是Java类定 ...

  4. JVM 堆内存设置原理

    堆内存设置 原理 JVM堆内存分为2块:Permanent Space 和 Heap Space. Permanent 即 持久代(Permanent Generation),主要存放的是Java类定 ...

  5. JVM 堆内存设置原理(转)

    堆内存设置 原理 JVM堆内存分为2块:Permanent Space 和 Heap Space. Permanent 即 持久代(Permanent Generation),主要存放的是Java类定 ...

  6. 【转】jvm 堆内存 栈内存 大小设置

    原文地址:http://blog.csdn.net/qh_java/article/details/46608395 4种方式配置不同作用域的jvm的堆栈内存! 1.Eclise 中设置jvm内存: ...

  7. jvm 堆内存 栈内存 大小设置

                        4种方式配置不同作用域的jvm的堆栈内存. 1.Eclise 中设置jvm内存: 改动eclipse的配置文件,对全部project都起作用 改动eclipse ...

  8. JVM堆内存监测的一种方式,性能调优依旧任重道远

    上月,由极客邦.InfoQ和听云联合主办2016 APMCon中国应用性能管理大会圆满落下帷幕.会上,Java冠军Martijn Verburg进行了一场Java and the Machine的分享 ...

  9. 巩固java(二)----JVM堆内存结构及垃圾回收机制

    前言:        我们在运行程序时,有时会碰到内存溢出(OutOfMemoryError)的问题,为了解决这种问题,我们有必要了解JVM的内存结构和垃圾回收机制. 正文: 1.JVM堆内存结构   ...

随机推荐

  1. Maven Test

    Failures表示要测试的结果与预期值不一致:Errors表示测试代码或产品代码发生了未预期的错误:Skipped表示那些被标记为忽略的测试方法.在Junit中用户可以使用@Ignore注解标记忽略 ...

  2. layabox 引擎

    layabox H5引擎性能之王LayaAir http://ldc.layabox.com/

  3. PHP与Golang如何通信?

    PHP与Golang如何通信? 最近遇到的一个场景:php项目中需要使用一个第三方的功能(结巴分词),而github上面恰好有一个用Golang写好的类库.那么问题就来了,要如何实现不同语言之间的通信 ...

  4. 第七周——Linux内核如何装载和启动一个可执行程序

    万子惠 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 part1 实验 ...

  5. windows下开启mysql表名大小限制

    找到my.ini文件 在文件中找到 [mysqld]的下面, 看有没有 [mysqld] lower_case_table_names 的设置, 如果没有 添加lower_case_table_nam ...

  6. CoInitialize浅析一

    大家都知道程序中若要使用COM组件则必须要先调用CoInitialize,该函数主要是用来初始化COM运行环境.但这个函数的作用域是以线程为单位还是以进程为单位呢?也许大家已经通过测试程序摸索出答案, ...

  7. 判断IP地址的类型

    #include <stdio.h> #include <stdlib.h> void main() { ]; int ip_addr; printf("请输入IP地 ...

  8. tcp之快速重传与恢复

    本文为原创,转载请注明:http://www.cnblogs.com/gistao/ Background 写网络程序的都知道,tcp的窗口控制分为慢启动阶段和拥塞避免阶段,重传机制有快速重传/恢复和 ...

  9. oracle 锁表查询与解锁

    查询锁住的表 SELECT s.sid, s.serial#, s.username, s.schemaname, s.osuser, s.process, s.machine,s.terminal, ...

  10. Flex box弹性布局 及 响应式前端设计的优化

    Flex box弹性布局 Flex box是CSS3新添加的一种模型属性,它的出现有力的打破了我们常常使用的浮动布局.实现垂直等高.水平均分.按比例划分,可以实现许多我们之前做不到的自适应布局.如果你 ...