一、VM核心参数
在JVM里有几个比较核心的参数,今天就主要来讲这几个:
- -Xms:JVM初始堆内存大小
- -Xmx:JVM堆内存的最大值
- -Xmn:JVM中新生代大小
- -XX:PermSize:永久代大小
- -XX:MaxPermSize:永久代最大值
- -Xss:JVM每个线程的大小
下面我们就对上面列举的几个核心参数进行说明。
1、-Xms和-Xmx分别表示JVM初始堆内存大小和堆内存最大值
这两个参数主要作用就是限定JVM中堆内存的大小。
2、-Xmn:表示JVM堆内存中新生代的大小
一般设置完堆内存,然后用堆内存-新生代大小=老年代大小
3、-XX:PermSize和-XX:MaxPermSize,分别代表永久代的大小和永久代的最大值
如果在JDK8以后,永久代被元空间替代了,所以这两个参数变成了-XX:MetaspaceSize和-XX:MaxMetaspaceSize。这两个参数就是限定永久代的大小。
实际用元空间代替永久代可以避免OOM,因为元空间用的是本地内存,不同于永久代是有上限的。更深层的原因是要合并HotSpot和JRockit,然后JRockit没有永久代,所以就使用了元空间。
4、-Xss:这个参数限定了JVM每个参数的大小
每个线程都有自己一个虚拟机栈,每次执行一个方法,就会把方法的栈帧压入栈中,方法执行完,该栈帧就会从线程里的栈出栈
启动系统的时候配置JVM参数
在自己的编译器(以Idea举例)设置启动时JVM参数,可以加快Idea的编译速度
在启动的main类选择Edit Configurations,然后在VM options配置JVM参数即可
除了以上的方法外,还有第二种方法是打开idea.vmoptions修改配置信息,一般在idea的Help菜单下的Edit Custom VM Options
把编译器的堆内存改大就可以使编译速度提高(当然也不是绝对的,大部分情况下可以)
线上部署应用的时候如何指定JVM参数
这个其实就很简单,举例采用"java -jar"的方式启动应用,可以采用以下的方式:
java -Xms512M -Xmx512M -Xmn256M -Xss1M -XX:PermSize=128M -XX:MaxPermSize=128M -jar test.jar
其他方式部署的也是可以配置启动参数,感兴趣可以自行了解。
二、新生代,老生代等
HotSpot JVM堆
从GC的角度可以将HotSpot JVM堆分为新生代、老年代。其中新生代默认占1/3堆空间,老年代默认占2/3堆空间。新生代又分为Eden 区、SurvivorFrom 区和SurvivorTo 区,Eden区默认占8/10新生代空间,SurvivorFrom区和SurvivorTo区默认分别占1/10新生代空间,如图所示。
1. 新生代:Eden区、SurvivorTo区和SurvivorFrom区
JVM新创建的对象(除大对象外)会被存放在新生代,默认占1/3堆内存空间。由于JVM会频繁创建对象,所以新生代会频繁触发MinorGC进行垃圾回收。新生代又分为Eden区、SurvivorTo区和SurvivorFrom区,如下所述。
(1)Eden区:Java新创建的对象首先会被存放在Eden区,如果新创建的对象属于大对象,则直接将其分配到老年代。大对象的定义与具体的 JVM版本、堆大小和垃圾回收策略有关,一般为2KB~128KB,可通过XX:PretenureSizeThreshold设置其大小。在Eden区的内存空间不足时会触发MinorGC,对新生代进行一次垃圾回收。
(2)SurvivorTo区:保留上次MinorGC时的幸存者。
(3)SurvivorFrom区:将上次MinorGC时的幸存者作为这次MinorGC的被扫描者。
新生代的GC过程叫作MinorGC,采用复制算法实现,具体过程如下。
(1)把在Eden区和SurvivorFrom区中存活的对象复制到SurvivorTo区。如果某对象的年龄达到老年代的标准(对象晋升老年代的标准由XX:MaxTenuringThreshold设置,默认为15),则将其复制到老年代,同时把这些对象的年龄加1;如果SurvivorTo区的内存空间不够,则也直接将其复制到老年代;如果对象属于大对象(大小为2KB~128KB的对象属于大对象,例如通过XX:PretenureSizeThreshold=2097152设置大对象为 2MB,1024×1024×2byte=2097152byte=2MB),则也直接将其复制到老年代。
(2)清空Eden区和SurvivorFrom区中的对象。
(3)将SurvivorTo区和SurvivorFrom区互换,原来的SurvivorTo区成为下一次GC时的SurvivorFrom区。
2. 老年代
老年代主要存放长生命周期的对象和大对象。老年代的GC过程叫作MajorGC。在老年代,对象比较稳定,MajorGC不会被频繁触发。在进行MajorGC前,JVM会进行一次MinorGC,在MinorGC后对象仍然出现在老年代且当老年代空间不足或无法找到足够大的连续内存空间分配给新创建的大对象时,会触发MajorGC进行垃圾回收,释放JVM的内存空间。
MajorGC采用标记清除算法,该算法首先会扫描所有对象并标记存活的对象,然后回收未被标记的对象,并释放内存空间。因为要先扫描老年代的所有对象再回收,所以MajorGC的耗时较长。MajorGC的标记清除算法容易产生内存碎片。在老年代没有内存空间可分配时,会抛出Out Of Memory异常。