Java内存模型
总的来说就分为两个区域,堆内存(Heap)和非堆内存(No-Heap),非堆内存又称为永久代(Permanent),“永久”其实有点儿容易使人误导,好像这部分内容不需要回收。但在永久区中的某些数据也是需要回收的!
在新的JDK8中,这部分的名称已经不叫Permanent了,改成更好理解的Metaspace了。这部分是用来存储JVM工作的相关数据的,比如Load下来的class定义、静态变量、用于调度的方法和线程栈。
分为两个区其实是为了更便于理解,No-Heap区只是非Heap的意思,其中的Stack、Method Area其实是和Heap同级的。
在GC日志中,你会发现PSPermGen也在其中:
[GC-- [PSYoungGen: 569856K->569856K(617472K)] 773089K->928306K(976896K), 0.3285123 secs] [Times: user=0.66 sys=0.08, real=0.33 secs]
[Full GC [PSYoungGen: 569856K->0K(617472K)] [ParOldGen: 358450K->359349K(499712K)] 928306K->359349K(1117184K) [PSPermGen: 64652K->64650K(131072K)], 1.4693823 secs] [Times: user=4.09 sys=0.02, real=1.47 secs]
[Full GC [PSYoungGen: 569856K->0K(617472K)] [ParOldGen: 359349K->488638K(672768K)] 929205K->488638K(1290240K) [PSPermGen: 65041K->65041K(131072K)], 2.4092196 secs] [Times: user=6.75 sys=0.11, real=2.41 secs]
意思是No-Heap(永久代)也需要垃圾回收!
因为在永久代中还存储着ClassLoader、Class的元信息(Metadata)、指向Heap区域对象的指针以及字符串池(Internal String)。这些数据其实也需要垃圾回收。
这样看来,把内存区域划分为年轻代(Young Gen)、老年代(Old Gen)和永久代(Permanent Gen)其实是有道理的,虽然老年代的数据不会提升到永久代中。但是这三个区域的数据都是需要垃圾回收的。
Heap(堆内存)
堆内存是垃圾回收器工作的地方,堆内存又分为Eden和两个大小一样的Survivor区,即From和To。
最开始的对象都会存储在Eden中(如果有些大对象无法存入到年轻代,则会直接存入老年代),然后经过Minor GC之后会被提升到Survivor区,然后再提升到老年代。
Stack(栈内存)
Thread Stack 用来存放栈信息,每个线程栈信息里各自有自己的方法栈(包括本地方法栈),在方法栈的每一帧里存储着方法调用的相关信息,比如参数值、局部变量、返回值等。
Program Counter:记录着当前语句执行到哪儿了。
下图中,其实Stack可以归并到No-Heap中。
垃圾回关注的指标
吞吐量
定义:用户代码执行时间 / ( 用户代码执行时间 + 垃圾回收时间)
越高越好,越高表示执行垃圾回收时间越少。
暂停时间
回收时可能需要暂停用户线程,暂停时间越短越好。
执行频率
单位时间垃圾回收执行的次数。
堆内存大小
比如G1回收器就要去比较大的Heap内存。
敏感度(Promptness)
对象变成垃圾到被回收的时间,时间越短表示回收器越敏感。
垃圾回收类型
串行搜集器(Serial)
年轻代(Young Gen)回收
老年代(Old Gen)回收
来年代的回收很简单,步骤是“标记-清除-压缩”:
串行回收器的使用场景
一般引用于不要求“低暂停”的client模式。这里参考server和client的区别。j2se5的client模式下默认使用串行回收器进行垃圾回收。
使用串行回收器的参数:-XX:+UseSerialGC
并行回收器(Paraller)
并行回收器可以利用多个CPU进行并行的垃圾回收。(在多个CPU场景下,使用Serial回收器时,其实只有一个CPU在工作,其他CPU相当于闲置状态)
并行回收器在年轻地啊和老年代进行垃圾回收的操作是一样的,都是标记、转移、压缩,只是它启用了多个CPU并发执行。串行和并行都需要stop-the-world。
并行回收器的使用场景
应用于多CPU场景下,但是没有太大的暂停时间要求,因为还是有可能会发生长时间的老年代垃圾回收。
适用于批处理、账单、财务、科学计算等场景。
j2se5的server模式下默认使用该回收器。
使用并行回收器的参数:-XX:+UseParallelGC
ParNew
这是一个加强版的Parallel回收器。它可以与下面提到的CMS回收器进行配合。
并行压缩回收器(Parallel Compacting)
年轻代使用并行回收器一样的算法(多CPU并行回收)。(stop-the-world)
-XX:+UseParallelOldGC.
对于老年代,回收过程分为三个阶段
并行标记(Marking)
标记出每个区域的活动数据。标记动作其实是和用户线程一起跑的。
计算总结(Summary)
计算各个区域的稠密程度,得出移动数据的方案。(稀疏的往稠密位置移动,压缩速度肯定比相反方向要快)
压缩(Compaction)
把数据移动到一端,保证另外一端空白。
并行压缩回收器的使用场景
多CPU,它相对于并行回收器感觉没有什么区别(谁知道吗???),因为在官方文档中是这样说的:
使用该回收器的参数:-XX:-UseParallelOldGC
并发标记清理回收器(Concurrent Mark-Sweep (CMS))
低延迟的回收器。
一般而言,年轻代的回收不会有太大的暂停。而老年代的回收不会经常执行,所以老年代的回收暂停时间长一点儿也没事。CMS的回收步骤如下:
年轻代还是使用并发回收器(Parallel)。
对于老年代:
初始标记(init mark):单线程stop-the-world执行,短暂停,确定出直接和程序关联的活动对象集合。
并发标记(concurrent marking):根据上一步标记出来的集合,并发找出与集合中元素关联的其他活动对象。这一步的执行是和用户线程并发执行的。
重新标记(remark):由于用户线程也在运行,所以上一步标记出来的对象还有可能又参数了垃圾,所以这里再次stop-the-world,重新找出活动数据。这一步对现场并发执行。很容易理解,这里的stop-the-world也是非常短的。
清理(sweep):根据上一步标记结果,清理内存。
从CMS的执行步骤可以看出,它的核心思想其实就是把事情分成多个步骤来做,不要一次把事情做完,且尽量和用户线程一起执行,从而实现对用户线程的低延迟。
CMS回收器使用场景
任何需要低延迟的应用。甚至在单CPU上都运行良好。
使用CMS回收器参数:-XX:+UseConcMarkSweepGC
G1(Gargage First)回收器
G1回收器是用于server模式下,多处理器、大内存的环境。其目标是高吞吐量、高可用性。在JDK7 update4以后的版本中都支持。长期计划中G1是用来代替CMS回收器的。
G1不像其他回收器那样,把内存明确地划分为三个固定大小的区域。而是把heap看成一个整体。
更多细节详见参考连接。
垃圾收集器参数总结
收集器设置:
-XX:+UseSerialGC:年轻串行(Serial),老年串行(Serial Old)
-XX:+UseParNewGC:年轻并行(ParNew),老年串行(Serial Old)
-XX:+UseConcMarkSweepGC:年轻并行(ParNew),老年串行(CMS),备份(Serial Old)
-XX:+UseParallelGC:年轻并行吞吐(Parallel Scavenge),老年串行(Serial Old)
-XX:+UseParalledlOldGC:年轻并行吞吐(Parallel Scavenge),老年并行吞吐(Parallel Old)
收集器参数:
-XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。
-XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间
-XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)
-XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。
-XX:ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。
个人学习总结,难免有误,欢迎指正讨论!
参考:
Java堆内存:http://www.blogjava.net/fancydeepin/archive/2013/09/29/jvm_heep.html
Memory Management in the Java HotSpot™ Virtual Machine:http://www.oracle.com/technetwork/java/javase/tech/memorymanagement-whitepaper-1-150020.pdf
JVM内部结构:http://blog.jamesdbloom.com/JVMInternals.html
JVM参数:http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html,http://www.cnblogs.com/redcreen/archive/2011/05/04/2037057.html
官网GC日志介绍:http://www.oracle.com/technetwork/java/javase/gc-tuning-6-140523.html
G1回收器:http://www.oracle.com/technetwork/tutorials/tutorials-1876574.html