新生代,老年代和永久代
java中堆是jvm所管理的最大的一块内存空间,主要用于存放各种类的实例对象。
jvm中内存的分配有如下公式:
- 堆 = 年轻代 + 老年代
- 年轻代 = eden space(新生代) + from survivor + to survivor
年轻代默认值保持为堆大小的1/15,特点是对象更新速度快,在短时间内产生大量的死亡对象,并且要产生连续可用的空间。
所以使用复制清楚算法和并行收集器进行垃圾回收,对年轻代的垃圾回收称作初级回收(minor gc)。
年轻代的工作机制
jvm,年轻代中每次只会使用eden space和其中一块survivor区域来为程序服务,所以无论如何总有一块survivor区域总是空闲的。
对象在 eden 中初始化,在经过一次minor gc后,如果对象还存活着,即被引用着,并且能够被另外一块survivor区域所容纳,则使用复制算法将这些仍然还存活的对象复制到另外一块survivor区域中。
年轻代如何变成老年代
初始化过程与上面一致,在eden中。在minor gc之后,如果对象还存活着,这些对象的年龄+1,当超过某个值(默认为15)这些对象进入老年代。
老年代的gc
堆内存的老年代不同于现实生活,老年代中的对象个个都是从survivor中熬过来的,所以老年代中的类不是那么容易死亡的。因此,full gc(又称major gc)发生的次数没有minor gc那么频繁,并且一次full gc要比minor gc时间要更长。
标记-清除算法收集垃圾的时候会产生许多的内存碎片 ( 即不连续的内存空间 ),此后需要为较大的对象分配内存空间时,若无法找到足够的连续的内存空间,就会提前触发一次 GC 的收集动作。
持久代
此外还有一个持久代,用于存放静态文件,如java类定义(相当于模版,不是实例对象)、方法、本地方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如hibernate等,在这种时候需要设置较大的持久代空间来存放这些运行过程中新增的类 。持久代大小通过-XX:MaxPermSize=
补充
有的虚拟机并没有持久代,java8开始持久层也已经被彻底删除了,取代它的是另一个内存区域也被称为元空间。
想要参考更多,读者可以参考这篇博客
jvm配置项
jvm配置项可以根据程序的实际要求来配置,如各个代的比例等。
这边原本应该用表格的形式展示,但是hexo在渲染markdown的表格语法时不支持,待到作者修复这个bug后进行更改。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* -Xms 初始堆大小。如:-Xms256m
* -Xmx 最大堆大小。如:-Xmx512m
* -Xmn 新生代大小。通常为 Xmx 的 1/3 或 1/4。新生代 = Eden +
2 个 Survivor 空间。实际可用空间为 = Eden + 1 个
Survivor,即 90%
* -Xss JDK1.5+ 每个线程堆栈大小为 1M,一般来说如果栈不是很深的
话, 1M 是绝对够用了的。
* -XX:NewRatio 新生代与老年代的比例,如 –XX:NewRatio=2,
则新生代占整个堆空间的1/3,老年代占2/3
* -XX:SurvivorRatio 新生代中 Eden 与 Survivor 的比值。默认值为 8。
即 Eden 占新生代空间的 8/10,另外两个 Survivor 各占
1/10
* -XX:PermSize 永久代(方法区)的初始大小
* -XX:MaxPermSize 永久代(方法区)的最大值
* -XX:+PrintGCDetails 打印 GC 信息
* -XX:+HeapDumpOnOutOfMemoryError 让虚拟机在发生内存溢出时 Dump 出当前的内
存堆转储快照,以便分析用