简介
JVM里面包含年轻代、老年代、永久代等,这里对年轻和老年进行分析。年轻代里面又包含 Eden区、2个survivor(S0、S1)。
常见参数
-Xms:1G , 就是说初始堆大小为1G
-Xmx:2G , 就是说最大堆大小为2G
-Xmn:500M ,就是说年轻代大小是500M(包括一个Eden和两个Survivor)
-XX:PermSize:方法区初始化内存分配大小。
-XX:MaxPermSize:64M 方法区分配的内存的最大上限。
-XX:+UseConcMarkSweepGC , 就是说使用使用CMS内存收集算法
-XX:SurvivorRatio=3 , 就是说Eden区与Survivor区的大小比值为3:1,新生代有2个survivor则新生代里面分配比例为3:1:1
计算此时的Eden区大小:500(3/(3+1+1))=300M*
-XX:NewRatio(年轻代和老年代的比值,年轻代多,除去持久代)
当设置了-XX:+UseConcMarkSweepGC后,会使-XX:NewRatio=4失效,此时需要使用-Xmn设置年轻代大小。
GC过程:
Eden 空间占满了, 会触发 minor GC。
eden中仍然存活的对象被复制到s0去。
触发 minor GC。
eden和s0(from)中的存活对象复制到 s1(to)中
触发 minor GC。
eden和s1(from)中的存活对象复制到s0(to)中
to 和from 在不断变更中,为什么需要有2个Survivor,是因为在第一次复制后虽然没有碎片,但是在第二次复制后就会产生碎片了,所以加一个过渡。
survivor中的对象,会被计数,默认15次,达到则会复制到老年代。
老年代满了则full gc,会stop world
什么样的对象会直接去老年代?
- 大对象直接分配到老年代是比较优的思路,通过【-XX:PretenureSizeThreshold】参数调大大对象的阈值,让朝生夕死的大对象变成相对上的小对象,不再直接被分配到老年代。
- 在Survivor中存不下,那么把存活的对象存入老年代
- 长期存活的对象将进入老年代
- 如果在From空间中,相同年龄所有对象的大小总和大于Survivor空间的一半,那么年龄大于等于该年龄的对象就会被移动到老年代。
有如下原因可能导致Full GC:
- 年老代(Tenured)被写满;
- 持久代(Perm)被写满;
- System.gc()被显示调用;
- 上一次GC之后Heap的各域分配策略动态变化;
参数调节与并发的关系:
若-Xmx:1024 提高堆内存(Heap)大小
该Java进程剩下的内存由, 方法区,程序计数器,虚拟机栈,本地方法栈 共同使用。
虚拟机栈使用的空间 = 进程可使用内容- Xmx(最大堆容量)-MaxPermSize (最大方法区容量) - 本地方法栈
一个线程对应一个虚拟机栈,那么Heap 越大,程序申请的内存空间越少,就是说线程数量越少。
如何提高并发?
- 对于高并发,创建对象不多的项目,可以降低Xmx的配置
- 对于低并发,创建对象多的项目,(数据处理型的) 可以适当提高,Xmx(因为对象和数组是存放到Heap内的,栈帧中其实只存了对象地址,所以不存在爆的情况)
文章内容由个人心得与部分摘录所得。